From 359dd2f2674bc310a527afd64cc5aa8175dc3be7 Mon Sep 17 00:00:00 2001 From: Dominik Schmidt Date: Mon, 7 Sep 2026 10:35:52 +0200 Subject: [PATCH] feat(graph): expose the sharer as the owner of a public link drive The public share mountpoint carries no space owner, so GET /drives/{pub} came back without one and the drop page had no name to show. The request runs as the share creator (publicshares auth), so the context user is who shared the link; fill drive.Owner from it, id and display name, the same source webdav fills oc:owner-display-name from. Deliberate disclosure to the anonymous visitor, matching webdav and MS Graph's sharedDriveItem.owner. --- services/graph/pkg/service/v0/drives.go | 13 +++++++++ tests/acceptance/bootstrap/GraphContext.php | 22 +++++++++++++++ .../publicLinkDriveItemListing.feature | 28 +++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index 954cd0791d..114f59d3e0 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -881,6 +881,19 @@ func (g Graph) cs3StorageSpaceToDrive(ctx context.Context, baseURL *url.URL, spa // DisplayName: , TODO read and cache from users provider }, } + } else if space.GetRoot().GetStorageId() == utils.PublicStorageProviderID { + // a public share mountpoint carries no space owner; the request runs as + // the share creator (publicshares auth), so the context user is who + // shared it, the same source webdav fills oc:owner-display-name from. + if u, ok := revactx.ContextGetUser(ctx); ok && u.GetId().GetOpaqueId() != "" { + id := u.GetId().GetOpaqueId() + drive.Owner = &libregraph.IdentitySet{ + User: &libregraph.Identity{ + Id: &id, + DisplayName: u.GetDisplayName(), + }, + } + } } if space.Mtime != nil { lastModified := cs3TimestampToTime(space.Mtime) diff --git a/tests/acceptance/bootstrap/GraphContext.php b/tests/acceptance/bootstrap/GraphContext.php index 64569d397e..ec5cd33e11 100644 --- a/tests/acceptance/bootstrap/GraphContext.php +++ b/tests/acceptance/bootstrap/GraphContext.php @@ -3758,6 +3758,28 @@ class GraphContext implements Context { $this->featureContext->setResponse($response); } + /** + * The public link drive endpoint rejects the token as a query parameter, + * so it rides in the header here. + * + * @param string|null $password + * + * @return void + */ + #[When('the public gets the drive of the last created public link with password :password using the Graph API')] + public function thePublicGetsTheDriveOfTheLastCreatedPublicLink(?string $password = null): void { + $token = $this->featureContext->shareNgGetLastCreatedLinkShareToken(); + $driveId = $this->publicLinkDriveId($token); + $response = HttpRequestHelper::get( + $this->featureContext->getBaseUrl() . "/graph/v1.0/drives/$driveId", + $this->featureContext->getStepLineRef(), + $password === null ? null : "public", + $this->featureContext->getActualPassword($password), + ["public-token" => $token] + ); + $this->featureContext->setResponse($response); + } + /** * Item anchored colon path: the anchor id is resolved through the public * children listing, so the step stays within the public API. diff --git a/tests/acceptance/features/apiGraph/publicLinkDriveItemListing.feature b/tests/acceptance/features/apiGraph/publicLinkDriveItemListing.feature index 4ee12b868d..db605dbd50 100644 --- a/tests/acceptance/features/apiGraph/publicLinkDriveItemListing.feature +++ b/tests/acceptance/features/apiGraph/publicLinkDriveItemListing.feature @@ -335,3 +335,31 @@ Feature: listing the content of a public link via the Graph API | delete | 400 | | rename | 400 | | list the permissions of | 404 | + + + Scenario: the public sees who shared the link on the drive + When the public gets the drive of the last created public link with password "%public%" using the Graph API + Then the HTTP status code should be "200" + And the JSON data of the response should match + """ + { + "type": "object", + "required": ["driveType", "owner"], + "properties": { + "driveType": { "const": "mountpoint" }, + "owner": { + "type": "object", + "required": ["user"], + "properties": { + "user": { + "type": "object", + "required": ["id", "displayName"], + "properties": { + "displayName": { "const": "Alice Hansen" } + } + } + } + } + } + } + """