Merge pull request #77 from juliushaertl/registration-api

Registration API
This commit is contained in:
Pellaeon Lin 2017-08-16 00:13:29 +08:00 committed by GitHub
commit 38543170d7
20 changed files with 1351 additions and 553 deletions

View File

@ -11,6 +11,7 @@
namespace OCA\Registration\AppInfo;
$app = new Application();
$controller = $app->getContainer()->query('SettingsController');
use OCA\Registration\Controller\SettingsController;
$controller = \OC::$server->query(SettingsController::class);
return $controller->displayPanel()->render();

View File

@ -11,9 +11,14 @@
namespace OCA\Registration\AppInfo;
$app = new Application();
$c = $app->getContainer();
\OC_App::registerLogIn([
'name' => \OC::$server->getL10N('registration')->t('Register'),
'href' => \OC::$server->getURLGenerator()->linkToRoute('registration.register.askEmail')
]);
\OC_App::registerLogIn(array('name' => $c->query('L10N')->t('Register'), 'href' => $c->query('URLGenerator')->linkToRoute('registration.register.askEmail')));
\OCP\App::registerAdmin('registration', 'admin');
\OCP\App::registerAdmin($c->getAppName(), 'admin');
if(interface_exists('\OCP\Capabilities\IPublicCapability')) {
$app = new \OCP\AppFramework\App('registration');
$app->getContainer()->registerCapability(\OCA\Registration\Capabilities::class);
}

View File

@ -1,100 +0,0 @@
<?php
/**
* ownCloud - registration
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @copyright Pellaeon Lin 2014
*/
namespace OCA\Registration\AppInfo;
use \OC\AppFramework\Utility\SimpleContainer;
use \OCP\AppFramework\App;
use \OCA\Registration\Controller\RegisterController;
use \OCA\Registration\Controller\SettingsController;
use \OCA\Registration\Wrapper;
use \OCA\Registration\Db\PendingRegist;
class Application extends App {
public function __construct (array $urlParams=array()) {
parent::__construct('registration', $urlParams);
$container = $this->getContainer();
/**
* Controllers
*/
$container->registerService('RegisterController', function(SimpleContainer $c) {
return new RegisterController(
$c->query('AppName'),
$c->query('Request'),
$c->query('Mailer'),
$c->query('L10N'),
$c->query('URLGenerator'),
$c->query('PendingRegist'),
$c->query('UserManager'),
$c->query('Config'),
$c->query('GroupManager'),
$c->query('Defaults'),
$c->query('ServerContainer')->getSecureRandom()->getMediumStrengthGenerator(),
$c->query('ServerContainer')->getUserSession()
);
});
$container->registerService('SettingsController', function(SimpleContainer $c) {
return new SettingsController(
$c->query('AppName'),
$c->query('Request'),
$c->query('L10N'),
$c->query('Config'),
$c->query('GroupManager')
);
});
/**
* Core
*/
$container->registerService('UserManager', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getUserManager();
});
$container->registerService('GroupManager', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getGroupManager();
});
$container->registerService('Config', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getConfig();
});
$container->registerService('Mailer', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getMailer();
});
$container->registerService('L10N', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getL10N($c->query('AppName'));
});
$container->registerService('URLGenerator', function(SimpleContainer $c) {
return $c->getServer()->getURLGenerator();
});
$container->registerService('PendingRegist', function(SimpleContainer $c) {
return new PendingRegist($c->query('ServerContainer')->getDatabaseConnection(),
$c->query('ServerContainer')->getSecureRandom()->getMediumStrengthGenerator());
});
$container->registerService('Defaults', function(SimpleContainer $c) {
return new \OCP\Defaults;
});
}
}

View File

@ -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>

View File

@ -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.5</version>
<version>0.2.5-1</version>
<website>https://github.com/pellaeon/registration</website>
<bugs>https://github.com/pellaeon/registration/issues</bugs>
<repository>https://github.com/pellaeon/registration</repository>

View File

@ -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']
]
];

51
capabilities.php Normal file
View File

@ -0,0 +1,51 @@
<?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\Capabilities\IPublicCapability;
use OCP\IURLGenerator;
class Capabilities implements IPublicCapability {
/** @var IURLGenerator */
private $urlGenerator;
public function __construct(IURLGenerator $urlGenerator) {
$this->urlGenerator = $urlGenerator;
}
public function getCapabilities() {
return [
'registration' =>
[
'enabled' => true,
'apiRoot' => $this->urlGenerator->linkTo(
'', 'ocs/v2.php/apps/registration/api/v1/'),
'apiLevel' => 'v1'
]
];
}
}

View File

@ -0,0 +1,182 @@
<?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;
const REGISTRATION_STATUS_COMPLETE = 0;
const REGISTRATION_STATUS_PENDING = 1;
const REGISTRATION_STATUS_EXISTING = 2;
public function __construct($appName,
IRequest $request,
RegistrationService $registrationService,
MailService $mailService,
IL10N $l10n,
Defaults $defaults) {
parent::__construct($appName, $request);
$this->registrationService = $registrationService;
$this->mailService = $mailService;
$this->l10n = $l10n;
$this->defaults = $defaults;
}
/**
* @PublicPage
* @AnonRateThrottle(limit=5, period=1)
*
* @param string $username
* @param string $displayname
* @param string $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
* @AnonRateThrottle(limit=10, period=1)
*
* @param string $clientSecret
* @throws OCSException
* @return DataResponse
*/
public function status($clientSecret) {
try {
/** @var Registration $registration */
$registration = $this->registrationService->getRegistrationForSecret($clientSecret);
} catch (DoesNotExistException $e) {
throw new OCSNotFoundException('No pending registration.');
}
if (!$registration->getEmailConfirmed()) {
return new DataResponse(
[
'registrationStatus' => self::REGISTRATION_STATUS_PENDING,
'message' => $this->l10n->t('Your registration is pending. Please confirm your email address.')
],
Http::STATUS_OK
);
} 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());
$data = [
'appPassword' => $appPassword,
'cloudUrl' => $this->defaults->getBaseUrl(),
'registrationStatus' => self::REGISTRATION_STATUS_COMPLETE
];
$this->registrationService->deleteRegistration($registration);
return new DataResponse($data, Http::STATUS_OK);
}
}
/**
* @PublicPage
* @AnonRateThrottle(limit=5, period=1)
*
* @param string $username
* @param string $displayname
* @param string $email
* @param string $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);
return new DataResponse(
[
'registrationStatus' => self::REGISTRATION_STATUS_EXISTING,
'message' => $this->l10n->t('There is already a pending registration with this email, a new verification email has been sent to the address.')
],
Http::STATUS_OK
);
}
$data['message'] = $this->l10n->t('Your registration is pending. Please confirm your email address.');
$data['registrationStatus'] = self::REGISTRATION_STATUS_PENDING;
if($secret !== null) {
$data['secret'] = $secret;
}
return new DataResponse($data, Http::STATUS_OK);
} catch (RegistrationException $exception) {
throw new OCSException($exception->getMessage(), $exception->getCode());
}
}
}

View File

@ -6,64 +6,57 @@
* later. See the COPYING file.
*
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @author Julius Härtl <jus@bitgrid.net>
* @copyright Pellaeon Lin 2014
*/
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;
use \OCP\AppFramework\Http\TemplateResponse;
use \OCP\AppFramework\Http\RedirectResponse;
use \OCP\AppFramework\Controller;
use \OCP\Defaults;
use \OCP\Util;
use \OCA\Registration\Wrapper;
use \OCP\IUserManager;
use \OCP\IUserSession;
use \OCP\IGroupManager;
use OCP\IURLGenerator;
use \OCP\IL10N;
use \OCP\IConfig;
use \OCP\Mail\IMailer;
use \OCP\Security\ISecureRandom;
use \OC_User;
use \OC_Util;
class RegisterController extends Controller {
private $mailer;
/** @var IL10N */
private $l10n;
/** @var IURLGenerator */
private $urlgenerator;
private $pendingreg;
private $usermanager;
private $config;
private $groupmanager;
/** @var \OCP\Defaults */
private $defaults;
private $random;
private $usersession;
protected $appName;
/** @var RegistrationService */
private $registrationService;
/** @var MailService */
private $mailService;
public function __construct($appName, IRequest $request, IMailer $mailer, IL10N $l10n, $urlgenerator,
$pendingreg, IUserManager $usermanager, IConfig $config, IGroupManager $groupmanager, Defaults $defaults,
ISecureRandom $random, IUserSession $us){
$this->mailer = $mailer;
public function __construct(
$appName,
IRequest $request,
IL10N $l10n,
IURLGenerator $urlgenerator,
RegistrationService $registrationService,
MailService $mailService
){
parent::__construct($appName, $request);
$this->l10n = $l10n;
$this->urlgenerator = $urlgenerator;
$this->pendingreg = $pendingreg;
$this->usermanager = $usermanager;
$this->config = $config;
$this->groupmanager = $groupmanager;
$this->defaults = $defaults;
$this->appName = $appName;
$this->random = $random;
$this->usersession = $us;
parent::__construct($appName, $request);
$this->registrationService = $registrationService;
$this->mailService = $mailService;
}
/**
* @NoCSRFRequired
* @PublicPage
*
* @param $errormsg
* @param $entered
* @return TemplateResponse
*/
public function askEmail($errormsg, $entered) {
$params = array(
@ -75,83 +68,26 @@ class RegisterController extends Controller {
/**
* @PublicPage
*
* @return TemplateResponse
*/
public function validateEmail() {
$email = $this->request->getParam('email');
if ( !$this->mailer->validateMailAddress($email) ) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('The email address you entered is not valid'),
'hint' => ''
))
), 'error');
}
if ( $this->pendingreg->find($email) ) {
$this->pendingreg->delete($email);
$token = $this->pendingreg->save($email);
try {
$this->sendValidationEmail($token, $email);
} catch (\Exception $e) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('A problem occurred sending email, please contact your administrator.'),
'hint' => ''
))
), 'error');
}
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('There is already a pending registration with this email, a new verification email has been sent to the address.'),
'hint' => ''
))
), 'error');
}
if ( $this->config->getUsersForUserValue('settings', 'email', $email) ) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('A user has already taken this email, maybe you already have an account?'),
'hint' => str_replace(
'{login}', $this->urlgenerator->getAbsoluteURL('/'),
$this->l10n->t('You can <a href="{login}">log in now</a>.'))
))
), 'error');
}
// allow only from specific email domain
$allowed_domains = $this->config->getAppValue($this->appName, 'allowed_domains', '');
if ( $allowed_domains !== '' ) {
$allowed_domains = explode(';', $allowed_domains);
$allowed = false;
foreach ( $allowed_domains as $domain ) {
$maildomain=explode("@",$email)[1];
// valid domain, everythings fine
if ($maildomain === $domain) {
$allowed=true;
break;
}
}
if ( $allowed === false ) {
return new TemplateResponse('registration', 'domains', ['domains' =>
$allowed_domains
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());
}
$token = $this->pendingreg->save($email);
try {
$this->sendValidationEmail($token, $email);
} catch (\Exception $e) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('A problem occurred sending email, please contact your administrator.'),
'hint' => ''
))
), 'error');
}
return new TemplateResponse('registration', 'message', array('msg' =>
$this->l10n->t('Verification email successfully sent.')
), 'guest');
@ -160,207 +96,72 @@ class RegisterController extends Controller {
/**
* @NoCSRFRequired
* @PublicPage
*
* @param $token
* @return TemplateResponse
*/
public function verifyToken($token) {
$email = $this->pendingreg->findEmailByToken($token);
if ( $email === false ) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('Invalid verification URL. No registration request with this verification URL is found.'),
'hint' => ''
))
), 'error');
} elseif ( $email ) {
return new TemplateResponse('registration', 'form', array('email' => $email, 'token' => $token), 'guest');
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());
}
}
/**
* @PublicPage
* @UseSession
*
* @param $token
* @return RedirectResponse|TemplateResponse
*/
public function createAccount($token) {
$email = $this->pendingreg->findEmailByToken($token);
if ( $email === false ) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('Invalid verification URL. No registration request with this verification URL is found.'),
'hint' => ''
))
), 'error');
} elseif ( $email ) {
$username = $this->request->getParam('username');
$password = $this->request->getParam('password');
$registration = $this->registrationService->getRegistrationForToken($token);
try {
$user = $this->usermanager->createUser($username, $password);
} catch (\Exception $e) {
$this->registrationService->createAccount($registration, $username, $password);
} catch (RegistrationException $exception) {
return $this->renderError($exception->getMessage(), $exception->getHint());
} catch (\InvalidArgumentException $exception) {
// Render form with previously sent values
return new TemplateResponse('registration', 'form',
array('email' => $email,
'entered_data' => array('username' => $username),
'errormsgs' => array($e->getMessage()),
'token' => $token), 'guest');
[
'email' => $registration->getEmail(),
'entered_data' => array('user' => $username),
'errormsgs' => array($exception->getMessage()),
'token' => $token
], 'guest');
}
if ( $user === false ) {
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'
);
}
private function renderError($error, $hint="") {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('Unable to create user, there are problems with the user backend.'),
'hint' => ''
))
), 'error');
} else {
$userId = $user->getUID();
// Set user email
try {
$this->config->setUserValue($userId, 'settings', 'email', $email);
} catch (\Exception $e) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('Unable to set user email: '.$e->getMessage()),
'hint' => ''
'error' => $error,
'hint' => $hint
))
), 'error');
}
// Add user to group
$registered_user_group = $this->config->getAppValue($this->appName, 'registered_user_group', 'none');
if ( $registered_user_group !== 'none' ) {
try {
$group = $this->groupmanager->get($registered_user_group);
$group->addUser($user);
} catch (\Exception $e) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $e->message,
))
), 'error');
}
}
// Delete pending reg request
$res = $this->pendingreg->delete($email);
if ( $res === false ) {
return new TemplateResponse('', 'error', array(
'errors' => array(array(
'error' => $this->l10n->t('Failed to delete pending registration request'),
'hint' => ''
))
), 'error');
}
// 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) {
\OCP\Util::writeLog('registration', 'Sending admin notification email failed: '. $e->getMessage, \OCP\Util::ERROR);
}
// Try to log user in
if ( method_exists($this->usersession, 'createSessionToken') ) {
$this->usersession->login($username, $password);
$this->usersession->createSessionToken($this->request, $userId, $username, $password);
return new RedirectResponse($this->urlgenerator->linkToRoute('files.view.index'));
} 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', array('msg' =>
str_replace('{link}',
$this->urlgenerator->getAbsoluteURL('/'),
$this->l10n->t('Your account has been successfully created, you can <a href="{link}">log in now</a>.')
)), 'guest');
}
}
}
}
/**
* Sends validation email
* @param string $token
* @param string $to
* @return null
* @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 \Exception('Failed recipients: '.print_r($failed_recipients, true));
}
/**
* Sends new user notification email to admin
* @param array $to
* @param string $username the new user
* @return null
* @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 \Exception('Failed recipients: '.print_r($failed_recipients, true));
}
/**
* Replicates OC::cleanupLoginTokens() since it's protected
* @param string $userId
* @return null
*/
private function cleanupLoginTokens($userId) {
$cutoff = time() - $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
$tokens = $this->config->getUserKeys($userId, 'login_token');
foreach ($tokens as $token) {
$time = $this->config->getUserValue($userId, 'login_token', $token);
if ($time < $cutoff) {
$this->config->deleteUserValue($userId, 'login_token', $token);
}
}
}
}

