Merge pull request #2287 from nextcloud/registrationEmail
Send password/activation link to new user
This commit is contained in:
commit
f25c89461c
|
@ -420,6 +420,11 @@ em {
|
||||||
input[type='text'] {
|
input[type='text'] {
|
||||||
width: 93%;
|
width: 93%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-text {
|
||||||
|
padding: 5px 0 7px 22px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#app-settings-header {
|
#app-settings-header {
|
||||||
|
|
|
@ -49,7 +49,9 @@ use OCP\IUserManager;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\Mail\IMailer;
|
use OCP\Mail\IMailer;
|
||||||
use OCP\IAvatarManager;
|
use OCP\IAvatarManager;
|
||||||
use Punic\Exception;
|
use OCP\Security\ICrypto;
|
||||||
|
use OCP\Security\ISecureRandom;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @package OC\Settings\Controller
|
* @package OC\Settings\Controller
|
||||||
|
@ -85,6 +87,13 @@ class UsersController extends Controller {
|
||||||
private $avatarManager;
|
private $avatarManager;
|
||||||
/** @var AccountManager */
|
/** @var AccountManager */
|
||||||
private $accountManager;
|
private $accountManager;
|
||||||
|
/** @var ISecureRandom */
|
||||||
|
private $secureRandom;
|
||||||
|
/** @var ITimeFactory */
|
||||||
|
private $timeFactory;
|
||||||
|
/** @var ICrypto */
|
||||||
|
private $crypto;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $appName
|
* @param string $appName
|
||||||
|
@ -103,6 +112,9 @@ class UsersController extends Controller {
|
||||||
* @param IAppManager $appManager
|
* @param IAppManager $appManager
|
||||||
* @param IAvatarManager $avatarManager
|
* @param IAvatarManager $avatarManager
|
||||||
* @param AccountManager $accountManager
|
* @param AccountManager $accountManager
|
||||||
|
* @param ISecureRandom $secureRandom
|
||||||
|
* @param ITimeFactory $timeFactory
|
||||||
|
* @param ICrypto $crypto
|
||||||
*/
|
*/
|
||||||
public function __construct($appName,
|
public function __construct($appName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
|
@ -119,8 +131,10 @@ class UsersController extends Controller {
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
IAppManager $appManager,
|
IAppManager $appManager,
|
||||||
IAvatarManager $avatarManager,
|
IAvatarManager $avatarManager,
|
||||||
AccountManager $accountManager
|
AccountManager $accountManager,
|
||||||
) {
|
ISecureRandom $secureRandom,
|
||||||
|
ITimeFactory $timeFactory,
|
||||||
|
ICrypto $crypto) {
|
||||||
parent::__construct($appName, $request);
|
parent::__construct($appName, $request);
|
||||||
$this->userManager = $userManager;
|
$this->userManager = $userManager;
|
||||||
$this->groupManager = $groupManager;
|
$this->groupManager = $groupManager;
|
||||||
|
@ -135,6 +149,9 @@ class UsersController extends Controller {
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->avatarManager = $avatarManager;
|
$this->avatarManager = $avatarManager;
|
||||||
$this->accountManager = $accountManager;
|
$this->accountManager = $accountManager;
|
||||||
|
$this->secureRandom = $secureRandom;
|
||||||
|
$this->timeFactory = $timeFactory;
|
||||||
|
$this->crypto = $crypto;
|
||||||
|
|
||||||
// check for encryption state - TODO see formatUserForIndex
|
// check for encryption state - TODO see formatUserForIndex
|
||||||
$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
|
$this->isEncryptionAppEnabled = $appManager->isEnabledForUser('encryption');
|
||||||
|
@ -362,6 +379,21 @@ class UsersController extends Controller {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$generatedPassword = false;
|
||||||
|
if ($password === '') {
|
||||||
|
if ($email === '') {
|
||||||
|
return new DataResponse(
|
||||||
|
array(
|
||||||
|
'message' => (string)$this->l10n->t('To send a password link to the user an email address is required.')
|
||||||
|
),
|
||||||
|
Http::STATUS_UNPROCESSABLE_ENTITY
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$password = $this->secureRandom->generate(32);
|
||||||
|
$generatedPassword = true;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = $this->userManager->createUser($username, $password);
|
$user = $this->userManager->createUser($username, $password);
|
||||||
} catch (\Exception $exception) {
|
} catch (\Exception $exception) {
|
||||||
|
@ -394,10 +426,27 @@ class UsersController extends Controller {
|
||||||
if($email !== '') {
|
if($email !== '') {
|
||||||
$user->setEMailAddress($email);
|
$user->setEMailAddress($email);
|
||||||
|
|
||||||
|
if ($generatedPassword) {
|
||||||
|
$token = $this->secureRandom->generate(
|
||||||
|
21,
|
||||||
|
ISecureRandom::CHAR_DIGITS .
|
||||||
|
ISecureRandom::CHAR_LOWER .
|
||||||
|
ISecureRandom::CHAR_UPPER
|
||||||
|
);
|
||||||
|
$tokenValue = $this->timeFactory->getTime() . ':' . $token;
|
||||||
|
$mailAddress = !is_null($user->getEMailAddress()) ? $user->getEMailAddress() : '';
|
||||||
|
$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
|
||||||
|
$this->config->setUserValue($username, 'core', 'lostpassword', $encryptedValue);
|
||||||
|
|
||||||
|
$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $username, 'token' => $token]);
|
||||||
|
} else {
|
||||||
|
$link = $this->urlGenerator->getAbsoluteURL('/');
|
||||||
|
}
|
||||||
|
|
||||||
// data for the mail template
|
// data for the mail template
|
||||||
$mailData = array(
|
$mailData = array(
|
||||||
'username' => $username,
|
'username' => $username,
|
||||||
'url' => $this->urlGenerator->getAbsoluteURL('/')
|
'url' => $link
|
||||||
);
|
);
|
||||||
|
|
||||||
$mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank');
|
$mail = new TemplateResponse('settings', 'email.new_user', $mailData, 'blank');
|
||||||
|
|
|
@ -918,7 +918,7 @@ $(document).ready(function () {
|
||||||
}));
|
}));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ($.trim(password) === '') {
|
if ($.trim(password) === '' && !$('#CheckboxMailOnUserCreate').is(':checked')) {
|
||||||
OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
|
OC.Notification.showTemporary(t('settings', 'Error creating user: {message}', {
|
||||||
message: t('settings', 'A valid password must be provided')
|
message: t('settings', 'A valid password must be provided')
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -72,6 +72,9 @@ translation('settings');
|
||||||
<?php p($l->t('Send email to new user')) ?>
|
<?php p($l->t('Send email to new user')) ?>
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
|
<p class="info-text">
|
||||||
|
<?php p($l->t('When the password of the new user is left empty an activation email with a link to set the password is send to the user')) ?>
|
||||||
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<input type="checkbox" name="EmailAddress" value="EmailAddress" id="CheckboxEmailAddress"
|
<input type="checkbox" name="EmailAddress" value="EmailAddress" id="CheckboxEmailAddress"
|
||||||
class="checkbox" <?php if ($_['show_email'] === 'true') print_unescaped('checked="checked"'); ?> />
|
class="checkbox" <?php if ($_['show_email'] === 'true') print_unescaped('checked="checked"'); ?> />
|
||||||
|
|
|
@ -16,6 +16,7 @@ use OC\Settings\Controller\UsersController;
|
||||||
use OCP\App\IAppManager;
|
use OCP\App\IAppManager;
|
||||||
use OCP\AppFramework\Http;
|
use OCP\AppFramework\Http;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
|
use OCP\AppFramework\Utility\ITimeFactory;
|
||||||
use OCP\IAvatar;
|
use OCP\IAvatar;
|
||||||
use OCP\IAvatarManager;
|
use OCP\IAvatarManager;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
|
@ -29,6 +30,8 @@ use OCP\IUser;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
use OCP\IUserSession;
|
use OCP\IUserSession;
|
||||||
use OCP\Mail\IMailer;
|
use OCP\Mail\IMailer;
|
||||||
|
use OCP\Security\ICrypto;
|
||||||
|
use OCP\Security\ISecureRandom;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @group DB
|
* @group DB
|
||||||
|
@ -59,8 +62,14 @@ class UsersControllerTest extends \Test\TestCase {
|
||||||
private $avatarManager;
|
private $avatarManager;
|
||||||
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $l;
|
private $l;
|
||||||
/** @var AccountManager | \PHPUnit_Framework_MockObject_MockObject */
|
/** @var AccountManager | \PHPUnit_Framework_MockObject_MockObject */
|
||||||
private $accountManager;
|
private $accountManager;
|
||||||
|
/** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $secureRandom;
|
||||||
|
/** @var ITimeFactory | \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $timeFactory;
|
||||||
|
/** @var ICrypto | \PHPUnit_Framework_MockObject_MockObject */
|
||||||
|
private $crypto;
|
||||||
|
|
||||||
protected function setUp() {
|
protected function setUp() {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
@ -76,6 +85,9 @@ class UsersControllerTest extends \Test\TestCase {
|
||||||
$this->appManager = $this->createMock(IAppManager::class);
|
$this->appManager = $this->createMock(IAppManager::class);
|
||||||
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
$this->avatarManager = $this->createMock(IAvatarManager::class);
|
||||||
$this->accountManager = $this->createMock(AccountManager::class);
|
$this->accountManager = $this->createMock(AccountManager::class);
|
||||||
|
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||||
|
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||||
|
$this->crypto = $this->createMock(ICrypto::class);
|
||||||
$this->l = $this->createMock(IL10N::class);
|
$this->l = $this->createMock(IL10N::class);
|
||||||
$this->l->method('t')
|
$this->l->method('t')
|
||||||
->will($this->returnCallback(function ($text, $parameters = []) {
|
->will($this->returnCallback(function ($text, $parameters = []) {
|
||||||
|
@ -120,7 +132,10 @@ class UsersControllerTest extends \Test\TestCase {
|
||||||
$this->urlGenerator,
|
$this->urlGenerator,
|
||||||
$this->appManager,
|
$this->appManager,
|
||||||
$this->avatarManager,
|
$this->avatarManager,
|
||||||
$this->accountManager
|
$this->accountManager,
|
||||||
|
$this->secureRandom,
|
||||||
|
$this->timeFactory,
|
||||||
|
$this->crypto
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return $this->getMockBuilder(UsersController::class)
|
return $this->getMockBuilder(UsersController::class)
|
||||||
|
@ -141,7 +156,10 @@ class UsersControllerTest extends \Test\TestCase {
|
||||||
$this->urlGenerator,
|
$this->urlGenerator,
|
||||||
$this->appManager,
|
$this->appManager,
|
||||||
$this->avatarManager,
|
$this->avatarManager,
|
||||||
$this->accountManager
|
$this->accountManager,
|
||||||
|
$this->secureRandom,
|
||||||
|
$this->timeFactory,
|
||||||
|
$this->crypto
|
||||||
]
|
]
|
||||||
)->setMethods($mockedMethods)->getMock();
|
)->setMethods($mockedMethods)->getMock();
|
||||||
}
|
}
|
||||||
|
@ -2227,4 +2245,148 @@ class UsersControllerTest extends \Test\TestCase {
|
||||||
$response = $controller->setEMailAddress($user->getUID(), $mailAddress);
|
$response = $controller->setEMailAddress($user->getUID(), $mailAddress);
|
||||||
$this->assertSame($responseCode, $response->getStatus());
|
$this->assertSame($responseCode, $response->getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCreateUnsuccessfulWithoutPasswordAndEmail() {
|
||||||
|
$controller = $this->getController(true);
|
||||||
|
|
||||||
|
$expectedResponse = new DataResponse(
|
||||||
|
array(
|
||||||
|
'message' => 'To send a password link to the user an email address is required.'
|
||||||
|
),
|
||||||
|
Http::STATUS_UNPROCESSABLE_ENTITY
|
||||||
|
);
|
||||||
|
$response = $controller->create('foo', '', array(), '');
|
||||||
|
$this->assertEquals($expectedResponse, $response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function testCreateSuccessfulWithoutPasswordAndWithEmail() {
|
||||||
|
$controller = $this->getController(true);
|
||||||
|
|
||||||
|
$user = $this->getMockBuilder('\OC\User\User')
|
||||||
|
->disableOriginalConstructor()->getMock();
|
||||||
|
$user
|
||||||
|
->method('getHome')
|
||||||
|
->will($this->returnValue('/home/user'));
|
||||||
|
$user
|
||||||
|
->method('getUID')
|
||||||
|
->will($this->returnValue('foo'));
|
||||||
|
$user
|
||||||
|
->expects($this->once())
|
||||||
|
->method('getBackendClassName')
|
||||||
|
->will($this->returnValue('bar'));
|
||||||
|
|
||||||
|
$this->userManager
|
||||||
|
->expects($this->once())
|
||||||
|
->method('createUser')
|
||||||
|
->will($this->onConsecutiveCalls($user));
|
||||||
|
|
||||||
|
$subadmin = $this->getMockBuilder('\OC\SubAdmin')
|
||||||
|
->disableOriginalConstructor()
|
||||||
|
->getMock();
|
||||||
|
$subadmin
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getSubAdminsGroups')
|
||||||
|
->with($user)
|
||||||
|
->will($this->returnValue([]));
|
||||||
|
$this->groupManager
|
||||||
|
->expects($this->any())
|
||||||
|
->method('getSubAdmin')
|
||||||
|
->will($this->returnValue($subadmin));
|
||||||
|
|
||||||
|
$this->secureRandom
|
||||||
|
->expects($this->at(0))
|
||||||
|
->method('generate')
|
||||||
|
->with(32)
|
||||||
|
->will($this->returnValue('abc123'));
|
||||||
|
$this->secureRandom
|
||||||
|
->expects($this->at(1))
|
||||||
|
->method('generate')
|
||||||
|
->with(21,
|
||||||
|
ISecureRandom::CHAR_DIGITS .
|
||||||
|
ISecureRandom::CHAR_LOWER .
|
||||||
|
ISecureRandom::CHAR_UPPER)
|
||||||
|
->will($this->returnValue('mytoken'));
|
||||||
|
$this->urlGenerator
|
||||||
|
->expects($this->once())
|
||||||
|
->method('linkToRouteAbsolute')
|
||||||
|
->with('core.lost.resetform', ['userId' => 'foo', 'token' => 'mytoken'])
|
||||||
|
->will($this->returnValue('link-with-my-token'));
|
||||||
|
|
||||||
|
$controller = $this->getController(true);
|
||||||
|
$message = $this->getMockBuilder('\OC\Mail\Message')
|
||||||
|
->disableOriginalConstructor()->getMock();
|
||||||
|
$message
|
||||||
|
->expects($this->at(0))
|
||||||
|
->method('setTo')
|
||||||
|
->with(['abc@example.org' => 'foo']);
|
||||||
|
$message
|
||||||
|
->expects($this->at(1))
|
||||||
|
->method('setSubject')
|
||||||
|
->with('Your account was created');
|
||||||
|
$htmlBody = new Http\TemplateResponse(
|
||||||
|
'settings',
|
||||||
|
'email.new_user',
|
||||||
|
[
|
||||||
|
'username' => 'foo',
|
||||||
|
'url' => 'link-with-my-token',
|
||||||
|
],
|
||||||
|
'blank'
|
||||||
|
);
|
||||||
|
$message
|
||||||
|
->expects($this->at(2))
|
||||||
|
->method('setHtmlBody')
|
||||||
|
->with($htmlBody->render());
|
||||||
|
$plainBody = new Http\TemplateResponse(
|
||||||
|
'settings',
|
||||||
|
'email.new_user_plain_text',
|
||||||
|
[
|
||||||
|
'username' => 'foo',
|
||||||
|
'url' => 'link-with-my-token',
|
||||||
|
],
|
||||||
|
'blank'
|
||||||
|
);
|
||||||
|
$message
|
||||||
|
->expects($this->at(3))
|
||||||
|
->method('setPlainBody')
|
||||||
|
->with($plainBody->render());
|
||||||
|
$message
|
||||||
|
->expects($this->at(4))
|
||||||
|
->method('setFrom')
|
||||||
|
->with(['no-reply@owncloud.com' => null]);
|
||||||
|
|
||||||
|
$this->mailer
|
||||||
|
->expects($this->at(0))
|
||||||
|
->method('validateMailAddress')
|
||||||
|
->with('abc@example.org')
|
||||||
|
->will($this->returnValue(true));
|
||||||
|
$this->mailer
|
||||||
|
->expects($this->at(1))
|
||||||
|
->method('createMessage')
|
||||||
|
->will($this->returnValue($message));
|
||||||
|
$this->mailer
|
||||||
|
->expects($this->at(2))
|
||||||
|
->method('send')
|
||||||
|
->with($message);
|
||||||
|
|
||||||
|
$expectedResponse = new DataResponse(
|
||||||
|
array(
|
||||||
|
'name' => 'foo',
|
||||||
|
'groups' => null,
|
||||||
|
'storageLocation' => '/home/user',
|
||||||
|
'backend' => 'bar',
|
||||||
|
'lastLogin' => null,
|
||||||
|
'displayname' => null,
|
||||||
|
'quota' => null,
|
||||||
|
'subadmin' => array(),
|
||||||
|
'email' => null,
|
||||||
|
'isRestoreDisabled' => false,
|
||||||
|
'isAvatarAvailable' => true,
|
||||||
|
),
|
||||||
|
Http::STATUS_CREATED
|
||||||
|
);
|
||||||
|
$response = $controller->create('foo', '', array(), 'abc@example.org');
|
||||||
|
$this->assertEquals($expectedResponse, $response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue