From bdf16e6fbbcaa0a07b5249123cc1723f65883d0a Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 20 Sep 2018 22:50:09 +0200 Subject: [PATCH 1/7] migration: 20180920202100_rename_password_link_to_user_activation.php --- ...ename_password_link_to_user_activation.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/migrations/20180920202100_rename_password_link_to_user_activation.php diff --git a/app/migrations/20180920202100_rename_password_link_to_user_activation.php b/app/migrations/20180920202100_rename_password_link_to_user_activation.php new file mode 100644 index 0000000..65e41f5 --- /dev/null +++ b/app/migrations/20180920202100_rename_password_link_to_user_activation.php @@ -0,0 +1,32 @@ +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(); + } +} From a52fc99dede30863624fc3fc7982ef6a91383fd6 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 20 Sep 2018 23:16:56 +0200 Subject: [PATCH 2/7] app/models/Data/PasswordLink.php: rename to UserActivation + Confirm to the database changes. --- app/controllers/UserController.php | 14 +++--- .../{PasswordLink.php => UserActivation.php} | 44 ++++++++++++------- 2 files changed, 34 insertions(+), 24 deletions(-) rename app/models/Data/{PasswordLink.php => UserActivation.php} (74%) diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php index 0d03705..5d27d89 100644 --- a/app/controllers/UserController.php +++ b/app/controllers/UserController.php @@ -4,9 +4,9 @@ namespace App\Controller; use App\Controller\ControllerBase, App\Form\UserSettings as UserSettingsForm, - App\Model\Data\ActivityLog, - App\Model\Data\PasswordLink, - App\Model\Data\User; + App\Model\Data\User, + App\Model\Data\UserActivation, + App\Model\Data\ActivityLog; class UserController extends ControllerBase { @@ -39,15 +39,15 @@ class UserController extends ControllerBase } // Else we create a password link and email. else { - $link = new PasswordLink(); - $link->setUserId($user->getId()) + $activation = new UserActivation(); + $activation->setUserId($user->getId()) ->setPassword($hash) ->save(); // Render the email content. $tpl = $this->di->get('template'); $content = $tpl->render('mail/password_activation', [ - 'link' => $link->getPublicId() + 'link' => $activation->getActivationKey() ]); // Send the email. @@ -113,7 +113,7 @@ class UserController extends ControllerBase */ public function activationLinkAction($id) { - $link = PasswordLink::findFirst(['public_id = ?0', 'bind' => [ $id ]]); + $link = UserActivation::findFirst(['activation_key = ?0', 'bind' => [ $id ]]); if ($link) { if ($link->isValid()) { diff --git a/app/models/Data/PasswordLink.php b/app/models/Data/UserActivation.php similarity index 74% rename from app/models/Data/PasswordLink.php rename to app/models/Data/UserActivation.php index 9f834c4..1703298 100644 --- a/app/models/Data/PasswordLink.php +++ b/app/models/Data/UserActivation.php @@ -6,7 +6,7 @@ use Httpcb\Mvc\Model\Behavior\RandomId as RandomIdBehavior; use Phalcon\Forms\Element\Date; use Phalcon\Mvc\Model\Behavior\SoftDelete; -class PasswordLink extends Base +class UserActivation extends Base { /** * @var int @@ -16,13 +16,18 @@ class PasswordLink extends Base /** * @var string */ - protected $public_id; + protected $activation_key; /** * @var int */ protected $user_id; + /** + * @var int + */ + protected $used; + /** * @var string */ @@ -38,7 +43,7 @@ class PasswordLink extends Base */ public function initialize() { - $this->setSource('password_link'); + $this->setSource('user_activation'); $this->useDynamicUpdate(true); // Relationships @@ -46,15 +51,20 @@ class PasswordLink extends Base // Behaviour $this->addBehavior(new RandomIdBehavior(array( - 'field' => 'public_id', - 'length' => 12, - 'expression' => '(password IS NULL OR HOUR(TIMEDIFF(date, NOW())) = 0)' + 'field' => 'activation_key', + 'length' => 40, + 'expression' => '(used = 0 OR HOUR(TIMEDIFF(date, NOW())) = 0)' ))); $this->addBehavior(new SoftDelete([ 'field' => 'password', 'value' => null ])); + + $this->addBehavior(new SoftDelete([ + 'field' => 'used', + 'value' => 1 + ])); } /** @@ -69,7 +79,7 @@ class PasswordLink extends Base * @param int $id * @return PasswordLink */ - public function setId(int $id) : PasswordLink + public function setId(int $id) : UserActivation { $this->id = $id; return $this; @@ -78,18 +88,18 @@ class PasswordLink extends Base /** * @return string */ - public function getPublicId() + public function getActivationKey() { - return $this->public_id; + return $this->activation_key; } /** * @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; } @@ -103,9 +113,9 @@ class PasswordLink extends Base /** * @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; return $this; @@ -121,9 +131,9 @@ class PasswordLink extends Base /** * @param string $password - * @return PasswordLink + * @return UserActivation */ - public function setPassword(string $password) : PasswordLink + public function setPassword(string $password) : UserActivation { $this->password = $password; return $this; @@ -136,7 +146,7 @@ class PasswordLink extends Base */ public function isUsed() : bool { - return strlen($this->password) < 1; + return (bool) $this->used; } /** From 19fdee782cae837b22f3729bd491675752151044 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 22 Sep 2018 01:15:47 +0200 Subject: [PATCH 3/7] adding app/templates/mail/account_activation.volt --- app/templates/mail/account_activation.volt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 app/templates/mail/account_activation.volt diff --git a/app/templates/mail/account_activation.volt b/app/templates/mail/account_activation.volt new file mode 100644 index 0000000..0d18d53 --- /dev/null +++ b/app/templates/mail/account_activation.volt @@ -0,0 +1,10 @@ + +{% set link_url = request.getScheme() ~ '://' + ~ request.getHttpHost() + ~ url(['for': 'activation-link', 'link': link ]) %} + +