View File

@ -6,6 +6,7 @@
* later. See the COPYING file.
*
* @author Pellaeon Lin <pellaeon@cnmc.tw>
* @author Julius Härtl <jus@bitgrid.net>
* @copyright Pellaeon Lin 2015
*/
@ -19,21 +20,24 @@ use \OCP\AppFramework\Controller;
use \OCP\IGroupManager;
use \OCP\IL10N;
use \OCP\IConfig;
use \OCP\IUser;
class SettingsController extends Controller {
/** @var IL10N */
private $l10n;
/** @var IConfig */
private $config;
/** @var IGroupManager */
private $groupmanager;
/** @var string */
protected $appName;
public function __construct($appName, IRequest $request, IL10N $l10n, IConfig $config, IGroupManager $groupmanager){
parent::__construct($appName, $request);
$this->l10n = $l10n;
$this->config = $config;
$this->groupmanager = $groupmanager;
$this->appName = $appName;
parent::__construct($appName, $request);
}
@ -89,6 +93,7 @@ class SettingsController extends Controller {
*/
public function displayPanel() {
$groups = $this->groupmanager->search('');
$group_id_list = [];
foreach ( $groups as $group ) {
$group_id_list[] = $group->getGid();
}

View File

@ -1,49 +0,0 @@
<?php
namespace OCA\Registration\Db;
use \OCP\IDbConnection;
use \OCP\Util;
use \OCP\Security\ISecureRandom;
class PendingRegist {
private $db;
/** @var \OCP\Security\ISecureRandom */
protected $random;
public function __construct(IDbConnection $db, ISecureRandom $random) {
$this->db = $db;
$this->random = $random;
}
public function save($email) {
$query = $this->db->prepare( 'INSERT INTO `*PREFIX*registration`'
.' ( `email`, `token`, `requested` ) VALUES( ?, ?, NOW() )' );
$token = $this->random->generate(6, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS);
$query->execute(array( $email, $token ));
return $token;
}
public function find($email) {
$query = $this->db->prepare('SELECT `email` FROM `*PREFIX*registration` WHERE `email` = ? ');
$query->execute(array($email));
return $query->fetchAll();
}
public function delete($email) {
$query = $this->db->prepare('DELETE FROM `*PREFIX*registration` WHERE `email` = ? ');
return $query->execute(array($email));
}
/**
* @return string|false
*/
public function findEmailByToken($token) {
$query = $this->db->prepare('SELECT `email` FROM `*PREFIX*registration` WHERE `token` = ? ');
$query->execute(array($token));
return $query->fetch()['email'];
}
}

44
db/registration.php Normal file
View File

@ -0,0 +1,44 @@
<?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\Db;
use OCP\AppFramework\Db\Entity;
class Registration extends Entity {
public $id;
protected $email;
protected $username;
protected $displayname;
protected $password;
protected $token;
protected $requested;
protected $emailConfirmed;
protected $clientSecret;
public function __construct() {
$this->addType('emailConfirmed', 'boolean');
}
}

100
db/registrationmapper.php Normal file
View File

@ -0,0 +1,100 @@
<?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\Db;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\Mapper;
use OCP\IDBConnection;
use OCP\Security\ISecureRandom;
class RegistrationMapper extends Mapper {
/** @var \OCP\Security\ISecureRandom */
protected $random;
public function __construct(IDBConnection $db, ISecureRandom $random) {
parent::__construct($db, 'registration', Registration::class);
$this->random = $random;
}
/**
* @param $token
* @return Registration|Entity
*/
public function findByToken($token) {
return $this->findEntity('SELECT * FROM `*PREFIX*registration` WHERE `token` = ? ', [$token]);
}
public function findBySecret($secret) {
return $this->findEntity('SELECT * FROM `*PREFIX*registration` WHERE `client_secret` = ? ', [$secret]);
}
public function usernameIsPending($username) {
try {
$entity = $this->findEntity(
'SELECT id FROM `*PREFIX*registration` WHERE `username` = ? ',
[$username]
);
} catch (DoesNotExistException $e) {
return false;
}
return true;
}
/**
* @param $email
* @return Registration|Entity
*/
public function find($email) {
$sql = 'SELECT * FROM `*PREFIX*registration` WHERE `email` = ? ';
return $this->findEntity($sql, [$email]);
}
/**
* @param Entity $entity
* @return Entity
*/
public function insert(Entity $entity) {
$entity->setRequested(date('Y-m-d H:i:s'));
return parent::insert($entity);
}
/**
* @param Registration $registration
*/
public function generateNewToken(Registration &$registration) {
$token = $this->random->generate(6, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS);
$registration->setToken($token);
}
/**
* @param Registration $registration
*/
public function generateClientSecret(Registration &$registration) {
$token = $this->random->generate(32, ISecureRandom::CHAR_HUMAN_READABLE);
$registration->setClientSecret($token);
}
}

View File

@ -1 +1,12 @@
<phpunit bootstrap="tests/autoloader.php"></phpunit>
<phpunit bootstrap="tests/autoloader.php" colors="true">
<testsuites>
<testsuite name="unit">
<directory>./tests/unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./</directory>
</whitelist>
</filter>
</phpunit>

156
service/mailservice.php Normal file
View File

@ -0,0 +1,156 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
* @copyright Copyright (c) 2017 Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
*
* @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;
}
/**
* @param string $email
* @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'));
}
}
/**
* @param Registration $registration
* @return bool
* @throws RegistrationException
*/
public function sendTokenByMail(Registration $registration) {
$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.'));
}
}
/**
* @param string $userId
*/
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));
}
}

