Merge branch '10-user-confirm-email-when-creating-new-password'
This commit is contained in:
commit
daa8803f5e
10 changed files with 418 additions and 6 deletions
|
|
@ -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/
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@ database:
|
|||
dbname: httpcb
|
||||
charset: utf8
|
||||
|
||||
#sendgrid
|
||||
#key: value
|
||||
|
||||
# OAuth
|
||||
#oauth:
|
||||
#providers:
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
));
|
||||
|
||||
|
|
|
|||
18
app/migrations/20180611202847_password_link.php
Normal file
18
app/migrations/20180611202847_password_link.php
Normal 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();
|
||||
}
|
||||
}
|
||||
174
app/models/Data/PasswordLink.php
Normal file
174
app/models/Data/PasswordLink.php
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
app/templates/mail/password_activation.volt
Normal file
10
app/templates/mail/password_activation.volt
Normal 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>
|
||||
|
|
@ -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
111
composer.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
Reference in a new issue