Archived
1
0
Fork 0

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:
Henrik Hautakoski 2018-09-25 22:25:42 +00:00
commit 7a81eb87e3
6 changed files with 124 additions and 44 deletions

View file

@ -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');
} }

View file

@ -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.');
} }

View file

@ -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.',

View file

@ -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();
}
}

View file

@ -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;
} }
/** /**

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