View File

@ -0,0 +1,49 @@
<?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;
class RegistrationException extends \Exception {
protected $hint;
/**
* RegistrationException constructor.
*
* @param string $message
* @param string $hint
* @param int $code
*/
public function __construct($message, $hint = "", $code = 400) {
parent::__construct($message, $code);
$this->hint = $hint;
}
/**
* @return string
*/
public function getHint() {
return $this->hint;
}
}

View File

@ -0,0 +1,431 @@
<?php
/**
* @copyright Copyright (c) 2017 Julius Härtl <jus@bitgrid.net>
* @copyright Copyright (c) 2017 Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
*
* @author Julius Härtl <jus@bitgrid.net>
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @author Lukas Reschke <lukas@statuscode.ch>
*
* @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\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;
use \OCP\AppFramework\Http\RedirectResponse;
use \OCP\Defaults;
use OCP\ILogger;
use OCP\IRequest;
use OCP\ISession;
use OCP\IURLGenerator;
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\Security\ISecureRandom;
use \OC_User;
use \OC_Util;
class RegistrationService {
/** @var string */
private $appName;
/** @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 __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, ISession $session, IProvider $tokenProvider, ICrypto $crypto){
$this->appName = $appName;
$this->mailService = $mailService;
$this->l10n = $l10n;
$this->urlGenerator = $urlGenerator;
$this->registrationMapper = $registrationMapper;
$this->userManager = $userManager;
$this->config = $config;
$this->groupManager = $groupManager;
$this->defaults = $defaults;
$this->random = $random;
$this->usersession = $us;
$this->request = $request;
$this->logger = $logger;
$this->session = $session;
$this->tokenProvider = $tokenProvider;
$this->crypto = $crypto;
}
/**
* @param Registration $registration
*/
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 string $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 string $email
* @return Registration
* @throws RegistrationException
*/
public function validateEmail($email) {
$this->mailService->validateEmail($email);
// check for pending registrations
try {
return $this->registrationMapper->find($email);
} catch (\Exception $e) {}
if ( $this->config->getUsersForUserValue('settings', 'email', $email) ) {
throw new RegistrationException(
$this->l10n->t('A user has already taken this email, maybe you already have an account?'),
$this->l10n->t('You can <a href="%s">log in now</a>.', [$this->urlGenerator->getAbsoluteURL('/')])
);
}
if (!$this->checkAllowedDomains($email)) {
throw new RegistrationException(
$this->l10n->t(
'Registration is only allowed for the following domains: ' .
$this->config->getAppValue($this->appName, 'allowed_domains', '')
)
);
}
return null;
}
/**
* @param string $displayname
* @throws RegistrationException
*/
public function validateDisplayname($displayname) {
if($displayname === "") {
throw new RegistrationException($this->l10n->t('Please provide a valid display name.'));
}
}
/**
* @param string $username
* @throws RegistrationException
*/
public function validateUsername($username) {
if($username === "") {
throw new RegistrationException($this->l10n->t('Please provide a valid user name.'));
}
if($this->registrationMapper->usernameIsPending($username) || $this->userManager->get($username) !== null) {
throw new RegistrationException($this->l10n->t('The username you have chosen already exists.'));
}
}
/**
* check if email domain is allowed
*
* @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);
$allowed = false;
foreach ($allowed_domains as $domain) {
$maildomain = explode("@", $email)[1];
// valid domain, everythings fine
if ($maildomain === $domain) {
$allowed = true;
break;
}
}
return $allowed;
}
return true;
}
/**
* @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 string $token
* @return string
* @throws RegistrationException
*/
public function verifyToken($token) {
try {
return $this->registrationMapper->findByToken($token);
} catch (DoesNotExistException $exception) {
throw new RegistrationException($this->l10n->t('Invalid verification URL. No registration request with this verification URL is found.', 404));
}
}
/**
* @param $registration
* @param string $username
* @param string $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));
}
if ($username === null) {
$username = $registration->getUsername();
}
if($registration->getPassword() !== null) {
$password = $this->crypto->decrypt($registration->getPassword());
}
$user = $this->userManager->createUser($username, $password);
if ($user === false) {
throw new RegistrationException($this->l10n->t('Unable to create user, there are problems with the user backend.'));
}
$userId = $user->getUID();
// Set user email
try {
$this->config->setUserValue($userId, 'settings', 'email', $registration->getEmail());
} catch (\Exception $e) {
throw new RegistrationException($this->l10n->t('Unable to set user email: ' . $e->getMessage()));
}
// Add user to group
$registered_user_group = $this->config->getAppValue($this->appName, 'registered_user_group', 'none');
if ( $registered_user_group !== 'none' ) {
try {
$group = $this->groupManager->get($registered_user_group);
$group->addUser($user);
} catch (\Exception $e) {
throw new RegistrationException($e->getMessage());
}
}
// 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->mailService->notifyAdmins($userId);
return $user;
}
/**
* @param $token
* @return Registration
*/
public function getRegistrationForToken($token) {
return $this->registrationMapper->findByToken($token);
}
/**
* @param $secret
* @return Registration
*/
public function getRegistrationForSecret($secret) {
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;
}
/**
* @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 string $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);
return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index'));
} 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'
);
}
/**
* Replicates OC::cleanupLoginTokens() since it's protected
* @param string $userId
*/
public function cleanupLoginTokens($userId) {
$cutoff = time() - $this->config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
$tokens = $this->config->getUserKeys($userId, 'login_token');
foreach ($tokens as $token) {
$time = $this->config->getUserValue($userId, 'login_token', $token);
if ($time < $cutoff) {
$this->config->deleteUserValue($userId, 'login_token', $token);
}
}
}
}

