From 3f2113b090458928a7f2add761476b5de1ebf123 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Mon, 11 Dec 2023 16:55:24 +0100 Subject: [PATCH 1/8] Kano redirect back (#1) * First attempt at slave controlled login flow Signed-off-by: Micke Nordin --- Makefile | 2 +- appinfo/info.xml | 2 +- lib/Controller/SlaveController.php | 61 +++++++++++++++++++++++---- lib/Master.php | 68 +++++++++++++----------------- 4 files changed, 85 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index 4f3cac13..2ca28a27 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build_dir=$(CURDIR)/build/artifacts source_dir=$(build_dir)/source sign_dir=$(build_dir)/sign package_name=$(app_name) -version+=2.3.1 +version+=2.5.0-beta1 all: appstore diff --git a/appinfo/info.xml b/appinfo/info.xml index 5a85ac5b..96aeb6d1 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -17,7 +17,7 @@ https://github.com/nextcloud/globalsiteselector/issues https://github.com/nextcloud/globalsiteselector - + OCA\GlobalSiteSelector\BackgroundJobs\UpdateLookupServer diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index a91d847e..65cc6b10 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -1,4 +1,5 @@ * @@ -54,7 +55,8 @@ * * @package OCA\GlobalSiteSelector\Controller */ -class SlaveController extends OCSController { +class SlaveController extends OCSController +{ public function __construct( $appName, @@ -83,7 +85,8 @@ public function __construct( * * @return RedirectResponse */ - public function autoLogin(string $jwt): RedirectResponse { + public function autoLogin(string $jwt): RedirectResponse + { $this->logger->debug('autologin incoming request with ' . $jwt); try { @@ -150,10 +153,49 @@ public function autoLogin(string $jwt): RedirectResponse { $this->slaveService->updateUserById($uid); $this->logger->debug('userdata updated on lus'); - $home = $this->urlGenerator->getAbsoluteURL($target); - $this->logger->debug('redirecting to ' . $home); + $redirectUrl = $this->urlGenerator->getAbsoluteURL($target); + + /* see if we need to handle client login */ + $clientFeatureEnabled = filter_var($this->config->getAppValue('globalsiteselector', 'client_feature_enabled', 'false'), FILTER_VALIDATE_BOOLEAN); + if ($clientFeatureEnabled) { + $this->logger->debug('Client redirect feature enabled'); + + $isClient = $this->request->isUserAgent( + [ + IRequest::USER_AGENT_CLIENT_IOS, + IRequest::USER_AGENT_CLIENT_ANDROID, + IRequest::USER_AGENT_CLIENT_DESKTOP, + '/^.*\(Android\)$/' + ] + ); + + $requestUri = $this->request->getRequestUri(); + // check for both possible direct webdav end-points + $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; + $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; + // direct webdav access with old client or general purpose webdav clients + if ($isClient && $isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client direct webdav request'); + $redirectUrl = $target . '/remote.php/webdav/'; + } elseif ($isClient && !$isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client request generating apptoken'); + $data = $this->createAppToken($jwt)->getData(); + if (!isset($data['token'])) { + $info = 'getAppToken - data doesn\'t contain token: ' . json_encode($data); + throw new \Exception($info); + } + $appToken = $data['token']; + + $redirectUrl = + 'nc://login/server:' . $requestUri . '&user:' . urlencode($uid) . '&password:' . urlencode( + $appToken + ); + } + } + + $this->logger->debug('redirecting to ' . $redirectUrl); - return new RedirectResponse($home); + return new RedirectResponse($redirectUrl); } /** @@ -164,7 +206,8 @@ public function autoLogin(string $jwt): RedirectResponse { * * @return DataResponse */ - public function createAppToken($jwt) { + public function createAppToken($jwt) + { if ($this->gss->getMode() === 'master' || empty($jwt)) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } @@ -202,7 +245,8 @@ public function createAppToken($jwt) { * @return array * @throws \Exception */ - protected function decodeJwt($jwt) { + protected function decodeJwt($jwt) + { $key = $this->gss->getJwtKey(); $decoded = (array)JWT::decode($jwt, new Key($key, Application::JWT_ALGORITHM)); @@ -228,7 +272,8 @@ protected function decodeJwt($jwt) { * @param string $uid * @param array $options */ - protected function autoprovisionIfNeeded($uid, $options) { + protected function autoprovisionIfNeeded($uid, $options) + { // make sure that a valid UID is given if (empty($uid)) { $this->logger->error('Uid "{uid}" is not valid.', ['app' => $this->appName, 'uid' => $uid]); diff --git a/lib/Master.php b/lib/Master.php index 4cc7a34e..34c4a562 100644 --- a/lib/Master.php +++ b/lib/Master.php @@ -231,34 +231,37 @@ protected function queryLookupServer(string &$uid, bool $matchUid = false): stri * @throws Exception */ protected function redirectUser($uid, $password, $location, array $options = []) { - $isClient = $this->request->isUserAgent( - [ - IRequest::USER_AGENT_CLIENT_IOS, - IRequest::USER_AGENT_CLIENT_ANDROID, - IRequest::USER_AGENT_CLIENT_DESKTOP, - '/^.*\(Android\)$/' - ] - ); + $this->logger->debug('redirectUser: direct login so forward to target node'); + $jwt = $this->createJwt($uid, $password, $options); + $redirectUrl = $location . '/index.php/apps/globalsiteselector/autologin?jwt=' . $jwt; + + $clientFeatureEnabled = filter_var($this->config->getAppValue('globalsiteselector', 'client_feature_enabled', 'false'), FILTER_VALIDATE_BOOLEAN); + if (!$clientFeatureEnabled) { + $isClient = $this->request->isUserAgent( + [ + IRequest::USER_AGENT_CLIENT_IOS, + IRequest::USER_AGENT_CLIENT_ANDROID, + IRequest::USER_AGENT_CLIENT_DESKTOP, + '/^.*\(Android\)$/' + ] + ); - $requestUri = $this->request->getRequestUri(); - // check for both possible direct webdav end-points - $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; - $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; - // direct webdav access with old client or general purpose webdav clients - if ($isClient && $isDirectWebDavAccess) { - $this->logger->debug('redirectUser: client direct webdav request'); - $redirectUrl = $location . '/remote.php/webdav/'; - } elseif ($isClient && !$isDirectWebDavAccess) { - $this->logger->debug('redirectUser: client request generating apptoken'); - $appToken = $this->getAppToken($location, $uid, $password, $options); - $redirectUrl = - 'nc://login/server:' . $location . '&user:' . urlencode($uid) . '&password:' . urlencode( - $appToken - ); - } else { - $this->logger->debug('redirectUser: direct login so forward to target node'); - $jwt = $this->createJwt($uid, $password, $options); - $redirectUrl = $location . '/index.php/apps/globalsiteselector/autologin?jwt=' . $jwt; + $requestUri = $this->request->getRequestUri(); + // check for both possible direct webdav end-points + $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; + $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; + // direct webdav access with old client or general purpose webdav clients + if ($isClient && $isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client direct webdav request'); + $redirectUrl = $location . '/remote.php/webdav/'; + } elseif ($isClient && !$isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client request generating apptoken'); + $appToken = $this->getAppToken($location, $uid, $password, $options); + $redirectUrl = + 'nc://login/server:' . $location . '&user:' . urlencode($uid) . '&password:' . urlencode( + $appToken + ); + } } $this->logger->debug('redirectUser: redirecting to: ' . $redirectUrl); @@ -288,17 +291,6 @@ protected function createJwt($uid, $password, $options) { return $jwt; } - /** - * get app token from the server the user is located - * - * @param string $location - * @param string $uid - * @param string $password - * @param array $options - * - * @return string - * @throws Exception - */ protected function getAppToken($location, $uid, $password, $options) { $client = $this->clientService->newClient(); $jwt = $this->createJwt($uid, $password, $options); From 783fdd6e7bcd53ce4e6f3cfaae719e28b8af6817 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Mon, 11 Dec 2023 16:58:34 +0100 Subject: [PATCH 2/8] OCA\Events\AfterLoginOnSlaveEvent: New event (#2) This patch will make the slave emit a new event "AfterLoginOnSlaveEvent" right before redirection. Signed-off-by: Micke Nordin Signed-off-by: Micke Nordin --- lib/Controller/SlaveController.php | 10 +++++++ lib/Events/AfterLoginOnSlaveEvent.php | 41 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 lib/Events/AfterLoginOnSlaveEvent.php diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index 65cc6b10..9349d09b 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -25,6 +25,7 @@ use OC\Authentication\Token\IToken; use OCA\GlobalSiteSelector\AppInfo\Application; +use OCA\GlobalSiteSelector\Events\AfterLoginOnSlaveEvent; use OCA\GlobalSiteSelector\Exceptions\MasterUrlException; use OCA\GlobalSiteSelector\GlobalSiteSelector; use OCA\GlobalSiteSelector\Service\SlaveService; @@ -38,6 +39,7 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\OCSController; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IConfig; use OCP\IRequest; use OCP\ISession; @@ -65,6 +67,7 @@ public function __construct( private IUserSession $userSession, private IURLGenerator $urlGenerator, private ICrypto $crypto, + private IEventDispatcher $eventDispatcher, private TokenHandler $tokenHandler, private IUserManager $userManager, private UserBackend $userBackend, @@ -153,6 +156,13 @@ public function autoLogin(string $jwt): RedirectResponse $this->slaveService->updateUserById($uid); $this->logger->debug('userdata updated on lus'); + $user = $this->userManager->get($uid); + if ($user instanceof IUser) { + $this->logger->debug('emiting AfterLoginOnSlaveEvent event'); + $this->eventDispatcher->dispatchTyped( + new AfterLoginOnSlaveEvent($user) + ); + } $redirectUrl = $this->urlGenerator->getAbsoluteURL($target); /* see if we need to handle client login */ diff --git a/lib/Events/AfterLoginOnSlaveEvent.php b/lib/Events/AfterLoginOnSlaveEvent.php new file mode 100644 index 00000000..0cc4f4cc --- /dev/null +++ b/lib/Events/AfterLoginOnSlaveEvent.php @@ -0,0 +1,41 @@ + + * + * @author 2023 Micke Nordin + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +namespace OCA\GlobalSiteSelector\Events; + +use OCP\EventDispatcher\Event; +use OCP\IUser; + +class AfterLoginOnSlaveEvent extends Event +{ + public function __construct(private IUser $user) + { + parent::__construct(); + } + public function getUser(): IUser + { + return $this->user; + } +} From a980df99486d8b8d302c8e1334ba82bcd60f3367 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Wed, 6 Mar 2024 09:23:40 +0100 Subject: [PATCH 3/8] Address formating feedback Signed-off-by: Micke Nordin --- appinfo/info.xml | 4 ++-- lib/Controller/SlaveController.php | 15 +++++---------- lib/Events/AfterLoginOnSlaveEvent.php | 6 ++++-- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 96aeb6d1..813f438c 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -4,7 +4,7 @@ Global Site Selector Nextcloud Portal to redirect users to the right instance The Global Site Selector allows you to run multiple small Nextcloud instances and redirect users to the right server - 2.5.0-beta1 + 2.3.1 agpl Bjoern Schiessle Maxence Lange @@ -17,7 +17,7 @@ https://github.com/nextcloud/globalsiteselector/issues https://github.com/nextcloud/globalsiteselector - + OCA\GlobalSiteSelector\BackgroundJobs\UpdateLookupServer diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index 9349d09b..c8c2308b 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -57,8 +57,7 @@ * * @package OCA\GlobalSiteSelector\Controller */ -class SlaveController extends OCSController -{ +class SlaveController extends OCSController { public function __construct( $appName, @@ -88,8 +87,7 @@ public function __construct( * * @return RedirectResponse */ - public function autoLogin(string $jwt): RedirectResponse - { + public function autoLogin(string $jwt): RedirectResponse { $this->logger->debug('autologin incoming request with ' . $jwt); try { @@ -216,8 +214,7 @@ public function autoLogin(string $jwt): RedirectResponse * * @return DataResponse */ - public function createAppToken($jwt) - { + public function createAppToken($jwt) { if ($this->gss->getMode() === 'master' || empty($jwt)) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } @@ -255,8 +252,7 @@ public function createAppToken($jwt) * @return array * @throws \Exception */ - protected function decodeJwt($jwt) - { + protected function decodeJwt($jwt) { $key = $this->gss->getJwtKey(); $decoded = (array)JWT::decode($jwt, new Key($key, Application::JWT_ALGORITHM)); @@ -282,8 +278,7 @@ protected function decodeJwt($jwt) * @param string $uid * @param array $options */ - protected function autoprovisionIfNeeded($uid, $options) - { + protected function autoprovisionIfNeeded($uid, $options) { // make sure that a valid UID is given if (empty($uid)) { $this->logger->error('Uid "{uid}" is not valid.', ['app' => $this->appName, 'uid' => $uid]); diff --git a/lib/Events/AfterLoginOnSlaveEvent.php b/lib/Events/AfterLoginOnSlaveEvent.php index 0cc4f4cc..8fd4e85e 100644 --- a/lib/Events/AfterLoginOnSlaveEvent.php +++ b/lib/Events/AfterLoginOnSlaveEvent.php @@ -28,8 +28,10 @@ use OCP\EventDispatcher\Event; use OCP\IUser; -class AfterLoginOnSlaveEvent extends Event -{ +/** + * This event is triggered after GSS login is finalized on the slave. + **/ +class AfterLoginOnSlaveEvent extends Event { public function __construct(private IUser $user) { parent::__construct(); From d4783f5d04486c3f9f4dbcd173ff21dbfe457154 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Wed, 6 Mar 2024 09:24:05 +0100 Subject: [PATCH 4/8] Update lib/Controller/SlaveController.php Co-authored-by: Maxence Lange Signed-off-by: Micke Nordin Signed-off-by: Micke Nordin --- lib/Controller/SlaveController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index c8c2308b..203466fb 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -179,8 +179,7 @@ public function autoLogin(string $jwt): RedirectResponse { $requestUri = $this->request->getRequestUri(); // check for both possible direct webdav end-points - $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; - $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; + $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false || strpos($requestUri, 'remote.php/dav') !== false; // direct webdav access with old client or general purpose webdav clients if ($isClient && $isDirectWebDavAccess) { $this->logger->debug('redirectUser: client direct webdav request'); From cec0597fb56e0d61ce77f79a818db043b11c1fa1 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Wed, 6 Mar 2024 09:37:04 +0100 Subject: [PATCH 5/8] Move redirect modifiacation to separate function Signed-off-by: Micke Nordin --- lib/Controller/SlaveController.php | 50 +++++++++++++++++------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index 203466fb..cec28eae 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -78,6 +78,32 @@ public function __construct( parent::__construct($appName, $request); } + private function modifyRedirectUriForClient() { + + $requestUri = $this->request->getRequestUri(); + // check for both possible direct webdav end-points + $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false || strpos($requestUri, 'remote.php/dav') !== false; + // direct webdav access with old client or general purpose webdav clients + if ($isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client direct webdav request'); + $redirectUrl = $target . '/remote.php/webdav/'; + } else { + $this->logger->debug('redirectUser: client request generating apptoken'); + $data = $this->createAppToken($jwt)->getData(); + if (!isset($data['token'])) { + $info = 'getAppToken - data doesn\'t contain token: ' . json_encode($data); + throw new \Exception($info); + } + $appToken = $data['token']; + + $redirectUrl = + 'nc://login/server:' . $requestUri . '&user:' . urlencode($uid) . '&password:' . urlencode( + $appToken + ); + } + return $redirectUrl; + } + /** * @PublicPage * @NoCSRFRequired @@ -176,28 +202,10 @@ public function autoLogin(string $jwt): RedirectResponse { '/^.*\(Android\)$/' ] ); + } - $requestUri = $this->request->getRequestUri(); - // check for both possible direct webdav end-points - $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false || strpos($requestUri, 'remote.php/dav') !== false; - // direct webdav access with old client or general purpose webdav clients - if ($isClient && $isDirectWebDavAccess) { - $this->logger->debug('redirectUser: client direct webdav request'); - $redirectUrl = $target . '/remote.php/webdav/'; - } elseif ($isClient && !$isDirectWebDavAccess) { - $this->logger->debug('redirectUser: client request generating apptoken'); - $data = $this->createAppToken($jwt)->getData(); - if (!isset($data['token'])) { - $info = 'getAppToken - data doesn\'t contain token: ' . json_encode($data); - throw new \Exception($info); - } - $appToken = $data['token']; - - $redirectUrl = - 'nc://login/server:' . $requestUri . '&user:' . urlencode($uid) . '&password:' . urlencode( - $appToken - ); - } + if ($isClient) { + $redirectUrl = $this->modifyRedirectUriForClient(); } $this->logger->debug('redirecting to ' . $redirectUrl); From 001ec17c81fd5af1cbd8b6b40dab965d532facb3 Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Wed, 6 Mar 2024 09:39:02 +0100 Subject: [PATCH 6/8] Revert version nr Signed-off-by: Micke Nordin --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2ca28a27..4f3cac13 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ build_dir=$(CURDIR)/build/artifacts source_dir=$(build_dir)/source sign_dir=$(build_dir)/sign package_name=$(app_name) -version+=2.5.0-beta1 +version+=2.3.1 all: appstore From d0d95fcd8dbbe692b89c84593caa9870edc5bd3b Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Wed, 6 Mar 2024 09:40:00 +0100 Subject: [PATCH 7/8] Revert version nr Signed-off-by: Micke Nordin --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 813f438c..7bdd6e98 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -17,7 +17,7 @@ https://github.com/nextcloud/globalsiteselector/issues https://github.com/nextcloud/globalsiteselector - + OCA\GlobalSiteSelector\BackgroundJobs\UpdateLookupServer From 277e564a45021140b1543a9cf8db75b84289c40c Mon Sep 17 00:00:00 2001 From: Micke Nordin Date: Tue, 14 May 2024 14:20:59 +0200 Subject: [PATCH 8/8] Apply changes for PR surpuplied by Maxence Signed-off-by: Micke Nordin Co-authored-by: Maxence Lange --- appinfo/info.xml | 2 +- lib/Controller/SlaveController.php | 83 +++++++++++++-------------- lib/Events/AfterLoginOnSlaveEvent.php | 15 +++-- lib/Master.php | 32 +++++------ 4 files changed, 62 insertions(+), 70 deletions(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index 7bdd6e98..5a85ac5b 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -4,7 +4,7 @@ Global Site Selector Nextcloud Portal to redirect users to the right instance The Global Site Selector allows you to run multiple small Nextcloud instances and redirect users to the right server - 2.3.1 + 2.5.0-beta1 agpl Bjoern Schiessle Maxence Lange diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index cec28eae..b8d679f7 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -1,5 +1,4 @@ * @@ -78,32 +77,6 @@ public function __construct( parent::__construct($appName, $request); } - private function modifyRedirectUriForClient() { - - $requestUri = $this->request->getRequestUri(); - // check for both possible direct webdav end-points - $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false || strpos($requestUri, 'remote.php/dav') !== false; - // direct webdav access with old client or general purpose webdav clients - if ($isDirectWebDavAccess) { - $this->logger->debug('redirectUser: client direct webdav request'); - $redirectUrl = $target . '/remote.php/webdav/'; - } else { - $this->logger->debug('redirectUser: client request generating apptoken'); - $data = $this->createAppToken($jwt)->getData(); - if (!isset($data['token'])) { - $info = 'getAppToken - data doesn\'t contain token: ' . json_encode($data); - throw new \Exception($info); - } - $appToken = $data['token']; - - $redirectUrl = - 'nc://login/server:' . $requestUri . '&user:' . urlencode($uid) . '&password:' . urlencode( - $appToken - ); - } - return $redirectUrl; - } - /** * @PublicPage * @NoCSRFRequired @@ -134,7 +107,7 @@ public function autoLogin(string $jwt): RedirectResponse { list($uid, $password, $options) = $this->decodeJwt($jwt); $this->logger->debug('uid: ' . $uid . ', options: ' . json_encode($options)); - $target = $options['target']; + $target = (string) $options['target']; if (($options['backend'] ?? '') === 'saml') { $this->logger->debug('saml enabled'); $this->autoprovisionIfNeeded($uid, $options); @@ -170,7 +143,6 @@ public function autoLogin(string $jwt): RedirectResponse { return new RedirectResponse($masterUrl); } catch (\Exception $e) { $this->logger->warning('issue during login process', ['exception' => $e]); - return new RedirectResponse($masterUrl); } @@ -182,30 +154,25 @@ public function autoLogin(string $jwt): RedirectResponse { $user = $this->userManager->get($uid); if ($user instanceof IUser) { - $this->logger->debug('emiting AfterLoginOnSlaveEvent event'); - $this->eventDispatcher->dispatchTyped( - new AfterLoginOnSlaveEvent($user) - ); + $this->logger->debug('emitting AfterLoginOnSlaveEvent event'); + $this->eventDispatcher->dispatchTyped(new AfterLoginOnSlaveEvent($user)); } - $redirectUrl = $this->urlGenerator->getAbsoluteURL($target); /* see if we need to handle client login */ - $clientFeatureEnabled = filter_var($this->config->getAppValue('globalsiteselector', 'client_feature_enabled', 'false'), FILTER_VALIDATE_BOOLEAN); - if ($clientFeatureEnabled) { - $this->logger->debug('Client redirect feature enabled'); - - $isClient = $this->request->isUserAgent( + $clientFeatureEnabled = ($this->config->getAppValue(Application::APP_ID, 'client_feature_enabled', 'false') === 'true'); + if ($clientFeatureEnabled + && $this->request->isUserAgent( [ IRequest::USER_AGENT_CLIENT_IOS, IRequest::USER_AGENT_CLIENT_ANDROID, IRequest::USER_AGENT_CLIENT_DESKTOP, '/^.*\(Android\)$/' ] - ); - } - - if ($isClient) { - $redirectUrl = $this->modifyRedirectUriForClient(); + )) { + $this->logger->debug('managing request as emerging from client'); + $redirectUrl = $this->modifyRedirectUriForClient($uid, $target, $jwt); + } else { + $redirectUrl = $this->urlGenerator->getAbsoluteURL($target); } $this->logger->debug('redirecting to ' . $redirectUrl); @@ -295,4 +262,32 @@ protected function autoprovisionIfNeeded($uid, $options) { $this->userBackend->createUserIfNotExists($uid); $this->userBackend->updateAttributes($uid, $options); } + + + private function modifyRedirectUriForClient( + string $uid, + string $target, + string $jwt + ): string { + $requestUri = $this->request->getRequestUri(); + $isDirectWebDavAccess = str_contains($requestUri, 'remote.php/webdav') || str_contains($requestUri, 'remote.php/dav'); + + // direct webdav access with old client or general purpose webdav clients + if ($isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client direct webdav request to ' . $target); + $redirectUrl = $target . '/remote.php/webdav/'; + } else { + $this->logger->debug('redirectUser: client request generating apptoken'); + $data = $this->createAppToken($jwt)->getData(); + if (!isset($data['token'])) { + throw new \Exception('getAppToken - data missing token: ' . json_encode($data)); + } + $appToken = $data['token']; + + $redirectUrl = 'nc://login/server:' . $requestUri . '&user:' . urlencode($uid) . '&password:' . urlencode($appToken); + } + + $this->logger->debug('generated client redirect url: ' . $redirectUrl); + return $redirectUrl; + } } diff --git a/lib/Events/AfterLoginOnSlaveEvent.php b/lib/Events/AfterLoginOnSlaveEvent.php index 8fd4e85e..2b98b08b 100644 --- a/lib/Events/AfterLoginOnSlaveEvent.php +++ b/lib/Events/AfterLoginOnSlaveEvent.php @@ -32,12 +32,11 @@ * This event is triggered after GSS login is finalized on the slave. **/ class AfterLoginOnSlaveEvent extends Event { - public function __construct(private IUser $user) - { - parent::__construct(); - } - public function getUser(): IUser - { - return $this->user; - } + public function __construct(private IUser $user) { + parent::__construct(); + } + + public function getUser(): IUser { + return $this->user; + } } diff --git a/lib/Master.php b/lib/Master.php index 34c4a562..20ef5246 100644 --- a/lib/Master.php +++ b/lib/Master.php @@ -234,33 +234,31 @@ protected function redirectUser($uid, $password, $location, array $options = []) $this->logger->debug('redirectUser: direct login so forward to target node'); $jwt = $this->createJwt($uid, $password, $options); $redirectUrl = $location . '/index.php/apps/globalsiteselector/autologin?jwt=' . $jwt; - - $clientFeatureEnabled = filter_var($this->config->getAppValue('globalsiteselector', 'client_feature_enabled', 'false'), FILTER_VALIDATE_BOOLEAN); - if (!$clientFeatureEnabled) { - $isClient = $this->request->isUserAgent( - [ - IRequest::USER_AGENT_CLIENT_IOS, - IRequest::USER_AGENT_CLIENT_ANDROID, - IRequest::USER_AGENT_CLIENT_DESKTOP, - '/^.*\(Android\)$/' - ] - ); + $clientFeatureEnabled = ($this->config->getAppValue(Application::APP_ID, 'client_feature_enabled', 'false') === 'true'); + $isClient = $this->request->isUserAgent( + [ + IRequest::USER_AGENT_CLIENT_IOS, + IRequest::USER_AGENT_CLIENT_ANDROID, + IRequest::USER_AGENT_CLIENT_DESKTOP, + '/^.*\(Android\)$/' + ] + ); + + $this->logger->debug('redirectUser client checks: ' . json_encode(['enabled' => $clientFeatureEnabled, 'isClient' => $isClient])); + if (!$clientFeatureEnabled && $isClient) { $requestUri = $this->request->getRequestUri(); // check for both possible direct webdav end-points $isDirectWebDavAccess = strpos($requestUri, 'remote.php/webdav') !== false; $isDirectWebDavAccess = $isDirectWebDavAccess || strpos($requestUri, 'remote.php/dav') !== false; // direct webdav access with old client or general purpose webdav clients - if ($isClient && $isDirectWebDavAccess) { + if ($isDirectWebDavAccess) { $this->logger->debug('redirectUser: client direct webdav request'); $redirectUrl = $location . '/remote.php/webdav/'; - } elseif ($isClient && !$isDirectWebDavAccess) { + } else { $this->logger->debug('redirectUser: client request generating apptoken'); $appToken = $this->getAppToken($location, $uid, $password, $options); - $redirectUrl = - 'nc://login/server:' . $location . '&user:' . urlencode($uid) . '&password:' . urlencode( - $appToken - ); + $redirectUrl = 'nc://login/server:' . $location . '&user:' . urlencode($uid) . '&password:' . urlencode($appToken); } }