Archived
1
0
Fork 0

Merge branch '10-user-confirm-email-when-creating-new-password'

This commit is contained in:
Henrik Hautakoski 2018-06-12 23:51:52 +02:00
commit daa8803f5e
10 changed files with 418 additions and 6 deletions

View file

@ -4,6 +4,7 @@ application:
modelsDir : ../app/models/
migrationsDir : ../app/migrations/
viewsDir : ../app/views/
templateDir : ../app/templates/
listenersDir : ../app/listeners/
libraryDir : ../app/library/
formsDir : ../app/forms/

View file

@ -11,6 +11,9 @@ database:
dbname: httpcb
charset: utf8
#sendgrid
#key: value
# OAuth
#oauth:
#providers:

View file

@ -134,6 +134,11 @@ $di->setShared('router', function() {
'action' => 'settings',
))->setName('user-settings');
$router->add('/act/{link}', array(
'controller' => 'user',
'action' => 'activationlink',
))->setName('activation-link');
return $router;
});
@ -176,6 +181,25 @@ $di->setShared('view', function () use ($di, $config) {
return $view;
});
$di->set('template', function() use ($config) {
$view = new Phalcon\Mvc\View\Simple();
$view->setViewsDir($config->application->templateDir);
$view->registerEngines([
'.volt' => function ($view, $di) use ($config) {
$volt = new VoltEngine($view, $di);
$volt->setOptions(array(
'compiledPath' => $config->application->viewCacheDir,
'compiledSeparator' => '_',
));
return $volt;
},
'.phtml' => 'Phalcon\Mvc\View\Engine\Php'
]);
return $view;
});
$di->setShared('assets', function () {
$manager = new AssetsManager();
@ -222,6 +246,10 @@ $di->setShared('db', function () use ($di, $config) {
return $db;
});
$di->setShared('sendgrid', function() use ($config) {
return new SendGrid($config->sendgrid->key);
});
$di->setShared('eventsManager', function () {
$activityLog = new ActivityLog();

View file

@ -4,7 +4,9 @@ namespace App\Controller;
use App\Controller\ControllerBase,
App\Form\UserSettings as UserSettingsForm,
App\Model\Data\ActivityLog;
App\Model\Data\ActivityLog,
App\Model\Data\PasswordLink,
SendGrid\Mail\Mail as SendGridMail;
class UserController extends ControllerBase
{
@ -28,9 +30,42 @@ class UserController extends ControllerBase
$new_pw = $form->getValue('passwordNew');
if (strlen($new_pw) > 0) {
$hash = password_hash($new_pw, PASSWORD_BCRYPT);
$user->setPassword($hash);
// User had a password before. just update.
if (strlen($user->getPassword()) > 0) {
$user->setPassword($hash);
}
// Else we create a password link and email.
else {
$link = new PasswordLink();
$link->setUserId($user->getId())
->setPassword($hash)
->save();
$tpl = $this->di->get('template');
$body = $tpl->render('mail/password_activation', [
'link' => $link->getPublicId()
]);
$mail = new SendGridMail();
$mail->setFrom('noreply@shufflingpixels.com');
$mail->setSubject('Httpcb password activation');
$mail->addTo($user->getEmail());
$mail->addContent('text/html', $body);
$sendgrid = $this->di->get('sendgrid');
$sendgrid->send($mail);
$msg = "For security reasons. Before a password can be created "
. "a email has been sent to <strong>{$user->getEmail()}</strong> with "
. "a activation link.";
$this->flash->notice($msg);
}
}
$user->save();
$form->initialize();
@ -44,6 +79,35 @@ class UserController extends ControllerBase
$this->view->form = $form;
}
/**
* Activate a password.
*
* @param $id
*/
public function activationLinkAction($id)
{
$link = PasswordLink::findFirst(['public_id = ?0', 'bind' => [ $id ]]);
if ($link) {
if ($link->isValid()) {
// Save the password.
$link->getUser()
->setPassword($link->getPassword())
->save();
$this->flash->success('Your password has been activated.');
} else {
$this->flash->error('This link has expired or has already been used.');
}
// Make sure the link is deleted.
$link->delete();
} else {
$this->flash->error('This does not seem to be an active link');
}
}
public function activityAction($page = 1)
{
$user = $this->_getAuth()->getUser();

View file

@ -60,6 +60,12 @@ class RandomId extends Behavior implements BehaviorInterface
$field = $this->_options['field'];
$len = $this->_options['length'];
if (isset($this->_options['expression'])) {
$expr = 'AND ' . $this->_options['expression'];
} else {
$expr = '';
}
if ($model->$field === null) {
$random = new \Phalcon\Security\Random();
@ -67,7 +73,7 @@ class RandomId extends Behavior implements BehaviorInterface
$id = substr($random->base64Safe(), 0, $len);
$count = $model->count(array(
"$field = ?0",
"$field = ?0 $expr",
'bind' => array($id)
));

View file

@ -0,0 +1,18 @@
<?php
use Phinx\Migration\AbstractMigration;
class PasswordLink extends AbstractMigration
{
public function up()
{
$this->table('password_link')
->addColumn('public_id', 'string', ['length' => 12 ])
->addColumn('user_id', 'integer')
->addForeignKey('user_id', 'user', ['id'], [ 'constraint' => 'FK_password_link_user' ])
->addColumn('date', 'datetime', [ 'default' => 'CURRENT_TIMESTAMP' ])
->addColumn('password', 'string', [ 'limit' => 255, 'null' => true ])
->save();
}
}

View file

@ -0,0 +1,174 @@
<?php
namespace App\Model\Data;
use Httpcb\Mvc\Model\Behavior\RandomId as RandomIdBehavior;
use Phalcon\Forms\Element\Date;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
class PasswordLink extends Base
{
/**
* @var int
*/
protected $id;
/**
* @var string
*/
protected $public_id;
/**
* @var int
*/
protected $user_id;
/**
* @var string
*/
protected $password;
/**
* @var string
*/
protected $date;
/**
* Initialize method for model.
*/
public function initialize()
{
$this->setSource('password_link');
$this->useDynamicUpdate(true);
// Relationships
$this->belongsTo('user_id', User::class, 'id', array('alias' => 'User'));
// Behaviour
$this->addBehavior(new RandomIdBehavior(array(
'field' => 'public_id',
'length' => 12,
'expression' => '(password IS NULL OR HOUR(TIMEDIFF(date, NOW())) = 0)'
)));
$this->addBehavior(new SoftDelete([
'field' => 'password',
'value' => null
]));
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @param int $id
* @return PasswordLink
*/
public function setId(int $id) : PasswordLink
{
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getPublicId()
{
return $this->public_id;
}
/**
* @param string $public_id
* @return PasswordLink
*/
public function setPublicId(string $public_id) : PasswordLink
{
$this->public_id = $public_id;
return $this;
}
/**
* @return int
*/
public function getUserId()
{
return $this->user_id;
}
/**
* @param int $user_id
* @return PasswordLink
*/
public function setUserId(int $user_id) : PasswordLink
{
$this->user_id = $user_id;
return $this;
}
/**
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* @param string $password
* @return PasswordLink
*/
public function setPassword(string $password) : PasswordLink
{
$this->password = $password;
return $this;
}
/**
* Has the link been used?
*
* @return bool
*/
public function isUsed() : bool
{
return strlen($this->password) < 1;
}
/**
* Is the link still valid?
*
* @return bool
*/
public function isValid() : bool
{
// Used links are not valid.
if ($this->isUsed()) {
return false;
}
$date = new \DateTime($this->date);
$now = new \DateTime();
// Get the differerence in hours.
$diff = $now->format('U') - $date->format('U');
$diff = ($diff / 60) / 60;
// Only valid for an hour.
return $diff >= 0 && $diff <= 1;
}
public function beforeCreate()
{
// Creating a new link automatically removes old ones.
$links = self::find(["user_id = ?0", 'bind' => [ $this->user_id ]]);
foreach($links as $link) {
$link->delete();
}
}
}

View file

@ -0,0 +1,10 @@
{% set link_url = request.getScheme() ~ '://'
~ request.getHttpHost()
~ url(['for': 'activation-link', 'link': link ]) %}
<h1>Httcb Password Activation</h1>
<p>Please click <a href="{{ link_url }}">here</a> to activate the password.</p>
<p style="color:grey">Or copy and paste this link into the address field in your browser: <i>{{ link_url }}</i></p>

View file

@ -5,6 +5,7 @@
"league/oauth2-github": "^2.0",
"league/oauth2-google": "^2.2",
"omines/oauth2-gitlab": "^3.1",
"localheinz/json-printer": "^2.0"
"localheinz/json-printer": "^2.0",
"sendgrid/sendgrid": "^7.0"
}
}

111
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "5b02615c35e5c720f8fc96266a7f87a3",
"content-hash": "1e872347d68f7521be008e5a53d4b5e4",
"hash": "cb468603a22f2d265e74e0e1cb692ca1",
"content-hash": "f519b8b85514afd2d01426a06e3bea02",
"packages": [
{
"name": "guzzlehttp/guzzle",
@ -687,6 +687,113 @@
],
"time": "2017-12-23 06:48:51"
},
{
"name": "sendgrid/php-http-client",
"version": "3.9.6",
"source": {
"type": "git",
"url": "https://github.com/sendgrid/php-http-client.git",
"reference": "e9a04d949ee2d19938ab83dc100933a3b41a8ec7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sendgrid/php-http-client/zipball/e9a04d949ee2d19938ab83dc100933a3b41a8ec7",
"reference": "e9a04d949ee2d19938ab83dc100933a3b41a8ec7",
"shasum": ""
},
"require": {
"php": ">=5.6"
},
"require-dev": {
"phpunit/phpunit": "~4.4",
"squizlabs/php_codesniffer": "~2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"SendGrid\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Bernier",
"email": "dx@sendgrid.com"
},
{
"name": "Elmer Thomas",
"email": "elmer@thinkingserious.com"
}
],
"description": "HTTP REST client, simplified for PHP",
"homepage": "http://github.com/sendgrid/php-http-client",
"keywords": [
"api",
"fluent",
"http",
"rest",
"sendgrid"
],
"time": "2018-04-10 18:06:08"
},
{
"name": "sendgrid/sendgrid",
"version": "7.0.0",
"source": {
"type": "git",
"url": "https://github.com/sendgrid/sendgrid-php.git",
"reference": "b659d96f19f69bcef6807300f88ac7a1b2449328"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sendgrid/sendgrid-php/zipball/b659d96f19f69bcef6807300f88ac7a1b2449328",
"reference": "b659d96f19f69bcef6807300f88ac7a1b2449328",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": ">=5.6",
"sendgrid/php-http-client": "~3.9"
},
"replace": {
"sendgrid/sendgrid-php": "*"
},
"require-dev": {
"phpunit/phpunit": "^5.7.9 || ^6.4.3",
"squizlabs/php_codesniffer": "3.*",
"swaggest/json-diff": "^3.4"
},
"type": "library",
"autoload": {
"psr-4": {
"SendGrid\\": "lib/",
"SendGrid\\Mail\\": "lib/mail/",
"SendGrid\\Contacts\\": "lib/contacts/",
"SendGrid\\Stats\\": "lib/stats/"
},
"files": [
"lib/SendGrid.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "This library allows you to quickly and easily send emails through SendGrid using PHP.",
"homepage": "http://github.com/sendgrid/sendgrid-php",
"keywords": [
"email",
"grid",
"send",
"sendgrid"
],
"time": "2018-05-19 16:38:14"
},
{
"name": "symfony/config",
"version": "v3.4.6",