From 6c14bf9873da044fc2687741ff4f858d946244d0 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Mon, 13 Aug 2018 01:42:54 +0200 Subject: [PATCH 01/26] app/config/routes.yml: add '/register' route. --- app/config/routes.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/config/routes.yml b/app/config/routes.yml index 11703ff..4c36381 100644 --- a/app/config/routes.yml +++ b/app/config/routes.yml @@ -43,6 +43,9 @@ router: oauth-disconnect-confirm: pattern: '/oauth/{provider:([a-z]+)}/disconnect/{confirm}' path: 'User::oauthdisconnect' + user-register: + pattern: '/register' + path: Auth::register user-settings: pattern: '/settings' path: From 808afbac174a44db8b29c42a43fc80a4deafd851 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Mon, 13 Aug 2018 01:57:42 +0200 Subject: [PATCH 02/26] adding app/assets/less/views/register.less --- app/assets/less/application.less | 1 + app/assets/less/views/register.less | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 app/assets/less/views/register.less diff --git a/app/assets/less/application.less b/app/assets/less/application.less index 850a879..4d323d0 100644 --- a/app/assets/less/application.less +++ b/app/assets/less/application.less @@ -42,6 +42,7 @@ @import "views/landingpage"; @import "views/about"; @import "views/login"; +@import "views/register"; // Plugins @import "vendor/ionicons/ionicons"; diff --git a/app/assets/less/views/register.less b/app/assets/less/views/register.less new file mode 100644 index 0000000..1cbe1c3 --- /dev/null +++ b/app/assets/less/views/register.less @@ -0,0 +1,6 @@ + +.register { + &:extend(.section all); + .center-block(); + width: 60%; +} From bfa71745e034c2a81b73f1b0411c21c667372dd9 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Mon, 13 Aug 2018 13:14:24 +0200 Subject: [PATCH 03/26] adding app/assets/less/components/spacer.less --- app/assets/less/application.less | 1 + app/assets/less/components/spacer.less | 6 ++++++ app/assets/less/variables.less | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 app/assets/less/components/spacer.less diff --git a/app/assets/less/application.less b/app/assets/less/application.less index 4d323d0..d851765 100644 --- a/app/assets/less/application.less +++ b/app/assets/less/application.less @@ -33,6 +33,7 @@ @import "components/button"; @import "components/badge"; @import "components/section"; +@import "components/spacer"; @import "components/pagination"; @import "components/blankslate"; @import "components/request-item"; diff --git a/app/assets/less/components/spacer.less b/app/assets/less/components/spacer.less new file mode 100644 index 0000000..b1084d2 --- /dev/null +++ b/app/assets/less/components/spacer.less @@ -0,0 +1,6 @@ + +.spacer { + display: block; + border-bottom: 1px solid @gray-light; + margin: @spacer-margin; +} diff --git a/app/assets/less/variables.less b/app/assets/less/variables.less index d4930f7..f3a7c1d 100644 --- a/app/assets/less/variables.less +++ b/app/assets/less/variables.less @@ -154,6 +154,13 @@ @blankslate-spacing: 25px 10px; @blankslate-spacing-sm: 15px 5px; +// ---------------------------------- +// Spacer +// ---------------------------------- + +@spacer-color: @gray-light; +@spacer-margin: 1em 0; + // ---------------------------------- // Pagination // ---------------------------------- From 316edec02057e0940cbf93ade4b9624c41b0cf8a Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Mon, 13 Aug 2018 13:33:17 +0200 Subject: [PATCH 04/26] app/library/Auth.php: in loginOauth() should return false if there is no user. --- app/library/Auth.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/library/Auth.php b/app/library/Auth.php index 2ec49ae..9afb5c0 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -44,24 +44,21 @@ class Auth extends Component * Login using OAuth * * @param UserDataInterface $data - * @return bool|\Phalcon\Mvc\Model\MessageInterface[] + * @return bool */ public function loginOauth(UserDataInterface $data) { $user = User::findFirstByOAuthID($data); + // Did not find any user. if (!$user) { - // Did not find any user. create him. - $user = User::createFromOAuthData($data); - - if ($user->save() === false) { - return $user->getMessages(); - } + return false; } + // Here we activate the user. // As for OAuth we perform registration if the user does not exist. // We should therefore activate deleted accounts. - else if ($user->Status == User::STATUS_DELETED) { + if ($user->Status == User::STATUS_DELETED) { $user->Status = User::STATUS_ACTIVE; $user->save(); } From ddb9d8934dcb1b99e660f70bf7d16b2d354b4592 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 17:31:07 +0200 Subject: [PATCH 05/26] adding app/library/Form.php --- app/library/Form.php | 70 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 app/library/Form.php diff --git a/app/library/Form.php b/app/library/Form.php new file mode 100644 index 0000000..ebbb8bc --- /dev/null +++ b/app/library/Form.php @@ -0,0 +1,70 @@ + 'control-label', + 'class' => [ 'col-sm-10' ], + 'message' => '' + ]; + + $ele = $this->get($name); + + if (isset($opt['label-length'])) { + $length = (int) $opt['label-length']; + } else { + $length = 2; + } + $options['label-class'] .= ' col-sm-' . $length; + + if (isset($opt['length'])) { + + $len = $opt['length']; + + if ($len === 'full') { + $options['class'] = []; + } else { + $options['class'] = [ 'col-sm-' . $len ]; + } + + unset($opt['length']); + } + + if ($ele->hasMessages()) { + $options['class'][] = 'has-error'; + $options['message'] = $ele->getMessages()->current(); + } + + return $this->_render($ele, $options); + } + + protected function _render(FormElement $ele, $opt) + { + $xhtml = ''; + + if (strlen($ele->getLabel()) > 0) { + + $xhtml .= sprintf( + '', + $opt['label-class'], $ele->getName(), $ele->getLabel()); + } + + $xhtml .= '
' + . $ele->render(); + + if (strlen($opt['message']) > 0) { + $xhtml .= '' . $opt['message'] . ''; + } + + $xhtml .= '
'; + + return $xhtml; + } +} From ad3bbdd6bce37eb3e66fdf36c7992aace90bc6c2 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 20:31:42 +0200 Subject: [PATCH 06/26] app/models/Data/User.php: adding setOAuthId() --- app/models/Data/User.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 22a7df7..df02c81 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -295,6 +295,20 @@ class User extends Model return $this->linkedin_id; } + /** + * @param string $provider + * @param string $id + * @return $this + */ + public function setOAuthId($provider, $id) + { + $method = 'set' . ucfirst($provider) . 'Id'; + if (method_exists($this, $method)) { + $this->$method($id); + } + return $this; + } + /** * @param string $id * @return User From 968ca64c1da3a96158e2476ee03b37d0e999a95d Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 20:35:09 +0200 Subject: [PATCH 07/26] adding app/views/auth/register.volt --- app/views/auth/register.volt | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 app/views/auth/register.volt diff --git a/app/views/auth/register.volt b/app/views/auth/register.volt new file mode 100644 index 0000000..7ae2c3d --- /dev/null +++ b/app/views/auth/register.volt @@ -0,0 +1,43 @@ + + +
+ +