Httcb Account Activation

+ +

Please click here to activate your account.

+ +

Or copy and paste this link into the address field in your browser: {{ link_url }}

From dde881829638f7400492aa525b3e9c8e6cca9247 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 22 Sep 2018 01:17:47 +0200 Subject: [PATCH 4/7] app/forms/Registration.php: enable user to change email. --- app/forms/Registration.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/app/forms/Registration.php b/app/forms/Registration.php index f6d5160..33cad4c 100644 --- a/app/forms/Registration.php +++ b/app/forms/Registration.php @@ -83,14 +83,10 @@ class Registration extends FormBase // Email $email = new Text('email', array( 'class' => 'form-control', - 'placeholder' => 'Email', - 'readonly' => '', + 'placeholder' => 'Email' )); $email->addValidators([ - new IdenticalValidator([ - 'accepted' => $user->getEmail(), - ]), new CallbackValidator([ 'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; }, 'message' => 'This email already exist.', From 4ff298e7eaefe48211ec197a4748d892d33e1b32 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 22 Sep 2018 01:19:55 +0200 Subject: [PATCH 5/7] app/controllers/AuthController.php: in oauthAction() redirect new users to register page even if their email is taken (they can change it now) --- app/controllers/AuthController.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 83c5531..1fcb8d6 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -90,12 +90,6 @@ class AuthController extends ControllerBase 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->response->redirect(['for' => 'user-register']); return; From 2faf85359a41c22a924c3ff2b9524e3264405ae9 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 22 Sep 2018 01:21:40 +0200 Subject: [PATCH 6/7] app/controllers/AuthController.php: in registerAction() send activation mail if email from oauth is changed. --- app/controllers/AuthController.php | 35 +++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 1fcb8d6..5c20761 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Controller\ControllerBase, App\Model\Data\User, + App\Model\Data\UserActivation, App\Form\Login as LoginForm, App\Form\Registration as RegistrationForm, Httpcb\OAuth\UserData\UserDataInterface, @@ -133,11 +134,39 @@ class AuthController extends ControllerBase $formData = $this->request->getPost(); 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()); if ($user->save()) { - $this->auth->systemLogin($user); - $this->flash->success('User successfully created. Now add your first callback!'); - $this->response->redirect('/callback/new'); + + 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->flash->success('User successfully created. Now add your first callback!'); + $this->response->redirect('/callback/new'); + } } else { $this->flash->error('Could not create user'); } From 2335c6164456e33f38b9e20186cc32420f1986ab Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Sat, 22 Sep 2018 01:23:45 +0200 Subject: [PATCH 7/7] app/controllers/UserController.php: in activateLinkAction() accept account activation also. --- app/controllers/UserController.php | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/app/controllers/UserController.php b/app/controllers/UserController.php index 5d27d89..3ce35fa 100644 --- a/app/controllers/UserController.php +++ b/app/controllers/UserController.php @@ -107,8 +107,9 @@ class UserController extends ControllerBase } /** - * Activate a password. + * Account/Password activation. * + * @Acl(resource="auth") * @param $id */ public function activationLinkAction($id) @@ -118,12 +119,20 @@ class UserController extends ControllerBase if ($link) { if ($link->isValid()) { - // Save the password. - $link->getUser() - ->setPassword($link->getPassword()) - ->save(); + $user = $link->getUser(); - $this->flash->success('Your password has been activated.'); + // Save password if any is set. + if (strlen($link->getPassword()) > 0) { + $user->setPassword($link->getPassword()); + $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 { $this->flash->error('This link has expired or has already been used.'); }