From 92e6c8f3ff2327376164d632feecac353d6a6503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Duffeck?= Date: Fri, 12 Jun 2026 15:13:32 +0200 Subject: [PATCH 1/8] Do not choke on users that weren't cleaned up yet --- tests/acceptance/bootstrap/GraphContext.php | 4 +- tests/acceptance/bootstrap/Provisioning.php | 126 ++++++++++++-------- 2 files changed, 77 insertions(+), 53 deletions(-) diff --git a/tests/acceptance/bootstrap/GraphContext.php b/tests/acceptance/bootstrap/GraphContext.php index 49782fa137..cf350be6be 100644 --- a/tests/acceptance/bootstrap/GraphContext.php +++ b/tests/acceptance/bootstrap/GraphContext.php @@ -287,9 +287,7 @@ class GraphContext implements Context { public function adminDeletesUserUsingTheGraphApi(string $user, ?string $byUser = null): ?ResponseInterface { $credentials = $this->getAdminOrUserCredentials($byUser); $userId = $this->featureContext->getAttributeOfCreatedUser($user, 'id'); - if ($userId === null) { - throw new \RuntimeException("Cannot delete user '$user': no userId found"); - } + $userId = $userId ?: $user; return GraphHelper::deleteUserByUserId( $this->featureContext->getBaseUrl(), $this->featureContext->getStepLineRef(), diff --git a/tests/acceptance/bootstrap/Provisioning.php b/tests/acceptance/bootstrap/Provisioning.php index 00a8cc26c8..b50ccf1f4e 100644 --- a/tests/acceptance/bootstrap/Provisioning.php +++ b/tests/acceptance/bootstrap/Provisioning.php @@ -469,6 +469,80 @@ trait Provisioning { $this->ldapCreatedUsers[] = $setting["userid"]; } + /** + * Creates a user through the Graph API and replaces a stale existing user if needed. + * + * @param string $userName + * @param string $password + * @param string|null $email + * @param string|null $displayName + * @param string|null $byUser + * + * @return string + * @throws Exception|GuzzleException + */ + private function createGraphUserWithReplacement( + string $userName, + string $password, + ?string $email, + ?string $displayName, + ?string $byUser = null + ): string { + $userName = $this->getActualUsername($userName); + $userName = \trim($userName); + if ($displayName === null) { + $displayName = $this->getDisplayNameForUser($userName); + if ($displayName === null) { + $displayName = $this->getDisplayNameForUser('regularuser'); + } + } + if ($email === null) { + $email = $this->getEmailAddressForUser($userName); + if ($email === null) { + $email = \str_replace(["@", " "], "", $userName) . '@opencloud.eu'; + } + } + $reqUser = $byUser ? $this->getActualUsername($byUser) : $this->getAdminUsername(); + $response = GraphHelper::createUser( + $this->getBaseUrl(), + $this->getStepLineRef(), + $reqUser, + $this->getPasswordForUser($reqUser), + $userName, + $password, + $email, + $displayName, + ); + + if ($response->getStatusCode() === 409) { + $responseBody = $this->getJsonDecodedResponse($response); + if (($responseBody['error']['code'] ?? null) === 'nameAlreadyExists') { + $deleteResponse = $this->deleteUser($userName); + $this->theHTTPStatusCodeShouldBe(204, "", $deleteResponse); + + $response = GraphHelper::createUser( + $this->getBaseUrl(), + $this->getStepLineRef(), + $reqUser, + $this->getPasswordForUser($reqUser), + $userName, + $password, + $email, + $displayName, + ); + } + } + + Assert::assertEquals( + 201, + $response->getStatusCode(), + __METHOD__ . " cannot create user '$userName'.\nResponse:" . + json_encode($this->getJsonDecodedResponse($response)) + ); + + return (string)$this->getJsonDecodedResponse($response)['id']; + } + /** * @param string $group group name * @@ -581,37 +655,7 @@ trait Provisioning { ); } } else { - // Use the same logic as userHasBeenCreated for email generation - if ($email === null) { - $email = $this->getEmailAddressForUser($userName); - if ($email === null) { - // escape @ & space if present in userId - $email = \str_replace(["@", " "], "", $userName) . '@opencloud.eu'; - } - } - - $userName = $this->getActualUsername($userName); - $userName = \trim($userName); - - $response = GraphHelper::createUser( - $this->getBaseUrl(), - $this->getStepLineRef(), - $this->getAdminUsername(), - $this->getAdminPassword(), - $userName, - $password, - $email, - $displayName, - ); - - Assert::assertEquals( - 201, - $response->getStatusCode(), - __METHOD__ . " cannot create user '$userName'.\nResponse:" . - json_encode($this->getJsonDecodedResponse($response)) - ); - - $userId = $this->getJsonDecodedResponse($response)['id']; + $userId = $this->createGraphUserWithReplacement($userName, $password, $email, $displayName); } $this->addUserToCreatedUsersList($userName, $password, $displayName, $email, $userId ?? null); @@ -1069,24 +1113,7 @@ trait Provisioning { ); } } else { - $reqUser = $byUser ? $this->getActualUsername($byUser) : $this->getAdminUsername(); - $response = GraphHelper::createUser( - $this->getBaseUrl(), - $this->getStepLineRef(), - $reqUser, - $this->getPasswordForUser($reqUser), - $user, - $password, - $email, - $displayName, - ); - Assert::assertEquals( - 201, - $response->getStatusCode(), - __METHOD__ . " cannot create user '$user'.\nResponse:" . - json_encode($this->getJsonDecodedResponse($response)) - ); - $userId = $this->getJsonDecodedResponse($response)['id']; + $userId = $this->createGraphUserWithReplacement($user, $password, $email, $displayName, $byUser); } $this->addUserToCreatedUsersList($user, $password, $displayName, $email, $userId); @@ -1709,7 +1736,6 @@ trait Provisioning { $this->ocsApiVersion ); } else { - // users can be deleted using the username in the GraphApi too $response = $this->graphContext->adminDeletesUserUsingTheGraphApi($user); } return $response; From f3f14e4a043dc65f70847bbbfcc8210b5c5661a4 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Mon, 15 Jun 2026 09:38:28 +0200 Subject: [PATCH 2/8] bump reva Signed-off-by: Christian Richter --- go.mod | 2 +- go.sum | 8 +-- .../http/services/archiver/handler.go | 68 ++++++++++++++++++- vendor/modules.txt | 2 +- 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 5ef0567f7e..31beb08bee 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/open-policy-agent/opa v1.15.2 github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d - github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 + github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b github.com/opensearch-project/opensearch-go/v4 v4.6.0 github.com/orcaman/concurrent-map v1.0.0 github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 97bdb032a8..45cb574aa0 100644 --- a/go.sum +++ b/go.sum @@ -948,12 +948,8 @@ github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+l github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw= github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q= -github.com/opencloud-eu/reva/v2 v2.46.3-0.20260611095012-6617969b3720 h1:UHJDrOoU9hoVFg0hgKmNIMp0hFEb/reiDYthVHlX5g8= -github.com/opencloud-eu/reva/v2 v2.46.3-0.20260611095012-6617969b3720/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= -github.com/opencloud-eu/reva/v2 v2.46.3 h1:7td4rXcku0goMyGYbWcJ3KGFZe5Ls+nDlBWWZRQfkJY= -github.com/opencloud-eu/reva/v2 v2.46.3/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= -github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 h1:H9RbSBCN/oMw21Q2kYYdEOf4pyxoZchfTjPCCDsR+80= -github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= +github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b h1:ejmGkLVlWauUkcnXYbSr7dY1a59dUdkKHOvzeGpwwgE= +github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b/go.mod h1:RoFQt+u7edxwzHr1IZ2Y6VaDinMiRPQupAvMBy3WVmE= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI= github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= diff --git a/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go b/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go index 49aa40e8d8..d665de7da1 100644 --- a/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go +++ b/vendor/github.com/opencloud-eu/reva/v2/internal/http/services/archiver/handler.go @@ -23,6 +23,8 @@ import ( "errors" "fmt" "net/http" + "path" + "strings" "time" "regexp" @@ -34,6 +36,7 @@ import ( "github.com/gdexlab/go-render/render" "github.com/go-viper/mapstructure/v2" "github.com/opencloud-eu/reva/v2/internal/http/services/archiver/manager" + "github.com/opencloud-eu/reva/v2/internal/http/services/owncloud/ocdav/net" "github.com/opencloud-eu/reva/v2/pkg/errtypes" "github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool" "github.com/opencloud-eu/reva/v2/pkg/rhttp" @@ -200,6 +203,56 @@ func (s *svc) allAllowed(paths []string) error { } */ +// resourceName resolves the name of a single resource so the archive can be named after it instead +// of the generic "download". It returns an empty string on any failure, so the caller keeps the +// default name. The name is sanitized via sanitizeArchiveName. +func (s *svc) resourceName(ctx context.Context, id *provider.ResourceId) (string, error) { + gatewayClient, err := s.gatewaySelector.Next() + if err != nil { + s.log.Debug().Err(err).Msg("archiver: could not select gateway to resolve the archive name, using the default") + return "", err + } + + res, err := gatewayClient.Stat(ctx, &provider.StatRequest{ + Ref: &provider.Reference{ResourceId: id}, + }) + if err != nil { + s.log.Debug().Err(err).Msg("archiver: stat failed while resolving the archive name, using the default") + return "", err + } + if code := res.GetStatus().GetCode(); code != rpc.Code_CODE_OK { + s.log.Debug().Str("code", code.String()).Msg("archiver: stat returned non-OK while resolving the archive name, using the default") + return "", fmt.Errorf("stat returned non-OK code %s", code.String()) + } + + name := res.GetInfo().GetName() + if name == "" { + name = path.Base(res.GetInfo().GetPath()) + } + return sanitizeArchiveName(name), nil +} + +// sanitizeArchiveName removes characters that would break the Content-Disposition header (CR, LF, +// double quote) or let the name act as a path (slash, backslash), plus all control characters +// (C0, DEL and C1). It returns an empty string if nothing usable is left. +func sanitizeArchiveName(name string) string { + name = strings.Map(func(r rune) rune { + switch { + case r == '"', r == '\\', r == '/': + return -1 + case r < 0x20 || (r >= 0x7f && r <= 0x9f): + return -1 + default: + return r + } + }, name) + name = strings.TrimSpace(name) + if name == "." || name == ".." { + return "" + } + return name +} + func (s *svc) writeHTTPError(rw http.ResponseWriter, err error) { s.log.Error().Msg(err.Error()) @@ -251,7 +304,20 @@ func (s *svc) Handler() http.Handler { return } + // Name the archive after the resource when a single one was requested, instead of the + // generic "download". The name must be resolved here, before the body is streamed: the + // Content-Disposition header below is written before CreateZip/CreateTar run, so the name + // the walker resolves while building the archive would come too late. + // See https://github.com/opencloud-eu/reva/issues/308 archName := s.config.Name + if len(resources) == 1 { + if name, err := s.resourceName(ctx, resources[0]); name != "" && err == nil { + archName = name + } else { + s.log.Debug().Err(err).Msg("could not resolve the archive name, using the default") + archName = "download" + } + } if format == "tar" { archName += ".tar" } else { @@ -260,7 +326,7 @@ func (s *svc) Handler() http.Handler { s.log.Debug().Msg("Requested the following resources to archive: " + render.Render(resources)) - rw.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", archName)) + rw.Header().Set(net.HeaderContentDisposistion, net.ContentDispositionAttachment(archName)) rw.Header().Set("Content-Transfer-Encoding", "binary") // create the archive diff --git a/vendor/modules.txt b/vendor/modules.txt index 19e72f589d..df6ad146c1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1360,7 +1360,7 @@ github.com/opencloud-eu/icap-client # github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d ## explicit; go 1.18 github.com/opencloud-eu/libre-graph-api-go -# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260612072211-aa5e96aa80e7 +# github.com/opencloud-eu/reva/v2 v2.46.4-0.20260615073558-209c2cd3b52b ## explicit; go 1.25.0 github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace github.com/opencloud-eu/reva/v2/cmd/revad/runtime From 363a047b21885905ca4ba1bafb993bc13c62ec89 Mon Sep 17 00:00:00 2001 From: Christian Richter Date: Mon, 15 Jun 2026 15:54:15 +0200 Subject: [PATCH 3/8] change web commit id & branch Signed-off-by: Christian Richter --- .woodpecker.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.woodpecker.env b/.woodpecker.env index 12745312ab..c42b928952 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=8e1c4ab91644a5441736522e72f98f9118fbefe3 -WEB_BRANCH=main +WEB_COMMITID=e73e5898cdafa8b6df6c002a745bdd0c6d43bd54 +WEB_BRANCH=ScharfViktor-patch-1 From 0d132c0309c16225a825d5f61126cc74b1f46ca3 Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Mon, 15 Jun 2026 16:49:26 +0200 Subject: [PATCH 4/8] Update .woodpecker.env --- .woodpecker.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.env b/.woodpecker.env index c42b928952..644e2d8dea 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=e73e5898cdafa8b6df6c002a745bdd0c6d43bd54 +WEB_COMMITID=dd76c90e03a41a5c6b89ef0b6c4c1bce1ed13541 WEB_BRANCH=ScharfViktor-patch-1 From 980ce494db338e9fd40d9f4facc5d70becde2a83 Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Mon, 15 Jun 2026 21:50:05 +0200 Subject: [PATCH 5/8] Update WEB_COMMITID in .woodpecker.env --- .woodpecker.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.env b/.woodpecker.env index 644e2d8dea..58c44defd5 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=dd76c90e03a41a5c6b89ef0b6c4c1bce1ed13541 +WEB_COMMITID=b87bd40c9ec7faed53dbe6a29d4cb8ed7d4f678f WEB_BRANCH=ScharfViktor-patch-1 From 84607766c3dac92d5e79752737a0485c4e7109e0 Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Tue, 16 Jun 2026 08:36:28 +0200 Subject: [PATCH 6/8] Update .woodpecker.env --- .woodpecker.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.env b/.woodpecker.env index 58c44defd5..4349595937 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=b87bd40c9ec7faed53dbe6a29d4cb8ed7d4f678f +WEB_COMMITID=00c23b67bc4a00263be6bdf0c5e9f30869f58a6a WEB_BRANCH=ScharfViktor-patch-1 From 50af682579a5c0ae067b59a8671523b34fd4d1d8 Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Tue, 16 Jun 2026 09:49:13 +0200 Subject: [PATCH 7/8] Update WEB_COMMITID in .woodpecker.env --- .woodpecker.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.env b/.woodpecker.env index 4349595937..9dc1e5dfe8 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=00c23b67bc4a00263be6bdf0c5e9f30869f58a6a +WEB_COMMITID=53cb4bb1bc8d7b0a39cb7dc788def79b9450c494 WEB_BRANCH=ScharfViktor-patch-1 From bb715fa4d44585f91d3e2fe2ee83215a204a959c Mon Sep 17 00:00:00 2001 From: Viktor Scharf Date: Tue, 16 Jun 2026 10:11:17 +0200 Subject: [PATCH 8/8] Update .woodpecker.env --- .woodpecker.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.env b/.woodpecker.env index 9dc1e5dfe8..1787c29a4b 100644 --- a/.woodpecker.env +++ b/.woodpecker.env @@ -1,3 +1,3 @@ # The test runner source for UI tests -WEB_COMMITID=53cb4bb1bc8d7b0a39cb7dc788def79b9450c494 +WEB_COMMITID=8164194c83c78ea7c7010cb8b771b3c81d26a23f WEB_BRANCH=ScharfViktor-patch-1