Skip to content

Commit 6b19f59

Browse files
committed
allow sharing ownership via api
Signed-off-by: Robert Landers <landers.robert@gmail.com>
1 parent 7b7caaa commit 6b19f59

File tree

5 files changed

+69
-0
lines changed

5 files changed

+69
-0
lines changed

cli/auth/keys.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ func DecorateContextWithUser(ctx context.Context, user *User) context.Context {
3535
return context.WithValue(ctx, appcontext.CurrentUserKey, user)
3636
}
3737

38+
func GetUserFromContext(ctx context.Context) *User {
39+
user, _ := ctx.Value(appcontext.CurrentUserKey).(*User)
40+
return user
41+
}
42+
3843
// ExtractUser extracts user information from the Authorization token in the HTTP request header.
3944
// It returns the user and a boolean indicating if the extraction was successful.
4045
//

cli/lib/api.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,46 @@ func Startup(ctx context.Context, js jetstream.JetStream, logger *zap.Logger, po
315315
}
316316
}
317317

318+
// POST /resource/{id}/share: share ownership of the resource with another user
319+
r.HandleFunc("/resource/{id}/share/{userid}", func(writer http.ResponseWriter, request *http.Request) {
320+
if stop := handleCors(writer, request); stop {
321+
return
322+
}
323+
324+
if request.Method != "POST" {
325+
http.Error(writer, "Method Not Allowed", http.StatusMethodNotAllowed)
326+
return
327+
}
328+
329+
ctx := getCorrelationId(ctx, &request.Header, nil)
330+
logRequest(logger, request, ctx)
331+
332+
vars := mux.Vars(request)
333+
id := &glue.StateId{
334+
Id: strings.TrimSpace(vars["id"]),
335+
}
336+
337+
// verify the user is authorized to access the resource
338+
ctx, done := authorize(writer, request, config, ctx, rm, id, logger, true, auth.Owner)
339+
if done {
340+
return
341+
}
342+
343+
r, err := rm.DiscoverResource(ctx, id, logger, true)
344+
if err != nil {
345+
logger.Error("Failed to discover resource", zap.Error(err))
346+
http.Error(writer, "Not Found", http.StatusNotFound)
347+
}
348+
349+
newUser := strings.TrimSpace(vars["userid"])
350+
351+
err = r.ShareOwnership(auth.UserId(newUser), auth.GetUserFromContext(ctx), true)
352+
if err != nil {
353+
logger.Error("Failed to share ownership", zap.Error(err))
354+
http.Error(writer, "Internal Server Error", http.StatusInternalServerError)
355+
}
356+
})
357+
318358
// GET /entity/{name}/{id}
319359
// get an entity state and status
320360
// PUT /entity/{name}/{id}

src/DurableClient.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
/*
34
* Copyright ©2024 Robert Landers
45
*
@@ -141,4 +142,9 @@ public function deleteEntity(EntityId $entityId): void
141142
{
142143
$this->entityClient->deleteEntity($entityId);
143144
}
145+
146+
public function shareOwnership(EntityId|OrchestrationInstance $resource, string $with): void
147+
{
148+
$this->entityClient->shareOwnership($resource, $with);
149+
}
144150
}

src/EntityClientInterface.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
use Bottledcode\DurablePhp\Search\EntityFilter;
2828
use Bottledcode\DurablePhp\State\EntityId;
2929
use Bottledcode\DurablePhp\State\EntityState;
30+
use Bottledcode\DurablePhp\State\OrchestrationInstance;
3031
use Closure;
3132
use DateTimeImmutable;
3233
use Generator;
@@ -80,4 +81,6 @@ public function getEntitySnapshot(EntityId $entityId): ?EntityState;
8081
* Deletes an entity
8182
*/
8283
public function deleteEntity(EntityId $entityId): void;
84+
85+
public function shareOwnership(EntityId|OrchestrationInstance $resource, string $with): void;
8386
}

src/RemoteEntityClient.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
use Bottledcode\DurablePhp\Search\EntityFilter;
3232
use Bottledcode\DurablePhp\State\EntityId;
3333
use Bottledcode\DurablePhp\State\EntityState;
34+
use Bottledcode\DurablePhp\State\Ids\StateId;
35+
use Bottledcode\DurablePhp\State\OrchestrationInstance;
3436
use Bottledcode\DurablePhp\State\Serializer;
3537
use Closure;
3638
use DateTimeImmutable;
@@ -158,4 +160,17 @@ public function deleteEntity(EntityId $entityId): void
158160
throw new Exception('Failed to delete entity');
159161
}
160162
}
163+
164+
public function shareOwnership(EntityId|OrchestrationInstance $resource, string $with): void
165+
{
166+
$id = $resource instanceof EntityId ? StateId::fromEntityId($resource) : StateId::fromInstance($resource);
167+
$req = new Request("{$this->apiHost}/resource/{$id}/share/{$with}", 'POST');
168+
if ($this->userToken) {
169+
$req->setHeader('Authorization', 'Bearer ' . $this->userToken);
170+
}
171+
$result = $this->client->request($req);
172+
if ($result->getStatus() !== 200) {
173+
throw new Exception('Failed to share ownership');
174+
}
175+
}
161176
}

0 commit comments

Comments
 (0)