View File

@ -9,44 +9,4 @@
* @copyright Pellaeon Lin 2014
*/
require_once __DIR__ . '/../../../3rdparty/Pimple/Pimple.php';
class OC {
public static $server;
public static $session;
}
// to execute without owncloud, we need to create our own classloader
spl_autoload_register(function ($className){
if (strpos($className, 'OCA\\') === 0) {
$path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php');
$relPath = __DIR__ . '/../..' . $path;
if(file_exists($relPath)){
require_once $relPath;
}
} else if(strpos($className, 'OCP\\') === 0) {
$path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php');
$relPath = __DIR__ . '/../../../lib/public' . $path;
if(file_exists($relPath)){
require_once $relPath;
}
} else if(strpos($className, 'OC_') === 0) {
$path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php');
$relPath = __DIR__ . '/../../../lib/private/' . $path;
if(file_exists($relPath)){
require_once $relPath;
}
} else if(strpos($className, 'OC\\') === 0) {
$path = strtolower(str_replace('\\', '/', substr($className, 2)) . '.php');
$relPath = __DIR__ . '/../../../lib/private' . $path;
if(file_exists($relPath)){
require_once $relPath;
}
}
});
require_once __DIR__ . '/../../../tests/bootstrap.php';

View File

@ -0,0 +1,174 @@
<?php
/**
* ownCloud - registration
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @copyright Pellaeon Lin 2014
*/
namespace OCA\Registration\Controller;
use OCA\Registration\Db\Registration;
use OCA\Registration\Service\MailService;
use OCA\Registration\Service\RegistrationService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\Defaults;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IUser;
use \Test\TestCase;
class ApiControllerTest extends TestCase {
/** @var IRequest */
private $request;
/** @var RegistrationService|\PHPUnit_Framework_MockObject_MockObject */
private $registrationService;
/** @var MailService */
private $mailService;
/** @var IL10N */
private $l10n;
/** @var Defaults */
private $defaults;
/** @var ApiController */
private $controller;
public function setUp () {
parent::setUp();
$this->request = $this->createMock(IRequest::class);
$this->registrationService = $this->createMock(RegistrationService::class);
$this->mailService = $this->createMock(MailService::class);
$this->l10n = $this->createMock(IL10N::class);
$this->defaults = $this->createMock(Defaults::class);
$this->controller = new ApiController(
"registration",
$this->request,
$this->registrationService,
$this->mailService,
$this->l10n,
$this->defaults
);
}
public function testValidate() {
$this->registrationService
->expects($this->once())
->method('validateEmail')
->with('test@example.com');
$this->registrationService
->expects($this->once())
->method('validateDisplayname')
->with('user test');
$this->registrationService
->expects($this->once())
->method('validateUsername')
->with('user1');
$expected = new DataResponse([
'username' => 'user1',
'displayname' => 'user test',
'email' => 'test@example.com'
], Http::STATUS_OK);
$actual = $this->controller->validate('user1', 'user test', 'test@example.com');
$this->assertEquals($expected, $actual);
}
/**
* @expectedException \OCP\AppFramework\OCS\OCSException
* @expectedExceptionCode 999
*/
public function testValidateFailEmail() {
$this->registrationService
->expects($this->once())
->method('validateEmail')
->willThrowException(new OCSException('', 999));
$this->controller->validate('user1', 'user test', 'test@example.com');
}
/**
* @expectedException \OCP\AppFramework\OCS\OCSException
* @expectedExceptionCode 999
*/
public function testValidateFailDisplayname() {
$this->registrationService
->expects($this->once())
->method('validateDisplayname')
->willThrowException(new OCSException('', 999));
$this->controller->validate('user1', 'user test', 'test@example.com');
}
/**
* @expectedException \OCP\AppFramework\OCS\OCSException
* @expectedExceptionCode 999
*/
public function testValidateFailUsername() {
$this->registrationService
->expects($this->once())
->method('validateUsername')
->willThrowException(new OCSException('', 999));
$this->controller->validate('user1', 'user test', 'test@example.com');
}
/**
* @expectedException \OCP\AppFramework\OCS\OCSNotFoundException
* @expectedExceptionCode 404
*/
public function testStatusNoRegistration() {
$this->registrationService
->method('getRegistrationForToken')
->with('ABCDEF')
->willThrowException(new DoesNotExistException(''));
$this->controller->status('ABCDEF');
}
/**
* @expectedException \OCP\AppFramework\OCS\OCSException
* @expectedExceptionCode 403
*/
public function testStatusPendingRegistration() {
$registration = new Registration();
$registration->setEmailConfirmed(false);
$this->registrationService
->method('getRegistrationForToken')
->with('ABCDEF')
->willReturn($registration);
$actual = $this->controller->status('ABCDEF');
}
public function testStatusConfirmedRegistration() {
$registration = new Registration();
$registration->setEmailConfirmed(true);
$registration->setClientSecret('mysecret');
$user = $this->createMock(IUser::class);
$this->registrationService
->method('getRegistrationForToken')
->with('ABCDEF')
->willReturn($registration);
$this->registrationService
->expects($this->once())
->method('getUserAccount')
->with($registration)
->willReturn($user);
$this->registrationService
->expects($this->once())
->method('loginUser');
$this->registrationService
->expects($this->once())
->method('generateAppPassword');
$actual = $this->controller->status('ABCDEF');
$expected = new DataResponse([]);
$this->assertEquals($expected, $actual);
}
public function testStatusConfirmedRegistrationWithSecret() {
}
}

View File

@ -1,53 +0,0 @@
<?php
/**
* ownCloud - registration
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Pellaeon Lin <pellaeon@hs.ntnu.edu.tw>
* @copyright Pellaeon Lin 2014
*/
namespace OCA\Registration\Controller;
use \OCP\IRequest;
use \OCP\AppFramework\Http\TemplateResponse;
use \OCP\AppFramework\Http\JSONResponse;
use \OCA\Registration\AppInfo\Application;
class PageControllerTest extends \PHPUnit_Framework_TestCase {
private $container;
public function setUp () {
$app = new Application();
$this->container = $app->getContainer();
}
public function testIndex () {
// swap out request
$this->container['Request'] = $this->getMockBuilder('\OCP\IRequest')
->getMock();
$this->container['UserId'] = 'john';
$result = $this->container['PageController']->index();
$this->assertEquals(array('user' => 'john'), $result->getParams());
$this->assertEquals('main', $result->getTemplateName());
$this->assertTrue($result instanceof TemplateResponse);
}
public function testEcho () {
$result = $this->container['PageController']->doEcho('hi');
$this->assertEquals(array('echo' => 'hi'), $result);
}
}