Merge branch '22-registration-enable-change-of-email' into 'dev'
Resolve "Registration - Enable change of email." Closes #22 See merge request pnx/httpcb!22
This commit is contained in:
commit
7a81eb87e3
6 changed files with 124 additions and 44 deletions
|
|
@ -4,6 +4,7 @@ namespace App\Controller;
|
||||||
|
|
||||||
use App\Controller\ControllerBase,
|
use App\Controller\ControllerBase,
|
||||||
App\Model\Data\User,
|
App\Model\Data\User,
|
||||||
|
App\Model\Data\UserActivation,
|
||||||
App\Form\Login as LoginForm,
|
App\Form\Login as LoginForm,
|
||||||
App\Form\Registration as RegistrationForm,
|
App\Form\Registration as RegistrationForm,
|
||||||
Httpcb\OAuth\UserData\UserDataInterface,
|
Httpcb\OAuth\UserData\UserDataInterface,
|
||||||
|
|
@ -90,12 +91,6 @@ class AuthController extends ControllerBase
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (User::findFirstByEmail($data->getEmail())) {
|
|
||||||
$this->flash->error('The email address is already in use.');
|
|
||||||
$this->response->redirect('/login');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->session->set('auth:register:data', $data);
|
$this->session->set('auth:register:data', $data);
|
||||||
$this->response->redirect(['for' => 'user-register']);
|
$this->response->redirect(['for' => 'user-register']);
|
||||||
return;
|
return;
|
||||||
|
|
@ -139,11 +134,39 @@ class AuthController extends ControllerBase
|
||||||
|
|
||||||
$formData = $this->request->getPost();
|
$formData = $this->request->getPost();
|
||||||
if ($form->isValid($formData)) {
|
if ($form->isValid($formData)) {
|
||||||
|
|
||||||
|
// Check if email was changed.
|
||||||
|
$activationNeeded = false;
|
||||||
|
if ($form->getValue('email') !== $data->getEmail()) {
|
||||||
|
$activationNeeded = true;
|
||||||
|
|
||||||
|
// Set suspended until the email address is confirmed
|
||||||
|
$user->setStatus(User::STATUS_SUSPENDED);
|
||||||
|
}
|
||||||
|
|
||||||
$user->setOAuthId($data->getProvider(), $data->getId());
|
$user->setOAuthId($data->getProvider(), $data->getId());
|
||||||
if ($user->save()) {
|
if ($user->save()) {
|
||||||
|
|
||||||
|
if ($activationNeeded) {
|
||||||
|
|
||||||
|
$activation = new UserActivation();
|
||||||
|
$activation->setUserId($user->getId())
|
||||||
|
->save();
|
||||||
|
|
||||||
|
$content = $this->di->getShared('template')->render('mail/account_activation', [
|
||||||
|
'link' => $activation->getActivationKey()
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->di->getMail()->send('Httpcb account activation', $user->getEmail(), $content);
|
||||||
|
|
||||||
|
$this->flash->success('User successfully created.');
|
||||||
|
$this->flash->notice("An email has been sent to {$form->getValue('email')} with an activation code.");
|
||||||
|
$this->response->redirect('/login');
|
||||||
|
} else {
|
||||||
$this->auth->systemLogin($user);
|
$this->auth->systemLogin($user);
|
||||||
$this->flash->success('User successfully created. Now add your first callback!');
|
$this->flash->success('User successfully created. Now add your first callback!');
|
||||||
$this->response->redirect('/callback/new');
|
$this->response->redirect('/callback/new');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->flash->error('Could not create user');
|
$this->flash->error('Could not create user');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ namespace App\Controller;
|
||||||
|
|
||||||
use App\Controller\ControllerBase,
|
use App\Controller\ControllerBase,
|
||||||
App\Form\UserSettings as UserSettingsForm,
|
App\Form\UserSettings as UserSettingsForm,
|
||||||
App\Model\Data\ActivityLog,
|
App\Model\Data\User,
|
||||||
App\Model\Data\PasswordLink,
|
App\Model\Data\UserActivation,
|
||||||
App\Model\Data\User;
|
App\Model\Data\ActivityLog;
|
||||||
|
|
||||||
class UserController extends ControllerBase
|
class UserController extends ControllerBase
|
||||||
{
|
{
|
||||||
|
|
@ -39,15 +39,15 @@ class UserController extends ControllerBase
|
||||||
}
|
}
|
||||||
// Else we create a password link and email.
|
// Else we create a password link and email.
|
||||||
else {
|
else {
|
||||||
$link = new PasswordLink();
|
$activation = new UserActivation();
|
||||||
$link->setUserId($user->getId())
|
$activation->setUserId($user->getId())
|
||||||
->setPassword($hash)
|
->setPassword($hash)
|
||||||
->save();
|
->save();
|
||||||
|
|
||||||
// Render the email content.
|
// Render the email content.
|
||||||
$tpl = $this->di->get('template');
|
$tpl = $this->di->get('template');
|
||||||
$content = $tpl->render('mail/password_activation', [
|
$content = $tpl->render('mail/password_activation', [
|
||||||
'link' => $link->getPublicId()
|
'link' => $activation->getActivationKey()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Send the email.
|
// Send the email.
|
||||||
|
|
@ -107,23 +107,32 @@ class UserController extends ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate a password.
|
* Account/Password activation.
|
||||||
*
|
*
|
||||||
|
* @Acl(resource="auth")
|
||||||
* @param $id
|
* @param $id
|
||||||
*/
|
*/
|
||||||
public function activationLinkAction($id)
|
public function activationLinkAction($id)
|
||||||
{
|
{
|
||||||
$link = PasswordLink::findFirst(['public_id = ?0', 'bind' => [ $id ]]);
|
$link = UserActivation::findFirst(['activation_key = ?0', 'bind' => [ $id ]]);
|
||||||
|
|
||||||
if ($link) {
|
if ($link) {
|
||||||
if ($link->isValid()) {
|
if ($link->isValid()) {
|
||||||
|
|
||||||
// Save the password.
|
$user = $link->getUser();
|
||||||
$link->getUser()
|
|
||||||
->setPassword($link->getPassword())
|
|
||||||
->save();
|
|
||||||
|
|
||||||
|
// Save password if any is set.
|
||||||
|
if (strlen($link->getPassword()) > 0) {
|
||||||
|
$user->setPassword($link->getPassword());
|
||||||
$this->flash->success('Your password has been activated.');
|
$this->flash->success('Your password has been activated.');
|
||||||
|
} else {
|
||||||
|
$user->setStatus(User::STATUS_ACTIVE);
|
||||||
|
$this->flash->success('Your account has been activated.');
|
||||||
|
|
||||||
|
// Also login the user.
|
||||||
|
$this->auth->systemLogin($user);
|
||||||
|
}
|
||||||
|
$user->save();
|
||||||
} else {
|
} else {
|
||||||
$this->flash->error('This link has expired or has already been used.');
|
$this->flash->error('This link has expired or has already been used.');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,10 @@ class Registration extends FormBase
|
||||||
// Email
|
// Email
|
||||||
$email = new Text('email', array(
|
$email = new Text('email', array(
|
||||||
'class' => 'form-control',
|
'class' => 'form-control',
|
||||||
'placeholder' => 'Email',
|
'placeholder' => 'Email'
|
||||||
'readonly' => '',
|
|
||||||
));
|
));
|
||||||
|
|
||||||
$email->addValidators([
|
$email->addValidators([
|
||||||
new IdenticalValidator([
|
|
||||||
'accepted' => $user->getEmail(),
|
|
||||||
]),
|
|
||||||
new CallbackValidator([
|
new CallbackValidator([
|
||||||
'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; },
|
'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; },
|
||||||
'message' => 'This email already exist.',
|
'message' => 'This email already exist.',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
class RenamePasswordLinkToUserActivation extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->table('password_link')
|
||||||
|
->rename('user_activation')
|
||||||
|
->changeColumn('public_id', 'string', [
|
||||||
|
'limit' => 40
|
||||||
|
])
|
||||||
|
->addColumn('used', 'integer', [
|
||||||
|
'limit' => 1,
|
||||||
|
'default' => 0,
|
||||||
|
'after' => 'user_id'
|
||||||
|
])->save();
|
||||||
|
|
||||||
|
$this->table('user_activation')
|
||||||
|
->renameColumn('public_id', 'activation_key')
|
||||||
|
->save();
|
||||||
|
|
||||||
|
// Set used = 1 on all rows where password = null
|
||||||
|
$this->getQueryBuilder()
|
||||||
|
->update('user_activation')
|
||||||
|
->set('used', 1)
|
||||||
|
->where('password IS NULL OR LENGTH(password) < 1')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,7 +6,7 @@ use Httpcb\Mvc\Model\Behavior\RandomId as RandomIdBehavior;
|
||||||
use Phalcon\Forms\Element\Date;
|
use Phalcon\Forms\Element\Date;
|
||||||
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
use Phalcon\Mvc\Model\Behavior\SoftDelete;
|
||||||
|
|
||||||
class PasswordLink extends Base
|
class UserActivation extends Base
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
|
|
@ -16,13 +16,18 @@ class PasswordLink extends Base
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
protected $public_id;
|
protected $activation_key;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*/
|
*/
|
||||||
protected $user_id;
|
protected $user_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected $used;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
|
|
@ -38,7 +43,7 @@ class PasswordLink extends Base
|
||||||
*/
|
*/
|
||||||
public function initialize()
|
public function initialize()
|
||||||
{
|
{
|
||||||
$this->setSource('password_link');
|
$this->setSource('user_activation');
|
||||||
$this->useDynamicUpdate(true);
|
$this->useDynamicUpdate(true);
|
||||||
|
|
||||||
// Relationships
|
// Relationships
|
||||||
|
|
@ -46,15 +51,20 @@ class PasswordLink extends Base
|
||||||
|
|
||||||
// Behaviour
|
// Behaviour
|
||||||
$this->addBehavior(new RandomIdBehavior(array(
|
$this->addBehavior(new RandomIdBehavior(array(
|
||||||
'field' => 'public_id',
|
'field' => 'activation_key',
|
||||||
'length' => 12,
|
'length' => 40,
|
||||||
'expression' => '(password IS NULL OR HOUR(TIMEDIFF(date, NOW())) = 0)'
|
'expression' => '(used = 0 OR HOUR(TIMEDIFF(date, NOW())) = 0)'
|
||||||
)));
|
)));
|
||||||
|
|
||||||
$this->addBehavior(new SoftDelete([
|
$this->addBehavior(new SoftDelete([
|
||||||
'field' => 'password',
|
'field' => 'password',
|
||||||
'value' => null
|
'value' => null
|
||||||
]));
|
]));
|
||||||
|
|
||||||
|
$this->addBehavior(new SoftDelete([
|
||||||
|
'field' => 'used',
|
||||||
|
'value' => 1
|
||||||
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -69,7 +79,7 @@ class PasswordLink extends Base
|
||||||
* @param int $id
|
* @param int $id
|
||||||
* @return PasswordLink
|
* @return PasswordLink
|
||||||
*/
|
*/
|
||||||
public function setId(int $id) : PasswordLink
|
public function setId(int $id) : UserActivation
|
||||||
{
|
{
|
||||||
$this->id = $id;
|
$this->id = $id;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -78,18 +88,18 @@ class PasswordLink extends Base
|
||||||
/**
|
/**
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getPublicId()
|
public function getActivationKey()
|
||||||
{
|
{
|
||||||
return $this->public_id;
|
return $this->activation_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $public_id
|
* @param string $public_id
|
||||||
* @return PasswordLink
|
* @return UserActivation
|
||||||
*/
|
*/
|
||||||
public function setPublicId(string $public_id) : PasswordLink
|
public function setActivationKey(string $key) : UserActivation
|
||||||
{
|
{
|
||||||
$this->public_id = $public_id;
|
$this->activation_key = $key;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,9 +113,9 @@ class PasswordLink extends Base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param int $user_id
|
* @param int $user_id
|
||||||
* @return PasswordLink
|
* @return UserActivation
|
||||||
*/
|
*/
|
||||||
public function setUserId(int $user_id) : PasswordLink
|
public function setUserId(int $user_id) : UserActivation
|
||||||
{
|
{
|
||||||
$this->user_id = $user_id;
|
$this->user_id = $user_id;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -121,9 +131,9 @@ class PasswordLink extends Base
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $password
|
* @param string $password
|
||||||
* @return PasswordLink
|
* @return UserActivation
|
||||||
*/
|
*/
|
||||||
public function setPassword(string $password) : PasswordLink
|
public function setPassword(string $password) : UserActivation
|
||||||
{
|
{
|
||||||
$this->password = $password;
|
$this->password = $password;
|
||||||
return $this;
|
return $this;
|
||||||
|
|
@ -136,7 +146,7 @@ class PasswordLink extends Base
|
||||||
*/
|
*/
|
||||||
public function isUsed() : bool
|
public function isUsed() : bool
|
||||||
{
|
{
|
||||||
return strlen($this->password) < 1;
|
return (bool) $this->used;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
10
app/templates/mail/account_activation.volt
Normal file
10
app/templates/mail/account_activation.volt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
|
||||||
|
{% set link_url = request.getScheme() ~ '://'
|
||||||
|
~ request.getHttpHost()
|
||||||
|
~ url(['for': 'activation-link', 'link': link ]) %}
|
||||||
|
|
||||||
|
<h1>Httcb Account Activation</h1>
|
||||||
|
|
||||||
|
<p>Please click <a href="{{ link_url }}">here</a> to activate your account.</p>
|
||||||
|
|
||||||
|
<p style="color:grey">Or copy and paste this link into the address field in your browser: <i>{{ link_url }}</i></p>
|
||||||
Reference in a new issue