Account registration

+ + + + + +
+ +
+ {{ form.renderDecorated('email') }} +
+ +
+ {{ form.renderDecorated('username') }} +
+ +
+ {{ form.renderDecorated('first-name', ['length': 4]) }} + {{ form.renderDecorated('last-name', ['length': 4]) }} +
+ + + +
+
+ {{ form.render('submit') }} +
+
+
+
From 31d5740681803f771c57fe18d8f65de6255b6a47 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 20:35:23 +0200 Subject: [PATCH 08/26] app/library/Auth.php: adding systemLogin() --- app/library/Auth.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/app/library/Auth.php b/app/library/Auth.php index 9afb5c0..f319c3c 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -71,6 +71,17 @@ class Auth extends Component return true; } + /** + * The system logs in a user (without credentials). + * + * @param User $user + */ + public function systemLogin(User $user) + { + $this->setIdentity($user->getId()); + $this->eventsManager->fire('auth:onLogin', $this, 'System'); + } + /** * @param $identity * @return Auth From 6eb08d7a7030e0ec32731d2813499d3cb10bcb55 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 21:28:30 +0200 Subject: [PATCH 09/26] adding app/forms/Registration.php --- app/forms/Registration.php | 118 +++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 app/forms/Registration.php diff --git a/app/forms/Registration.php b/app/forms/Registration.php new file mode 100644 index 0000000..c95adff --- /dev/null +++ b/app/forms/Registration.php @@ -0,0 +1,118 @@ +setValidation(new \Phalcon\Validation()); + + // Username + $username = new Text('username', array( + 'class' => 'form-control', + 'placeholder' => 'Username', + )); + + $username->setLabel('Username'); + + $username->addValidator(new AlnumValidator()); + + $validator = new UniquenessValidator(array( + 'model' => new UserModel(), + 'message' => 'The username already exists.', + 'attribute' => 'username', + )); + + $username->addValidator($validator); + + $this->add($username); + + // Names + foreach([ 'first-name' => 'Firstname', 'last-name' => 'Lastname' ] as $id => $label) { + + $name = new Text($id, array( + 'class' => 'form-control', + 'placeholder' => $label, + )); + + $name->setLabel($label); + $name->addValidator(new AlphaValidator([ + 'allowSpace' => false, + 'allowEmpty' => true, + ])); + + $this->add($name); + } + + // Email + $email = new Text('email', array( + 'class' => 'form-control', + 'placeholder' => 'Email', + 'readonly' => '', + )); + + $email->addValidators([ + new IdenticalValidator([ + 'accepted' => $this->getEntity()->getEmail(), + ]), + new UniquenessValidator([ + 'model' => new UserModel(), + 'message' => 'This email already exist.', + 'attribute' => 'email', + ]) + ]); + + $email->setLabel('Email'); + $this->add($email); + + // Submit + $submit = new Submit('submit', array('class' => 'button button-success', 'value' => 'Register')); + $this->add($submit); + } + + public function bind(array $data, $entity, $whitelist = null) + { + parent::bind($data, $entity, $whitelist); + + if ($entity instanceof User && $this->getEntity() instanceof UserDataInterface) { + $provider = $this->getEntity()->getProvider(); + $id = $this->getEntity()->getId(); + $entity->setOAuthId($provider, $id); + } + } +} From 4a22f67e62c198d4785909b5138b4152c4466447 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 22:32:51 +0200 Subject: [PATCH 10/26] adding app/library/OAuth/UserData/UserData.php --- app/library/OAuth/UserData/UserData.php | 104 ++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 app/library/OAuth/UserData/UserData.php diff --git a/app/library/OAuth/UserData/UserData.php b/app/library/OAuth/UserData/UserData.php new file mode 100644 index 0000000..00ba85c --- /dev/null +++ b/app/library/OAuth/UserData/UserData.php @@ -0,0 +1,104 @@ +_provider; + } + + /** + * {@inheritDoc} + */ + public function getId() + { + return $this->_id; + } + + /** + * {@inheritDoc} + */ + public function getUsername() + { + return $this->_username; + } + + /** + * {@inheritDoc} + */ + public function getName() + { + if ($this->_name === null && strlen($this->_firstname) > 0) { + $name = $this->_firstname; + if (strlen($this->_lastname) > 0) { + $name .= ' ' . $this->_lastname; + } + return $name; + } + return $this->_name; + } + + /** + * {@inheritDoc} + */ + public function getFirstname() + { + if ($this->_firstname === null) { + $pos = strpos($this->getName(), ' '); + return $pos !== false ? substr($this->getName(), 0, $pos) : $this->getName(); + + } + return $this->_firstname; + } + + /** + * {@inheritDoc} + */ + public function getLastname() + { + if ($this->_lastname === null) { + $pos = strpos($this->getName(), ' '); + return $pos !== false ? substr($this->getName(), $pos+1) : null; + } + return $this->_lastname; + } + + /** + * {@inheritDoc} + */ + public function getEmail() + { + return $this->_email; + } + + public function toArray() + { + $data = []; + foreach(get_class_methods($this) as $method) { + if (substr($method, 0, 3) == 'get') { + $field = lcfirst(substr($method, 3)); + $data[$field] = $this->$method(); + } + } + return $data; + } +} From 43417740a7a5a91945b8ee8ddb57e8674b8a30ab Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 22:33:26 +0200 Subject: [PATCH 11/26] app/library/OAuth/UserData: make all concrete classes extend UserData. --- app/library/OAuth/UserData/Github.php | 61 +++------------------ app/library/OAuth/UserData/Gitlab.php | 66 +++-------------------- app/library/OAuth/UserData/Google.php | 68 +++-------------------- app/library/OAuth/UserData/LinkedIn.php | 72 +++---------------------- 4 files changed, 24 insertions(+), 243 deletions(-) diff --git a/app/library/OAuth/UserData/Github.php b/app/library/OAuth/UserData/Github.php index a465770..0de7fea 100644 --- a/app/library/OAuth/UserData/Github.php +++ b/app/library/OAuth/UserData/Github.php @@ -2,67 +2,18 @@ namespace Httpcb\OAuth\UserData; -class Github implements UserDataInterface +class Github extends UserData { - protected $data; + protected $_provider = 'Github'; /** * {@inheritDoc} */ public function __construct(array $data) { - $this->data = $data; - } - - /** - * {@inheritDoc} - */ - public function getProvider() - { - return 'Github'; - } - - /** - * {@inheritDoc} - */ - public function getId() - { - return (int) $this->data['id']; - } - - /** - * {@inheritDoc} - */ - public function getUsername() - { - return $this->data['login']; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return $this->data['name']; - } - - public function getFirstname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), 0, $pos) : $this->getName(); - } - - public function getLastname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), $pos+1) : null; - } - - /** - * {@inheritDoc} - */ - public function getEmail() - { - return $this->data['email']; + $this->_id = $data['id']; + $this->_username = $data['login']; + $this->_name = $data['name']; + $this->_email = $data['email']; } } diff --git a/app/library/OAuth/UserData/Gitlab.php b/app/library/OAuth/UserData/Gitlab.php index 85f3653..9ea6d41 100644 --- a/app/library/OAuth/UserData/Gitlab.php +++ b/app/library/OAuth/UserData/Gitlab.php @@ -2,72 +2,18 @@ namespace Httpcb\OAuth\UserData; -class Gitlab implements UserDataInterface +class Gitlab extends UserData { - protected $data; + protected $_provider = 'Gitlab'; /** * {@inheritDoc} */ public function __construct(array $data) { - $this->data = $data; - } - - /** - * {@inheritDoc} - */ - public function getProvider() - { - return 'Gitlab'; - } - - /** - * {@inheritDoc} - */ - public function getId() - { - return (int) $this->data['id']; - } - - /** - * {@inheritDoc} - */ - public function getUsername() - { - return $this->data['username']; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return $this->data['name']; - } - - /** - * {@inheritDoc} - */ - public function getFirstname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), 0, $pos) : $this->getName(); - } - - /** - * {@inheritDoc} - */ - public function getLastname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), $pos+1) : null; - } - /** - * {@inheritDoc} - */ - public function getEmail() - { - return $this->data['email']; + $this->_id = $data['id']; + $this->_username = $data['username']; + $this->_name = $data['name']; + $this->_email = $data['email']; } } diff --git a/app/library/OAuth/UserData/Google.php b/app/library/OAuth/UserData/Google.php index 28aa7a1..096215e 100644 --- a/app/library/OAuth/UserData/Google.php +++ b/app/library/OAuth/UserData/Google.php @@ -2,76 +2,20 @@ namespace Httpcb\OAuth\UserData; -class Google implements UserDataInterface +class Google extends UserData { - protected $data; + protected $_provider = 'Google'; /** * {@inheritDoc} */ public function __construct(array $data) { - $this->data = $data; - } + $this->_id = $data['id']; + $this->_name = $data['displayName']; - /** - * {@inheritDoc} - */ - public function getProvider() - { - return 'Google'; - } - - /** - * {@inheritDoc} - */ - public function getId() - { - return (int) $this->data['id']; - } - - /** - * {@inheritDoc} - */ - public function getUsername() - { - return null; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return $this->data['displayName']; - } - - /** - * {@inheritDoc} - */ - public function getFirstname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), 0, $pos) : $this->getName(); - } - - /** - * {@inheritDoc} - */ - public function getLastname() - { - $pos = strpos($this->getName(), ' '); - return $pos !== false ? substr($this->getName(), $pos+1) : null; - } - - /** - * {@inheritDoc} - */ - public function getEmail() - { - if (isset($this->data['emails'][0]['value'])) { - return $this->data['emails'][0]['value']; + if (isset($data['emails'][0]['value'])) { + $this->_email = $data['emails'][0]['value']; } - return null; } } diff --git a/app/library/OAuth/UserData/LinkedIn.php b/app/library/OAuth/UserData/LinkedIn.php index 0907453..6f1d969 100644 --- a/app/library/OAuth/UserData/LinkedIn.php +++ b/app/library/OAuth/UserData/LinkedIn.php @@ -2,78 +2,18 @@ namespace Httpcb\OAuth\UserData; -class LinkedIn implements UserDataInterface +class LinkedIn extends UserData { - protected $data; + protected $_provider = 'LinkedIn'; /** * {@inheritDoc} */ public function __construct(array $data) { - $this->data = $data; - } - - /** - * {@inheritDoc} - */ - public function getProvider() - { - return 'LinkedIn'; - } - - /** - * {@inheritDoc} - */ - public function getId() - { - return $this->data['id']; - } - - /** - * {@inheritDoc} - */ - public function getUsername() - { - return null; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - $name = ''; - if ($this->getFirstname() !== null) { - $name = $this->getFirstname(); - } - if ($this->getLastname() !== null) { - $name .= ' ' . $this->getLastname(); - } - return $name; - } - - /** - * {@inheritDoc} - */ - public function getFirstname() - { - return isset($this->data['firstName']) ? $this->data['firstName'] : null; - } - - /** - * {@inheritDoc} - */ - public function getLastname() - { - return isset($this->data['lastName']) ? $this->data['lastName'] : null; - } - - /** - * {@inheritDoc} - */ - public function getEmail() - { - return $this->data['emailAddress']; + $this->_id = $data['id']; + $this->_firstname = isset($data['firstName']) ? $data['firstName'] : null; + $this->_lastname = isset($data['lastName']) ? $data['lastName'] : null; + $this->_email = $data['emailAddress']; } } From a337ccd4fad45ef30f2f837c1508d2f91cced130 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 22:33:39 +0200 Subject: [PATCH 12/26] app/library/OAuth/UserData/UserDataInterface.php: add toArray() --- app/library/OAuth/UserData/UserDataInterface.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/library/OAuth/UserData/UserDataInterface.php b/app/library/OAuth/UserData/UserDataInterface.php index 456b72d..358cafb 100644 --- a/app/library/OAuth/UserData/UserDataInterface.php +++ b/app/library/OAuth/UserData/UserDataInterface.php @@ -54,4 +54,9 @@ interface UserDataInterface * @return string */ public function getEmail(); + + /** + * @return array + */ + public function toArray(); } From 7b2f53e4dcca16d90bbd1fbaa6a834b91f589dc4 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 22:53:24 +0200 Subject: [PATCH 13/26] app/controllers/AuthController.php: add registerAction() --- app/controllers/AuthController.php | 35 ++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index ec97e5f..8537e12 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -2,9 +2,12 @@ namespace App\Controller; -use App\Controller\ControllerBase; +use App\Controller\ControllerBase, + App\Model\Data\User, + App\Form\Login as LoginForm, + App\Form\Registration as RegistrationForm; -use App\Form\Login as LoginForm; +use Httpcb\OAuth\UserData\UserDataInterface; class AuthController extends ControllerBase { @@ -106,6 +109,34 @@ class AuthController extends ControllerBase } } + public function registerAction() + { + $data = $this->session->get('auth:register:data'); + if (!($data instanceof UserDataInterface)) { + $this->response->redirect('/'); + return; + } + + $form = new RegistrationForm($data); + + if ($this->request->isPost()) { + $user = new User(); + $formData = $this->request->getPost(); + if ($form->isValid($formData, $user) && $user->save()) { + $this->auth->systemLogin($user); + $this->flash->success('User successfully created. Now add your first callback!'); + $this->response->redirect('/callback/new'); + return; + } + $form->setEntity($formData); + } else { + $form->isValid($data->toArray()); + } + + $this->view->provider = $data->getProvider(); + $this->view->form = $form; + } + public function logoutAction() { $this->auth->clearIdentity(); From 2529d29d807e2421531f2ab9245a8a9acd7c64fc Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 22:54:13 +0200 Subject: [PATCH 14/26] app/controllers/AuthController.php: in oauthAction() redirect to registration page if no user is found. --- app/controllers/AuthController.php | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 8537e12..51fe187 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -81,17 +81,21 @@ class AuthController extends ControllerBase else { $result = $this->auth->loginOauth($data); - // There was an error when creating the account - if (is_array($result)) { - $msg = ''; - foreach ($result as $message) { - $msg .= '
  • ' . $message->getMessage() . '
  • '; + if ($result === false) { + + if (User::findFirstByUsernameOrEmail($data->getEmail())) { + $this->flash->error('The email address is already in use.'); + $this->response->redirect('/login'); + return; } - $this->flash->message('error', "Failed to create account:
      {$msg}
    "); - $this->response->redirect('/login'); - } else { - $this->response->redirect('/'); + + $this->session->set('auth:register:data', $data); + $this->response->redirect(['for' => 'user-register']); + return; } + + // User is logged in. + $this->response->redirect('/'); } } catch(\Exception $e) { $this->flash->message('error', 'Failed to authenticate.'); From ec5c6f2d14f93ee98d5a62aef8453ae43301bc3c Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Tue, 14 Aug 2018 23:17:34 +0200 Subject: [PATCH 15/26] app/models/Data/User.php: adding findFirstByEmail() --- app/models/Data/User.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index df02c81..bd57cc0 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -358,6 +358,14 @@ class User extends Model ]); } + static public function findFirstByEmail($email) + { + return self::findFirst([ + "email = :email: AND status = :s:", + "bind" => [ 'email' => $email, 's' => self::STATUS_ACTIVE ] + ]); + } + static public function findFirstByOAuthID(UserDataInterface $oauth) { $column = strtolower($oauth->getProvider()); From aced9f8bf3717368c557a47dabd37e3d5340901a Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:40:26 +0200 Subject: [PATCH 16/26] app/models/Data/User.php: in findFirstByEmail() only fetch rows with Status != deleted. --- app/models/Data/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index bd57cc0..5efe39b 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -361,8 +361,8 @@ class User extends Model static public function findFirstByEmail($email) { return self::findFirst([ - "email = :email: AND status = :s:", - "bind" => [ 'email' => $email, 's' => self::STATUS_ACTIVE ] + "email = :email: AND status != :s:", + "bind" => [ 'email' => $email, 's' => self::STATUS_DELETED ] ]); } From 6876cc13be41b260cfde38f374b52c09f3b2cdde Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:41:05 +0200 Subject: [PATCH 17/26] app/models/Data/User.php: adding findFirstByUsername() --- app/models/Data/User.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 5efe39b..86a8707 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -366,6 +366,14 @@ class User extends Model ]); } + static public function findFirstByUsername($username) + { + return self::findFirst([ + "username = :username: AND status != :s:", + "bind" => [ 'username' => $username, 's' => self::STATUS_DELETED ] + ]); + } + static public function findFirstByOAuthID(UserDataInterface $oauth) { $column = strtolower($oauth->getProvider()); From 0bf7c545395cad064abd1e50e35412d0d4101cd4 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:42:43 +0200 Subject: [PATCH 18/26] app/models/Data/User.php: change validation from Uniqueness to Callback and check findFirstByUsername() and findFirstByEmail() --- app/models/Data/User.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 86a8707..1135372 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -4,7 +4,7 @@ namespace App\Model\Data; use Phalcon\Mvc\Model, Phalcon\Validation, - Phalcon\Validation\Validator\Uniqueness, + Phalcon\Validation\Validator\Callback as CallbackValidator, InvalidArgumentException, Httpcb\OAuth\UserData\UserDataInterface; @@ -52,8 +52,14 @@ class User extends Model // Validation $validator = new Validation(); - $validator->add('username', new Uniqueness(['message' => 'The username already exists.'])); - $validator->add('email', new Uniqueness(['message' => 'The email address already exists.'])); + $validator->add('username', new CallbackValidator([ + 'callback' => function() { return $this->findFirstByUsername($this->getUsername()) === false; }, + 'message' => 'The username already exists.' + ])); + $validator->add('email', new CallbackValidator([ + 'callback' => function() { return $this->findFirstByEmail($this->getEmail()) === false; }, + 'message' => 'The email address already exists.' + ])); return $this->validate($validator); } From 9bd0ef86fc671bc99f0d3629bb6c621309a35983 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:48:29 +0200 Subject: [PATCH 19/26] app/models/Data/User.php: in findFirstByOAuthID() also only fetch rows where status != deleted --- app/models/Data/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 1135372..46c6ef8 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -385,8 +385,8 @@ class User extends Model $column = strtolower($oauth->getProvider()); return self::findFirst([ - "{$column}_id = :id:", - "bind" => [ 'id' => $oauth->getId() ] + "{$column}_id = :id: AND status != :s:", + "bind" => [ 'id' => $oauth->getId(), 's' => self::STATUS_DELETED ] ]); } From c71416908a9bcd03289f53d4693cc58ffd020bbf Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:56:54 +0200 Subject: [PATCH 20/26] app/library/Auth.php: in loginOauth() no need to set status active anymore User::findFirstByOAuth() do not return deleted users. --- app/library/Auth.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/library/Auth.php b/app/library/Auth.php index f319c3c..5adfc8c 100644 --- a/app/library/Auth.php +++ b/app/library/Auth.php @@ -55,14 +55,6 @@ class Auth extends Component return false; } - // Here we activate the user. - // As for OAuth we perform registration if the user does not exist. - // We should therefore activate deleted accounts. - if ($user->Status == User::STATUS_DELETED) { - $user->Status = User::STATUS_ACTIVE; - $user->save(); - } - $this->setIdentity($user->getId()); $this->eventsManager->fire('auth:onLogin', $this, From 8237a71f52b353eac54c7e68840388ed38f0136f Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 21:59:21 +0200 Subject: [PATCH 21/26] app/forms/Registration.php: change Uniqueness validator to Callback + cleanup username validators abit. --- app/forms/Registration.php | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/app/forms/Registration.php b/app/forms/Registration.php index c95adff..785bee1 100644 --- a/app/forms/Registration.php +++ b/app/forms/Registration.php @@ -28,7 +28,6 @@ use Phalcon\Forms\Element\Text, * Validators */ use Phalcon\Validation\Validator\Callback as CallbackValidator, - Phalcon\Validation\Validator\Uniqueness as UniquenessValidator, Phalcon\Validation\Validator\Alnum as AlnumValidator, Phalcon\Validation\Validator\Email as EmailValidator, Phalcon\Validation\Validator\StringLength as StringLengthValidator, @@ -50,15 +49,20 @@ class Registration extends FormBase $username->setLabel('Username'); - $username->addValidator(new AlnumValidator()); - - $validator = new UniquenessValidator(array( - 'model' => new UserModel(), - 'message' => 'The username already exists.', - 'attribute' => 'username', - )); - - $username->addValidator($validator); + $username->addValidators([ + new AlnumValidator([ + 'message' => 'Username must contain only letters and numbers.' + ]), + new StringLengthValidator([ + 'min' => 2, + 'messageMinimum' => 'Username must be at least :min characters long.', + ]), + new CallbackValidator([ + 'callback' => function($data) { return User::findFirstByUsername($data['username']) === false; }, + 'message' => 'The username already exists.', + 'attribute' => 'username', + ]) + ]); $this->add($username); @@ -90,10 +94,9 @@ class Registration extends FormBase new IdenticalValidator([ 'accepted' => $this->getEntity()->getEmail(), ]), - new UniquenessValidator([ - 'model' => new UserModel(), + new CallbackValidator([ + 'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; }, 'message' => 'This email already exist.', - 'attribute' => 'email', ]) ]); From e072ae686ddc8f0eb60d69b82f14a7204db7df41 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 22:03:20 +0200 Subject: [PATCH 22/26] app/forms/Registration.php: cleanup, get rid of bind() --- app/forms/Registration.php | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/app/forms/Registration.php b/app/forms/Registration.php index 785bee1..418402e 100644 --- a/app/forms/Registration.php +++ b/app/forms/Registration.php @@ -5,21 +5,17 @@ namespace App\Form; /** * Models */ -use App\Model\Data\User as UserModel; +use App\Model\Data\User; /** * Phalcon Form */ - -use App\Model\Data\User; use Httpcb\Form as FormBase, Phalcon\Forms\Element as FormElement; /** * Element types */ - -use Httpcb\OAuth\UserData\UserDataInterface; use Phalcon\Forms\Element\Text, Phalcon\Forms\Element\Password, Phalcon\Forms\Element\Submit; @@ -27,7 +23,8 @@ use Phalcon\Forms\Element\Text, /** * Validators */ -use Phalcon\Validation\Validator\Callback as CallbackValidator, +use Phalcon\Validation, + Phalcon\Validation\Validator\Callback as CallbackValidator, Phalcon\Validation\Validator\Alnum as AlnumValidator, Phalcon\Validation\Validator\Email as EmailValidator, Phalcon\Validation\Validator\StringLength as StringLengthValidator, @@ -39,7 +36,7 @@ class Registration extends FormBase { public function initialize() { - $this->setValidation(new \Phalcon\Validation()); + $this->setValidation(new Validation()); // Username $username = new Text('username', array( @@ -107,15 +104,4 @@ class Registration extends FormBase $submit = new Submit('submit', array('class' => 'button button-success', 'value' => 'Register')); $this->add($submit); } - - public function bind(array $data, $entity, $whitelist = null) - { - parent::bind($data, $entity, $whitelist); - - if ($entity instanceof User && $this->getEntity() instanceof UserDataInterface) { - $provider = $this->getEntity()->getProvider(); - $id = $this->getEntity()->getId(); - $entity->setOAuthId($provider, $id); - } - } } From c75b24d2a979dbbf7ff409d8abdf4e329ee09d38 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 22:04:46 +0200 Subject: [PATCH 23/26] app/forms/Registration.php: add User as entity parameter to initialize() --- app/forms/Registration.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/forms/Registration.php b/app/forms/Registration.php index 418402e..f6d5160 100644 --- a/app/forms/Registration.php +++ b/app/forms/Registration.php @@ -34,7 +34,7 @@ use Phalcon\Validation, class Registration extends FormBase { - public function initialize() + public function initialize(User $user) { $this->setValidation(new Validation()); @@ -89,7 +89,7 @@ class Registration extends FormBase $email->addValidators([ new IdenticalValidator([ - 'accepted' => $this->getEntity()->getEmail(), + 'accepted' => $user->getEmail(), ]), new CallbackValidator([ 'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; }, From 82fb8690427fc45e460e6c64c98796c50c30431e Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Wed, 15 Aug 2018 22:07:19 +0200 Subject: [PATCH 24/26] app/controllers/AuthController.php: fixing registerAction() --- app/controllers/AuthController.php | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 51fe187..310e9de 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -121,16 +121,24 @@ class AuthController extends ControllerBase return; } - $form = new RegistrationForm($data); + $user = new User(); + $user->assign($data->toArray(), null, + [ 'email', 'username', 'firstname', 'lastname' ]); + + $form = new RegistrationForm($user); if ($this->request->isPost()) { - $user = new User(); + $formData = $this->request->getPost(); - if ($form->isValid($formData, $user) && $user->save()) { - $this->auth->systemLogin($user); - $this->flash->success('User successfully created. Now add your first callback!'); - $this->response->redirect('/callback/new'); - return; + if ($form->isValid($formData)) { + $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'); + } else { + $this->flash->error('Could not create user'); + } } $form->setEntity($formData); } else { From 1e46302267d98f042466943b614b2f0d5db196ad Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 16 Aug 2018 18:59:21 +0200 Subject: [PATCH 25/26] app/models/Data/User.php: findFirstByUsernameOrEmail() should only fetch rows where status != deleted --- app/models/Data/User.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/Data/User.php b/app/models/Data/User.php index 46c6ef8..d813e8b 100644 --- a/app/models/Data/User.php +++ b/app/models/Data/User.php @@ -359,8 +359,8 @@ class User extends Model static public function findFirstByUsernameOrEmail($value) { return self::findFirst([ - "(email = :v: OR username = :v:) AND status = :s:", - "bind" => [ 'v' => $value, 's' => self::STATUS_ACTIVE ] + "(email = :v: OR username = :v:) AND status != :s:", + "bind" => [ 'v' => $value, 's' => self::STATUS_DELETED ] ]); } From 1d1c1c29ce66ab2b0191d0185e08f332cb3b5558 Mon Sep 17 00:00:00 2001 From: Henrik Hautakoski Date: Thu, 16 Aug 2018 19:00:01 +0200 Subject: [PATCH 26/26] app/controllers/AuthController.php: in oauthAction() use User::findFirstByEmail() --- app/controllers/AuthController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/AuthController.php b/app/controllers/AuthController.php index 310e9de..012185b 100644 --- a/app/controllers/AuthController.php +++ b/app/controllers/AuthController.php @@ -83,7 +83,7 @@ class AuthController extends ControllerBase if ($result === false) { - if (User::findFirstByUsernameOrEmail($data->getEmail())) { + if (User::findFirstByEmail($data->getEmail())) { $this->flash->error('The email address is already in use.'); $this->response->redirect('/login'); return;