Merge pull request #293 from nextcloud/feature/287/terms-of-service-integration

Terms of service integration
This commit is contained in:
Joas Schilling 2021-04-14 11:40:19 +02:00 committed by GitHub
commit 0d85552b02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 221 additions and 2 deletions

View File

@ -19,6 +19,9 @@ namespace OCA\Registration\Controller;
use Exception; use Exception;
use OCA\Registration\AppInfo\Application; use OCA\Registration\AppInfo\Application;
use OCA\Registration\Db\Registration; use OCA\Registration\Db\Registration;
use OCA\Registration\Events\PassedFormEvent;
use OCA\Registration\Events\ShowFormEvent;
use OCA\Registration\Events\ValidateFormEvent;
use OCA\Registration\Service\LoginFlowService; use OCA\Registration\Service\LoginFlowService;
use OCA\Registration\Service\MailService; use OCA\Registration\Service\MailService;
use OCA\Registration\Service\RegistrationException; use OCA\Registration\Service\RegistrationException;
@ -31,6 +34,7 @@ use OCP\AppFramework\Http\RedirectToDefaultAppResponse;
use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\Response;
use OCP\AppFramework\Http\StandaloneTemplateResponse; use OCP\AppFramework\Http\StandaloneTemplateResponse;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IL10N; use OCP\IL10N;
use OCP\IRequest; use OCP\IRequest;
use OCP\IURLGenerator; use OCP\IURLGenerator;
@ -50,6 +54,8 @@ class RegisterController extends Controller {
private $mailService; private $mailService;
/** @var LoginFlowService */ /** @var LoginFlowService */
private $loginFlowService; private $loginFlowService;
/** @var IEventDispatcher */
private $eventDispatcher;
public function __construct( public function __construct(
string $appName, string $appName,
@ -59,7 +65,8 @@ class RegisterController extends Controller {
IConfig $config, IConfig $config,
RegistrationService $registrationService, RegistrationService $registrationService,
LoginFlowService $loginFlowService, LoginFlowService $loginFlowService,
MailService $mailService MailService $mailService,
IEventDispatcher $eventDispatcher
) { ) {
parent::__construct($appName, $request); parent::__construct($appName, $request);
$this->l10n = $l10n; $this->l10n = $l10n;
@ -68,6 +75,7 @@ class RegisterController extends Controller {
$this->registrationService = $registrationService; $this->registrationService = $registrationService;
$this->loginFlowService = $loginFlowService; $this->loginFlowService = $loginFlowService;
$this->mailService = $mailService; $this->mailService = $mailService;
$this->eventDispatcher = $eventDispatcher;
} }
/** /**
@ -96,6 +104,8 @@ class RegisterController extends Controller {
} }
} }
$this->eventDispatcher->dispatchTyped(new ShowFormEvent(ShowFormEvent::STEP_EMAIL));
$params = [ $params = [
'email' => $email, 'email' => $email,
'message' => $message ?: $emailHint, 'message' => $message ?: $emailHint,
@ -113,6 +123,13 @@ class RegisterController extends Controller {
* @return TemplateResponse * @return TemplateResponse
*/ */
public function submitEmailForm(string $email): Response { public function submitEmailForm(string $email): Response {
$validateFormEvent = new ValidateFormEvent(ValidateFormEvent::STEP_EMAIL);
$this->eventDispatcher->dispatchTyped($validateFormEvent);
if (!empty($validateFormEvent->getErrors())) {
return $this->showEmailForm($email, implode(' ', $validateFormEvent->getErrors()));
}
try { try {
// Registration already in progress, update token and continue with verification // Registration already in progress, update token and continue with verification
$registration = $this->registrationService->getRegistrationForEmail($email); $registration = $this->registrationService->getRegistrationForEmail($email);
@ -129,6 +146,8 @@ class RegisterController extends Controller {
} }
if ($this->config->getAppValue($this->appName, 'disable_email_verification', 'no') === 'yes') { if ($this->config->getAppValue($this->appName, 'disable_email_verification', 'no') === 'yes') {
$this->eventDispatcher->dispatchTyped(new PassedFormEvent(PassedFormEvent::STEP_EMAIL, $registration->getClientSecret()));
return new RedirectResponse( return new RedirectResponse(
$this->urlGenerator->linkToRoute( $this->urlGenerator->linkToRoute(
'registration.register.showUserForm', 'registration.register.showUserForm',
@ -148,6 +167,8 @@ class RegisterController extends Controller {
return $this->showEmailForm($email, $this->l10n->t('A problem occurred sending email, please contact your administrator.')); return $this->showEmailForm($email, $this->l10n->t('A problem occurred sending email, please contact your administrator.'));
} }
$this->eventDispatcher->dispatchTyped(new PassedFormEvent(PassedFormEvent::STEP_EMAIL, $registration->getClientSecret()));
return new RedirectResponse( return new RedirectResponse(
$this->urlGenerator->linkToRoute( $this->urlGenerator->linkToRoute(
'registration.register.showVerificationForm', 'registration.register.showVerificationForm',
@ -171,6 +192,8 @@ class RegisterController extends Controller {
return $this->validateSecretAndTokenErrorPage(); return $this->validateSecretAndTokenErrorPage();
} }
$this->eventDispatcher->dispatchTyped(new ShowFormEvent(ShowFormEvent::STEP_VERIFICATION, $secret));
return new TemplateResponse('registration', 'form/verification', [ return new TemplateResponse('registration', 'form/verification', [
'message' => $message, 'message' => $message,
], 'guest'); ], 'guest');
@ -198,6 +221,15 @@ class RegisterController extends Controller {
return $this->validateSecretAndTokenErrorPage(); return $this->validateSecretAndTokenErrorPage();
} }
$validateFormEvent = new ValidateFormEvent(ValidateFormEvent::STEP_VERIFICATION, $secret);
$this->eventDispatcher->dispatchTyped($validateFormEvent);
if (!empty($validateFormEvent->getErrors())) {
return $this->showVerificationForm($secret, implode(' ', $validateFormEvent->getErrors()));
}
$this->eventDispatcher->dispatchTyped(new PassedFormEvent(PassedFormEvent::STEP_VERIFICATION, $secret));
return new RedirectResponse( return new RedirectResponse(
$this->urlGenerator->linkToRoute( $this->urlGenerator->linkToRoute(
'registration.register.showUserForm', 'registration.register.showUserForm',
@ -230,6 +262,8 @@ class RegisterController extends Controller {
$additional_hint = $this->config->getAppValue('registration', 'additional_hint'); $additional_hint = $this->config->getAppValue('registration', 'additional_hint');
$this->eventDispatcher->dispatchTyped(new ShowFormEvent(ShowFormEvent::STEP_USER, $secret));
return new TemplateResponse('registration', 'form/user', [ return new TemplateResponse('registration', 'form/user', [
'email' => $registration->getEmail(), 'email' => $registration->getEmail(),
'email_is_login' => $this->config->getAppValue('registration', 'email_is_login', 'no') === 'yes', 'email_is_login' => $this->config->getAppValue('registration', 'email_is_login', 'no') === 'yes',
@ -270,6 +304,13 @@ class RegisterController extends Controller {
$loginname = $registration->getEmail(); $loginname = $registration->getEmail();
} }
$validateFormEvent = new ValidateFormEvent(ValidateFormEvent::STEP_USER, $secret);
$this->eventDispatcher->dispatchTyped($validateFormEvent);
if (!empty($validateFormEvent->getErrors())) {
return $this->showUserForm($secret, $token, $loginname, $fullname, $phone, $password, implode(' ', $validateFormEvent->getErrors()));
}
try { try {
$user = $this->registrationService->createAccount($registration, $loginname, $fullname, $phone, $password); $user = $this->registrationService->createAccount($registration, $loginname, $fullname, $phone, $password);
} catch (Exception $exception) { } catch (Exception $exception) {
@ -279,6 +320,8 @@ class RegisterController extends Controller {
// Delete registration // Delete registration
$this->registrationService->deleteRegistration($registration); $this->registrationService->deleteRegistration($registration);
$this->eventDispatcher->dispatchTyped(new PassedFormEvent(PassedFormEvent::STEP_USER, $secret, $user));
if ($user->isEnabled()) { if ($user->isEnabled()) {
$this->registrationService->loginUser($user->getUID(), $user->getUID(), $password); $this->registrationService->loginUser($user->getUID(), $user->getUID(), $password);

53
lib/Events/AFormEvent.php Normal file
View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 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\Events;
use OCP\EventDispatcher\Event;
abstract class AFormEvent extends Event {
public const STEP_EMAIL = 'email';
public const STEP_VERIFICATION = 'verification';
public const STEP_USER = 'user';
/** @var string */
protected $step;
/** @var string */
protected $registrationId;
public function __construct(string $step, string $registrationId = '') {
parent::__construct();
$this->step = $step;
$this->registrationId = $registrationId;
}
public function getStep(): string {
return $this->step;
}
public function getRegistrationIdentifier(): string {
return $this->registrationId;
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 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\Events;
use OCP\IUser;
class PassedFormEvent extends AFormEvent {
/** @var IUser|null */
protected $user;
public function __construct(string $step, string $registrationId = '', ?IUser $user = null) {
parent::__construct($step, $registrationId);
$this->user = $user;
}
public function getUser(): ?IUser {
return $this->user;
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 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\Events;
class ShowFormEvent extends AFormEvent {
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 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\Events;
class ValidateFormEvent extends AFormEvent {
/** @var string[] */
protected $errors;
public function __construct(string $step, string $registrationId = '') {
parent::__construct($step, $registrationId);
$this->errors = [];
}
public function addError(string $error): void {
$this->errors[] = $error;
}
public function getErrors(): array {
return $this->errors;
}
}

View File

@ -17,6 +17,9 @@ script('registration', 'registration-form');
<label for="email" class="infield"><?php p($l->t('Email')); ?></label> <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=""/> <img id="email-icon" class="svg" src="<?php print_unescaped(image_path('', 'actions/mail.svg')); ?>" alt=""/>
</p> </p>
<div id="terms_of_service"></div>
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" /> <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']); ?>" />
<input type="submit" id="submit" value="<?php <input type="submit" id="submit" value="<?php
if ($_['disable_email_verification'] === 'yes') { if ($_['disable_email_verification'] === 'yes') {

View File

@ -35,6 +35,7 @@ use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\RedirectToDefaultAppResponse; use OCP\AppFramework\Http\RedirectToDefaultAppResponse;
use OCP\AppFramework\Http\StandaloneTemplateResponse; use OCP\AppFramework\Http\StandaloneTemplateResponse;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig; use OCP\IConfig;
use OCP\IL10N; use OCP\IL10N;
use OCP\IRequest; use OCP\IRequest;
@ -59,6 +60,8 @@ class RegisterControllerTest extends TestCase {
private $loginFlowService; private $loginFlowService;
/** @var MailService|MockObject */ /** @var MailService|MockObject */
private $mailService; private $mailService;
/** @var IEventDispatcher|MockObject */
private $eventDispatcher;
public function setUp(): void { public function setUp(): void {
parent::setUp(); parent::setUp();
@ -69,6 +72,7 @@ class RegisterControllerTest extends TestCase {
$this->registrationService = $this->createMock(RegistrationService::class); $this->registrationService = $this->createMock(RegistrationService::class);
$this->loginFlowService = $this->createMock(LoginFlowService::class); $this->loginFlowService = $this->createMock(LoginFlowService::class);
$this->mailService = $this->createMock(MailService::class); $this->mailService = $this->createMock(MailService::class);
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->l10n->expects($this->any()) $this->l10n->expects($this->any())
->method('t') ->method('t')
@ -91,7 +95,8 @@ class RegisterControllerTest extends TestCase {
$this->config, $this->config,
$this->registrationService, $this->registrationService,
$this->loginFlowService, $this->loginFlowService,
$this->mailService $this->mailService,
$this->eventDispatcher
); );
} }
@ -106,6 +111,7 @@ class RegisterControllerTest extends TestCase {
$this->registrationService, $this->registrationService,
$this->loginFlowService, $this->loginFlowService,
$this->mailService, $this->mailService,
$this->eventDispatcher,
]) ])
->getMock(); ->getMock();
} }