diff --git a/composer.json b/composer.json index 6fa0befb..cb08442d 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ "slim/twig-view": "^2.2", "techwilk/twig-linewrap": "^1.0", "techwilk/twig-slim-csrf": "^1.0", - "twig/extensions": "^1.4" + "twig/extensions": "^1.4", + "divineomega/password_exposed": "^2.3" }, "autoload": { "psr-4": { @@ -65,4 +66,4 @@ "Tests\\": "tests/" } } -} \ No newline at end of file +} diff --git a/composer.lock b/composer.lock index be2de832..63d46284 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "98159bdec8cf6f714563e667d20b8cac", + "content-hash": "ab8c79f8ff58bbafa0f6f6d7840e1e05", "packages": [ { "name": "aptoma/twig-markdown", @@ -146,6 +146,51 @@ "homepage": "https://github.com/container-interop/container-interop", "time": "2017-02-14T19:40:03+00:00" }, + { + "name": "divineomega/password_exposed", + "version": "v2.3.3", + "source": { + "type": "git", + "url": "https://github.com/DivineOmega/password_exposed.git", + "reference": "13d314d4b886fb3b9690b2863a25ac433499cda2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DivineOmega/password_exposed/zipball/13d314d4b886fb3b9690b2863a25ac433499cda2", + "reference": "13d314d4b886fb3b9690b2863a25ac433499cda2", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.3", + "rapidwebltd/rw-file-cache-psr-6": "^1.0" + }, + "require-dev": { + "fzaninotto/faker": "^1.7", + "phpunit/phpunit": "^5.7", + "satooshi/php-coveralls": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "DivineOmega\\PasswordExposed\\": "src/" + }, + "files": [ + "src/PasswordExposedFunction.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-only" + ], + "authors": [ + { + "name": "Jordan Hall", + "email": "jordan@hall05.co.uk" + } + ], + "description": "This PHP package provides a `password_exposed` helper function, that uses the haveibeenpwned.com API to check if a password has been exposed in a data breach.", + "time": "2018-03-03T14:41:20+00:00" + }, { "name": "dpolac/dictionary", "version": "v1.0.0", @@ -1465,7 +1510,53 @@ "orm", "persistence" ], - "time": "2018-02-19T13:36:42+00:00" + "time": "2018-02-19 13:36:42" + }, + { + "name": "psr/cache", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8", + "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "time": "2016-08-06T20:24:11+00:00" }, { "name": "psr/container", @@ -1613,6 +1704,94 @@ ], "time": "2016-10-10T12:19:37+00:00" }, + { + "name": "rapidwebltd/rw-file-cache", + "version": "v1.2.5", + "source": { + "type": "git", + "url": "https://github.com/rapidwebltd/RW-File-Cache.git", + "reference": "4a1d5aaefa6ffafec8e2d60787f12bcd9890977e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rapidwebltd/RW-File-Cache/zipball/4a1d5aaefa6ffafec8e2d60787f12bcd9890977e", + "reference": "4a1d5aaefa6ffafec8e2d60787f12bcd9890977e", + "shasum": "" + }, + "require": { + "php": ">=5.2.1" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "rapidweb\\RWFileCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-only" + ], + "description": "RW File Cache is a PHP File-based Caching Library. Its syntax is designed to closely resemble the PHP memcache extension.", + "homepage": "https://github.com/rapidwebltd/RW-File-Cache", + "keywords": [ + "cache", + "caching", + "caching library", + "file cache", + "library", + "php" + ], + "time": "2018-01-23T17:20:58+00:00" + }, + { + "name": "rapidwebltd/rw-file-cache-psr-6", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/rapidwebltd/RW-File-Cache-PSR-6.git", + "reference": "b74ea201d4c964f0e6db0fb036d1ab28a570df66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rapidwebltd/RW-File-Cache-PSR-6/zipball/b74ea201d4c964f0e6db0fb036d1ab28a570df66", + "reference": "b74ea201d4c964f0e6db0fb036d1ab28a570df66", + "shasum": "" + }, + "require": { + "psr/cache": "^1.0", + "rapidwebltd/rw-file-cache": "^1.2.3" + }, + "require-dev": { + "cache/integration-tests": "^0.16.0", + "phpunit/phpunit": "^5.7" + }, + "type": "library", + "autoload": { + "psr-4": { + "rapidweb\\RWFileCachePSR6\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-only" + ], + "authors": [ + { + "name": "Jordan Hall", + "email": "jordan.hall@rapidweb.biz" + } + ], + "description": "PSR-6 adapter for RW File Cache", + "time": "2018-01-30T19:13:45+00:00" + }, { "name": "slim/csrf", "version": "0.8.2", diff --git a/generated-classes/TechWilk/Rota/User.php b/generated-classes/TechWilk/Rota/User.php index 8ec997a8..7542af46 100644 --- a/generated-classes/TechWilk/Rota/User.php +++ b/generated-classes/TechWilk/Rota/User.php @@ -3,6 +3,9 @@ namespace TechWilk\Rota; use DateTime; +use DivineOmega\PasswordExposed\PasswordExposedChecker; +use DivineOmega\PasswordExposed\PasswordStatus; +use Exception; use Propel\Runtime\ActiveQuery\Criteria; use TechWilk\Rota\Authoriser\UserAuthoriser; use TechWilk\Rota\Base\User as BaseUser; @@ -68,10 +71,16 @@ public function setPassword($v) $v = (string) $v; } + $exposedChecker = new PasswordExposedChecker(); + + if ($exposedChecker->passwordExposed($v) === PasswordStatus::EXPOSED) { + throw new Exception('Password exposed in recent data breach.'); + } + if (!password_verify($v, $this->password)) { $bcrypt_options = [ - 'cost' => 12, - ]; + 'cost' => 12, + ]; $this->password = password_hash($v, PASSWORD_BCRYPT, $bcrypt_options); $this->modifiedColumns[UserTableMap::COL_PASSWORD] = true; diff --git a/src/classes/Controller/UserController.php b/src/classes/Controller/UserController.php index d24aacce..fc0dc502 100644 --- a/src/classes/Controller/UserController.php +++ b/src/classes/Controller/UserController.php @@ -2,6 +2,7 @@ namespace TechWilk\Rota\Controller; +use Exception; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use TechWilk\Rota\Crypt; @@ -186,8 +187,14 @@ public function postUserPasswordChange(ServerRequestInterface $request, Response return $this->view->render($response, 'user-password.twig', ['user' => $u, 'message' => $message]); } - $u->setPassword($new); - $u->save(); + try { + $u->setPassword($new); + $u->save(); + } catch (Exception $e) { + $message = 'Password has been exposed in a recent data breach. For your safety, we check all passwords against data breaches from other organisations and prevent you from using passwords which have been leaked. If you use this password for any other account, we strongly advise you change it immediately.'; + + return $this->view->render($response, 'user-password.twig', ['user' => $u, 'message' => $message]); + } return $response->withStatus(303)->withHeader('Location', $this->router->pathFor('user', ['id' => $u->getId()])); }