Merge pull request #234 from nextcloud/techdebt/223/login-flow-v2
Login Flow v2 compatibility
This commit is contained in:
commit
10bce3e131
|
|
@ -1,3 +1,5 @@
|
|||
/.php_cs.cache
|
||||
/tests/clover.xml
|
||||
/tests/coverage-html
|
||||
/tests/.phpunit.result.cache
|
||||
/vendor
|
||||
|
|
|
|||
|
|
@ -13,14 +13,16 @@
|
|||
return [
|
||||
'routes' => [
|
||||
['name' => 'settings#admin', 'url' => '/settings', 'verb' => 'POST'],
|
||||
['name' => 'register#askEmail', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'register#validateEmail', 'url' => '/', 'verb' => 'POST'],
|
||||
['name' => 'register#verifyToken', 'url' => '/verify/{token}', 'verb' => 'GET'],
|
||||
['name' => 'register#createAccount', 'url' => '/verify/{token}', 'verb' => 'POST']
|
||||
['name' => 'register#showEmailForm', 'url' => '/', 'verb' => 'GET'],
|
||||
['name' => 'register#submitEmailForm', 'url' => '/', 'verb' => 'POST'],
|
||||
['name' => 'register#showVerificationForm', 'url' => '/verify/{secret}', 'verb' => 'GET'],
|
||||
['name' => 'register#submitVerificationForm', 'url' => '/verify/{secret}', 'verb' => 'POST'],
|
||||
['name' => 'register#showUserForm', 'url' => '/register/{secret}/{token}', 'verb' => 'GET'],
|
||||
['name' => 'register#submitUserForm', 'url' => '/register/{secret}/{token}', 'verb' => 'POST'],
|
||||
],
|
||||
'ocs' => [
|
||||
['root' => '/registration', 'name' => 'api#validate', 'url' => '/v1/validate', 'verb' => 'POST'],
|
||||
['root' => '/registration', 'name' => 'api#status', 'url' => '/v1/status', 'verb' => 'POST'],
|
||||
['root' => '/registration', 'name' => 'api#register', 'url' => '/v1/register', 'verb' => 'POST']
|
||||
['root' => '/registration', 'name' => 'api#register', 'url' => '/v1/register', 'verb' => 'POST'],
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@
|
|||
color: var(--color-primary-element);
|
||||
}
|
||||
|
||||
.register-button:only-child {
|
||||
#alternative-logins .register-button:only-child {
|
||||
width: 220px;
|
||||
margin-left: 70px !important;
|
||||
}
|
||||
|
||||
#alternative-logins .register-button.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
#body-login #email, #body-login #username, #body-login #password {
|
||||
#body-login #email,
|
||||
#body-login #token,
|
||||
#body-login #username,
|
||||
#body-login #password {
|
||||
width: calc(100% - 56px);
|
||||
padding-left: 36px;
|
||||
}
|
||||
#email-icon, #username-icon, #password-icon {
|
||||
#email-icon,
|
||||
#token-icon,
|
||||
#username-icon,
|
||||
#password-icon {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 22px;
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="m8 0a3 3 0 0 0 -2.828 2 3 3 0 0 0 -0.172 -0 3 3 0 0 0 -3 3 3 3 0 0 0 0 0.172 3 3 0 0 0 -2 2.828 3 3 0 0 0 2 2.828 3 3 0 0 0 -0 0.172 3 3 0 0 0 3 3 3 3 0 0 0 0.172 -0 3 3 0 0 0 2.828 2 3 3 0 0 0 2.828 -2.01 3 3 0 0 0 0.172 0.01 3 3 0 0 0 3 -3 3 3 0 0 0 -0 -0.172 3 3 0 0 0 2 -2.828 3 3 0 0 0 -2.01 -2.828 3 3 0 0 0 0.01 -0.172 3 3 0 0 0 -3 -3 3 3 0 0 0 -0.172 0 3 3 0 0 0 -2.828 -2zm2.934 4.5625 1.433 1.4336-5.7772 5.7789-2.9511-2.9508 1.414-1.414 1.5371 1.5351 4.3442-4.3828z" fill="#000000"/></svg>
|
||||
|
After Width: | Height: | Size: 607 B |
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* ownCloud - registration
|
||||
*
|
||||
|
|
@ -7,51 +10,62 @@
|
|||
*
|
||||
* @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\IRequest;
|
||||
use \OCP\AppFramework\Http\TemplateResponse;
|
||||
use \OCP\AppFramework\Http\RedirectResponse;
|
||||
use \OCP\AppFramework\Controller;
|
||||
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\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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -59,136 +73,213 @@ class RegisterController extends Controller {
|
|||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @param $errormsg
|
||||
* @param $entered
|
||||
* @param string $email
|
||||
* @param string $message
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function askEmail($errormsg, $entered) {
|
||||
public function showEmailForm(string $email = '', string $message = ''): TemplateResponse {
|
||||
$params = [
|
||||
'errormsg' => $errormsg ? $errormsg : $this->request->getParam('errormsg'),
|
||||
'entered' => $entered ? $entered : $this->request->getParam('entered')
|
||||
'email' => $email,
|
||||
'message' => $message,
|
||||
];
|
||||
return new TemplateResponse('registration', 'register', $params, 'guest');
|
||||
return new TemplateResponse('registration', 'form/email', $params, 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* User POST email, if email is valid and not duplicate, we send token by mail
|
||||
* @PublicPage
|
||||
* @AnonRateThrottle(limit=5, period=1)
|
||||
*
|
||||
* @param string $email
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function validateEmail($email) {//TODO rename to receiveUserEmail
|
||||
if (!$this->registrationService->checkAllowedDomains($email)) {//TODO Duplicate code with Service
|
||||
return new TemplateResponse('registration', 'domains', [
|
||||
'domains' => $this->registrationService->getAllowedDomains()
|
||||
], 'guest');
|
||||
}
|
||||
public function submitEmailForm(string $email): Response {
|
||||
try {
|
||||
$reg = $this->registrationService->validateEmail($email);
|
||||
if ($reg === true) {
|
||||
$registration = $this->registrationService->createRegistration($email);
|
||||
$this->mailService->sendTokenByMail($registration);
|
||||
} else {
|
||||
$this->registrationService->generateNewToken($reg);
|
||||
$this->mailService->sendTokenByMail($reg);
|
||||
return new TemplateResponse('registration', 'message', ['msg' =>
|
||||
$this->l10n->t('There is already a pending registration with this email, a new verification email has been sent to the address.')
|
||||
], 'guest');
|
||||
}
|
||||
// Registration already in progress, update token and continue with verification
|
||||
$registration = $this->registrationService->getRegistrationForEmail($email);
|
||||
$this->registrationService->generateNewToken($registration);
|
||||
} catch (DoesNotExistException $e) {
|
||||
// No registration in progress
|
||||
try {
|
||||
$this->registrationService->validateEmail($email);
|
||||
} catch (RegistrationException $e) {
|
||||
return new TemplateResponse('registration', 'message', ['msg' =>
|
||||
$e->getMessage().'<br/>'.$e->getHint()
|
||||
], 'guest');
|
||||
return $this->showEmailForm($email, $e->getMessage());
|
||||
}
|
||||
|
||||
$registration = $this->registrationService->createRegistration($email);
|
||||
}
|
||||
|
||||
return new TemplateResponse('registration', 'message', ['msg' =>
|
||||
$this->l10n->t('Verification email successfully sent.')
|
||||
], 'guest');
|
||||
try {
|
||||
$this->mailService->sendTokenByMail($registration);
|
||||
} catch (RegistrationException $e) {
|
||||
return $this->showEmailForm($email, $e->getMessage());
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->linkToRoute(
|
||||
'registration.register.showVerificationForm',
|
||||
['secret' => $registration->getClientSecret()]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @param $token
|
||||
* @param string $secret
|
||||
* @param string $message
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function verifyToken($token) {
|
||||
public function showVerificationForm(string $secret, string $message = ''): TemplateResponse {
|
||||
try {
|
||||
/** @var Registration $registration */
|
||||
$registration = $this->registrationService->verifyToken($token);
|
||||
$this->registrationService->confirmEmail($registration);
|
||||
$this->registrationService->getRegistrationForSecret($secret);
|
||||
} catch (DoesNotExistException $e) {
|
||||
return $this->validateSecretAndTokenErrorPage();
|
||||
}
|
||||
|
||||
// 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/verification', [
|
||||
'message' => $message,
|
||||
], 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @AnonRateThrottle(limit=5, period=1)
|
||||
*
|
||||
* @param string $secret
|
||||
* @param string $token
|
||||
* @return Response
|
||||
*/
|
||||
public function submitVerificationForm(string $secret, string $token): Response {
|
||||
try {
|
||||
$registration = $this->registrationService->getRegistrationForSecret($secret);
|
||||
|
||||
if ($registration->getToken() !== $token) {
|
||||
return $this->showVerificationForm(
|
||||
$secret,
|
||||
$this->l10n->t('The entered verification code is wrong')
|
||||
);
|
||||
}
|
||||
} catch (DoesNotExistException $e) {
|
||||
return $this->validateSecretAndTokenErrorPage();
|
||||
}
|
||||
|
||||
return new RedirectResponse(
|
||||
$this->urlGenerator->linkToRoute(
|
||||
'registration.register.showUserForm',
|
||||
[
|
||||
'secret' => $secret,
|
||||
'token' => $token,
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new TemplateResponse('registration', 'form', [
|
||||
/**
|
||||
* @NoCSRFRequired
|
||||
* @PublicPage
|
||||
*
|
||||
* @param string $secret
|
||||
* @param string $token
|
||||
* @param string $username
|
||||
* @param string $message
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function showUserForm(string $secret, string $token, string $username = '', string $message = ''): TemplateResponse {
|
||||
try {
|
||||
$registration = $this->validateSecretAndToken($secret, $token);
|
||||
} catch (RegistrationException $e) {
|
||||
return $this->validateSecretAndTokenErrorPage();
|
||||
}
|
||||
|
||||
return new TemplateResponse('registration', 'form/user', [
|
||||
'email' => $registration->getEmail(),
|
||||
'email_is_login' => $this->config->getAppValue('registration', 'email_is_login', '0') === '1',
|
||||
'token' => $registration->getToken(),
|
||||
'username' => $username,
|
||||
'message' => $message,
|
||||
], 'guest');
|
||||
} catch (RegistrationException $exception) {
|
||||
return $this->renderError($exception->getMessage(), $exception->getHint());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @UseSession
|
||||
* @AnonRateThrottle(limit=5, period=1)
|
||||
*
|
||||
* @param $token
|
||||
* @param string $secret
|
||||
* @param string $token
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return RedirectResponse|TemplateResponse
|
||||
*/
|
||||
public function createAccount($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());
|
||||
}
|
||||
|
||||
// Delete registration
|
||||
$this->registrationService->deleteRegistration($registration);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private function renderError($error, $hint="") {
|
||||
return new TemplateResponse('', 'error', [
|
||||
'errors' => [[
|
||||
'error' => $error,
|
||||
'hint' => $hint
|
||||
]]
|
||||
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;
|
||||
}
|
||||
|
||||
protected function validateSecretAndTokenErrorPage(): TemplateResponse {
|
||||
return new TemplateResponse('core', 'error', [
|
||||
'errors' => [
|
||||
$this->l10n->t('The verification failed.'),
|
||||
],
|
||||
], 'error');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class RegistrationLoginOption implements IAlternativeLogin {
|
|||
}
|
||||
|
||||
public function getLink(): string {
|
||||
return $this->url->linkToRoute('registration.register.askEmail');
|
||||
return $this->url->linkToRoute('registration.register.showEmailForm');
|
||||
}
|
||||
|
||||
public function getClass(): string {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +81,10 @@ class MailService {
|
|||
* @throws RegistrationException
|
||||
*/
|
||||
public function sendTokenByMail(Registration $registration): void {
|
||||
$link = $this->urlGenerator->linkToRouteAbsolute('registration.register.verifyToken', ['token' => $registration->getToken()]);
|
||||
$link = $this->urlGenerator->linkToRouteAbsolute('registration.register.showUserForm', [
|
||||
'secret' => $registration->getClientSecret(),
|
||||
'token' => $registration->getToken(),
|
||||
]);
|
||||
$subject = $this->l10n->t('Verify your %s registration request', [$this->defaults->getName()]);
|
||||
|
||||
$template = $this->mailer->createEMailTemplate('registration_verify', [
|
||||
|
|
@ -100,6 +103,10 @@ class MailService {
|
|||
$body
|
||||
);
|
||||
|
||||
$template->addBodyText(
|
||||
$this->l10n->t('Verification code: %s', $registration->getToken())
|
||||
);
|
||||
|
||||
$template->addBodyButton(
|
||||
$this->l10n->t('Continue registration'),
|
||||
$link
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
|
||||
* @copyright Copyright (c) 2017 Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
|
||||
|
|
@ -34,12 +37,11 @@ use OC\Authentication\Token\IToken;
|
|||
use OCA\Registration\Db\Registration;
|
||||
use OCA\Registration\Db\RegistrationMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use \OCP\AppFramework\Http\TemplateResponse;
|
||||
use \OCP\AppFramework\Http\RedirectResponse;
|
||||
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;
|
||||
|
|
@ -70,7 +72,7 @@ class RegistrationService {
|
|||
/** @var ISecureRandom */
|
||||
private $random;
|
||||
/** @var IUserSession */
|
||||
private $usersession;
|
||||
private $userSession;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var ILogger */
|
||||
|
|
@ -82,9 +84,23 @@ class RegistrationService {
|
|||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
public function __construct($appName, MailService $mailService, IL10N $l10n, IURLGenerator $urlGenerator,
|
||||
RegistrationMapper $registrationMapper, IUserManager $userManager, IConfig $config, IGroupManager $groupManager,
|
||||
ISecureRandom $random, IUserSession $us, IRequest $request, ILogger $logger, ISession $session, IProvider $tokenProvider, ICrypto $crypto) {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
MailService $mailService,
|
||||
IL10N $l10n,
|
||||
IURLGenerator $urlGenerator,
|
||||
RegistrationMapper $registrationMapper,
|
||||
IUserManager $userManager,
|
||||
IConfig $config,
|
||||
IGroupManager $groupManager,
|
||||
ISecureRandom $random,
|
||||
IUserSession $userSession,
|
||||
IRequest $request,
|
||||
ILogger $logger,
|
||||
ISession $session,
|
||||
IProvider $tokenProvider,
|
||||
ICrypto $crypto
|
||||
) {
|
||||
$this->appName = $appName;
|
||||
$this->mailService = $mailService;
|
||||
$this->l10n = $l10n;
|
||||
|
|
@ -94,7 +110,7 @@ class RegistrationService {
|
|||
$this->config = $config;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->random = $random;
|
||||
$this->usersession = $us;
|
||||
$this->userSession = $userSession;
|
||||
$this->request = $request;
|
||||
$this->logger = $logger;
|
||||
$this->session = $session;
|
||||
|
|
@ -102,21 +118,16 @@ class RegistrationService {
|
|||
$this->crypto = $crypto;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function confirmEmail(Registration $registration) {
|
||||
public function confirmEmail(Registration $registration): void {
|
||||
$registration->setEmailConfirmed(true);
|
||||
$this->registrationMapper->update($registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function generateNewToken(Registration $registration) {
|
||||
public function generateNewToken(Registration $registration): void {
|
||||
$this->registrationMapper->generateNewToken($registration);
|
||||
$this->registrationMapper->update($registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create registration request, used by both the API and form
|
||||
* @param string $email
|
||||
|
|
@ -125,7 +136,7 @@ class RegistrationService {
|
|||
* @param string $displayname
|
||||
* @return Registration
|
||||
*/
|
||||
public function createRegistration($email, $username="", $password="", $displayname="") {
|
||||
public function createRegistration(string $email, string $username = '', string $password = '', string $displayname = ''): Registration {
|
||||
$registration = new Registration();
|
||||
$registration->setEmail($email);
|
||||
$registration->setUsername($username);
|
||||
|
|
@ -135,24 +146,24 @@ class RegistrationService {
|
|||
$registration->setPassword($password);
|
||||
}
|
||||
$this->registrationMapper->generateNewToken($registration);
|
||||
if ($password !== '' && $username !== '') {
|
||||
$this->registrationMapper->generateClientSecret($registration);
|
||||
}
|
||||
$this->registrationMapper->insert($registration);
|
||||
return $registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
* @return Registration|true if there is a pending reg with this email, return the pending reg, if there are no problems with the email, return true.
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateEmail($email) {
|
||||
public function validateEmail(string $email): void {
|
||||
$this->mailService->validateEmail($email);
|
||||
|
||||
// check for pending registrations
|
||||
try {
|
||||
return $this->registrationMapper->find($email);//if not found DB will throw a exception
|
||||
$this->registrationMapper->find($email);//if not found DB will throw a exception
|
||||
throw new RegistrationException(
|
||||
$this->l10n->t('A user has already taken this email, maybe you already have an account?')
|
||||
);
|
||||
} catch (DoesNotExistException $e) {
|
||||
}
|
||||
|
||||
|
|
@ -171,15 +182,14 @@ class RegistrationService {
|
|||
)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $displayname
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateDisplayname($displayname) {
|
||||
if ($displayname === "") {
|
||||
public function validateDisplayname(string $displayname): void {
|
||||
if ($displayname === '') {
|
||||
throw new RegistrationException($this->l10n->t('Please provide a valid display name.'));
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +198,7 @@ class RegistrationService {
|
|||
* @param string $username
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateUsername($username) {
|
||||
public function validateUsername(string $username): void {
|
||||
if ($username === "") {
|
||||
throw new RegistrationException($this->l10n->t('Please provide a valid user name.'));
|
||||
}
|
||||
|
|
@ -204,15 +214,15 @@ class RegistrationService {
|
|||
* @param string $email
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAllowedDomains($email) {
|
||||
$allowed_domains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
if ($allowed_domains !== '') {
|
||||
$allowed_domains = explode(';', $allowed_domains);
|
||||
public function checkAllowedDomains(string $email): bool {
|
||||
$allowedDomains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
if ($allowedDomains !== '') {
|
||||
$allowedDomains = explode(';', $allowedDomains);
|
||||
$allowed = false;
|
||||
foreach ($allowed_domains as $domain) {
|
||||
$maildomain = explode("@", $email)[1];
|
||||
// valid domain, everythings fine
|
||||
if ($maildomain === $domain) {
|
||||
foreach ($allowedDomains as $domain) {
|
||||
[,$mailDomain] = explode('@', $email, 2);
|
||||
// valid domain, everything's fine
|
||||
if ($mailDomain === $domain) {
|
||||
$allowed = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -223,22 +233,22 @@ class RegistrationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return string[]
|
||||
*/
|
||||
public function getAllowedDomains() {
|
||||
$allowed_domains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
$allowed_domains = explode(';', $allowed_domains);
|
||||
return $allowed_domains;
|
||||
public function getAllowedDomains(): array {
|
||||
$allowedDomains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
$allowedDomains = explode(';', $allowedDomains);
|
||||
return $allowedDomains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find registration entity for token
|
||||
*
|
||||
* @param string $token
|
||||
* @return string
|
||||
* @return Registration
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function verifyToken($token) {
|
||||
public function verifyToken(string $token): Registration {
|
||||
try {
|
||||
return $this->registrationMapper->findByToken($token);
|
||||
} catch (DoesNotExistException $exception) {
|
||||
|
|
@ -248,12 +258,12 @@ class RegistrationService {
|
|||
|
||||
/**
|
||||
* @param $registration
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return \OCP\IUser
|
||||
* @param string|null $username
|
||||
* @param string|null $password
|
||||
* @return IUser
|
||||
* @throws RegistrationException|InvalidTokenException
|
||||
*/
|
||||
public function createAccount(Registration $registration, $username = null, $password = null) {
|
||||
public function createAccount(Registration $registration, ?string $username = null, ?string $password = null): IUser {
|
||||
if ($password === null && $registration->getPassword() === null) {
|
||||
$generatedPassword = $this->generateRandomDeviceToken();
|
||||
$registration->setPassword($this->crypto->encrypt($generatedPassword));
|
||||
|
|
@ -281,6 +291,7 @@ class RegistrationService {
|
|||
throw new RegistrationException($this->l10n->t('Unable to create user, there are problems with the user backend.'));
|
||||
}
|
||||
$userId = $user->getUID();
|
||||
|
||||
// Set user email
|
||||
try {
|
||||
$user->setEMailAddress($registration->getEmail());
|
||||
|
|
@ -289,72 +300,64 @@ class RegistrationService {
|
|||
}
|
||||
|
||||
// Add user to group
|
||||
$registered_user_group = $this->config->getAppValue($this->appName, 'registered_user_group', 'none');
|
||||
if ($registered_user_group !== 'none') {
|
||||
$group = $this->groupManager->get($registered_user_group);
|
||||
$registeredUserGroup = $this->config->getAppValue($this->appName, 'registered_user_group', 'none');
|
||||
if ($registeredUserGroup !== 'none') {
|
||||
$group = $this->groupManager->get($registeredUserGroup);
|
||||
if ($group === null) {
|
||||
// This might happen if $registered_user_group is deleted after setting the value
|
||||
// Here I choose to log error instead of stopping the user to register
|
||||
$this->logger->error("You specified newly registered users be added to '$registered_user_group' group, but it does not exist.");
|
||||
$this->logger->error("You specified newly registered users be added to '$registeredUserGroup' group, but it does not exist.");
|
||||
$groupId = '';
|
||||
} else {
|
||||
$group->addUser($user);
|
||||
$groupId = $group->getGID();
|
||||
}
|
||||
} else {
|
||||
$groupId = "";
|
||||
$groupId = '';
|
||||
}
|
||||
|
||||
// disable user if this is requested by config
|
||||
$admin_approval_required = $this->config->getAppValue($this->appName, 'admin_approval_required', "no");
|
||||
if ($admin_approval_required === "yes") {
|
||||
$adminApprovalRequired = $this->config->getAppValue($this->appName, 'admin_approval_required', 'no');
|
||||
if ($adminApprovalRequired === 'yes') {
|
||||
$user->setEnabled(false);
|
||||
}
|
||||
|
||||
// Delete pending registration if no client secret is stored
|
||||
// with client secret implies registered via API
|
||||
// without client secret implies registered via form
|
||||
// if registered via API, the registration request will be deleted in apicontroller::status
|
||||
if ($registration->getClientSecret() === null) {
|
||||
$res = $this->registrationMapper->delete($registration);
|
||||
if ($res === false) {
|
||||
throw new RegistrationException($this->l10n->t('Failed to delete pending registration request'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->mailService->notifyAdmins($userId, $user->isEnabled(), $groupId);
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $token
|
||||
* @param string $email
|
||||
* @return Registration
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getRegistrationForToken($token) {
|
||||
public function getRegistrationForEmail(string $email): Registration {
|
||||
return $this->registrationMapper->find($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return Registration
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getRegistrationForToken(string $token): Registration {
|
||||
return $this->registrationMapper->findByToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $secret
|
||||
* @param string $secret
|
||||
* @return Registration
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getRegistrationForSecret($secret) {
|
||||
public function getRegistrationForSecret(string $secret): Registration {
|
||||
return $this->registrationMapper->findBySecret($secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registation
|
||||
* @return null|\OCP\IUser
|
||||
*/
|
||||
public function getUserAccount(Registration $registation) {
|
||||
$user = $this->userManager->get($registation->getUsername());
|
||||
return $user;
|
||||
public function getUserAccount(Registration $registration): ?IUser {
|
||||
return $this->userManager->get($registration->getUsername());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function deleteRegistration(Registration $registration) {
|
||||
public function deleteRegistration(Registration $registration): void {
|
||||
$this->registrationMapper->delete($registration);
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +368,7 @@ class RegistrationService {
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateRandomDeviceToken() {
|
||||
private function generateRandomDeviceToken(): string {
|
||||
$groups = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE);
|
||||
|
|
@ -378,7 +381,7 @@ class RegistrationService {
|
|||
* @return string
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function generateAppPassword($uid) {
|
||||
public function generateAppPassword(string $uid): string {
|
||||
$name = $this->l10n->t('Registration app auto setup');
|
||||
try {
|
||||
$sessionId = $this->session->getId();
|
||||
|
|
@ -404,40 +407,25 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replicates OC::cleanupLoginTokens() since it's protected
|
||||
* @param string $userId
|
||||
*/
|
||||
public function cleanupLoginTokens($userId) {
|
||||
public function cleanupLoginTokens(string $userId): void {
|
||||
$cutoff = time() - $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
|
||||
$tokens = $this->config->getUserKeys($userId, 'login_token');
|
||||
foreach ($tokens as $token) {
|
||||
|
|
|
|||
|
|
@ -26,9 +26,13 @@ foreach ($_['groups'] as $group) {
|
|||
</p>
|
||||
|
||||
<div style="margin-top: 10px;">
|
||||
<p>
|
||||
<input type="checkbox" id="admin_approval_required" class="checkbox" name="admin_approval_required" <?php if ($_['approval_required'] === "yes") {
|
||||
echo " checked";
|
||||
} ?>>
|
||||
<label for="admin_approval_required"><?php p($l->t('Require admin approval?')); ?></label>
|
||||
</p>
|
||||
|
||||
<em><?php p($l->t('Enabling "admin approval" will prevent registrations from mobile and desktop clients to complete as the credentials can not be verified by the client until the user was enabled.'));?></em>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
?>
|
||||
<div class="error">
|
||||
<h2><?php p($l->t('Approval required')) ?></h2>
|
||||
<ul>
|
||||
<li>
|
||||
<p><?php p($l->t('Your account has been successfully created, but it still needs approval from an administrator.')) ?></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
?>
|
||||
<ul class="error-wide">
|
||||
<li class='error'><?php p($l->t('Registration is only allowed for the following domains:')); ?>
|
||||
<?php
|
||||
foreach ($_['domains'] as $domain) {
|
||||
echo "<p class='hint'>";
|
||||
p($domain);
|
||||
echo "</p>";
|
||||
}
|
||||
?>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
?>
|
||||
<form action="" method="post">
|
||||
<fieldset>
|
||||
<?php if ($_['message']): ?>
|
||||
<ul class="error">
|
||||
<li><?php p($_['message']); ?></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<p class="groupofone">
|
||||
<input type="email" name="email" id="email" placeholder="<?php p($l->t('Email')); ?>" value="<?php p($_['email']); ?>" required autofocus />
|
||||
<label for="email" class="infield"><?php p($l->t('Email')); ?></label>
|
||||
<img id="email-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/mail.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" />
|
||||
<input type="submit" id="submit" value="<?php p($l->t('Request verification link')); ?>" />
|
||||
|
||||
<a id="lost-password-back" href="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('core.login.showLoginForm')) ?>">
|
||||
<?php p($l->t('Back to login')); ?>
|
||||
</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
@ -23,7 +23,7 @@ script('registration', 'form');
|
|||
<img id="email-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/mail.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
|
||||
<?php if ($_['email_is_login']) { ?>
|
||||
<?php if (!$_['email_is_login']) { ?>
|
||||
<p class="groupmiddle">
|
||||
<input type="text" name="username" id="username" value="<?php if (!empty($_['entered_data']['user'])) {
|
||||
p($_['entered_data']['user']);
|
||||
|
|
@ -31,6 +31,8 @@ script('registration', 'form');
|
|||
<label for="username" class="infield"><?php p($l->t('Username')); ?></label>
|
||||
<img id="username-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/user.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<?php } else { ?>
|
||||
<input type="hidden" name="username" value="<?php p($_['email']); ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<p class="groupbottom">
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
?>
|
||||
<form action="" method="post">
|
||||
<fieldset>
|
||||
<?php if ($_['message']): ?>
|
||||
<ul class="error">
|
||||
<li><?php p($_['message']); ?></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
|
||||
<p class="groupofone">
|
||||
<input type="text" name="token" id="token" placeholder="<?php p($l->t('Verification code')); ?>" value="" required autofocus />
|
||||
<label for="token" class="infield"><?php p($l->t('Verification code')); ?></label>
|
||||
<img id="token-icon" class="svg" src="<?php print_unescaped(image_path('registration', 'verify.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" />
|
||||
<input type="submit" id="submit" value="<?php p($l->t('Verify')); ?>" />
|
||||
|
||||
<a id="lost-password-back" href="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('core.login.showLoginForm')) ?>">
|
||||
<?php p($l->t('Back to login')); ?>
|
||||
</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
?>
|
||||
<ul class="msg error-wide nc-theming-main-text">
|
||||
<li><?php print_unescaped($_['msg'])?></li>
|
||||
</ul>
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?php
|
||||
/** @var array $_ */
|
||||
/** @var \OCP\IL10N $l */
|
||||
style('registration', 'style');
|
||||
if ($_['entered']): ?>
|
||||
<?php if (empty($_['errormsg'])): ?>
|
||||
<ul class="success">
|
||||
<li>
|
||||
<?php p($l->t('Thank you for registering, you should receive a verification link in a few minutes.')); ?>
|
||||
</li>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<form action="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('registration.register.validateEmail')) ?>" method="post">
|
||||
<fieldset>
|
||||
<ul class="error">
|
||||
<li><?php p($_['errormsg']); ?></li>
|
||||
</ul>
|
||||
<p class="groupofone">
|
||||
<input type="email" name="email" id="email" placeholder="<?php p($l->t('Email')); ?>" value="" required autofocus />
|
||||
<label for="email" class="infield"><?php p($l->t('Email')); ?></label>
|
||||
<img id="email-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/mail.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" />
|
||||
<input type="submit" id="submit" value="<?php p($l->t('Request verification link')); ?>" />
|
||||
|
||||
<a id="lost-password-back" href="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('core.login.showLoginForm')) ?>">
|
||||
<?php p($l->t('Back to login')); ?>
|
||||
</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<form action="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('registration.register.validateEmail')) ?>" method="post">
|
||||
<fieldset>
|
||||
<?php if ($_['errormsg']): ?>
|
||||
<ul class="error">
|
||||
<li><?php p($_['errormsg']); ?></li>
|
||||
<li><?php p($l->t('Please re-enter a valid email address')); ?></li>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<ul class="msg">
|
||||
<li><?php p($l->t('You will receive an email with a verification link')); ?></li>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<p class="groupofone">
|
||||
<input type="email" name="email" id="email" placeholder="<?php p($l->t('Email')); ?>" value="" required autofocus />
|
||||
<label for="email" class="infield"><?php p($l->t('Email')); ?></label>
|
||||
<img id="email-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/mail.svg')); ?>" alt=""/>
|
||||
</p>
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" />
|
||||
<input type="submit" id="submit" value="<?php p($l->t('Request verification link')); ?>" />
|
||||
|
||||
<a id="lost-password-back" href="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('core.login.showLoginForm')) ?>">
|
||||
<?php p($l->t('Back to login')); ?>
|
||||
</a>
|
||||
</fieldset>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace OCA\Registration\Tests\Integration\Controller;
|
||||
|
||||
use OCA\Registration\Controller\RegisterController;
|
||||
use OCA\Registration\Db\RegistrationMapper;
|
||||
use OCA\Registration\Service\MailService;
|
||||
use OCA\Registration\Service\RegistrationService;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OC\Authentication\Token\IProvider;
|
||||
use OCP\IRequest;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use OCP\Security\ICrypto;
|
||||
use OCP\ISession;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
|
||||
use \OCP\AppFramework\Http\TemplateResponse;
|
||||
|
||||
use ChristophWurst\Nextcloud\Testing\DatabaseTransaction;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
|
||||
/**
|
||||
* class RegistrationControllerTest
|
||||
*
|
||||
* @group DB
|
||||
*/
|
||||
class RegisterControllerTest extends TestCase {
|
||||
use DatabaseTransaction;
|
||||
|
||||
/** @var MailService */
|
||||
private $mailService;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var RegistrationMapper */
|
||||
private $registrationMapper;
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
/** @var \OCP\Defaults */
|
||||
private $defaults;
|
||||
/** @var ISecureRandom */
|
||||
private $random;
|
||||
/** @var IUserSession */
|
||||
private $usersession;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
/** @var ISession */
|
||||
private $session;
|
||||
/** @var IProvider */
|
||||
private $tokenProvider;
|
||||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->mailService = $this->createMock(MailService::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
#$this->userManager = $this->createMock(IUserManager::class);
|
||||
$this->userManager = \OC::$server->getUserManager();
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->groupManager = \OC::$server->getGroupManager();
|
||||
$this->random = \OC::$server->getSecureRandom();
|
||||
$this->usersession = $this->createMock(IUserSession::class);
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->logger = $this->createMock(ILogger::class);
|
||||
$this->session = $this->createMock(ISession::class);
|
||||
$this->tokenProvider = $this->createMock(IProvider::class);
|
||||
$this->crypto = $this->createMock(ICrypto::class);
|
||||
|
||||
$this->registrationMapper = new RegistrationMapper(
|
||||
\OC::$server->getDatabaseConnection(),
|
||||
$this->random
|
||||
);
|
||||
|
||||
$this->registrationService = new RegistrationService(
|
||||
'registration',
|
||||
$this->mailService,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->registrationMapper,
|
||||
$this->userManager,
|
||||
$this->config,
|
||||
$this->groupManager,
|
||||
$this->random,
|
||||
$this->usersession,
|
||||
$this->request,
|
||||
$this->logger,
|
||||
$this->session,
|
||||
$this->tokenProvider,
|
||||
$this->crypto
|
||||
);
|
||||
|
||||
$this->controller = new RegisterController(
|
||||
'registration',
|
||||
$this->request,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->config,
|
||||
$this->registrationService,
|
||||
$this->mailService
|
||||
);
|
||||
}
|
||||
|
||||
public function testValidateEmailNormal() {
|
||||
$email = 'aaaa@example.com';
|
||||
|
||||
$this->config->expects($this->atLeastOnce())
|
||||
->method('getAppValue')
|
||||
->with("registration", 'allowed_domains', '')
|
||||
->willReturn('');
|
||||
$this->mailService->expects($this->once())
|
||||
->method('sendTokenByMail');
|
||||
|
||||
$this->assertEquals($this->registrationService->validateEmail($email), true);
|
||||
|
||||
$ret = $this->controller->validateEmail($email);
|
||||
|
||||
$expected = new TemplateResponse('registration', 'message', ['msg' =>
|
||||
$this->l10n->t('Verification email successfully sent.')
|
||||
], 'guest');
|
||||
|
||||
|
||||
$this->assertEquals($expected, $ret, print_r($ret, true));
|
||||
}
|
||||
}
|
||||
|
|
@ -61,6 +61,9 @@ class RegistrationServiceTest extends TestCase {
|
|||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
/** @var RegistrationService */
|
||||
private $service;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->mailService = $this->createMock(MailService::class);
|
||||
|
|
@ -107,13 +110,10 @@ class RegistrationServiceTest extends TestCase {
|
|||
|
||||
$this->config->expects($this->once())
|
||||
->method('getAppValue')
|
||||
->with("registration", 'allowed_domains', '')
|
||||
->with('registration', 'allowed_domains', '')
|
||||
->willReturn('');
|
||||
|
||||
$ret = $this->service->validateEmail($email);
|
||||
|
||||
//$this->assertInstanceOf(Registration::class, $ret);
|
||||
$this->assertTrue($ret);
|
||||
$this->service->validateEmail($email);
|
||||
}
|
||||
|
||||
public function testValidateNewEmailWithinAllowedDomain() {
|
||||
|
|
@ -121,18 +121,23 @@ class RegistrationServiceTest extends TestCase {
|
|||
|
||||
$this->config->expects($this->atLeastOnce())
|
||||
->method('getAppValue')
|
||||
->with("registration", 'allowed_domains', '')
|
||||
->with('registration', 'allowed_domains', '')
|
||||
->willReturn('example.com');
|
||||
|
||||
$ret = $this->service->validateEmail($email);
|
||||
$this->assertTrue($ret, print_r($ret, true));
|
||||
$this->service->validateEmail($email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testValidateNewEmailWithinAllowedDomain
|
||||
*/
|
||||
public function testValidateNewEmailNotWithinAllowedDomain() {
|
||||
$email2 = 'bbbb@gmail.com';
|
||||
|
||||
$this->config->expects($this->atLeastOnce())
|
||||
->method('getAppValue')
|
||||
->with('registration', 'allowed_domains', '')
|
||||
->willReturn('example.com');
|
||||
|
||||
$this->expectException(RegistrationException::class);
|
||||
$this->service->validateEmail($email2);
|
||||
}
|
||||
|
|
@ -143,18 +148,24 @@ class RegistrationServiceTest extends TestCase {
|
|||
|
||||
$this->config->expects($this->atLeastOnce())
|
||||
->method('getAppValue')
|
||||
->with("registration", 'allowed_domains', '')
|
||||
->with('registration', 'allowed_domains', '')
|
||||
->willReturn('example.com;gmail.com');
|
||||
|
||||
$this->assertTrue($this->service->validateEmail($email));
|
||||
$this->assertTrue($this->service->validateEmail($email2));
|
||||
$this->service->validateEmail($email);
|
||||
$this->service->validateEmail($email2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testValidateNewEmailWithinMultipleAllowedDomain
|
||||
*/
|
||||
public function testValidateNewEmailNotWithinMultipleAllowedDomain() {
|
||||
$email2 = 'cccc@yahoo.com';
|
||||
|
||||
$this->config->expects($this->atLeastOnce())
|
||||
->method('getAppValue')
|
||||
->with('registration', 'allowed_domains', '')
|
||||
->willReturn('example.com;gmail.com');
|
||||
|
||||
$this->expectException(RegistrationException::class);
|
||||
$this->service->validateEmail($email2);
|
||||
}
|
||||
|
|
@ -180,10 +191,8 @@ class RegistrationServiceTest extends TestCase {
|
|||
$email = 'aaaa@example.com';
|
||||
|
||||
$this->service->createRegistration($email, 'alice');
|
||||
$ret = $this->service->validateEmail($email);
|
||||
|
||||
$this->assertInstanceOf(Registration::class, $ret);
|
||||
$this->assertEquals($email, $ret->getEmail());
|
||||
$this->expectException(RegistrationException::class);
|
||||
$this->service->validateEmail($email);
|
||||
}
|
||||
|
||||
public function testCreateAccountWebForm() {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,11 @@ class ApiControllerTest extends TestCase {
|
|||
$registration = new Registration();
|
||||
$registration->setEmailConfirmed(true);
|
||||
$registration->setClientSecret('mysecret');
|
||||
$registration->setUsername('user');
|
||||
$registration->setPassword('password');
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('getUID')
|
||||
->willReturn('user');
|
||||
$this->registrationService
|
||||
->method('getRegistrationForSecret')
|
||||
->with('mysecret')
|
||||
|
|
|
|||
|
|
@ -0,0 +1,717 @@
|
|||
<?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\Tests\Unit\Controller;
|
||||
|
||||
use OCA\Registration\Controller\RegisterController;
|
||||
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\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\RedirectToDefaultAppResponse;
|
||||
use OCP\AppFramework\Http\StandaloneTemplateResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\IConfig;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
use OCP\IURLGenerator;
|
||||
use ChristophWurst\Nextcloud\Testing\TestCase;
|
||||
use OCP\IUser;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class RegisterControllerTest extends TestCase {
|
||||
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var IL10N|MockObject */
|
||||
private $l10n;
|
||||
/** @var IURLGenerator|MockObject */
|
||||
private $urlGenerator;
|
||||
/** @var IConfig|MockObject */
|
||||
private $config;
|
||||
/** @var RegistrationService|MockObject */
|
||||
private $registrationService;
|
||||
/** @var LoginFlowService|MockObject */
|
||||
private $loginFlowService;
|
||||
/** @var MailService|MockObject */
|
||||
private $mailService;
|
||||
|
||||
public function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->l10n = $this->createMock(IL10N::class);
|
||||
$this->urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
$this->config = $this->createMock(IConfig::class);
|
||||
$this->registrationService = $this->createMock(RegistrationService::class);
|
||||
$this->loginFlowService = $this->createMock(LoginFlowService::class);
|
||||
$this->mailService = $this->createMock(MailService::class);
|
||||
|
||||
$this->l10n->expects($this->any())
|
||||
->method('t')
|
||||
->willReturnCallback(function ($text, $parameters = []) {
|
||||
return vsprintf($text, $parameters);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $methods
|
||||
* @return RegisterController|MockObject
|
||||
*/
|
||||
protected function getController(array $methods = []) {
|
||||
if (empty($methods)) {
|
||||
return new RegisterController(
|
||||
'registration',
|
||||
$this->request,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->config,
|
||||
$this->registrationService,
|
||||
$this->loginFlowService,
|
||||
$this->mailService
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getMockBuilder(RegisterController::class)
|
||||
->onlyMethods($methods)
|
||||
->setConstructorArgs([
|
||||
'registration',
|
||||
$this->request,
|
||||
$this->l10n,
|
||||
$this->urlGenerator,
|
||||
$this->config,
|
||||
$this->registrationService,
|
||||
$this->loginFlowService,
|
||||
$this->mailService,
|
||||
])
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function dataShowEmailForm(): array {
|
||||
return [
|
||||
['', ''],
|
||||
['test@example.tld', 'Registration is only allowed for the following domains: nextcloud.com'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataShowEmailForm
|
||||
* @param string $email
|
||||
* @param string $message
|
||||
*/
|
||||
public function testShowEmailForm(string $email, string $message): void {
|
||||
$controller = $this->getController();
|
||||
$response = $controller->showEmailForm($email, $message);
|
||||
|
||||
self::assertSame(TemplateResponse::RENDER_AS_GUEST, $response->getRenderAs());
|
||||
self::assertSame('form/email', $response->getTemplateName());
|
||||
|
||||
self::assertSame([
|
||||
'email' => $email,
|
||||
'message' => $message,
|
||||
], $response->getParams());
|
||||
}
|
||||
|
||||
public function testSubmitEmailForm(): void {
|
||||
$email = 'nextcloud@example.tld';
|
||||
|
||||
$this->registrationService
|
||||
->method('getRegistrationForEmail')
|
||||
->with($email)
|
||||
->willThrowException(new DoesNotExistException($email));
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => 'clientSecret',
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('validateEmail')
|
||||
->with($email);
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('createRegistration')
|
||||
->with($email)
|
||||
->willReturn($registration);
|
||||
|
||||
$this->mailService
|
||||
->expects($this->once())
|
||||
->method('sendTokenByMail')
|
||||
->with($registration);
|
||||
|
||||
$this->urlGenerator
|
||||
->method('linkToRoute')
|
||||
->willReturnCallback(function () {
|
||||
return json_encode(func_get_args());
|
||||
});
|
||||
|
||||
$controller = $this->getController();
|
||||
$response = $controller->submitEmailForm($email);
|
||||
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
/** @var RedirectResponse $response */
|
||||
self::assertSame('["registration.register.showVerificationForm",{"secret":"clientSecret"}]', $response->getRedirectURL());
|
||||
}
|
||||
|
||||
public function testSubmitEmailFormInvalidEmail(): void {
|
||||
$email = 'nextcloud@example.tld';
|
||||
|
||||
$this->registrationService
|
||||
->method('getRegistrationForEmail')
|
||||
->with($email)
|
||||
->willThrowException(new DoesNotExistException($email));
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('validateEmail')
|
||||
->with($email)
|
||||
->willThrowException(new RegistrationException('Invalid email'));
|
||||
$this->registrationService
|
||||
->expects($this->never())
|
||||
->method('createRegistration');
|
||||
|
||||
$controller = $this->getController([
|
||||
'showEmailForm',
|
||||
]);
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller->expects($this->once())
|
||||
->method('showEmailForm')
|
||||
->with($email, 'Invalid email')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitEmailForm($email));
|
||||
}
|
||||
|
||||
public function testSubmitEmailFormErrorSendingEmail(): void {
|
||||
$email = 'nextcloud@example.tld';
|
||||
|
||||
$this->registrationService
|
||||
->method('getRegistrationForEmail')
|
||||
->with($email)
|
||||
->willThrowException(new DoesNotExistException($email));
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => 'clientSecret',
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('validateEmail')
|
||||
->with($email);
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('createRegistration')
|
||||
->with($email)
|
||||
->willReturn($registration);
|
||||
|
||||
$this->mailService
|
||||
->expects($this->once())
|
||||
->method('sendTokenByMail')
|
||||
->with($registration)
|
||||
->willThrowException(new RegistrationException('Error sending email'));
|
||||
|
||||
$controller = $this->getController([
|
||||
'showEmailForm',
|
||||
]);
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller->expects($this->once())
|
||||
->method('showEmailForm')
|
||||
->with($email, 'Error sending email')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitEmailForm($email));
|
||||
}
|
||||
|
||||
public function testSubmitEmailFormResendPendingRequest(): void {
|
||||
$email = 'nextcloud@example.tld';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => 'clientSecret',
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->method('getRegistrationForEmail')
|
||||
->with($email)
|
||||
->willReturn($registration);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('generateNewToken')
|
||||
->with($registration);
|
||||
|
||||
$this->mailService
|
||||
->expects($this->once())
|
||||
->method('sendTokenByMail')
|
||||
->with($registration);
|
||||
|
||||
$this->urlGenerator
|
||||
->method('linkToRoute')
|
||||
->willReturnCallback(function () {
|
||||
return json_encode(func_get_args());
|
||||
});
|
||||
|
||||
$controller = $this->getController();
|
||||
$response = $controller->submitEmailForm($email);
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
/** @var RedirectResponse $response */
|
||||
self::assertSame('["registration.register.showVerificationForm",{"secret":"clientSecret"}]', $response->getRedirectURL());
|
||||
}
|
||||
|
||||
public function dataShowVerificationForm(): array {
|
||||
return [
|
||||
[''],
|
||||
['The entered verification code is wrong'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataShowVerificationForm
|
||||
* @param string $message
|
||||
*/
|
||||
public function testShowVerificationForm(string $message): void {
|
||||
$secret = '123456789';
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('getRegistrationForSecret')
|
||||
->with($secret);
|
||||
|
||||
$controller = $this->getController();
|
||||
$response = $controller->showVerificationForm($secret, $message);
|
||||
|
||||
self::assertSame(TemplateResponse::RENDER_AS_GUEST, $response->getRenderAs());
|
||||
self::assertSame('form/verification', $response->getTemplateName());
|
||||
|
||||
self::assertSame([
|
||||
'message' => $message,
|
||||
], $response->getParams());
|
||||
}
|
||||
|
||||
public function testShowVerificationFormInvalidSecret(): void {
|
||||
$secret = '123456789';
|
||||
$message = '';
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('getRegistrationForSecret')
|
||||
->with($secret)
|
||||
->willThrowException(new DoesNotExistException('Not found'));
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndTokenErrorPage'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndTokenErrorPage')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->showVerificationForm($secret, $message));
|
||||
}
|
||||
|
||||
public function testSubmitVerificationForm(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => $secret,
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('getRegistrationForSecret')
|
||||
->with($secret)
|
||||
->willReturn($registration);
|
||||
|
||||
$this->urlGenerator
|
||||
->method('linkToRoute')
|
||||
->willReturnCallback(function () {
|
||||
return json_encode(func_get_args());
|
||||
});
|
||||
|
||||
$controller = $this->getController();
|
||||
$response = $controller->submitVerificationForm($secret, $token);
|
||||
self::assertInstanceOf(RedirectResponse::class, $response);
|
||||
/** @var RedirectResponse $response */
|
||||
self::assertSame('["registration.register.showUserForm",{"secret":"123456789","token":"abcdefghi"}]', $response->getRedirectURL());
|
||||
}
|
||||
|
||||
public function testSubmitVerificationFormInvalidToken(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => $secret,
|
||||
'token' => 'zyxwvu',
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('getRegistrationForSecret')
|
||||
->with($secret)
|
||||
->willReturn($registration);
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller = $this->getController([
|
||||
'showVerificationForm',
|
||||
]);
|
||||
$controller->expects($this->once())
|
||||
->method('showVerificationForm')
|
||||
->with($secret, 'The entered verification code is wrong')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitVerificationForm($secret, $token));
|
||||
}
|
||||
|
||||
public function testSubmitVerificationFormInvalidSecret(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'clientSecret' => $secret,
|
||||
'token' => $token,
|
||||
]);
|
||||
|
||||
$this->registrationService
|
||||
->expects($this->once())
|
||||
->method('getRegistrationForSecret')
|
||||
->with($secret)
|
||||
->willThrowException(new DoesNotExistException('Invalid secret'));
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndTokenErrorPage',
|
||||
]);
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndTokenErrorPage')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitVerificationForm($secret, $token));
|
||||
}
|
||||
|
||||
public function dataShowUserForm(): array {
|
||||
return [
|
||||
['', ''],
|
||||
['tester', ''],
|
||||
['', 'Unable to create user, there are problems with the user backend.'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataShowUserForm
|
||||
* @param string $username
|
||||
* @param string $message
|
||||
*/
|
||||
public function testShowUserForm(string $username, string $message): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$email = 'nextcloud@example.tld';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$response = $controller->showUserForm($secret, $token, $username, $message);
|
||||
|
||||
self::assertSame(TemplateResponse::RENDER_AS_GUEST, $response->getRenderAs());
|
||||
self::assertSame('form/user', $response->getTemplateName());
|
||||
|
||||
self::assertSame([
|
||||
'email' => $email,
|
||||
'email_is_login' => false,
|
||||
'username' => $username,
|
||||
'message' => $message,
|
||||
], $response->getParams());
|
||||
}
|
||||
|
||||
public function testShowUserFormInvalidSecretAndToken(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'validateSecretAndTokenErrorPage',
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willThrowException(new RegistrationException('Invalid secret or token'));
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndTokenErrorPage')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->showUserForm($secret, $token));
|
||||
}
|
||||
|
||||
public function testSubmitUserFormInvalidSecretAndToken(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'validateSecretAndTokenErrorPage',
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willThrowException(new RegistrationException('Invalid secret or token'));
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndTokenErrorPage')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitUserForm($secret, $token, '', ''));
|
||||
}
|
||||
|
||||
public function testSubmitUserFormCreateAccountException(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$username = 'user';
|
||||
$password = 'password';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'showUserForm'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$response = $this->createMock(TemplateResponse::class);
|
||||
$controller->expects($this->once())
|
||||
->method('showUserForm')
|
||||
->willReturn($response);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('createAccount')
|
||||
->with($registration, $username, $password)
|
||||
->willThrowException(new RegistrationException('Invalid account data'));
|
||||
|
||||
self::assertSame($response, $controller->submitUserForm($secret, $token, $username, $password));
|
||||
}
|
||||
|
||||
public function testSubmitUserFormRequiresAdminApproval(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$username = 'user';
|
||||
$password = 'password';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'showUserForm'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('isEnabled')
|
||||
->willReturn(false);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('createAccount')
|
||||
->with($registration, $username, $password)
|
||||
->willReturn($user);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('deleteRegistration')
|
||||
->with($registration);
|
||||
|
||||
$response = $controller->submitUserForm($secret, $token, $username, $password);
|
||||
|
||||
self::assertInstanceOf(StandaloneTemplateResponse::class, $response);
|
||||
self::assertSame(TemplateResponse::RENDER_AS_GUEST, $response->getRenderAs());
|
||||
self::assertSame('approval-required', $response->getTemplateName());
|
||||
}
|
||||
|
||||
public function testSubmitUserFormSuccessful(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$username = 'user';
|
||||
$password = 'password';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'showUserForm'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('isEnabled')
|
||||
->willReturn(true);
|
||||
$user->method('getUID')
|
||||
->willReturn($username);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('createAccount')
|
||||
->with($registration, $username, $password)
|
||||
->willReturn($user);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('deleteRegistration')
|
||||
->with($registration);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('loginUser')
|
||||
->with($username, $username, $password);
|
||||
|
||||
$response = $controller->submitUserForm($secret, $token, $username, $password);
|
||||
|
||||
self::assertInstanceOf(RedirectToDefaultAppResponse::class, $response);
|
||||
}
|
||||
|
||||
public function testSubmitUserFormSuccessfulLoginFlow2(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$username = 'user';
|
||||
$password = 'password';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'showUserForm'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('isEnabled')
|
||||
->willReturn(true);
|
||||
$user->method('getUID')
|
||||
->willReturn($username);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('createAccount')
|
||||
->with($registration, $username, $password)
|
||||
->willReturn($user);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('deleteRegistration')
|
||||
->with($registration);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('loginUser')
|
||||
->with($username, $username, $password);
|
||||
|
||||
$this->loginFlowService->method('isUsingLoginFlow')
|
||||
->with(2)
|
||||
->willReturn(true);
|
||||
|
||||
$response = $this->createMock(StandaloneTemplateResponse::class);
|
||||
$this->loginFlowService->method('tryLoginFlowV2')
|
||||
->with($user)
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitUserForm($secret, $token, $username, $password));
|
||||
}
|
||||
|
||||
public function testSubmitUserFormSuccessfulLoginFlow1(): void {
|
||||
$secret = '123456789';
|
||||
$token = 'abcdefghi';
|
||||
$username = 'user';
|
||||
$password = 'password';
|
||||
|
||||
$registration = Registration::fromParams([
|
||||
'email' => 'nextcloud@example.tld',
|
||||
]);
|
||||
|
||||
$controller = $this->getController([
|
||||
'validateSecretAndToken',
|
||||
'showUserForm'
|
||||
]);
|
||||
|
||||
$controller->expects($this->once())
|
||||
->method('validateSecretAndToken')
|
||||
->willReturn($registration);
|
||||
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->method('isEnabled')
|
||||
->willReturn(true);
|
||||
$user->method('getUID')
|
||||
->willReturn($username);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('createAccount')
|
||||
->with($registration, $username, $password)
|
||||
->willReturn($user);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('deleteRegistration')
|
||||
->with($registration);
|
||||
|
||||
$this->registrationService->expects($this->once())
|
||||
->method('loginUser')
|
||||
->with($username, $username, $password);
|
||||
|
||||
$this->loginFlowService->method('isUsingLoginFlow')
|
||||
->withConsecutive([2], [1])
|
||||
->willReturnOnConsecutiveCalls(false, true);
|
||||
|
||||
$response = $this->createMock(RedirectResponse::class);
|
||||
$response->method('getStatus')
|
||||
->willReturn(Http::STATUS_SEE_OTHER);
|
||||
$this->loginFlowService->method('tryLoginFlowV1')
|
||||
->willReturn($response);
|
||||
|
||||
self::assertSame($response, $controller->submitUserForm($secret, $token, $username, $password));
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,8 @@
|
|||
<!-- filters for code coverage -->
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">../</directory>
|
||||
<directory suffix=".php">../../registration/appinfo</directory>
|
||||
<directory suffix=".php">../../registration/lib</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
|
|
|
|||
Loading…
Reference in New Issue