Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
0.00%
0 / 16
CRAP
0.00%
0 / 148
Auth
0.00%
0 / 1
0.00%
0 / 16
420
0.00%
0 / 148
 __construct(Application $app)
0.00%
0 / 1
2
0.00%
0 / 5
 assignRouteClass($route, $className)
0.00%
0 / 1
2
0.00%
0 / 5
 forgotPassword(UserRowGateway $user)
0.00%
0 / 1
6
0.00%
0 / 32
 setTitle($title)
0.00%
0 / 1
2
0.00%
0 / 5
 getTitle()
0.00%
0 / 1
2
0.00%
0 / 4
 setHeaderHtml($headerHtml)
0.00%
0 / 1
2
0.00%
0 / 5
 getHeaderHtml()
0.00%
0 / 1
2
0.00%
0 / 4
 setLayoutScriptPath($layoutScriptPath)
0.00%
0 / 1
2
0.00%
0 / 5
 getLayoutScriptPath()
0.00%
0 / 1
2
0.00%
0 / 4
 setLayoutScript($layoutScript)
0.00%
0 / 1
2
0.00%
0 / 5
 getLayoutScript()
0.00%
0 / 1
2
0.00%
0 / 4
 getUser()
0.00%
0 / 1
6
0.00%
0 / 9
 hasUser()
0.00%
0 / 1
2
0.00%
0 / 4
 init()
0.00%
0 / 1
12
0.00%
0 / 32
 anonymous function ()
0.00%
0 / 1
2
0.00%
0 / 3
 getSecurityFirewallsConfig()
0.00%
0 / 1
2
0.00%
0 / 19
<?php
/**
* Dewdrop
*
* @link https://github.com/DeltaSystems/dewdrop
* @copyright Delta Systems (http://deltasys.com)
* @license https://github.com/DeltaSystems/dewdrop/LICENSE
*/
namespace Dewdrop;
use Dewdrop\Auth\Db\UserPasswordChangeTokensTableGateway;
use Dewdrop\Auth\Db\UserRowGateway;
use Dewdrop\Mail\View\View as MailView;
use Silex\Application;
use Silex\Provider\RememberMeServiceProvider;
use Silex\Provider\SecurityServiceProvider;
use Silex\Provider\SwiftmailerServiceProvider;
use Swift_Message;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
/**
* This class provides authentication and authorization services for Dewdrop applications outside of WordPress.
*
* First, this component needs to be made available within the Silex application object. A good place to do this is the
* application bootstrap:
*
* <pre>
* $application['auth'] = $application->share(
* function (\Silex\Application $app) {
* $auth = new Auth($app);
* $auth->setTitle('My Project');
* return $auth;
* }
* );
* </pre>
*
* \Dewdrop\Bootstrap\Detector creates a 'users-gateway' service, an instance of \Dewdrop\Db\Table\UsersTableGateway.
* This requires database schema similar to the following:
*
* <pre>
* CREATE TABLE security_levels (
* security_level_id SERIAL PRIMARY KEY,
* name VARCHAR(64) NOT NULL UNIQUE
* );
*
* CREATE TABLE users (
* user_id SERIAL PRIMARY KEY,
* security_level_id INTEGER NOT NULL REFERENCES security_levels,
* username VARCHAR(64) NOT NULL UNIQUE,
* first_name VARCHAR(128) NOT NULL,
* last_name VARCHAR(128) NOT NULL,
* email_address VARCHAR(512) NOT NULL UNIQUE,
* password_hash VARCHAR(60) NOT NULL
* );
*
* CREATE TABLE user_password_change_tokens (
* user_password_change_token_id SERIAL PRIMARY KEY,
* user_id INTEGER NOT NULL REFERENCES users,
* token VARCHAR(60) NOT NULL UNIQUE,
* date_created TIMESTAMP NOT NULL DEFAULT NOW()
* used BOOLEAN NOT NULL DEFAULT FALSE
* );
* </pre>
*
* Of course additional tables and columns are permissible, but those above are required. You can extend the user table
* and user row classes, \Dewdrop\Auth\Db\UsersTableGateway and \Dewdrop\Auth\Db\UserRowGateway if necessary.
*
* You can easily generate a password hash for development and testing purposes using a Dewdrop CLI command:
*
* <pre>
* $ ./vendor/bin/dewdrop auth-hash-password --plaintext <password>
* </pre>
*
* Next, the component needs to be initialized early in the application lifecycle. Notice how in the following example
* the Dewdrop user management component is also registered with the admin component:
*
* <pre>
* $zend = realpath(__DIR__ . '/../zend');
*
* if (file_exists($zend)) {
* define('PROJECT_ROOT', $zend);
* } else {
* define('PROJECT_ROOT', realpath(__DIR__ . '/../'));
* }
*
* require_once PROJECT_ROOT . '/vendor/autoload.php';
*
* $silex = \Dewdrop\Pimple::getInstance();
*
* $silex['auth']->init();
*
* $silex['admin']->registerComponentsInPath();
*
* $silex['admin']->registerComponent(new \Dewdrop\Admin\Component\Stock\Users\Component());
*
* $silex->run();
* </pre>
*/
class Auth
{
/**
* Silex application
*
* @var Application
*/
protected $app;
/**
* Route class map
*
* @var array
*/
protected $routeClassMap = array(
'/auth/login' => '\Dewdrop\Auth\Page\Login',
'/auth/forgot-password' => '\Dewdrop\Auth\Page\ForgotPassword',
'/auth/reset-password' => '\Dewdrop\Auth\Page\ResetPassword',
);
/**
* Title. This will be escaped and used in the title tag.
*
* @var string
*/
protected $title = 'Welcome';
/**
* Optionally specify some HTML to use in the header of the panel
* on each page (e.g. a logo image). If not specified, then the
* title text will be used.
*
* @var string
*/
protected $headerHtml;
/**
* Layout script path
*
* @var string
*/
protected $layoutScriptPath;
/**
* Layout script filename
*
* @var string
*/
protected $layoutScript = 'layout.phtml';
/**
* Constructor
*
* @param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
$this->layoutScriptPath = __DIR__ . '/Auth/Page/view-scripts';
}
/**
* Allow over-riding of default page classes for auth routes. Makes it
* possible to do custom pages, rather than being stuck with the Dewdrop
* defaults.
*
* @param string $route
* @param string $className
* @return Auth
*/
public function assignRouteClass($route, $className)
{
$this->routeClassMap[$route] = $className;
return $this;
}
/**
* Generate a password change token and send the given user an email with a link to reset password
*
* @param UserRowGateway $user
* @return Auth
*/
public function forgotPassword(UserRowGateway $user)
{
$token = password_hash(openssl_random_pseudo_bytes(64), PASSWORD_BCRYPT);
$tokenTableDataGateway = new UserPasswordChangeTokensTableGateway();
$tokenTableDataGateway->insert([
'user_id' => $user->getId(),
'token' => $token,
]);
if ('80' !== $_SERVER['SERVER_PORT']) {
$port = ":{$_SERVER['SERVER_PORT']}";
} else {
$port = '';
}
$resetPasswordUrl = "http://{$_SERVER['SERVER_NAME']}{$port}/auth/reset-password?token=" .
rawurlencode($token);
$mailView = new MailView();
$mailView
->assign('resetPasswordUrl', $resetPasswordUrl)
->assign('title', $this->title)
->setScriptPath(__DIR__ . '/Auth/view-scripts');
$bodyHtml = $mailView->render('forgot-password-email-html.phtml');
$bodyText = $mailView->render('forgot-password-email-text.phtml');
$message = Swift_Message::newInstance();
$message
->setSubject('Reset Password')
->setFrom("noreply@{$_SERVER['SERVER_NAME']}")
->setTo($user->getEmailAddress());
$message->setBody($bodyHtml, 'text/html');
$message->addPart($bodyText, 'text/plain');
$this->app['mailer']->send($message);
return $this;
}
/**
* Set title
*
* @param string $title
* @return Auth
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* HTML to use on the header of the panel on each page.
*
* @param $headerHtml
* @return $this
*/
public function setHeaderHtml($headerHtml)
{
$this->headerHtml = $headerHtml;
return $this;
}
/**
* Get the HTML that should be used in the header of the panel on each page.
*
* @return string
*/
public function getHeaderHtml()
{
return $this->headerHtml;
}
/**
* Set layout script path
*
* @param string $layoutScriptPath
* @return Auth
*/
public function setLayoutScriptPath($layoutScriptPath)
{
$this->layoutScriptPath = $layoutScriptPath;
return $this;
}
/**
* Get layout script path
*
* @return string
*/
public function getLayoutScriptPath()
{
return $this->layoutScriptPath;
}
/**
* Set layout script
*
* @param string $layoutScript
* @return Auth
*/
public function setLayoutScript($layoutScript)
{
$this->layoutScript = $layoutScript;
return $this;
}
/**
* Get layout script
*
* @return string
*/
public function getLayoutScript()
{
return $this->layoutScript;
}
/**
* Get user
*
* @return UserRowGateway|null
*/
public function getUser()
{
$user = null;
$token = $this->app['security']->getToken();
if (null !== $token) {
$user = $token->getUser();
}
return $user;
}
/**
* Returns whether user is available
*
* @return bool
*/
public function hasUser()
{
return null !== $this->getUser();
}
/**
* Initializations
*
* @return Auth
*/
public function init()
{
$app = $this->app;
$app->register(new SecurityServiceProvider());
$app->register(new RememberMeServiceProvider());
$app->register(new SwiftmailerServiceProvider());
$app['security.firewalls'] = $this->getSecurityFirewallsConfig();
$app['security.encoder.digest'] = $app->share(
function () {
return new BCryptPasswordEncoder(6);
}
);
$app['user'] = $app->share(
function () {
$token = $this->app['security']->getToken();
if (null !== $token) {
return $token->getUser();
} else {
return null;
}
}
);
foreach ($this->routeClassMap as $route => $pageClassName) {
$app->match(
$route,
function (Request $request) use ($app, $pageClassName) {
$page = new $pageClassName($this, $app, $request);
return $page->respond();
}
);
}
return $this;
}
/**
* Get security firewalls configuration
*
* @return array
*/
protected function getSecurityFirewallsConfig()
{
return [
'^/auth/reset-password' => [
'pattern' => '^/auth/reset-password',
'anonymous' => true,
],
'^/admin/' => [
'pattern' => '^/admin/',
'form' => [
'login_path' => '/auth/login',
'check_path' => '/admin/login-check',
],
'logout' => [
'logout_path' => '/admin/logout',
],
'remember_me' => [
'key' => 'yj/5Hf#K#^{G.T*T>g0I+iXKFyy{%KM:DkRH~X6>dV"s|$1UhDEM(Uy5?-Pbp',
],
'users' => $this->app->share(function () {
return $this->app['users-gateway'];
}),
],
];
}
}