Add registration API
Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
parent
55c04b21ff
commit
8f192c49fe
|
|
@ -17,3 +17,6 @@ namespace OCA\Registration\AppInfo;
|
|||
]);
|
||||
|
||||
\OCP\App::registerAdmin('registration', 'admin');
|
||||
|
||||
$app = new \OCP\AppFramework\App('registration');
|
||||
$app->getContainer()->registerCapability(\OCA\Registration\Capabilities::class);
|
||||
|
|
|
|||
|
|
@ -20,11 +20,33 @@
|
|||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>username</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>password</name>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>email_confirmed</name>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</field>
|
||||
<field>
|
||||
<name>token</name>
|
||||
<type>text</type>
|
||||
<notnull>true</notnull>
|
||||
</field>
|
||||
<field>
|
||||
<name>client_secret</name>
|
||||
<type>text</type>
|
||||
</field>
|
||||
<field>
|
||||
<name>requested</name>
|
||||
<type>timestamp</type>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<description>User registration</description>
|
||||
<licence>agpl</licence>
|
||||
<author mail="pellaeon@cnmc.tw" homepage="https://nyllep.wordpress.com/about-2">Pellaeon Lin</author>
|
||||
<version>0.2.3</version>
|
||||
<version>0.2.3-3</version>
|
||||
<website>https://github.com/pellaeon/registration</website>
|
||||
<bugs>https://github.com/pellaeon/registration/issues</bugs>
|
||||
<repository>https://github.com/pellaeon/registration</repository>
|
||||
|
|
|
|||
|
|
@ -6,13 +6,21 @@
|
|||
* later. See the COPYING file.
|
||||
*
|
||||
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
* @copyright Pellaeon Lin 2014
|
||||
*/
|
||||
|
||||
return ['routes' => [
|
||||
array('name' => 'settings#admin', 'url' => '/settings', 'verb' => 'POST'),
|
||||
array('name' => 'register#askEmail', 'url' => '/', 'verb' => 'GET'),
|
||||
array('name' => 'register#validateEmail', 'url' => '/', 'verb' => 'POST'),
|
||||
array('name' => 'register#verifyToken', 'url' => '/verify/{token}', 'verb' => 'GET'),
|
||||
array('name' => 'register#createAccount', 'url' => '/verify/{token}', 'verb' => 'POST')
|
||||
]];
|
||||
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']
|
||||
],
|
||||
'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']
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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;
|
||||
|
||||
use OCP\Capabilities\ICapability;
|
||||
use OCP\IURLGenerator;
|
||||
|
||||
class Capabilities implements ICapability {
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
public function __construct(IURLGenerator $urlGenerator) {
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
}
|
||||
|
||||
public function getCapabilities() {
|
||||
return [
|
||||
'registration' =>
|
||||
[
|
||||
'enabled' => true,
|
||||
'apiRoot' => $this->urlGenerator->linkTo(
|
||||
'', 'ocs/v1.php/apps/registration/api/v1/'),
|
||||
'apiLevel' => 'v1'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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\Controller;
|
||||
|
||||
use OCA\Registration\Db\Registration;
|
||||
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\OCS\OCSBadRequestException;
|
||||
use OCP\AppFramework\OCS\OCSNotFoundException;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\AppFramework\OCS\OCSException;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\Defaults;
|
||||
use OCP\IL10N;
|
||||
use OCP\IRequest;
|
||||
|
||||
class ApiController extends OCSController {
|
||||
|
||||
/** @var RegistrationService */
|
||||
private $registrationService;
|
||||
/** @var MailService */
|
||||
private $mailService;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
|
||||
public function __construct($appName,
|
||||
IRequest $request,
|
||||
$corsMethods = 'PUT, POST, GET, DELETE, PATCH',
|
||||
$corsAllowedHeaders = 'Authorization, Content-Type, Accept',
|
||||
$corsMaxAge = 1728000,
|
||||
RegistrationService $registrationService,
|
||||
MailService $mailService,
|
||||
IL10N $l10n,
|
||||
Defaults $defaults) {
|
||||
parent::__construct($appName, $request, $corsMethods, $corsAllowedHeaders, $corsMaxAge);
|
||||
$this->registrationService = $registrationService;
|
||||
$this->mailService = $mailService;
|
||||
$this->l10n = $l10n;
|
||||
$this->defaults = $defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @AnonRateThrottle(limit=5, period=1)
|
||||
*
|
||||
* @param $username
|
||||
* @param $displayname
|
||||
* @param $email
|
||||
* @throws OCSException
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function validate($username, $displayname, $email) {
|
||||
try {
|
||||
$this->registrationService->validateEmail($email);
|
||||
$this->registrationService->validateDisplayname($displayname);
|
||||
$this->registrationService->validateUsername($username);
|
||||
} catch (RegistrationException $e) {
|
||||
throw new OCSBadRequestException($e->getMessage());
|
||||
}
|
||||
$data = [
|
||||
'username' => $username,
|
||||
'displayname' => $displayname,
|
||||
'email' => $email
|
||||
];
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
*
|
||||
* @param $registrationToken
|
||||
* @param $clientSecret
|
||||
* @throws OCSException
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function status($registrationToken, $clientSecret=null) {
|
||||
$data = [];
|
||||
try {
|
||||
/** @var Registration $registration */
|
||||
$registration = $this->registrationService->getRegistrationForToken($registrationToken);
|
||||
if(!$registration->getEmailConfirmed()) {
|
||||
$data = [
|
||||
'status' => Registration::STATUS_PENDING,
|
||||
'message' => $this->l10n->t('Your registration is pending. Please confirm your email address.')
|
||||
];
|
||||
} else {
|
||||
// create account if email confirmed and not already created
|
||||
$user = $this->registrationService->getUserAccount($registration);
|
||||
if($user === null) {
|
||||
$user = $this->registrationService->createAccount($registration);
|
||||
}
|
||||
$this->registrationService->loginUser($user->getUID(), $registration->getUsername(), $registration->getPassword(), true);
|
||||
$appPassword = $this->registrationService->generateAppPassword($user->getUID());
|
||||
if ($clientSecret === $registration->getClientSecret()) {
|
||||
$data = [
|
||||
'status' => Registration::STATUS_FINISHED,
|
||||
'appPassword' => $appPassword,
|
||||
'cloudUrl' => $this->defaults->getBaseUrl()
|
||||
];
|
||||
$this->registrationService->deleteRegistration($registration);
|
||||
}
|
||||
}
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
} catch (DoesNotExistException $e) {
|
||||
throw new OCSNotFoundException('No pending registration.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
*
|
||||
* @param $username
|
||||
* @param $displayname
|
||||
* @param $email
|
||||
* @param $password
|
||||
* @throws OCSException
|
||||
* @return DataResponse
|
||||
*/
|
||||
public function register($username, $displayname, $email, $password) {
|
||||
$data = [];
|
||||
try {
|
||||
$secret = null;
|
||||
$registration = $this->registrationService->validateEmail($email);
|
||||
if($registration === null) {
|
||||
$this->registrationService->validateDisplayname($displayname);
|
||||
$this->registrationService->validateUsername($username);
|
||||
$registration = $this->registrationService->createRegistration($email, $username, $password, $displayname);
|
||||
$this->mailService->sendTokenByMail($registration);
|
||||
$secret = $registration->getClientSecret();
|
||||
} else {
|
||||
$this->registrationService->generateNewToken($registration);
|
||||
$this->mailService->sendTokenByMail($registration);
|
||||
throw new RegistrationException($this->l10n->t('There is already a pending registration with this email, a new verification email has been sent to the address.'));
|
||||
}
|
||||
|
||||
$data['message'] = $this->l10n->t('Your registration is pending. Please confirm your email address.');
|
||||
$data['token'] = $registration->getToken();
|
||||
$data['status'] = Registration::STATUS_PENDING;
|
||||
if($secret !== null) {
|
||||
$data['secret'] = $secret;
|
||||
}
|
||||
return new DataResponse($data, Http::STATUS_OK);
|
||||
} catch (RegistrationException $exception) {
|
||||
throw new OCSException($exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
namespace OCA\Registration\Controller;
|
||||
|
||||
use OCA\Registration\Db\Registration;
|
||||
use OCA\Registration\Service\MailService;
|
||||
use OCA\Registration\Service\RegistrationException;
|
||||
use OCA\Registration\Service\RegistrationService;
|
||||
use \OCP\IRequest;
|
||||
|
|
@ -23,10 +25,14 @@ use \OCP\IL10N;
|
|||
|
||||
class RegisterController extends Controller {
|
||||
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var IURLGenerator */
|
||||
private $urlgenerator;
|
||||
/** @var RegistrationService */
|
||||
private $registrationService;
|
||||
/** @var MailService */
|
||||
private $mailService;
|
||||
|
||||
|
||||
public function __construct(
|
||||
|
|
@ -34,13 +40,14 @@ class RegisterController extends Controller {
|
|||
IRequest $request,
|
||||
IL10N $l10n,
|
||||
IURLGenerator $urlgenerator,
|
||||
RegistrationService $registrationService
|
||||
RegistrationService $registrationService,
|
||||
MailService $mailService
|
||||
){
|
||||
parent::__construct($appName, $request);
|
||||
$this->request = $request;
|
||||
$this->l10n = $l10n;
|
||||
$this->urlgenerator = $urlgenerator;
|
||||
$this->registrationService = $registrationService;
|
||||
$this->mailService = $mailService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,19 +68,26 @@ class RegisterController extends Controller {
|
|||
|
||||
/**
|
||||
* @PublicPage
|
||||
*
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function validateEmail() {
|
||||
$email = $this->request->getParam('email');
|
||||
try {
|
||||
$validation = $this->registrationService->validateEmail($email);
|
||||
if($validation instanceof TemplateResponse) {
|
||||
return $validation;
|
||||
|
||||
if (!$this->registrationService->checkAllowedDomains($email)) {
|
||||
return new TemplateResponse('registration', 'domains', [
|
||||
'domains' => $this->registrationService->getAllowedDomains()
|
||||
], 'guest');
|
||||
}
|
||||
try {
|
||||
$this->registrationService->validateEmail($email);
|
||||
$registration = $this->registrationService->createRegistration($email);
|
||||
$this->mailService->sendTokenByMail($registration);
|
||||
} catch (RegistrationException $e) {
|
||||
return $this->renderError($e->getMessage(), $e->getHint());
|
||||
}
|
||||
|
||||
|
||||
return new TemplateResponse('registration', 'message', array('msg' =>
|
||||
$this->l10n->t('Verification email successfully sent.')
|
||||
), 'guest');
|
||||
|
|
@ -88,7 +102,19 @@ class RegisterController extends Controller {
|
|||
*/
|
||||
public function verifyToken($token) {
|
||||
try {
|
||||
/** @var Registration $registration */
|
||||
$registration = $this->registrationService->verifyToken($token);
|
||||
$this->registrationService->confirmEmail($registration);
|
||||
|
||||
// create account without form if username/password are already stored
|
||||
if ($registration->getUsername() !== "" && $registration->getPassword() !== "") {
|
||||
$this->registrationService->createAccount($registration);
|
||||
return new TemplateResponse('registration', 'message',
|
||||
['msg' => $this->l10n->t('Your account has been successfully created, you can <a href="%s">log in now</a>.', [$this->urlgenerator->getAbsoluteURL('/')])],
|
||||
'guest'
|
||||
);
|
||||
}
|
||||
|
||||
return new TemplateResponse('registration', 'form', ['email' => $registration->getEmail(), 'token' => $registration->getToken()], 'guest');
|
||||
} catch (RegistrationException $exception) {
|
||||
return $this->renderError($exception->getMessage(), $exception->getHint());
|
||||
|
|
@ -109,7 +135,7 @@ class RegisterController extends Controller {
|
|||
$registration = $this->registrationService->getRegistrationForToken($token);
|
||||
|
||||
try {
|
||||
$this->registrationService->createAccount($token, $username, $password);
|
||||
$this->registrationService->createAccount($registration, $username, $password);
|
||||
} catch (RegistrationException $exception) {
|
||||
return $this->renderError($exception->getMessage(), $exception->getHint());
|
||||
} catch (\InvalidArgumentException $exception) {
|
||||
|
|
|
|||
|
|
@ -27,13 +27,21 @@ use OCP\AppFramework\Db\Entity;
|
|||
|
||||
class Registration extends Entity {
|
||||
|
||||
const STATUS_FINISHED = 0;
|
||||
const STATUS_PENDING = 1;
|
||||
|
||||
public $id;
|
||||
protected $email;
|
||||
protected $username;
|
||||
protected $displayname;
|
||||
protected $password;
|
||||
protected $token;
|
||||
protected $requested;
|
||||
protected $confirmed;
|
||||
protected $emailConfirmed;
|
||||
protected $clientSecret;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('confirmed', 'boolean');
|
||||
$this->addType('emailConfirmed', 'boolean');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
namespace OCA\Registration\Db;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\AppFramework\Db\Mapper;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
|
@ -37,33 +38,46 @@ class RegistrationMapper extends Mapper {
|
|||
$this->random = $random;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $token
|
||||
* @return Registration|Entity
|
||||
*/
|
||||
public function findByToken($token) {
|
||||
return $this->findEntity('SELECT * FROM `*PREFIX*registration` WHERE `token` = ? ', [$token]);
|
||||
}
|
||||
|
||||
public function findEmailByToken($token) {
|
||||
$entity = $this->findByToken($token);
|
||||
return $entity->getEmail();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
* @return Registration|Entity
|
||||
*/
|
||||
public function find($email) {
|
||||
$sql = 'SELECT `email` FROM `*PREFIX*registration` WHERE `email` = ? ';
|
||||
$sql = 'SELECT * FROM `*PREFIX*registration` WHERE `email` = ? ';
|
||||
return $this->findEntity($sql, [$email]);
|
||||
}
|
||||
|
||||
public function deleteByEmail($email) {
|
||||
$entity = $this->findEntity('SELECT * FROM `*PREFIX*registration` WHERE `email` = ?', [$email]);
|
||||
return $this->delete($entity);
|
||||
/**
|
||||
* @param Entity $entity
|
||||
* @return Entity
|
||||
*/
|
||||
public function insert(Entity $entity) {
|
||||
$entity->setRequested(date('Y-m-d H:i:s'));
|
||||
return parent::insert($entity);
|
||||
}
|
||||
|
||||
public function save($email) {
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function generateNewToken(Registration &$registration) {
|
||||
$token = $this->random->generate(6, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS);
|
||||
$registration = new Registration();
|
||||
$registration->setEmail($email);
|
||||
$registration->setToken($token);
|
||||
$registration->setRequested(date('Y-m-d H:i:s'));
|
||||
return $this->insert($registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function generateClientSecret(Registration &$registration) {
|
||||
$token = $this->random->generate(32, ISecureRandom::CHAR_HUMAN_READABLE);
|
||||
$registration->setClientSecret($token);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @author Julius Härtl <jus@bitgrid.net>
|
||||
*
|
||||
* @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 OCA\Registration\Db\Registration;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IGroupManager;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Util;
|
||||
|
||||
class MailService {
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
/** @var IGroupManager */
|
||||
private $groupManager;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
|
||||
public function __construct(IURLGenerator $urlGenerator, IMailer $mailer, Defaults $defaults, IL10N $l10n, IConfig $config, IGroupManager $groupManager, ILogger $logger) {
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->mailer = $mailer;
|
||||
$this->defaults = $defaults;
|
||||
$this->l10n = $l10n;
|
||||
$this->config = $config;
|
||||
$this->groupManager = $groupManager;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function validateEmail($email) {
|
||||
if ( !$this->mailer->validateMailAddress($email) ) {
|
||||
throw new RegistrationException($this->l10n->t('The email address you entered is not valid'));
|
||||
}
|
||||
}
|
||||
|
||||
public function sendTokenByMail(Registration $registration) {
|
||||
return true;
|
||||
$link = $this->urlGenerator->linkToRoute('registration.register.verifyToken', array('token' => $registration->getToken()));
|
||||
$link = $this->urlGenerator->getAbsoluteURL($link);
|
||||
$template_var = [
|
||||
'link' => $link,
|
||||
'sitename' => $this->defaults->getName()
|
||||
];
|
||||
$html_template = new TemplateResponse('registration', 'email.validate_html', $template_var, 'blank');
|
||||
$html_part = $html_template->render();
|
||||
$plaintext_template = new TemplateResponse('registration', 'email.validate_plaintext', $template_var, 'blank');
|
||||
$plaintext_part = $plaintext_template->render();
|
||||
$subject = $this->l10n->t('Verify your %s registration request', [$this->defaults->getName()]);
|
||||
|
||||
$from = Util::getDefaultEmailAddress('register');
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setFrom([$from => $this->defaults->getName()]);
|
||||
$message->setTo([$registration->getEmail()]);
|
||||
$message->setSubject($subject);
|
||||
$message->setPlainBody($plaintext_part);
|
||||
$message->setHtmlBody($html_part);
|
||||
$failed_recipients = $this->mailer->send($message);
|
||||
if ( !empty($failed_recipients) ) {
|
||||
throw new RegistrationException($this->l10n->t('A problem occurred sending email, please contact your administrator.'));
|
||||
}
|
||||
}
|
||||
|
||||
public function notifyAdmins($userId) {
|
||||
// Notify admin
|
||||
$admin_users = $this->groupManager->get('admin')->getUsers();
|
||||
$to_arr = array();
|
||||
foreach ( $admin_users as $au ) {
|
||||
$au_email = $this->config->getUserValue($au->getUID(), 'settings', 'email');
|
||||
if ( $au_email !== '' ) {
|
||||
$to_arr[$au_email] = $au->getDisplayName();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->sendNewUserNotifEmail($to_arr, $userId);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Sending admin notification email failed: '. $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends new user notification email to admin
|
||||
* @param array $to
|
||||
* @param string $username the new user
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendNewUserNotifEmail(array $to, $username) {
|
||||
$template_var = [
|
||||
'user' => $username,
|
||||
'sitename' => $this->defaults->getName()
|
||||
];
|
||||
$html_template = new TemplateResponse('registration', 'email.newuser_html', $template_var, 'blank');
|
||||
$html_part = $html_template->render();
|
||||
$plaintext_template = new TemplateResponse('registration', 'email.newuser_plaintext', $template_var, 'blank');
|
||||
$plaintext_part = $plaintext_template->render();
|
||||
$subject = $this->l10n->t('A new user "%s" has created an account on %s', [$username, $this->defaults->getName()]);
|
||||
|
||||
$from = Util::getDefaultEmailAddress('register');
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setFrom([$from => $this->defaults->getName()]);
|
||||
$message->setTo($to);
|
||||
$message->setSubject($subject);
|
||||
$message->setPlainBody($plaintext_part);
|
||||
$message->setHtmlBody($html_part);
|
||||
$failed_recipients = $this->mailer->send($message);
|
||||
if ( !empty($failed_recipients) )
|
||||
throw new RegistrationException('Failed recipients: '.print_r($failed_recipients, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -23,6 +23,11 @@
|
|||
|
||||
namespace OCA\Registration\Service;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Exceptions\PasswordlessTokenException;
|
||||
use OC\Authentication\Token\IProvider;
|
||||
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;
|
||||
|
|
@ -30,22 +35,25 @@ use \OCP\AppFramework\Http\RedirectResponse;
|
|||
use \OCP\Defaults;
|
||||
use OCP\ILogger;
|
||||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IURLGenerator;
|
||||
use \OCP\Util;
|
||||
use OCP\Security\ICrypto;
|
||||
use OCP\Session\Exceptions\SessionNotAvailableException;
|
||||
use \OCP\IUserManager;
|
||||
use \OCP\IUserSession;
|
||||
use \OCP\IGroupManager;
|
||||
use \OCP\IL10N;
|
||||
use \OCP\IConfig;
|
||||
use \OCP\Mail\IMailer;
|
||||
use \OCP\Security\ISecureRandom;
|
||||
use \OC_User;
|
||||
use \OC_Util;
|
||||
|
||||
class RegistrationService {
|
||||
|
||||
/** @var IMailer */
|
||||
private $mailer;
|
||||
/** @var string */
|
||||
private $appName;
|
||||
/** @var MailService */
|
||||
private $mailService;
|
||||
/** @var IL10N */
|
||||
private $l10n;
|
||||
/** @var IURLGenerator */
|
||||
|
|
@ -64,17 +72,22 @@ class RegistrationService {
|
|||
private $random;
|
||||
/** @var IUserSession */
|
||||
private $usersession;
|
||||
/** @var string */
|
||||
private $appName;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
/** @var ILogger */
|
||||
private $logger;
|
||||
/** @var ISession */
|
||||
private $session;
|
||||
/** @var IProvider */
|
||||
private $tokenProvider;
|
||||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
public function __construct($appName, IMailer $mailer, IL10N $l10n, IURLGenerator $urlGenerator,
|
||||
public function __construct($appName, MailService $mailService, IL10N $l10n, IURLGenerator $urlGenerator,
|
||||
RegistrationMapper $registrationMapper, IUserManager $userManager, IConfig $config, IGroupManager $groupManager, Defaults $defaults,
|
||||
ISecureRandom $random, IUserSession $us, IRequest $request, ILogger $logger){
|
||||
$this->mailer = $mailer;
|
||||
ISecureRandom $random, IUserSession $us, IRequest $request, ILogger $logger, ISession $session, IProvider $tokenProvider, ICrypto $crypto){
|
||||
$this->appName = $appName;
|
||||
$this->mailService = $mailService;
|
||||
$this->l10n = $l10n;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->registrationMapper = $registrationMapper;
|
||||
|
|
@ -84,29 +97,60 @@ class RegistrationService {
|
|||
$this->defaults = $defaults;
|
||||
$this->random = $random;
|
||||
$this->usersession = $us;
|
||||
$this->appName = $appName;
|
||||
$this->request = $request;
|
||||
$this->logger = $logger;
|
||||
$this->session = $session;
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
$this->crypto = $crypto;
|
||||
}
|
||||
|
||||
public function confirmEmail(Registration &$registration) {
|
||||
$registration->setEmailConfirmed(true);
|
||||
$this->registrationMapper->update($registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function generateNewToken(Registration &$registration) {
|
||||
$this->registrationMapper->generateNewToken($registration);
|
||||
$this->registrationMapper->update($registration);
|
||||
}
|
||||
/**
|
||||
* @param $email
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param string $displayname
|
||||
* @return Registration
|
||||
*/
|
||||
public function createRegistration($email, $username="", $password="", $displayname="") {
|
||||
$registration = new Registration();
|
||||
$registration->setEmail($email);
|
||||
$registration->setUsername($username);
|
||||
$registration->setDisplayname();
|
||||
if($password !== "") {
|
||||
$password = $this->crypto->encrypt($password);
|
||||
$registration->setPassword($password);
|
||||
}
|
||||
$this->registrationMapper->generateNewToken($registration);
|
||||
$this->registrationMapper->generateClientSecret($registration);
|
||||
$this->registrationMapper->insert($registration);
|
||||
return $registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
* @return Registration
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateEmail($email) {
|
||||
|
||||
if ( !$this->mailer->validateMailAddress($email) ) {
|
||||
throw new RegistrationException($this->l10n->t('The email address you entered is not valid'));
|
||||
}
|
||||
$this->mailService->validateEmail($email);
|
||||
|
||||
// check for pending registrations
|
||||
try {
|
||||
$registration = $this->registrationMapper->find($email);
|
||||
} catch (\Exception $e) {
|
||||
$registration = null;
|
||||
}
|
||||
// check if email already tried to register
|
||||
if ( $registration !== null) {
|
||||
$this->registrationMapper->delete($registration);
|
||||
$this->generateToken($email);
|
||||
throw new RegistrationException($this->l10n->t('There is already a pending registration with this email, a new verification email has been sent to the address.'));
|
||||
}
|
||||
return $this->registrationMapper->find($email);
|
||||
} catch (\Exception $e) {}
|
||||
|
||||
if ( $this->config->getUsersForUserValue('settings', 'email', $email) ) {
|
||||
throw new RegistrationException(
|
||||
|
|
@ -115,32 +159,34 @@ class RegistrationService {
|
|||
);
|
||||
}
|
||||
|
||||
// allow only from specific email domain}
|
||||
if (!$this->checkAllowedDomains($email)) {
|
||||
$allowed_domains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
$allowed_domains = explode(';', $allowed_domains);
|
||||
return new TemplateResponse('registration', 'domains', [
|
||||
'domains' => $allowed_domains
|
||||
], 'guest');
|
||||
throw new RegistrationException(
|
||||
$this->l10n->t(
|
||||
'Registration is only allowed for the following domains: ' .
|
||||
$this->config->getAppValue($this->appName, 'allowed_domains', '')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$this->generateToken($email);
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
public function generateToken($email) {
|
||||
try {
|
||||
$registration = $this->registrationMapper->find($email);
|
||||
$this->registrationMapper->delete($registration);
|
||||
} catch (\Exception $exception) {}
|
||||
$registration = $this->registrationMapper->save($email);
|
||||
/**
|
||||
* @param $displayname
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateDisplayname($displayname) {
|
||||
if($displayname === "") {
|
||||
throw new RegistrationException($this->l10n->t('Please provide a valid display name.'));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$this->sendValidationEmail($registration->getToken(), $email);
|
||||
} catch (\Exception $e) {
|
||||
throw new RegistrationException($this->l10n->t('A problem occurred sending email, please contact your administrator.'));
|
||||
/**
|
||||
* @param $username
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function validateUsername($username) {
|
||||
if($username === "" || $this->userManager->get($username) !== null) {
|
||||
throw new RegistrationException($this->l10n->t('Please provide a valid user name.'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +215,17 @@ class RegistrationService {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedDomains() {
|
||||
$allowed_domains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
|
||||
$allowed_domains = explode(';', $allowed_domains);
|
||||
return $allowed_domains;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find registration entity for token
|
||||
*
|
||||
* @param $token
|
||||
* @return string
|
||||
* @throws RegistrationException
|
||||
|
|
@ -181,11 +238,25 @@ class RegistrationService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $registration
|
||||
* @param null $username
|
||||
* @param null $password
|
||||
* @return \OCP\IUser
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function createAccount(Registration &$registration, $username = null, $password = null) {
|
||||
if($password === null && $registration->getPassword() === null) {
|
||||
$generatedPassword = $this->generateRandomDeviceToken();
|
||||
$registration->setPassword($this->crypto->encrypt($generatedPassword));
|
||||
}
|
||||
|
||||
public function createAccount($token, $username, $password) {
|
||||
$email = $this->registrationMapper->findEmailByToken($token);
|
||||
if ( $email === false ) {
|
||||
throw new RegistrationException($this->l10n->t('Invalid verification URL. No registration request with this verification URL is found.'));
|
||||
if ($username === null) {
|
||||
$username = $registration->getUsername();
|
||||
}
|
||||
|
||||
if($registration->getPassword() !== null) {
|
||||
$password = $this->crypto->decrypt($registration->getPassword());
|
||||
}
|
||||
|
||||
$user = $this->userManager->createUser($username, $password);
|
||||
|
|
@ -195,7 +266,7 @@ class RegistrationService {
|
|||
$userId = $user->getUID();
|
||||
// Set user email
|
||||
try {
|
||||
$this->config->setUserValue($userId, 'settings', 'email', $email);
|
||||
$this->config->setUserValue($userId, 'settings', 'email', $registration->getEmail());
|
||||
} catch (\Exception $e) {
|
||||
throw new RegistrationException($this->l10n->t('Unable to set user email: ' . $e->getMessage()));
|
||||
}
|
||||
|
|
@ -211,19 +282,98 @@ class RegistrationService {
|
|||
}
|
||||
}
|
||||
|
||||
// Delete pending reg request
|
||||
$res = $this->registrationMapper->deleteByEmail($email);
|
||||
// Delete pending registration if no client secret is stored
|
||||
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->notifyAdmins($userId);
|
||||
|
||||
$this->loginUser($userId, $username, $password);
|
||||
|
||||
}
|
||||
|
||||
public function loginUser($userId, $username, $password) {
|
||||
$this->mailService->notifyAdmins($userId);
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $token
|
||||
* @return Registration
|
||||
*/
|
||||
public function getRegistrationForToken($token) {
|
||||
return $this->registrationMapper->findByToken($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registation
|
||||
* @return null|\OCP\IUser
|
||||
*/
|
||||
public function getUserAccount(Registration $registation) {
|
||||
$user = $this->userManager->get($registation->getUsername());
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Registration $registration
|
||||
*/
|
||||
public function deleteRegistration(Registration $registration) {
|
||||
$this->registrationMapper->delete($registration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a 25 digit device password
|
||||
*
|
||||
* Example: AbCdE-fGhIj-KlMnO-pQrSt-12345
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function generateRandomDeviceToken() {
|
||||
$groups = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$groups[] = $this->random->generate(5, ISecureRandom::CHAR_HUMAN_READABLE);
|
||||
}
|
||||
return implode('-', $groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uid
|
||||
* @return string
|
||||
* @throws RegistrationException
|
||||
*/
|
||||
public function generateAppPassword($uid) {
|
||||
$name = $this->l10n->t('Registration app auto setup');
|
||||
try {
|
||||
$sessionId = $this->session->getId();
|
||||
} catch (SessionNotAvailableException $ex) {
|
||||
throw new RegistrationException('Failed to generate an app token.');
|
||||
}
|
||||
|
||||
try {
|
||||
$sessionToken = $this->tokenProvider->getToken($sessionId);
|
||||
$loginName = $sessionToken->getLoginName();
|
||||
try {
|
||||
$password = $this->tokenProvider->getPassword($sessionToken, $sessionId);
|
||||
} catch (PasswordlessTokenException $ex) {
|
||||
$password = null;
|
||||
}
|
||||
} catch (InvalidTokenException $ex) {
|
||||
throw new RegistrationException('Failed to generate an app token.');
|
||||
}
|
||||
|
||||
$token = $this->generateRandomDeviceToken();
|
||||
$this->tokenProvider->generateToken($token, $uid, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $userId
|
||||
* @param $username
|
||||
* @param $password
|
||||
* @param $decrypt
|
||||
* @return RedirectResponse|TemplateResponse
|
||||
*/
|
||||
public function loginUser($userId, $username, $password, $decrypt = false) {
|
||||
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);
|
||||
|
|
@ -244,83 +394,6 @@ class RegistrationService {
|
|||
|
||||
}
|
||||
|
||||
public function notifyAdmins($userId) {
|
||||
// Notify admin
|
||||
$admin_users = $this->groupManager->get('admin')->getUsers();
|
||||
$to_arr = array();
|
||||
foreach ( $admin_users as $au ) {
|
||||
$au_email = $this->config->getUserValue($au->getUID(), 'settings', 'email');
|
||||
if ( $au_email !== '' ) {
|
||||
$to_arr[$au_email] = $au->getDisplayName();
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->sendNewUserNotifEmail($to_arr, $userId);
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('Sending admin notification email failed: '. $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends validation email
|
||||
* @param string $token
|
||||
* @param string $to
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendValidationEmail($token, $to) {
|
||||
$link = $this->urlGenerator->linkToRoute('registration.register.verifyToken', array('token' => $token));
|
||||
$link = $this->urlGenerator->getAbsoluteURL($link);
|
||||
$template_var = [
|
||||
'link' => $link,
|
||||
'sitename' => $this->defaults->getName()
|
||||
];
|
||||
$html_template = new TemplateResponse('registration', 'email.validate_html', $template_var, 'blank');
|
||||
$html_part = $html_template->render();
|
||||
$plaintext_template = new TemplateResponse('registration', 'email.validate_plaintext', $template_var, 'blank');
|
||||
$plaintext_part = $plaintext_template->render();
|
||||
$subject = $this->l10n->t('Verify your %s registration request', [$this->defaults->getName()]);
|
||||
|
||||
$from = Util::getDefaultEmailAddress('register');
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setFrom([$from => $this->defaults->getName()]);
|
||||
$message->setTo([$to]);
|
||||
$message->setSubject($subject);
|
||||
$message->setPlainBody($plaintext_part);
|
||||
$message->setHtmlBody($html_part);
|
||||
$failed_recipients = $this->mailer->send($message);
|
||||
if ( !empty($failed_recipients) )
|
||||
throw new RegistrationException('Failed recipients: '.print_r($failed_recipients, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends new user notification email to admin
|
||||
* @param array $to
|
||||
* @param string $username the new user
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function sendNewUserNotifEmail(array $to, $username) {
|
||||
$template_var = [
|
||||
'user' => $username,
|
||||
'sitename' => $this->defaults->getName()
|
||||
];
|
||||
$html_template = new TemplateResponse('registration', 'email.newuser_html', $template_var, 'blank');
|
||||
$html_part = $html_template->render();
|
||||
$plaintext_template = new TemplateResponse('registration', 'email.newuser_plaintext', $template_var, 'blank');
|
||||
$plaintext_part = $plaintext_template->render();
|
||||
$subject = $this->l10n->t('A new user "%s" has created an account on %s', [$username, $this->defaults->getName()]);
|
||||
|
||||
$from = Util::getDefaultEmailAddress('register');
|
||||
$message = $this->mailer->createMessage();
|
||||
$message->setFrom([$from => $this->defaults->getName()]);
|
||||
$message->setTo($to);
|
||||
$message->setSubject($subject);
|
||||
$message->setPlainBody($plaintext_part);
|
||||
$message->setHtmlBody($html_part);
|
||||
$failed_recipients = $this->mailer->send($message);
|
||||
if ( !empty($failed_recipients) )
|
||||
throw new RegistrationException('Failed recipients: '.print_r($failed_recipients, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replicates OC::cleanupLoginTokens() since it's protected
|
||||
* @param string $userId
|
||||
|
|
@ -336,7 +409,4 @@ class RegistrationService {
|
|||
}
|
||||
}
|
||||
|
||||
public function getRegistrationForToken($token) {
|
||||
return $this->registrationMapper->findByToken($token);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue