Commit 654735e5 authored by Vassilis Kanellopoulos's avatar Vassilis Kanellopoulos
Browse files

acl service, navigation with acl, identiy in twig

parent 620913ac
......@@ -26,11 +26,24 @@ $container['view'] = function ($c) {
$view->addExtension(new Knlv\Slim\Views\TwigMessages(
$c->get('flash')
));
$view->addExtension(new GrEduLabs\Twig\Extension\Identity(
$c->get('authentication_service')
));
if (isset($settings['navigation']) && is_array($settings['navigation'])) {
$authService = $c->get('authentication_service');
$role = $settings['acl']['default_role'];
if ($authService->hasIdentity()) {
$identity = $authService->getIdentity();
if ($identity instanceof GrEduLabs\Authorization\RoleAwareInterface) {
$role = $identity->getRole();
}
}
$view->addExtension(new GrEduLabs\Twig\Extension\Navigation(
$settings['navigation'],
$c->get('router'),
$c->get('request')
$c->get('request'),
$c->get('acl_service'),
$role
));
}
......@@ -100,8 +113,10 @@ $container['authentication_db_adapter'] = function ($c) {
$container['authentication_cas_adapter'] = function ($c) {
$settings = $c->get('settings');
$adapter = new GrEduLabs\Authentication\Adapter\Cas($settings['phpcas']);
$adapter->setIdentityPrototype(GrEduLabs\Authorization\Identity::class);
return new GrEduLabs\Authentication\Adapter\Cas($settings['phpcas']);
return $adapter;
};
$container['authentication_storage'] = function ($c) {
......@@ -126,6 +141,32 @@ $container['set_identity_in_request'] = function ($c) {
);
};
// Acl
$container['acl_service'] = function ($c) {
$settings = $c->get('settings');
return new GrEduLabs\Authorization\Acl($settings['acl'], $c);
};
$container['acl_guard_middleware'] = function ($c) {
$settings = $c->get('settings');
$authService = $c->get('authentication_service');
$role = $settings['acl']['default_role'];
if ($authService->hasIdentity()) {
$identity = $authService->getIdentity();
if ($identity instanceof GrEduLabs\Authorization\RoleAwareInterface) {
$role = $identity->getRole();
}
}
return new GrEduLabs\Authorization\GuardMiddleware($c->get('acl_service'), $role);
};
$container['provide_role_middleware'] = function ($c) {
return new GrEduLabs\Authorization\ProvideRoleMiddleware($c->get('authentication_service'));
};
// Inventory service
$container['inventory_service'] = function ($c) {
......
......@@ -9,4 +9,5 @@
// Application middleware
$app->add('acl_guard_middleware');
$app->add('set_identity_in_request');
......@@ -9,15 +9,19 @@
$app->get('/', 'GrEduLabs\\Action\\Index')->setName('index');
$app->get('/faq', function () {})->setName('faq');
// authentication
$app->group('/user', function () {
$this->map(['GET', 'POST'], '/login', 'GrEduLabs\\Action\\User\\Login')
->setName('user.login')
->add('provide_role_middleware')
->add('csrf');
$this->get('/login-sso', 'GrEduLabs\\Action\\User\\LoginSso')
->setName('user.loginSso');
->setName('user.loginSso')
->add('provide_role_middleware');
$this->get('/logout', 'GrEduLabs\\Action\\User\\Logout')
->setName('user.logout')
......
......@@ -10,13 +10,14 @@
namespace GrEduLabs\Authentication\Adapter;
use Exception;
use GrEduLabs\Authentication\Identity;
use phpCAS;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Authentication\Result;
class Cas implements AdapterInterface
{
use IdentityPrototypeCapableTrait;
/**
* @var bool
*/
......@@ -44,7 +45,7 @@ class Cas implements AdapterInterface
return new Result(
Result::SUCCESS,
self::identityFormCasAttributes(),
$this->identityFormCasAttributes(),
['Authentication success']
);
} catch (Exception $e) {
......@@ -65,7 +66,7 @@ class Cas implements AdapterInterface
phpCAS::logout();
}
private static function identityFormCasAttributes()
private function identityFormCasAttributes()
{
$attributes = phpCAS::getAttributes();
$identity = phpCAS::getUser();
......@@ -81,8 +82,9 @@ class Cas implements AdapterInterface
return $attributes[$attribute];
};
$identityClass = $this->identityPrototype;
return new Identity(
return new $identityClass(
$identity,
$filterAttribute('mail'),
$filterAttribute('cn'),
......
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authentication\Adapter;
use GrEduLabs\Authentication\Identity;
trait IdentityPrototypeCapableTrait
{
private $identityPrototype = Identity::class;
public function setIdentityPrototype($identity)
{
$this->identityPrototype = $identity;
return $this;
}
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authorization;
use Exception;
use Interop\Container\ContainerInterface;
use Zend\Permissions\Acl\Acl as ZendAcl;
use Zend\Permissions\Acl\Assertion\AssertionInterface;
use Zend\Permissions\Acl\Assertion\CallbackAssertion;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;
use Zend\Permissions\Acl\Role\GenericRole as Role;
class Acl extends ZendAcl
{
public function __construct(array $configuration, ContainerInterface $container = null)
{
// setup $roles
foreach ($configuration['roles'] as $role => $parents) {
$this->addRole(new Role($role), $parents);
}
// setup resources
if (array_key_exists('resources', $configuration)) {
foreach (array_unique($configuration['resources']) as $resource => $parent) {
$this->addResource(new Resource($resource), $parent);
}
}
if (array_key_exists('guards', $configuration)) {
foreach ($configuration['guards'] as $guardType => $guardRules) {
foreach ($guardRules as $rule) {
if (($ruleLength = count($rule)) < 2) {
throw new Exception('Error Processing Request');
}
list($resource, $roles, $privileges, $assertion) = array_merge(
$rule, array_fill(0, 4, null)
);
switch ($guardType) {
case 'resources':
break;
case 'routes':
if ($ruleLength < 3) {
throw new Exception('Error Processing Request');
}
$resource = 'route' . $resource;
break;
case 'callables':
$resource = 'callable/' . $resource;
if (!!$privileges) {
$assertion = $privileges;
$privileges = null;
}
break;
default:
throw new Exception('Error Processing Request');
}
if (!$this->hasResource($resource)) {
$this->addResource(new Resource($resource));
}
if (is_array($privileges)) {
$privileges = array_map('strtolower', $privileges);
}
if (null !== $assertion) {
if (is_string($assertion) && !is_callable($assertion)
&& null !== $container && $container->has($assertion)) {
$assertion = $container->get($assertion);
}
if (is_callable($assertion)) {
$assertion = new CallbackAssertion($assertion);
}
if (!$assertion instanceof AssertionInterface) {
throw new Exception('Error Processing Request');
}
}
$this->allow($roles, $resource, $privileges, $assertion);
}
}
}
}
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authorization;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Permissions\Acl\AclInterface;
class GuardMiddleware
{
/**
* @param AclInterface $acl The preconfigured ACL service
* @param string $currentUserRole
*/
public function __construct(AclInterface $acl, $currentUserRole)
{
$this->acl = $acl;
$this->currentUserRole = $currentUserRole;
}
/**
* Invoke middleware
*
* @param RequestInterface $request PSR7 request object
* @param ResponseInterface $response PSR7 response object
* @param callable $next Next middleware callable
*
* @return ResponseInterface PSR7 response object
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
$isAllowed = false;
if ($this->acl->hasResource('route' . $request->getAttribute('route')->getPattern())) {
$isAllowed = $isAllowed || $this->acl->isAllowed($this->currentUserRole, 'route' . $request->getAttribute('route')->getPattern(), strtolower($request->getMethod()));
}
if ($this->acl->hasResource('callable/' . $request->getAttribute('route')->getCallable())) {
$isAllowed = $isAllowed || $this->acl->isAllowed($this->currentUserRole, 'callable/' . $request->getAttribute('route')->getCallable());
}
if (!$isAllowed) {
return $response->withStatus(403, $this->currentUserRole . ' is not allowed access to this location.');
}
return $next($request, $response);
}
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authorization;
use GrEduLabs\Authentication\Identity as BaseIdentity;
use LogicException;
class Identity extends BaseIdentity implements RoleAwareInterface
{
private $role;
public function setRole($role)
{
// allow only once
if (null !== $this->role) {
throw new LogicException('Role already defined');
}
$this->role = $role;
return $this;
}
public function getRole()
{
return $this->role;
}
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authorization;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Authentication\AuthenticationService;
class ProvideRoleMiddleware
{
protected $authService;
public function __construct(AuthenticationService $authService)
{
$this->authService = $authService;
}
public function __invoke(RequestInterface $req, ResponseInterface $res, callable $next)
{
$res = $next($req, $res);
if (null !== ($identity = $this->authService->getIdentity())
&& $identity instanceof RoleAwareInterface) {
if (null === $identity->getRole()) {
$identity->setRole('user');
$this->authService->getStorage()->write($identity);
}
}
return $res;
}
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Authorization;
interface RoleAwareInterface
{
public function setRole($role);
public function getRole();
}
<?php
/**
* gredu_labs
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
*/
namespace GrEduLabs\Twig\Extension;
use Twig_Extension;
use Twig_SimpleFunction;
use Zend\Authentication\AuthenticationServiceInterface;
class Identity extends Twig_Extension
{
/**
* @var RouterInterface
*/
private $authService;
public function __construct(AuthenticationServiceInterface $authService)
{
$this->authService = $authService;
}
/**
* Extension name.
*
* @return string
*/
public function getName()
{
return 'slim-identity';
}
/**
* Callback for twig.
*
* @return array
*/
public function getFunctions()
{
return [
new Twig_SimpleFunction('identity', [$this, 'identity']),
];
}
/**
*
*/
public function identity($prop = null)
{
if (!$this->authService->hasIdentity()) {
return;
}
$identity = $this->authService->getIdentity();
if (null !== $prop) {
return $identity->{$prop};
}
return $identity;
}
}
<?php
/**
* gredu_labs
*
*
* @link https://github.com/eellak/gredu_labs for the canonical source repository
* @copyright Copyright (c) 2008-2015 Greek Free/Open Source Software Society (https://gfoss.ellak.gr/)
* @license GNU GPLv3 http://www.gnu.org/licenses/gpl-3.0-standalone.html
......@@ -13,6 +13,7 @@ use Psr\Http\Message\ServerRequestInterface;
use Slim\Interfaces\RouterInterface;
use Twig_Extension;
use Twig_SimpleFunction;
use Zend\Permissions\Acl\AclInterface;
class Navigation extends Twig_Extension
{
......@@ -31,14 +32,28 @@ class Navigation extends Twig_Extension
*/
private $request;
/**
* @var AclInterface
*/
private $acl;
/**
* @var string
*/
private $currentRole;
public function __construct(
array $navigation,
RouterInterface $router,
ServerRequestInterface $request
ServerRequestInterface $request,
AclInterface $acl = null,
$currentRole = null
) {
$this->navigation = $navigation;
$this->router = $router;
$this->request = $request;
$this->navigation = $navigation;
$this->router = $router;
$this->request = $request;
$this->acl = $acl;
$this->currentRole = $currentRole;
}
/**
......@@ -70,7 +85,19 @@ class Navigation extends Twig_Extension
{
$navigation = (null !== $root) ? $this->navigation[$root] : $this->navigation;
$prepare = function ($page) use (&$prepare) {
$aclFilter = function ($page) {
if (!$this->acl) {
return true;
}
$path = parse_url($page['href'], PHP_URL_PATH);
$resource = 'route' . $path;
return $this->acl->isAllowed($this->currentRole, $resource, 'get');
};
$prepare = function ($page) use (&$prepare, &$aclFilter) {
if (isset($page['route'])) {
$routeData = isset($page['route_data']) ? $page['route_data'] : [];
......@@ -78,16 +105,16 @@ class Navigation extends Twig_Extension
$page['href'] = $this->router->pathFor($page['route'], $routeData, $query);
}
$uri = $this->request->getUri();
$path = $uri->getPath();
$page['active'] = $path === parse_url($page['href'], PHP_URL_PATH);
$path = parse_url($page['href'], PHP_URL_PATH);
$page['active'] = $path === $this->request->getUri()->getPath();
if (isset($page['pages']) && is_array($page['pages'])) {
$page['pages'] = array_map($prepare, $page['pages']);
$page['pages'] = array_filter(array_map($prepare, $page['pages']), $aclFilter);
}
return $page;
};
return array_map($prepare, $navigation);
return array_filter(array_map($prepare, $navigation), $aclFilter);
}
}
......@@ -3,6 +3,8 @@
<div class="jumbotron">
<h1>Σχολικά εργαστήρια</h1>
<p class="lead">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam vel volutpat diam, vitae consectetur libero. Sed et sem et nisi aliquet condimentum. Morbi id magna iaculis, dictum lectus vitae, suscipit nisl. Proin molestie dolor vitae pulvinar tristique. Integer egestas varius lorem id scelerisque. Praesent velit metus, egestas facilisis iaculis id, auctor nec mi. Suspendisse ultrices laoreet justo eget viverra.</p>
{% if not identity() %}
<p><a class="btn btn-lg btn-success" href="{{ path_for('user.login') }}" role="button">Σύνδεση</a></p>
{% endif %}
</div>
{% endblock %}
\ No newline at end of file
......@@ -2,7 +2,9 @@
<ul class="nav nav-pills pull-right">
{% for page in nav('main') %}
<li role="presentation" class="{% if page.active %}active{% endif %}">
<a href="{{ page.href }}">{{ page.label }}</a>
<a href="{{ page.href }}" {% if page.id %}id="{{ page.id }}"{% endif %} >
{{ page.label }}
</a>
</li>
{% endfor %}
</ul>
......
......@@ -877,16 +877,16 @@
},
{
"name": "twig/twig",
"version": "v1.23.3",
"version": "v1.24.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "ae53fc2c312fdee63773b75cb570304f85388b08"
"reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/ae53fc2c312fdee63773b75cb570304f85388b08",
"reference": "ae53fc2c312fdee63773b75cb570304f85388b08",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
"reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8",
"shasum": ""
},
"require": {
......@@ -899,7 +899,7 @@
"type": "library",
"extra": {
"branch-alias": {