This example is about exentending the HelloWorld-example with a simple Login-Check for which we modify the existing controller and add two RequestHandlers and a new service.
In case the credentials provided are valid "Hello '< username >' will be shown, otherwise we ask for credentials.
Let's have a look at the source code first and then look at the other files.
The src-folder gets three new files and we adjust the HelloController.php-file.
HelloController.php
We adjust the namedAction-method to use 'World' as default value for the $person argument and also overwrite the $person-value with the 'authenticated'-value from the request.
public function namedAction($person = 'World')
{
$request = $this->get('request');
if ($request->vars()->get('authenticated')) {
$person = $request->vars()->get('authenticated');
}
$person = '\''.urldecode($person).'\'';
$model = [
'person' => $person
];
return $this->renderHtml('index.twig', $model);
}
Since 'authenticated' isn't a value that is normally stored in a request, we add a LoginRequestHandler which will set it in case the authentification was a success.
Note that we look for value inside the 'vars'-array of the request.
LoginRequestHandler.php:
A simple RequestHandlerInterface implementation which:
LoginValidator-class to decide whether the credentials are valid.<?php
namespace Graphit\Examples;
class LoginRequestHandler implements \Graphit\Core\RequestHandlerInterface
{
/** @var Kernel */
protected $kernel;
public function __construct(\Graphit\Core\Kernel $kernel)
{
$this->kernel = $kernel;
}
protected function requestCredentials($request)
{
$hostname = $request->getHost();
$headers = [
'WWW-Authenticate' => ["Basic realm=\"{$hostname}\""]
];
return new \Graphit\Core\Response ('', 401, $headers);
}
public function handleRequest(\Graphit\Core\Request $request)
{
$username = $request->getUser();
$password = $request->getPassword();
$validator = new LoginValidator($this->kernel);
if ($validator->validate($username, $password)) {
$request->vars()->set('authenticated', $username);
} else {
return $this->requestCredentials($request);
}
}
}
Since thew LoginValidator we use to validate the credentials needs to be added, we also add a new LoginValidator.php-file.
LoginValidator.php: A simple class which only offers one method validate($username, $password) which returns a boolean whether the username&password-combination is valid or not.
<?php
namespace Graphit\Examples;
class LoginValidator
{
/** @var Kernel */
protected $kernel;
public function __construct(\Graphit\Core\Kernel $kernel)
{
$this->kernel = $kernel;
}
public function validate($username = false, $password = false)
{
return isset($this->kernel['authentication'][$username])
&& $this->kernel['authentication'][$username] == $password;
}
}
Important: This is not a proper way to handle passwords! Passwords should only be saved as hashs. Also they should not be hardcoded but looked up from a trusworthy source not a config file.
So far so good, but since the 'LoginRequestHandler' doesn't return a respons in case the authentication was a success, we need an additional RequestHandler.
The HelloRequestHandler is the DefaultRequestHandler which simple adds the LoginRequestHandler before the ControllerRequestHandler. So when the LoginRequestHandler returns a response to request the user credentials the ControllerRequestHandler will not be called, but when it returns null the ControllerRequestHandler will be called.
<?php
namespace Graphit\Examples;
class HelloRequestHandler implements \Graphit\Core\RequestHandlerInterface
{
/** @var Kernel */
protected $kernel;
/** @var RequestHandlerInterface */
protected $handler;
public function __construct(\Graphit\Core\Kernel $kernel)
{
$this->kernel = $kernel;
$this->handler = new \Graphit\Core\ChainRequestHandler();
$default = $kernel['defaultLocale'] ?: 'de';
$possible = $kernel['possibleLocales'] ?: ['de'];
$handler = new \Graphit\Core\LocaleRequestHandler($default, $possible);
$this->handler->addRequestHandler($handler);
$router = $kernel['router'];
$handler = new \Graphit\Core\RouterRequestHandler($router);
$this->handler->addRequestHandler($handler);
$handler = new \Graphit\Core\PossibleLocaleRequestHandler();
$this->handler->addRequestHandler($handler);
$handler = new LoginRequestHandler($kernel);
$this->handler->addRequestHandler($handler);
$handler = new \Graphit\Core\ControllerRequestHandler($kernel);
$this->handler->addRequestHandler($handler);
}
public function handleRequest(\Graphit\Core\Request $request)
{
return $this->handler->handleRequest($request);
}
}
The composer.json stays the same as in HelloWorld example..
The app.json stays the same as in the HelloWorld example.
In the routes.json, we throw out the "index"-route and just keep the "named"-route.
{
"routes": {
"named": {
"pattern": "/{person}",
"default": {
"_controller": "Graphit\\Examples\\Hello:named"
},
"requirements": {
"_method": "GET"
}
}
}
}
The services.json got two new entries one for the LoginRequestHandler-class anmd one for the HelloRequestHandler-class.
{
"authentication": {
"username": "password",
"test": "lalala"
},
"router": {
"type": "service",
"class": "Graphit\\Core\\Router",
"config": [
{ "method": "addRoutes",
"args": [
{ "type": "service", "name": "routes" }
]
}
]
},
"twig": {
"type": "service",
"class": "Graphit\\Core\\Twig"
},
"LoginRequestHandler": {
"type": "service",
"class": "Graphit\\Examples\\LoginRequestHandler",
"args": [
{ "type": "service", "name": "kernel" }
]
},
"requestHandler": {
"type": "service",
"class": "Graphit\\Examples\\HelloRequestHandler",
"args": [
{ "type": "service", "name": "kernel" }
]
}
}
Note that:
LoginRequestHandler constructor is called with the current Kernel.