diff options
author | Joas Schilling <coding@schilljs.com> | 2020-07-16 22:15:39 +0300 |
---|---|---|
committer | Joas Schilling <coding@schilljs.com> | 2020-08-18 18:11:26 +0300 |
commit | 0b4fac2edfd1aa8dd7bd8225fb9ef239bfa565f6 (patch) | |
tree | ccad362547b93fee4318d7815512542d8bf95025 /lib | |
parent | 4d5fb2628a1862d2e80ed38fffc7a3a233c14d7f (diff) |
Redirect to ClientLoginFlow and ClientLoginFlowV2 when it was used
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Controller/RegisterController.php | 187 | ||||
-rw-r--r-- | lib/Service/LoginFlowService.php | 107 | ||||
-rw-r--r-- | lib/Service/RegistrationService.php | 38 |
3 files changed, 218 insertions, 114 deletions
diff --git a/lib/Controller/RegisterController.php b/lib/Controller/RegisterController.php index ee0b795..94abb43 100644 --- a/lib/Controller/RegisterController.php +++ b/lib/Controller/RegisterController.php @@ -1,4 +1,7 @@ <?php + +declare(strict_types=1); + /** * ownCloud - registration * @@ -7,53 +10,63 @@ * * @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw> * @author Julius Härtl <jus@bitgrid.net> + * @author 2020 Joas Schilling <coding@schilljs.com> * @copyright Pellaeon Lin 2014 */ namespace OCA\Registration\Controller; +use Exception; use OCA\Registration\Db\Registration; +use OCA\Registration\Service\LoginFlowService; use OCA\Registration\Service\MailService; use OCA\Registration\Service\RegistrationException; use OCA\Registration\Service\RegistrationService; +use OCP\AppFramework\Controller; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\RedirectToDefaultAppResponse; use OCP\AppFramework\Http\Response; -use \OCP\IRequest; -use \OCP\AppFramework\Http\TemplateResponse; -use \OCP\AppFramework\Http\RedirectResponse; -use \OCP\AppFramework\Controller; +use OCP\AppFramework\Http\StandaloneTemplateResponse; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\IL10N; +use OCP\IRequest; use OCP\IURLGenerator; -use \OCP\IConfig; -use \OCP\IL10N; +use OCP\IConfig; +use OCP\IL10N; class RegisterController extends Controller { /** @var IL10N */ private $l10n; /** @var IURLGenerator */ - private $urlgenerator; + private $urlGenerator; /** @var IConfig */ private $config; /** @var RegistrationService */ private $registrationService; /** @var MailService */ private $mailService; - + /** @var LoginFlowService */ + private $loginFlowService; public function __construct( - $appName, + string $appName, IRequest $request, IL10N $l10n, - IURLGenerator $urlgenerator, + IURLGenerator $urlGenerator, IConfig $config, RegistrationService $registrationService, + LoginFlowService $loginFlowService, MailService $mailService ) { parent::__construct($appName, $request); $this->l10n = $l10n; - $this->urlgenerator = $urlgenerator; + $this->urlGenerator = $urlGenerator; $this->config = $config; $this->registrationService = $registrationService; + $this->loginFlowService = $loginFlowService; $this->mailService = $mailService; } @@ -102,7 +115,7 @@ class RegisterController extends Controller { } return new RedirectResponse( - $this->urlgenerator->linkToRoute( + $this->urlGenerator->linkToRoute( 'registration.register.showVerificationForm', ['secret' => $registration->getClientSecret()] ) @@ -120,12 +133,8 @@ class RegisterController extends Controller { public function showVerificationForm(string $secret, string $message = ''): TemplateResponse { try { $this->registrationService->getRegistrationForSecret($secret); - } catch (RegistrationException $e) { - return new TemplateResponse('core', 'error', [ - 'errors' => [ - $this->l10n->t('The verification secret does not exist anymore'), - ], - ], 'error'); + } catch (DoesNotExistException $e) { + return $this->validateSecretAndTokenErrorPage(); } return new TemplateResponse('registration', 'form/verification', [ @@ -151,16 +160,12 @@ class RegisterController extends Controller { $this->l10n->t('The entered verification code is wrong') ); } - } catch (RegistrationException $e) { - return new TemplateResponse('core', 'error', [ - 'errors' => [ - $this->l10n->t('The verification secret does not exist anymore'), - ], - ], 'error'); + } catch (DoesNotExistException $e) { + return $this->validateSecretAndTokenErrorPage(); } return new RedirectResponse( - $this->urlgenerator->linkToRoute( + $this->urlGenerator->linkToRoute( 'registration.register.showUserForm', [ 'secret' => $secret, @@ -176,95 +181,101 @@ class RegisterController extends Controller { * * @param string $secret * @param string $token + * @param string $username + * @param string $message * @return TemplateResponse */ - public function showUserForm(string $secret, string $token): TemplateResponse { + public function showUserForm(string $secret, string $token, string $username = '', string $message = ''): TemplateResponse { try { - $registration = $this->registrationService->getRegistrationForSecret($secret); - - if ($registration->getToken() !== $token) { - throw new RegistrationException('Invalid verification token'); - } + $registration = $this->validateSecretAndToken($secret, $token); } catch (RegistrationException $e) { - return new TemplateResponse('core', 'error', [ - 'errors' => [ - $this->l10n->t('The verification secret does not exist anymore or the verification token is invalid'), - ], - ], 'error'); + return $this->validateSecretAndTokenErrorPage(); } - try { - /** @var Registration $registration */ - $registration = $this->registrationService->verifyToken($token); - $this->registrationService->confirmEmail($registration); - - // create account without form if username/password are already stored - if ($registration->getUsername() !== "" && $registration->getPassword() !== "") { - $this->registrationService->createAccount($registration); - return new TemplateResponse('registration', 'message', - ['msg' => $this->l10n->t('Your account has been successfully created, you can <a href="%s">log in now</a>.', [$this->urlgenerator->getAbsoluteURL('/')])], - 'guest' - ); - } - - return new TemplateResponse('registration', 'form/user', [ - 'email' => $registration->getEmail(), - 'email_is_login' => $this->config->getAppValue('registration', 'email_is_login', '0') === '1', - 'token' => $registration->getToken(), - ], 'guest'); - } catch (RegistrationException $exception) { - return $this->renderError($exception->getMessage(), $exception->getHint()); - } + return new TemplateResponse('registration', 'form/user', [ + 'email' => $registration->getEmail(), + 'email_is_login' => $this->config->getAppValue('registration', 'email_is_login', '0') === '1', + 'username' => $username, + 'message' => $message, + ], 'guest'); } /** * @PublicPage * @UseSession * - * @param $token + * @param string $secret + * @param string $token + * @param string $username + * @param string $password * @return RedirectResponse|TemplateResponse */ - public function submitUserForm($token) { - $registration = $this->registrationService->getRegistrationForToken($token); + public function submitUserForm(string $secret, string $token, string $username, string $password): Response { + try { + $registration = $this->validateSecretAndToken($secret, $token); + } catch (RegistrationException $e) { + return $this->validateSecretAndTokenErrorPage(); + } + if ($this->config->getAppValue('registration', 'email_is_login', '0') === '1') { $username = $registration->getEmail(); - } else { - $username = $this->request->getParam('username'); } - $password = $this->request->getParam('password'); try { $user = $this->registrationService->createAccount($registration, $username, $password); - } catch (\Exception $exception) { - // Render form with previously sent values - return new TemplateResponse('registration', 'form', - [ - 'email' => $registration->getEmail(), - 'entered_data' => ['user' => $username], - 'errormsgs' => [$exception->getMessage()], - 'token' => $token - ], 'guest'); + } catch (Exception $exception) { + return $this->showUserForm($secret, $token, $username, $exception->getMessage()); } if ($user->isEnabled()) { - // log the user - return $this->registrationService->loginUser($user->getUID(), $username, $password, false); - } else { - // warn the user their account needs admin validation - return new TemplateResponse( - 'registration', - 'message', - ['msg' => $this->l10n->t("Your account has been successfully created, but it still needs approval from an administrator.")], - 'guest'); + $this->registrationService->loginUser($user->getUID(), $user->getUID(), $password); + + if ($this->loginFlowService->isUsingLoginFlow(2)) { + $response = $this->loginFlowService->tryLoginFlowV2($user); + if ($response instanceof Response) { + return $response; + } + } + + if ($this->loginFlowService->isUsingLoginFlow(1)) { + $response = $this->loginFlowService->tryLoginFlowV1(); + if ($response instanceof Response && $response->getStatus() === Http::STATUS_SEE_OTHER) { + return $response; + } + } + + return new RedirectToDefaultAppResponse(); } + + // warn the user their account needs admin validation + return new StandaloneTemplateResponse('registration', 'approval-required', [], 'guest'); + } + + /** + * @param string $secret + * @param string $token + * @return Registration + * @throws RegistrationException + */ + protected function validateSecretAndToken(string $secret, string $token): Registration { + try { + $registration = $this->registrationService->getRegistrationForSecret($secret); + } catch (DoesNotExistException $e) { + throw new RegistrationException('Invalid secret'); + } + + if ($registration->getToken() !== $token) { + throw new RegistrationException('Invalid token'); + } + + return $registration; } - private function renderError($error, $hint="") { - return new TemplateResponse('', 'error', [ - 'errors' => [[ - 'error' => $error, - 'hint' => $hint - ]] + protected function validateSecretAndTokenErrorPage(): TemplateResponse { + return new TemplateResponse('core', 'error', [ + 'errors' => [ + $this->l10n->t('The verification failed.'), + ], ], 'error'); } } diff --git a/lib/Service/LoginFlowService.php b/lib/Service/LoginFlowService.php new file mode 100644 index 0000000..9e5e7e3 --- /dev/null +++ b/lib/Service/LoginFlowService.php @@ -0,0 +1,107 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> + * + * @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 <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Registration\Service; + +use OC\Core\Controller\ClientFlowLoginController; +use OC\Core\Controller\ClientFlowLoginV2Controller; +use OC\Core\Service\LoginFlowV2Service; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\StandaloneTemplateResponse; +use OCP\IRequest; +use OCP\ISession; +use OCP\IUser; + +class LoginFlowService { + + /** @var IRequest */ + protected $request; + /** @var ISession */ + protected $session; + /** @var LoginFlowV2Service */ + protected $loginFlowV2Service; + + public function __construct( + IRequest $request, + ISession $session, + LoginFlowV2Service $loginFlowV2Service + ) { + $this->request = $request; + $this->session = $session; + $this->loginFlowV2Service = $loginFlowV2Service; + } + + public function isUsingLoginFlow(?int $version = null): bool { + if (($version === 1 || $version === null) && $this->session->get(ClientFlowLoginController::STATE_NAME) !== null) { + return true; + } + + if (($version === 2 || $version === null) && $this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME) !== null) { + return true; + } + + return false; + } + + public function tryLoginFlowV1(): ?Response { + /** @var ClientFlowLoginController $controller */ + $container = \OC::$server->getRegisteredAppContainer('core'); + $controller = $container->query(ClientFlowLoginController::class); + return $controller->generateAppPassword( + $this->session->get(ClientFlowLoginController::STATE_NAME) + ); + } + + public function tryLoginFlowV2(IUser $user): ?StandaloneTemplateResponse { + $result = $this->loginFlowV2Service->flowDone( + $this->session->get(ClientFlowLoginV2Controller::TOKEN_NAME), + $this->session->getId(), + $this->getServerPath(), + $user->getUID() + ); + + if (!$result) { + return null; + } + + return new StandaloneTemplateResponse( + 'core', + 'loginflowv2/done', + [], + 'guest' + ); + } + + private function getServerPath(): string { + $serverPostfix = ''; + + if (strpos($this->request->getRequestUri(), '/index.php') !== false) { + $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/index.php')); + } elseif (strpos($this->request->getRequestUri(), '/login/v2') !== false) { + $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/login/v2')); + } + + $protocol = $this->request->getServerProtocol(); + return $protocol . '://' . $this->request->getServerHost() . $serverPostfix; + } +} diff --git a/lib/Service/RegistrationService.php b/lib/Service/RegistrationService.php index 3d5bb85..49528d8 100644 --- a/lib/Service/RegistrationService.php +++ b/lib/Service/RegistrationService.php @@ -40,6 +40,7 @@ use OCP\ILogger; use OCP\IRequest; use OCP\ISession; use OCP\IURLGenerator; +use OCP\IUser; use OCP\Security\ICrypto; use OCP\Session\Exceptions\SessionNotAvailableException; use \OCP\IUserManager; @@ -247,12 +248,12 @@ class RegistrationService { /** * @param $registration - * @param string $username - * @param string $password + * @param string|null $username + * @param string|null $password * @return \OCP\IUser * @throws RegistrationException|InvalidTokenException */ - public function createAccount(Registration $registration, $username = null, $password = null) { + public function createAccount(Registration $registration, ?string $username = null, ?string $password = null) { if ($password === null && $registration->getPassword() === null) { $generatedPassword = $this->generateRandomDeviceToken(); $registration->setPassword($this->crypto->encrypt($generatedPassword)); @@ -414,33 +415,18 @@ class RegistrationService { } /** - * @param $userId - * @param $username - * @param $password - * @param $decrypt - * @return RedirectResponse|TemplateResponse + * @param string $userId + * @param string $username + * @param string $password + * @param bool $decrypt */ - public function loginUser($userId, $username, $password, $decrypt = false) { + public function loginUser(string $userId, string $username, string $password, bool $decrypt = false): void { if ($decrypt) { $password = $this->crypto->decrypt($password); } - if (method_exists($this->usersession, 'createSessionToken')) { - $this->usersession->login($username, $password); - $this->usersession->createSessionToken($this->request, $userId, $username, $password); - return new RedirectResponse($this->urlGenerator->linkTo('', 'index.php')); - } elseif (\OC_User::login($username, $password)) { - $this->cleanupLoginTokens($userId); - // FIXME unsetMagicInCookie will fail from session already closed, so now we always remember - $logintoken = $this->random->generate(32); - $this->config->setUserValue($userId, 'login_token', $logintoken, time()); - \OC_User::setMagicInCookie($userId, $logintoken); - \OC_Util::redirectToDefaultPage(); - } - // Render message in case redirect failed - return new TemplateResponse('registration', 'message', - ['msg' => $this->l10n->t('Your account has been successfully created, you can <a href="%s">log in now</a>.', [$this->urlGenerator->getAbsoluteURL('/')])] - , 'guest' - ); + + $this->usersession->login($username, $password); + $this->usersession->createSessionToken($this->request, $userId, $username, $password); } /** |