diff --git a/lib/Controller/SlaveController.php b/lib/Controller/SlaveController.php index a91d847e..b8d679f7 100644 --- a/lib/Controller/SlaveController.php +++ b/lib/Controller/SlaveController.php @@ -24,6 +24,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; @@ -37,6 +38,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; @@ -63,6 +65,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, @@ -104,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); @@ -140,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); } @@ -150,10 +152,32 @@ 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); + $user = $this->userManager->get($uid); + if ($user instanceof IUser) { + $this->logger->debug('emitting AfterLoginOnSlaveEvent event'); + $this->eventDispatcher->dispatchTyped(new AfterLoginOnSlaveEvent($user)); + } + + /* see if we need to handle client login */ + $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\)$/' + ] + )) { + $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); - return new RedirectResponse($home); + return new RedirectResponse($redirectUrl); } /** @@ -238,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 new file mode 100644 index 00000000..2b98b08b --- /dev/null +++ b/lib/Events/AfterLoginOnSlaveEvent.php @@ -0,0 +1,42 @@ + + * + * @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; + +/** + * 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; + } +} diff --git a/lib/Master.php b/lib/Master.php index 4cc7a34e..20ef5246 100644 --- a/lib/Master.php +++ b/lib/Master.php @@ -231,6 +231,11 @@ protected function queryLookupServer(string &$uid, bool $matchUid = false): stri * @throws Exception */ 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 = ($this->config->getAppValue(Application::APP_ID, 'client_feature_enabled', 'false') === 'true'); $isClient = $this->request->isUserAgent( [ IRequest::USER_AGENT_CLIENT_IOS, @@ -240,25 +245,21 @@ protected function redirectUser($uid, $password, $location, array $options = []) ] ); - $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; + $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 ($isDirectWebDavAccess) { + $this->logger->debug('redirectUser: client direct webdav request'); + $redirectUrl = $location . '/remote.php/webdav/'; + } 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); + } } $this->logger->debug('redirectUser: redirecting to: ' . $redirectUrl); @@ -288,17 +289,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);