From 6aa9831cf1db94cdf551477a2b51eea913faead8 Mon Sep 17 00:00:00 2001 From: Nicolas Varlot <165952530+n-iv@users.noreply.github.com> Date: Thu, 7 May 2026 14:44:55 +0200 Subject: [PATCH 1/2] fix: preserve method and URI when redirecting WebDAV through GSS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Basic credentials are presented on a direct WebDAV endpoint, the master now forwards the request as-is to the slave with a 307, which preserves both method and URI per RFC 9110 §15.4.8. The autologin JWT flow is kept intact for every other path. Signed-off-by: Nicolas Varlot Signed-off-by: Nicolas Varlot <165952530+n-iv@users.noreply.github.com> --- lib/Master.php | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/Master.php b/lib/Master.php index f6f5c38..1d2ad53 100644 --- a/lib/Master.php +++ b/lib/Master.php @@ -272,6 +272,15 @@ protected function redirectUser($uid, $password, $location, array $options = []) // check for both possible direct webdav end-points $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; + + // detect HTTP Basic Auth on the incoming request; RFC 9110 §11.1 makes + // the scheme name case-insensitive, so we lowercase before comparing. + $authHeader = $this->request->getHeader('Authorization'); + $hasBasicAuth = $authHeader !== '' && str_starts_with(strtolower($authHeader), 'basic '); + + // default redirect status code; overridden below for the 307 forward. + $statusCode = 302; + // direct webdav access with old client or general purpose webdav clients if ($isClient && $isDirectWebDavAccess) { $this->logger->debug('redirectUser: client direct webdav request'); @@ -289,6 +298,18 @@ protected function redirectUser($uid, $password, $location, array $options = []) // fallback to v1 $redirectUrl = 'nc://login/server:' . $location . '&user:' . urlencode($uid) . '&password:' . urlencode($appToken); } + } elseif ($isDirectWebDavAccess && $hasBasicAuth) { + // Third-party WebDAV clients authenticated with HTTP Basic + // (curl, rclone, davfs2, sabre/dav based clients, generic DAV + // consumers, etc.): forward the request as-is to the slave with + // a 307 (RFC 9110 §15.4.8) so that PUT, PROPFIND, MKCOL, DELETE, + // COPY and MOVE are not downgraded to GET, and the original + // request URI is preserved end-to-end. The client re-issues the + // same request to the slave, including the Authorization header + // it already presented to the master. + $this->logger->debug('redirectUser: third-party webdav request with Basic Auth, forwarding with 307'); + $redirectUrl = rtrim($location, '/') . $requestUri; + $statusCode = 307; } else { $this->logger->debug('redirectUser: direct login so forward to target node'); $jwt = $this->createJwt($uid, $password, $options); @@ -296,7 +317,7 @@ protected function redirectUser($uid, $password, $location, array $options = []) } $this->logger->debug('redirectUser: redirecting to: ' . $redirectUrl); - header('Location: ' . $redirectUrl, true, 302); + header('Location: ' . $redirectUrl, true, $statusCode); die(); } From 7f5bd61024376a95f6cf9e5f88c8272706770dff Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Fri, 17 Jul 2026 11:35:23 -0100 Subject: [PATCH 2/2] implements config redirectWebDAV Signed-off-by: Maxence Lange --- lib/ConfigLexicon.php | 2 ++ lib/Master.php | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/ConfigLexicon.php b/lib/ConfigLexicon.php index d24d440..778f905 100644 --- a/lib/ConfigLexicon.php +++ b/lib/ConfigLexicon.php @@ -17,6 +17,7 @@ class ConfigLexicon implements ILexicon { public const GS_TOKENS = 'globalScaleTokens'; public const LOCAL_TOKEN = 'localToken'; + public const REDIRECT_WEBDAV = 'redirectWebDAV'; #[\Override] public function getStrictness(): Strictness { @@ -31,6 +32,7 @@ public function getAppConfigs(): array { return [ new Entry(key: self::GS_TOKENS, type: ValueType::ARRAY, defaultRaw: [], definition: 'list of token+host to navigate through GlobalScale', lazy: true), new Entry(key: self::LOCAL_TOKEN, type: ValueType::STRING, defaultRaw: '', definition: 'local token to id instance within GlobalScale', lazy: true), + new Entry(key: self::REDIRECT_WEBDAV, type: ValueType::BOOL, defaultRaw: false, definition: 'redirect WebDAV request on Master to Slaves', lazy: false), ]; } diff --git a/lib/Master.php b/lib/Master.php index 1d2ad53..128f0ed 100644 --- a/lib/Master.php +++ b/lib/Master.php @@ -13,6 +13,7 @@ use OC\Core\Controller\ClientFlowLoginV2Controller; use OC\Core\Service\LoginFlowV2Service; use OCA\GlobalSiteSelector\AppInfo\Application; +use OCA\GlobalSiteSelector\ConfigLexicon; use OCA\GlobalSiteSelector\UserDiscoveryModules\IUserDiscoveryModule; use OCA\GlobalSiteSelector\Vendor\Firebase\JWT\JWT; use OCA\GlobalSiteSelector\Vendor\Firebase\JWT\Key; @@ -20,6 +21,7 @@ use OCP\Authentication\IApacheBackend; use OCP\HintException; use OCP\Http\Client\IClientService; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; @@ -48,6 +50,7 @@ public function __construct( private readonly Lookup $lookup, private readonly IRequest $request, private readonly IClientService $clientService, + private readonly IAppConfig $appConfig, private readonly IConfig $config, private readonly LoggerInterface $logger, ) { @@ -273,10 +276,9 @@ protected function redirectUser($uid, $password, $location, array $options = []) $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; - // detect HTTP Basic Auth on the incoming request; RFC 9110 §11.1 makes - // the scheme name case-insensitive, so we lowercase before comparing. - $authHeader = $this->request->getHeader('Authorization'); - $hasBasicAuth = $authHeader !== '' && str_starts_with(strtolower($authHeader), 'basic '); + $authHeader = $this->request->getHeader('Authorization'); + $redirectWebDav = $this->appConfig->getValueBool(Application::APP_ID, ConfigLexicon::REDIRECT_WEBDAV); + $hasBasicAuth = $redirectWebDav && $authHeader !== '' && str_starts_with(strtolower($authHeader), 'basic '); // default redirect status code; overridden below for the 307 forward. $statusCode = 302;