From c19daeec36f9081bb85190f2b336a27eef90837b Mon Sep 17 00:00:00 2001 From: Sujith H Date: Mon, 27 Aug 2018 22:59:04 +0530 Subject: [PATCH] [stable10] Backport of Make email field as default to create user Make email field as default to create user. Previously we had username, password. With this change it becomes username, email. This would help admins to create users with email address. Signed-off-by: Sujith H --- lib/public/InvalidUserTokenException.php | 41 ++ lib/public/UserTokenException.php | 42 ++ lib/public/UserTokenExpiredException.php | 41 ++ lib/public/UserTokenMismatchException.php | 41 ++ settings/Application.php | 3 +- settings/Controller/UsersController.php | 299 ++++++++- settings/css/setpassword.css | 15 + settings/js/setpassword.js | 52 ++ settings/js/users/users.js | 59 +- settings/routes.php | 4 + settings/templates/email.new_user.php | 2 +- settings/templates/resendtokenbymail.php | 33 + settings/templates/setpassword.php | 39 ++ settings/templates/tokensendnotify.php | 2 + settings/templates/users/main.php | 8 +- settings/templates/users/part.createuser.php | 4 +- settings/users.php | 2 +- .../Controller/UsersControllerTest.php | 579 +++++++++++++++++- tests/acceptance/features/lib/UsersPage.php | 2 +- 19 files changed, 1206 insertions(+), 62 deletions(-) create mode 100644 lib/public/InvalidUserTokenException.php create mode 100644 lib/public/UserTokenException.php create mode 100644 lib/public/UserTokenExpiredException.php create mode 100644 lib/public/UserTokenMismatchException.php create mode 100644 settings/css/setpassword.css create mode 100644 settings/js/setpassword.js create mode 100644 settings/templates/resendtokenbymail.php create mode 100644 settings/templates/setpassword.php create mode 100644 settings/templates/tokensendnotify.php diff --git a/lib/public/InvalidUserTokenException.php b/lib/public/InvalidUserTokenException.php new file mode 100644 index 000000000000..502d73a07450 --- /dev/null +++ b/lib/public/InvalidUserTokenException.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCP; + +/** + * Class InvalidUserTokenException + * + * @package OCP + * @since 10.0.10 + */ +class InvalidUserTokenException extends UserTokenException { + /** + * InvalidUserTokenException constructor. + * + * @param string $message + * @param int $code + * @since 10.0.10 + */ + public function __construct($message = "", $code = 0) { + parent::__construct($message, $code, $this); + } +} diff --git a/lib/public/UserTokenException.php b/lib/public/UserTokenException.php new file mode 100644 index 000000000000..603aedc996a7 --- /dev/null +++ b/lib/public/UserTokenException.php @@ -0,0 +1,42 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCP; + +/** + * Class UserTokenException + * + * @package OCP + * @since 10.0.10 + */ +class UserTokenException extends \Exception { + /** + * UserTokenException constructor. + * + * @param string $message + * @param $code + * @param \Exception|null $previous + * @since 10.0.10 + */ + public function __construct($message = "", $code, \Exception $previous = null) { + parent::__construct($message, $code, $previous); + } +} diff --git a/lib/public/UserTokenExpiredException.php b/lib/public/UserTokenExpiredException.php new file mode 100644 index 000000000000..57dd27fba5c0 --- /dev/null +++ b/lib/public/UserTokenExpiredException.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCP; + +/** + * Class UserTokenExpiredException + * + * @package OCP + * @since 10.0.10 + */ +class UserTokenExpiredException extends UserTokenException { + /** + * UserTokenExpiredException constructor. + * + * @param string $message + * @param int $code + * @since 10.0.10 + */ + public function __construct($message = "", $code = 0) { + parent::__construct($message, $code, $this); + } +} diff --git a/lib/public/UserTokenMismatchException.php b/lib/public/UserTokenMismatchException.php new file mode 100644 index 000000000000..f5578ec53322 --- /dev/null +++ b/lib/public/UserTokenMismatchException.php @@ -0,0 +1,41 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +namespace OCP; + +/** + * Class UserTokenMismatchException + * + * @package OCP + * @since 10.0.10 + */ +class UserTokenMismatchException extends UserTokenException { + /** + * UserTokenMismatchException constructor. + * + * @param string $message + * @param int $code + * @since 10.0.10 + */ + public function __construct($message = "", $code = 0) { + parent::__construct($message, $code, $this); + } +} diff --git a/settings/Application.php b/settings/Application.php index a63f010b43b6..d8ae06377532 100644 --- a/settings/Application.php +++ b/settings/Application.php @@ -158,7 +158,8 @@ public function __construct(array $urlParams=[]) { $c->query('DefaultMailAddress'), $c->query('URLGenerator'), $c->query('OCP\\App\\IAppManager'), - $c->query('OCP\\IAvatarManager') + $c->query('OCP\\IAvatarManager'), + $c->query('ServerContainer')->getEventDispatcher() ); }); $container->registerService('LogSettingsController', function (IContainer $c) { diff --git a/settings/Controller/UsersController.php b/settings/Controller/UsersController.php index 4ea2de3aa4c1..4c4bde402d71 100644 --- a/settings/Controller/UsersController.php +++ b/settings/Controller/UsersController.php @@ -31,6 +31,7 @@ namespace OC\Settings\Controller; use OC\AppFramework\Http; +use OC\User\Session; use OC\User\User; use OCP\App\IAppManager; use OCP\AppFramework\Controller; @@ -43,14 +44,19 @@ use OCP\IGroupManager; use OCP\IL10N; use OCP\ILogger; +use OCP\InvalidUserTokenException; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; -use OCP\IUserSession; use OCP\Mail\IMailer; use OCP\IAvatarManager; use OCP\Security\ISecureRandom; +use OCP\UserTokenException; +use OCP\UserTokenExpiredException; +use OCP\UserTokenMismatchException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; /** * @package OC\Settings\Controller @@ -58,7 +64,7 @@ class UsersController extends Controller { /** @var IL10N */ private $l10n; - /** @var IUserSession */ + /** @var Session */ private $userSession; /** @var bool */ private $isAdmin; @@ -88,13 +94,15 @@ class UsersController extends Controller { protected $secureRandom; /** @var ITimeFactory */ protected $timeFactory; + /** @var EventDispatcherInterface */ + private $eventDispatcher; /** * @param string $appName * @param IRequest $request * @param IUserManager $userManager * @param IGroupManager $groupManager - * @param IUserSession $userSession + * @param Session $userSession * @param IConfig $config * @param ISecureRandom $secureRandom * @param $isAdmin @@ -107,12 +115,13 @@ class UsersController extends Controller { * @param IURLGenerator $urlGenerator * @param IAppManager $appManager * @param IAvatarManager $avatarManager + * @param EventDispatcherInterface $eventDispatcher */ public function __construct($appName, IRequest $request, IUserManager $userManager, IGroupManager $groupManager, - IUserSession $userSession, + Session $userSession, IConfig $config, ISecureRandom $secureRandom, $isAdmin, @@ -124,7 +133,8 @@ public function __construct($appName, $fromMailAddress, IURLGenerator $urlGenerator, IAppManager $appManager, - IAvatarManager $avatarManager) { + IAvatarManager $avatarManager, + EventDispatcherInterface $eventDispatcher) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->groupManager = $groupManager; @@ -140,6 +150,7 @@ public function __construct($appName, $this->fromMailAddress = $fromMailAddress; $this->urlGenerator = $urlGenerator; $this->avatarManager = $avatarManager; + $this->eventDispatcher = $eventDispatcher; // check for encryption state - TODO see formatUserForIndex $this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption'); @@ -341,6 +352,40 @@ public function index($offset = 0, $limit = 10, $gid = '', $pattern = '', $backe return new DataResponse($users); } + /** + * @param string $userId + * @param string $email + */ + private function generateTokenAndSendMail($userId, $email) { + $token = $this->secureRandom->generate(21, + ISecureRandom::CHAR_DIGITS, + ISecureRandom::CHAR_LOWER, ISecureRandom::CHAR_UPPER); + $this->config->setUserValue($userId, 'owncloud', + 'lostpassword', $this->timeFactory->getTime() . ':' . $token); + + // data for the mail template + $mailData = [ + 'username' => $userId, + 'url' => $this->urlGenerator->linkToRouteAbsolute('settings.Users.setPasswordForm', ['userId' => $userId, 'token' => $token]) + ]; + + $mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank'); + $mailContent = $mail->render(); + + $mail = new TemplateResponse('settings', 'email.new_user_plain_text', $mailData, 'blank'); + $plainTextMailContent = $mail->render(); + + $subject = $this->l10n->t('Your %s account was created', [$this->defaults->getName()]); + + $message = $this->mailer->createMessage(); + $message->setTo([$email => $userId]); + $message->setSubject($subject); + $message->setHtmlBody($mailContent); + $message->setPlainBody($plainTextMailContent); + $message->setFrom([$this->fromMailAddress => $this->defaults->getName()]); + $this->mailer->send($message); + } + /** * @NoAdminRequired * @@ -398,6 +443,15 @@ public function create($username, $password, array $groups= [], $email='') { } try { + if (($password === '') && ($email !== '')) { + $event = new GenericEvent(); + $this->eventDispatcher->dispatch('OCP\User::createPassword', $event); + if ($event->hasArgument('password')) { + $password = $event->getArgument('password'); + } else { + $password = $this->secureRandom->generate(20); + } + } $user = $this->userManager->createUser($username, $password); } catch (\Exception $exception) { $message = $exception->getMessage(); @@ -429,28 +483,8 @@ public function create($username, $password, array $groups= [], $email='') { if ($email !== '') { $user->setEMailAddress($email); - // data for the mail template - $mailData = [ - 'username' => $username, - 'url' => $this->urlGenerator->getAbsoluteURL('/') - ]; - - $mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank'); - $mailContent = $mail->render(); - - $mail = new TemplateResponse('settings', 'email.new_user_plain_text', $mailData, 'blank'); - $plainTextMailContent = $mail->render(); - - $subject = $this->l10n->t('Your %s account was created', [$this->defaults->getName()]); - try { - $message = $this->mailer->createMessage(); - $message->setTo([$email => $username]); - $message->setSubject($subject); - $message->setHtmlBody($mailContent); - $message->setPlainBody($plainTextMailContent); - $message->setFrom([$this->fromMailAddress => $this->defaults->getName()]); - $this->mailer->send($message); + $this->generateTokenAndSendMail($username, $email); } catch (\Exception $e) { $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), ['app' => 'settings']); } @@ -472,6 +506,219 @@ public function create($username, $password, array $groups= [], $email='') { ); } + /** + * Set password for user using link + * + * @PublicPage + * @NoCSRFRequired + * @NoAdminRequired + * @NoSubadminRequired + * + * @param string $token + * @param string $userId + * @return TemplateResponse + */ + public function setPasswordForm($token, $userId) { + try { + $this->checkPasswordSetToken($token, $userId); + } catch (UserTokenException $e) { + if ($e->getPrevious() instanceof UserTokenExpiredException) { + return new TemplateResponse( + 'settings', 'resendtokenbymail', + [ + 'link' => $this->urlGenerator->linkToRouteAbsolute('settings.Users.resendToken', ['userId' => $userId]) + ], 'guest' + ); + } + $this->log->logException($e, ['app' => 'settings']); + return new TemplateResponse( + 'core', 'error', + [ + "errors" => [["error" => $e->getMessage()]] + ], 'guest' + ); + } + + return new TemplateResponse( + 'settings', 'setpassword', + [ + 'link' => $this->urlGenerator->linkToRouteAbsolute('settings.Users.setPassword', ['userId' => $userId, 'token' => $token]) + ], 'guest' + ); + } + + /** + * @param string $token + * @param string $userId + * @return null + * @throws InvalidUserTokenException + * @throws UserTokenExpiredException + * @throws UserTokenMismatchException + */ + private function checkPasswordSetToken($token, $userId) { + $user = $this->userManager->get($userId); + + $splittedToken = \explode(':', $this->config->getUserValue($userId, 'owncloud', 'lostpassword', null)); + if (\count($splittedToken) !== 2) { + $this->config->deleteUserValue($userId, 'owncloud', 'lostpassword'); + throw new InvalidUserTokenException($this->l10n->t('The token provided is invalid.')); + } + + //The value 43200 = 60*60*12 = 1/2 day + if ($splittedToken[0] < ($this->timeFactory->getTime() - (int)$this->config->getAppValue('user_management', 'token_expire_time', '43200')) || + $user->getLastLogin() > $splittedToken[0]) { + $this->config->deleteUserValue($userId, 'owncloud', 'lostpassword'); + throw new UserTokenExpiredException($this->l10n->t('The token provided had expired.')); + } + + if (!\hash_equals($splittedToken[1], $token)) { + $this->config->deleteUserValue($userId, 'owncloud', 'lostpassword'); + throw new UserTokenMismatchException($this->l10n->t('The token provided is invalid.')); + } + } + + /** + * @PublicPage + * @NoCSRFRequired + * @NoAdminRequired + * @NoSubadminRequired + * + * @param $userId + * @return TemplateResponse + */ + public function resendToken($userId) { + $user = $this->userManager->get($userId); + + if ($user === null) { + $this->log->error('User: ' . $userId . ' does not exist', ['app' => 'settings']); + return new TemplateResponse( + 'core', 'error', + [ + "errors" => [["error" => $this->l10n->t('Failed to create activation link. Please contact your administrator.')]] + ], + 'guest' + ); + } + + if ($user->getEMailAddress() === null) { + $this->log->error('Email address not set for: ' . $userId, ['app' => 'settings']); + return new TemplateResponse( + 'core', 'error', + [ + "errors" => [["error" => $this->l10n->t('Failed to create activation link. Please contact your administrator.', [$userId])]] + ], + 'guest' + ); + } + + try { + $this->generateTokenAndSendMail($user->getUID(), $user->getEMailAddress()); + } catch (\Exception $e) { + $this->log->error("Can't send new user mail to " . $user->getEMailAddress() . ": " . $e->getMessage(), ['app' => 'settings']); + return new TemplateResponse( + 'core', 'error', + [ + "errors" => [[ + "error" => $this->l10n->t('Can\'t send email to the user. Contact your administrator.')]] + ], 'guest' + ); + } + + return new TemplateResponse( + 'settings', 'tokensendnotify', [], 'guest' + ); + } + + /** + * @PublicPage + * @NoAdminRequired + * @NoSubadminRequired + * @NoCSRFRequired + * + * @param $token + * @param $userId + * @param $password + * @return JSONResponse + */ + public function setPassword($token, $userId, $password) { + $user = $this->userManager->get($userId); + + if ($user === null) { + $this->log->error('User: ' . $userId . ' does not exist.', ['app' => 'settings']); + return new JSONResponse( + [ + 'status' => 'error', + 'message' => $this->l10n->t('Failed to set password. Please contact the administrator.', [$userId]), + 'type' => 'usererror' + ], Http::STATUS_NOT_FOUND + ); + } + + try { + $this->checkPasswordSetToken($token, $userId); + + if (!$user->setPassword($password)) { + $this->log->error('The password can not be set for user: '. $userId); + return new JSONResponse( + [ + 'status' => 'error', + 'message' => $this->l10n->t('Failed to set password. Please contact your administrator.', [$userId]), + 'type' => 'passwordsetfailed' + ], Http::STATUS_FORBIDDEN + ); + } + + \OC_Hook::emit('\OC\Core\LostPassword\Controller\LostController', 'post_passwordReset', ['uid' => $userId, 'password' => $password]); + //\OC_User::unsetMagicInCookie(); + $this->userSession->unsetMagicInCookie(); + } catch (UserTokenException $e) { + $this->log->logException($e, ['app' => 'settings']); + return new JSONResponse( + [ + 'status' => 'error', + 'message' => $e->getMessage(), + 'type' => 'tokenfailure' + ], Http::STATUS_UNAUTHORIZED + ); + } + + try { + $this->sendNotificationMail($userId); + } catch (\Exception $e) { + $this->log->logException($e, ['app' => 'settings']); + return new JSONResponse( + [ + 'status' => 'error', + 'message' => $this->l10n->t('Failed to send email. Please contact your administrator.'), + 'type' => 'emailsendfailed' + ], Http::STATUS_INTERNAL_SERVER_ERROR + ); + } + + return new JSONResponse(['status' => 'success']); + } + + /** + * @param $userId + * @throws \Exception + */ + protected function sendNotificationMail($userId) { + $user = $this->userManager->get($userId); + $email = $user->getEMailAddress(); + + if ($email !== '') { + $tmpl = new \OC_Template('core', 'lostpassword/notify'); + $msg = $tmpl->fetchPage(); + + $message = $this->mailer->createMessage(); + $message->setTo([$email => $userId]); + $message->setSubject($this->l10n->t('%s password changed successfully', [$this->defaults->getName()])); + $message->setPlainBody($msg); + $message->setFrom([$email => $this->defaults->getName()]); + $this->mailer->send($message); + } + } + /** * @NoAdminRequired * diff --git a/settings/css/setpassword.css b/settings/css/setpassword.css new file mode 100644 index 000000000000..67cd2ff89f89 --- /dev/null +++ b/settings/css/setpassword.css @@ -0,0 +1,15 @@ +#reset-password p { + position: relative; +} + +.text-center { + text-align: center; +} + +#submit { + width: 100%; +} + +#password { + width: 100%; +} diff --git a/settings/js/setpassword.js b/settings/js/setpassword.js new file mode 100644 index 000000000000..c589b263a7c6 --- /dev/null +++ b/settings/js/setpassword.js @@ -0,0 +1,52 @@ +(function () { + var SetPassword = { + init : function() { + $('#set-password #submit').click(this.onClickSetPassword); + }, + + onClickSetPassword : function(event){ + event.preventDefault(); + var passwordObj = $('#password'); + if (passwordObj.val()){ + $.post( + passwordObj.parents('form').attr('action'), + {password : passwordObj.val()} + ).done(function (result) { + OCA.UserManagement.SetPassword._resetDone(result); + }).fail(function (result) { + OCA.UserManagement.SetPassword._onSetPasswordFail(result); + }); + } + }, + + _onSetPasswordFail: function(result) { + var responseObj = JSON.parse(result.responseText); + var errorObject = $('#error-message'); + var showErrorMessage = false; + + var errorMessage; + errorMessage = responseObj.message; + + if (errorMessage) { + errorObject.text(errorMessage); + errorObject.show(); + $('#submit').prop('disabled', true); + } + }, + + _resetDone : function(result){ + if (result && result.status === 'success') { + OC.redirect(OC.getRootPath()); + } + } + }; + + if (!OCA.UserManagement) { + OCA.UserManagement = {}; + } + OCA.UserManagement.SetPassword = SetPassword; +})(); + +$(document).ready(function () { + OCA.UserManagement.SetPassword.init(); +}); diff --git a/settings/js/users/users.js b/settings/js/users/users.js index e0edb282719f..e07e42fc2837 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -711,6 +711,19 @@ $(document).ready(function () { // TODO: move other init calls inside of initialize UserList.initialize($('#userlist')); + OC.AppConfig.getValue('core', 'umgmt_set_password', 'false', function (data) { + var showPassword = $.parseJSON(data); + if (showPassword === true) { + $("#newuserpassword").show(); + $("#newemail").hide(); + $('#CheckBoxPasswordOnUserCreate').attr('checked', true); + } else { + $("#newemail").show(); + $("#newuserpassword").hide(); + $('#CheckBoxPasswordOnUserCreate').attr('checked', false); + } + }); + $userListBody.on('click', '.password', function (event) { event.stopPropagation(); @@ -883,20 +896,20 @@ $(document).ready(function () { })); return false; } - if ($.trim(password) === '') { - OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', { - message: t('settings', 'A valid password must be provided') - })); - return false; - } - if(!$('#CheckboxMailOnUserCreate').is(':checked')) { - email = ''; - } - if ($('#CheckboxMailOnUserCreate').is(':checked') && $.trim(email) === '') { - OC.Notification.showTemporary( t('settings', 'Error creating user: {message}', { - message: t('settings', 'A valid email must be provided') - })); - return false; + if ($('#CheckBoxPasswordOnUserCreate').is(':checked')) { + if ($.trim(password) === '') { + OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', { + message: t('settings', 'A valid password must be provided') + })); + return false; + } + } else { + if ($.trim(email) === '') { + OC.Notification.showTemporary( t('settings', 'Error creating user: {message}', { + message: t('settings', 'A valid email must be provided') + })); + return false; + } } var promise; @@ -1014,17 +1027,19 @@ $(document).ready(function () { } }); - if ($('#CheckboxMailOnUserCreate').is(':checked')) { - $("#newemail").show(); + if ($('#CheckBoxPasswordOnUserCreate').is(':checked')) { + $("#newuserpassword").show(); } // Option to display/hide the "E-Mail" input field - $('#CheckboxMailOnUserCreate').click(function() { - if ($('#CheckboxMailOnUserCreate').is(':checked')) { - $("#newemail").show(); - OC.AppConfig.setValue('core', 'umgmt_send_email', 'true'); + $('#CheckBoxPasswordOnUserCreate').click(function () { + if ($('#CheckBoxPasswordOnUserCreate').is(':checked')) { + OC.AppConfig.setValue('core', 'umgmt_set_password', 'true'); + $('#newemail').hide(); + $('#newuserpassword').show(); } else { - $("#newemail").hide(); - OC.AppConfig.setValue('core', 'umgmt_send_email', 'false'); + OC.AppConfig.setValue('core', 'umgmt_set_password', 'false'); + $('#newemail').show(); + $("#newuserpassword").hide(); } }); diff --git a/settings/routes.php b/settings/routes.php index 4231fb05e576..97df80900416 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -67,6 +67,10 @@ ['name' => 'Cors#removeDomain', 'url' => '/settings/domains/{id}', 'verb' => 'DELETE'], ['name' => 'LegalSettings#setImprintUrl', 'url' => '/settings/admin/legal/imprint', 'verb' => 'POST'], ['name' => 'LegalSettings#setPrivacyPolicyUrl', 'url' => '/settings/admin/legal/privacypolicy', 'verb' => 'POST'], + ['name' => 'ChangePassword#changePassword', 'url' => '/users/changepassword', 'verb' => 'POST'], + ['name' => 'Users#setPasswordForm', 'url' => '/settings/users/setpassword/form/{token}/{userId}', 'verb' => 'GET'], + ['name' => 'Users#resendToken', 'url' => '/resend/token/{userId}', 'verb' => 'POST'], + ['name' => 'Users#setPassword', 'url' => '/setpassword/{token}/{userId}', 'verb' => 'POST'], ] ]); diff --git a/settings/templates/email.new_user.php b/settings/templates/email.new_user.php index 6f9f16e865b8..716755df22a6 100644 --- a/settings/templates/email.new_user.php +++ b/settings/templates/email.new_user.php @@ -12,7 +12,7 @@   t('Hey there,

just letting you know that you now have an %s account.

Your username: %s
Access it: %s

', [$theme->getName(), $_['username'], $_['url'], $_['url']])); + print_unescaped($l->t('Hey there,

just letting you know that you now have an %s account.

Your username: %s
Please set the password by accessing it: Here

', [$theme->getName(), $_['username'], $_['url']])); // TRANSLATORS term at the end of a mail p($l->t('Cheers!')); diff --git a/settings/templates/resendtokenbymail.php b/settings/templates/resendtokenbymail.php new file mode 100644 index 000000000000..0c6bedbcf065 --- /dev/null +++ b/settings/templates/resendtokenbymail.php @@ -0,0 +1,33 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ + +?> + +
+
+

+ +

+ +
+
diff --git a/settings/templates/setpassword.php b/settings/templates/setpassword.php new file mode 100644 index 000000000000..62b0282ef712 --- /dev/null +++ b/settings/templates/setpassword.php @@ -0,0 +1,39 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see + * + */ +style('settings', 'setpassword'); +script('settings', 'setpassword'); +?> + + +
+
+

+ + +

+ +
+
diff --git a/settings/templates/tokensendnotify.php b/settings/templates/tokensendnotify.php new file mode 100644 index 000000000000..13176743b951 --- /dev/null +++ b/settings/templates/tokensendnotify.php @@ -0,0 +1,2 @@ +t('Activation link was sent to an email address, if one was configured.')); diff --git a/settings/templates/users/main.php b/settings/templates/users/main.php index cbf05977d4fa..4814a76251da 100644 --- a/settings/templates/users/main.php +++ b/settings/templates/users/main.php @@ -81,12 +81,12 @@ class="checkbox"

- /> -

diff --git a/settings/templates/users/part.createuser.php b/settings/templates/users/part.createuser.php index d65227d3fcac..2245f1c66f5b 100644 --- a/settings/templates/users/part.createuser.php +++ b/settings/templates/users/part.createuser.php @@ -4,10 +4,10 @@ placeholder="t('Username'))?>" autocomplete="off" autocapitalize="off" autocorrect="off" /> -

diff --git a/settings/users.php b/settings/users.php index 28996aca813f..149009870166 100644 --- a/settings/users.php +++ b/settings/users.php @@ -124,6 +124,6 @@ $tmpl->assign('show_last_login', $config->getAppValue('core', 'umgmt_show_last_login', 'false')); $tmpl->assign('show_email', $config->getAppValue('core', 'umgmt_show_email', 'false')); $tmpl->assign('show_backend', $config->getAppValue('core', 'umgmt_show_backend', 'false')); -$tmpl->assign('send_email', $config->getAppValue('core', 'umgmt_send_email', 'false')); +$tmpl->assign('set_password', $config->getAppValue('core', 'umgmt_set_password', 'false')); $tmpl->printPage(); diff --git a/tests/Settings/Controller/UsersControllerTest.php b/tests/Settings/Controller/UsersControllerTest.php index 87b7274bc993..5b974bcdbdc6 100644 --- a/tests/Settings/Controller/UsersControllerTest.php +++ b/tests/Settings/Controller/UsersControllerTest.php @@ -12,6 +12,8 @@ use OC\Settings\Application; use OC\Settings\Controller\UsersController; +use OC\User\Session; +use OC\User\User; use OCP\AppFramework\Http; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\RedirectResponse; @@ -32,6 +34,7 @@ use OC\Mail\Message; use OCP\IUser; use OC\Group\Manager; +use Symfony\Component\EventDispatcher\EventDispatcher; /** * @group DB @@ -82,6 +85,7 @@ protected function setUp() { ->disableOriginalConstructor()->getMock(); $this->container['Mailer'] = $this->getMockBuilder('\OCP\Mail\IMailer') ->disableOriginalConstructor()->getMock(); + $this->container['EventDispatcher'] = $this->createMock(EventDispatcher::class); /* * Set default avtar behaviour for whole testsuite @@ -98,6 +102,7 @@ protected function setUp() { ['foo', $avatarExists], ['bar', $avatarExists], ['admin', $avatarNotExists], + ['foobazz', $avatarExists] ])); $this->container['Config'] @@ -1831,7 +1836,7 @@ public function testSetSelfEmailAddress($userName, $userPassword, $loginUser, $s $irequest = $this->createMock(IRequest::class); $userManager = $this->createMock(IUserManager::class); $groupManager = $this->createMock(Manager::class); - $userSession = $this->createMock(IUserSession::class); + $userSession = $this->createMock(Session::class); $iConfig = $this->createMock(IConfig::class); $iSecureRandom = $this->createMock(ISecureRandom::class); $iL10 = $this->createMock(IL10N::class); @@ -1843,9 +1848,10 @@ public function testSetSelfEmailAddress($userName, $userPassword, $loginUser, $s $urlGenerator = $this->createMock(IURLGenerator::class); $appManager = $this->createMock(IAppManager::class); $iAvatarManager = $this->createMock(IAvatarManager::class); + $eventDispatcher = $this->createMock(EventDispatcher::class); $userController = new UsersController($appName, $irequest, $userManager, $groupManager, $userSession, $iConfig, $iSecureRandom, false, $iL10, $iLogger, $ocDefault, $iMailer, - $iTimeFactory, "", $urlGenerator, $appManager, $iAvatarManager); + $iTimeFactory, "", $urlGenerator, $appManager, $iAvatarManager, $eventDispatcher); $this->loginAsUser($loginUser); @@ -1891,7 +1897,7 @@ public function testSetEmailAddressSendEmail($id, $mailaddress) { $irequest = $this->createMock(IRequest::class); $userManager = $this->createMock(IUserManager::class); $groupManager = $this->createMock(IGroupManager::class); - $userSession = $this->createMock(IUserSession::class); + $userSession = $this->createMock(Session::class); $iConfig = $this->createMock(IConfig::class); $iSecureRandom = $this->createMock(ISecureRandom::class); $iL10 = $this->createMock(IL10N::class); @@ -1903,9 +1909,10 @@ public function testSetEmailAddressSendEmail($id, $mailaddress) { $urlGenerator = $this->createMock(IURLGenerator::class); $appManager = $this->createMock(IAppManager::class); $iAvatarManager = $this->createMock(IAvatarManager::class); + $eventDispatcher = $this->createMock(EventDispatcher::class); $userController = new UsersController($appName, $irequest, $userManager, $groupManager, $userSession, $iConfig, $iSecureRandom, false, $iL10, $iLogger, $ocDefault, $iMailer, - $iTimeFactory, "", $urlGenerator, $appManager, $iAvatarManager); + $iTimeFactory, "", $urlGenerator, $appManager, $iAvatarManager, $eventDispatcher); $this->loginAsUser($id); @@ -2714,4 +2721,568 @@ public function testEnableNotAccessibleToSubAdmin() { $response = $this->container['UsersController']->setEnabled('UserToEnable', 'true'); $this->assertEquals($expectedResponse, $response); } + + public function testCreateSuccessfulWithEmailAndUsername() { + $this->container['Mailer']->expects($this->once()) + ->method('validateMailAddress') + ->willReturn(true); + + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + + //$user = $this->createMock(IUser::class); + + $this->container['UserSession']->method('getUser') + ->willReturn($user); + + $this->container['GroupManager'] + ->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->container['UserManager'] + ->expects($this->once()) + ->method('userExists') + ->willReturn(false); + + $this->container['SecureRandom'] + ->method('generate') + ->willReturn('AsDfGh12345'); + + $user->method('getUID') + ->willReturn('foobazz'); + $user->expects($this->once()) + ->method('setEMailAddress') + ->with('validMail@Adre.ss'); + + $this->container['UserManager'] + ->expects($this->once()) + ->method('createUser') + ->with('foobazz', 'AsDfGh12345') + ->willReturn($user); + + $message = $this->createMock(Message::class); + $message->expects($this->once()) + ->method('setTo') + ->with(['validMail@Adre.ss' => 'foobazz']); + $message->expects($this->once()) + ->method('setSubject') + ->with('Your account was created'); + + $this->container['Mailer'] + ->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $this->container['Mailer'] + ->expects($this->once()) + ->method('send') + ->with($message); + $subadmin = $this->createMock(SubAdmin::class); + $subadmin->method('getSubAdminsGroups') + ->with($user) + ->will($this->returnValue([])); + $this->container['GroupManager'] + ->method('getSubAdmin') + ->will($this->returnValue($subadmin)); + + $response = $this->container['UsersController']->create('foobazz', '', [], 'validMail@Adre.ss'); + $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); + } + + public function testSetPasswordForm() { + $user = $this->createMock(IUser::class); + + $this->container['UserManager'] + ->expects($this->once()) + ->method('get') + ->with('foo') + ->willReturn($user); + + $this->container['Config'] + ->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + $this->container['Config'] + ->expects($this->once()) + ->method('getAppValue') + ->willReturn('43200'); + + $this->container['TimeFactory'] + ->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + + $this->container['URLGenerator'] + ->expects($this->once()) + ->method('linkToRouteAbsolute') + ->willReturn('http://localhost/apps/settings/setpassword/form/1234/foo'); + $result = $this->container['UsersController']->setPasswordForm('fooBaZ1', 'foo'); + $this->assertEquals(new Http\TemplateResponse( + 'settings', 'setpassword', + ['link' => 'http://localhost/apps/settings/setpassword/form/1234/foo'], + 'guest'), $result); + } + + public function providesUserTokenExceptionData() { + return [ + ['invalid_token'], + ['expired_token'], + ['mismatch_token'] + ]; + } + + /** + * @dataProvider providesUserTokenExceptionData + */ + public function testSetPasswordFormExceptionResponse($tokenException) { + $user = $this->createMock(IUser::class); + + $this->container['UserManager']->expects($this->once()) + ->method('get') + ->with('foo') + ->willReturn($user); + + if ($tokenException === 'expired_token') { + $this->container['Config'] + ->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + $this->container['TimeFactory'] + ->expects($this->once()) + ->method('getTime') + ->willReturn(44444); + + $this->container['URLGenerator']->expects($this->once()) + ->method('linkToRouteAbsolute') + ->willReturn('http://localhost/settings/setpassword/form/1234/foo'); + + $result = $this->container['UsersController']->setPasswordForm('fooBaZ1', 'foo'); + $this->assertEquals( + new Http\TemplateResponse('settings', 'resendtokenbymail', + ['link' => 'http://localhost/settings/setpassword/form/1234/foo'], + 'guest'), $result); + } elseif ($tokenException === 'mismatch_token') { + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ11'); + $this->container['TimeFactory'] + ->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + $this->container['Config']->expects($this->once()) + ->method('getAppValue') + ->willReturn('43200'); + $result = $this->container['UsersController']->setPasswordForm('fooBaZ1', 'foo'); + $this->assertEquals( + new Http\TemplateResponse( + 'core', 'error', + ['errors' => [['error' => 'The token provided is invalid.']]], 'guest'), $result + ); + } elseif ($tokenException === 'invalid_token') { + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn(''); + $result = $this->container['UsersController']->setPasswordForm('fooBaZ1', 'foo'); + $this->assertEquals( + new Http\TemplateResponse('core', 'error', + ['errors' => [["error" => 'The token provided is invalid.']]], 'guest'), $result); + } + } + + public function testResendToken() { + $user = $this->createMock(IUser::class); + $user->method('getEMailAddress') + ->willReturn('foo@bar.com'); + + $this->container['UserManager']->expects($this->once()) + ->method('get') + ->willReturn($user); + + $this->container['SecureRandom']->expects($this->once()) + ->method('generate') + ->willReturn('foOBaZ1'); + $this->container['URLGenerator']->expects($this->once()) + ->method('linkToRouteAbsolute') + ->willReturn('http://localhost/setpassword/foOBaZ1/foo'); + + $message = $this->createMock(Message::class); + $message->expects($this->once()) + ->method('setTo') + ->willReturn($message); + $message->expects($this->once()) + ->method('setSubject') + ->willReturn($message); + $message->expects($this->once()) + ->method('setHtmlBody') + ->willReturn($message); + $message->expects($this->once()) + ->method('setPlainBody') + ->willReturn($message); + $message->expects($this->once()) + ->method('setFrom') + ->willReturn($message); + + $this->container['Defaults']->method('getName') + ->willReturn('ownCloud'); + + $this->container['Mailer']->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $this->container['Mailer']->expects($this->once()) + ->method('send') + ->with($message) + ->willReturn([]); + + $result = $this->container['UsersController']->resendToken('foo'); + $this->assertEquals( + new Http\TemplateResponse( + 'settings', 'tokensendnotify', + [], 'guest'), $result); + } + + /** + * @param $conditionForException + */ + public function testResendTokenNullUserResponse() { + $result = $this->container['UsersController']->resendToken('foo'); + $this->assertEquals( + new Http\TemplateResponse( + 'core', 'error', + ["errors" => [["error" =>"Failed to create activation link. Please contact your administrator."]]], + 'guest'), $result); + } + + public function testResendTokenEmailNotSendResponse() { + $user = $this->createMock(IUser::class); + + $this->container['UserManager']->expects($this->once()) + ->method('get') + ->willReturn($user); + $result = $this->container['UsersController']->resendToken('foo'); + $this->assertEquals( + new Http\TemplateResponse( + 'core', 'error', + ["errors" => [["error" =>"Failed to create activation link. Please contact your administrator."]]], + 'guest'), $result); + } + + public function testResendTokenSendMailFailedResponse() { + $user = $this->createMock(IUser::class); + + $user->method('getEMailAddress') + ->willReturn('foo@bar.com'); + + $this->container['UserManager']->expects($this->once()) + ->method('get') + ->willReturn($user); + + $this->container['SecureRandom']->expects($this->once()) + ->method('generate') + ->willReturn('foOBaZ1'); + $this->container['URLGenerator']->expects($this->once()) + ->method('linkToRouteAbsolute') + ->willReturn('http://localhost/setpassword/foOBaZ1/foo'); + + $message = $this->createMock(Message::class); + $message->expects($this->once()) + ->method('setTo') + ->willReturn($message); + $message->expects($this->once()) + ->method('setSubject') + ->willReturn($message); + $message->expects($this->once()) + ->method('setHtmlBody') + ->willReturn($message); + $message->expects($this->once()) + ->method('setPlainBody') + ->willReturn($message); + $message->expects($this->once()) + ->method('setFrom') + ->willReturn($message); + + $this->container['Defaults']->method('getName') + ->willReturn('ownCloud'); + + $this->container['Mailer']->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $this->container['Mailer']->expects($this->once()) + ->method('send') + ->with($message) + ->willThrowException(new \Exception('Mail can not be sent')); + $result = $this->container['UsersController']->resendToken('foo'); + $this->assertEquals( + new Http\TemplateResponse( + 'core', 'error', + ["errors" => [["error" =>"Can't send email to the user. Contact your administrator."]]], + 'guest'), $result); + } + + /** + * @param $conditionForException + */ + public function testSetPasswordNullUserExcception() { + $result = $this->container['UsersController']->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals( + new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'Failed to set password. Please contact the administrator.', + 'type' => 'usererror' + ], Http::STATUS_NOT_FOUND + ), $result); + } + + public function testSetPasswordInvalidTokenExcception() { + $user = $this->createMock(IUser::class); + $this->container['UserManager']->method('get') + ->with('foo') + ->willReturn($user); + + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn(''); + + $result = $this->container['UsersController']->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'The token provided is invalid.', + 'type' => 'tokenfailure' + ], Http::STATUS_UNAUTHORIZED + ), $result); + } + + public function testSetPasswordExpiredTokenException() { + $user = $this->createMock(IUser::class); + + $this->container['UserManager']->method('get') + ->with('foo') + ->willReturn($user); + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + $this->container['TimeFactory']->expects($this->once()) + ->method('getTime') + ->willReturn(44444); + + $result = $this->container['UsersController']->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'The token provided had expired.', + 'type' => 'tokenfailure' + ], Http::STATUS_UNAUTHORIZED + ), $result); + } + + public function testSetPasswordMismatchTokenException() { + $user = $this->createMock(IUser::class); + + $this->container['UserManager']->method('get') + ->with('foo') + ->willReturn($user); + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ11'); + $this->container['Config']->expects($this->once()) + ->method('getAppValue') + ->willReturn('43200'); + $this->container['TimeFactory']->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + + $result = $this->container['UsersController']->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'The token provided is invalid.', + 'type' => 'tokenfailure' + ], Http::STATUS_UNAUTHORIZED + ), $result); + } + + public function testSetPasswordSetFailed() { + $user = $this->createMock(IUser::class); + + $this->container['Config']->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + + $this->container['TimeFactory']->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + $this->container['Config']->expects($this->once()) + ->method('getAppValue') + ->willReturn('43200'); + + $this->container['UserManager']->method('get') + ->with('foo') + ->willReturn($user); + + $user->expects($this->once()) + ->method('setPassword') + ->with('123') + ->willReturn(false); + + $result = $this->container['UsersController']->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'Failed to set password. Please contact your administrator.', + 'type' => 'passwordsetfailed' + ], Http::STATUS_FORBIDDEN + ), $result); + } + + public function testSetPassword() { + $request = $this->createMock(IRequest::class); + $userManager = $this->createMock(IUserManager::class); + $groupManager = $this->createMock(IGroupManager::class); + $userSession = $this->createMock(Session::class); + $config = $this->createMock(IConfig::class); + $secureRandom = $this->createMock(ISecureRandom::class); + $l10n = $this->createMock(IL10N::class); + $logger = $this->createMock(ILogger::class); + $defaults = $this->createMock(\OC_Defaults::class); + $mailer = $this->createMock(IMailer::class); + $timeFactory = $this->createMock(ITimeFactory::class); + $urlGenerator = $this->createMock(IURLGenerator::class); + $appManager = $this->createMock(IAppManager::class); + $avatarManager = $this->createMock(IAvatarManager::class); + $eventDispatcher = $this->createMock(EventDispatcher::class); + $usersController = new UsersController('settings', $request, + $userManager, $groupManager, $userSession, $config, $secureRandom, + 'true', $l10n, $logger, $defaults, $mailer, $timeFactory, + 'no-reply@foo.com', $urlGenerator, $appManager, $avatarManager, + $eventDispatcher); + + $user = $this->createMock(IUser::class); + $user->expects($this->once()) + ->method('setPassword') + ->willReturn(true); + $user->expects($this->once()) + ->method('getEMailAddress') + ->willReturn('foo@bar.com'); + + $userManager->method('get') + ->with('foo') + ->willReturn($user); + + $config->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + + $timeFactory->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + $config->expects($this->once()) + ->method('getAppValue') + ->willReturn(43200); + + $message = $this->createMock(Message::class); + $message->expects($this->once()) + ->method('setTo') + ->willReturn($message); + $message->expects($this->once()) + ->method('setSubject') + ->willReturn($message); + $message->expects($this->once()) + ->method('setFrom') + ->willReturn($message); + + $defaults->method('getName') + ->willReturn('ownCloud'); + + $mailer->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $mailer->expects($this->once()) + ->method('send') + ->with($message) + ->willReturn([]); + + $result = $usersController->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse(['status' => 'success']), $result); + } + + public function testSetPasswordSendMailFailed() { + $request = $this->createMock(IRequest::class); + $userManager = $this->createMock(IUserManager::class); + $groupManager = $this->createMock(IGroupManager::class); + $userSession = $this->createMock(Session::class); + $config = $this->createMock(IConfig::class); + $secureRandom = $this->createMock(ISecureRandom::class); + $l10n = $this->createMock(IL10N::class); + $logger = $this->createMock(ILogger::class); + $defaults = $this->createMock(\OC_Defaults::class); + $mailer = $this->createMock(IMailer::class); + $timeFactory = $this->createMock(ITimeFactory::class); + $urlGenerator = $this->createMock(IURLGenerator::class); + $appManager = $this->createMock(IAppManager::class); + $avatarManager = $this->createMock(IAvatarManager::class); + $eventDispatcher = $this->createMock(EventDispatcher::class); + $usersController = new UsersController('settings', $request, + $userManager, $groupManager, $userSession, $config, $secureRandom, + 'true', $l10n, $logger, $defaults, $mailer, $timeFactory, + 'no-reply@foo.com', $urlGenerator, $appManager, $avatarManager, + $eventDispatcher); + + $user = $this->createMock(IUser::class); + + $config->expects($this->once()) + ->method('getUserValue') + ->willReturn('1234:fooBaZ1'); + + $timeFactory->expects($this->once()) + ->method('getTime') + ->willReturn(44430); + $config->expects($this->once()) + ->method('getAppValue') + ->willReturn('43200'); + + $userManager->method('get') + ->with('foo') + ->willReturn($user); + + $user->expects($this->once()) + ->method('setPassword') + ->with('123') + ->willReturn(true); + $user->expects($this->once()) + ->method('getEMailAddress') + ->willReturn('foo@bar.com'); + + $message = $this->createMock(Message::class); + $message->expects($this->once()) + ->method('setTo') + ->willReturn($message); + $message->expects($this->once()) + ->method('setSubject') + ->willReturn($message); + $message->expects($this->once()) + ->method('setPlainBody') + ->willReturn($message); + $message->expects($this->once()) + ->method('setFrom') + ->willReturn($message); + + $mailer->expects($this->once()) + ->method('createMessage') + ->willReturn($message); + $mailer->expects($this->once()) + ->method('send') + ->willThrowException(new \Exception('can not send mail')); + $l10n->method('t') + ->willReturn('Failed to send email. Please contact your administrator.'); + + $result = $usersController->setPassword('fooBaZ1', 'foo', '123'); + $this->assertEquals(new Http\JSONResponse( + [ + 'status' => 'error', + 'message' => 'Failed to send email. Please contact your administrator.', + 'type' => 'emailsendfailed' + ], Http::STATUS_INTERNAL_SERVER_ERROR + ), $result); + } } diff --git a/tests/acceptance/features/lib/UsersPage.php b/tests/acceptance/features/lib/UsersPage.php index 45c1fcb4a01f..96f561506fdf 100644 --- a/tests/acceptance/features/lib/UsersPage.php +++ b/tests/acceptance/features/lib/UsersPage.php @@ -211,9 +211,9 @@ public function setSetting($setting, $value = true) { public function createUser( Session $session, $username, $password, $email = null, $groups = null ) { + $this->setSetting("Set password for new users", $password !== null); $this->fillField($this->newUserUsernameFieldId, $username); $this->fillField($this->newUserPasswordFieldId, $password); - $this->setSetting("Send email to new user", $email !== null); if ($email !== null) { $this->fillField($this->newUserEmailFieldId, $email); }