authentication is ready

parent 1c28c588
......@@ -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": "11c7d698e7938806987bf3c2d2c48951",
"content-hash": "04893d46c00465d48b6b1585d2f09c2d",
"hash": "2b6ef5eafbeda796b77abd927e9e74b2",
"content-hash": "b76fa7640686792f43397a7ff0ded352",
"packages": [
{
"name": "container-interop/container-interop",
......@@ -1079,6 +1079,56 @@
],
"time": "2015-06-15 19:37:28"
},
{
"name": "zendframework/zend-crypt",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-crypt.git",
"reference": "1b2f5600bf6262904167116fa67b58ab1457036d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-crypt/zipball/1b2f5600bf6262904167116fa67b58ab1457036d",
"reference": "1b2f5600bf6262904167116fa67b58ab1457036d",
"shasum": ""
},
"require": {
"container-interop/container-interop": "~1.0",
"php": "^5.5 || ^7.0",
"zendframework/zend-math": "^2.6",
"zendframework/zend-stdlib": "^2.7 || ^3.0"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"ext-mcrypt": "Required for most features of Zend\\Crypt"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev",
"dev-develop": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\Crypt\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://github.com/zendframework/zend-crypt",
"keywords": [
"crypt",
"zf2"
],
"time": "2016-02-03 23:46:30"
},
{
"name": "zendframework/zend-eventmanager",
"version": "3.0.0",
......@@ -1241,6 +1291,56 @@
],
"time": "2015-06-03 15:32:02"
},
{
"name": "zendframework/zend-math",
"version": "2.6.0",
"source": {
"type": "git",
"url": "https://github.com/zendframework/zend-math.git",
"reference": "395ebb981e01f2fc708ba07d8ee0d86f6e3e9ed6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zendframework/zend-math/zipball/395ebb981e01f2fc708ba07d8ee0d86f6e3e9ed6",
"reference": "395ebb981e01f2fc708ba07d8ee0d86f6e3e9ed6",
"shasum": ""
},
"require": {
"php": "^5.5 || ^7.0"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"ircmaxell/random-lib": "~1.1",
"phpunit/phpunit": "~4.0"
},
"suggest": {
"ext-bcmath": "If using the bcmath functionality",
"ext-gmp": "If using the gmp functionality",
"ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.6-dev",
"dev-develop": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\Math\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"homepage": "https://github.com/zendframework/zend-math",
"keywords": [
"math",
"zf2"
],
"time": "2016-02-02 23:15:14"
},
{
"name": "zendframework/zend-permissions-acl",
"version": "2.6.0",
......
......@@ -12,9 +12,9 @@ return [
'modules' => [
'module/authentication/bootstrap.php',
'module/authorization/bootstrap.php',
'module/sch_ldap/bootstrap.php',
// 'module/sch_ldap/bootstrap.php',
'module/sch_sso/bootstrap.php',
'module/sch_auto_create/bootstrap.php',
// 'module/sch_auto_create/bootstrap.php',
'module/application/bootstrap.php',
],
'cache_config' => 'data/cache/config/settings.php',
......
<?php
return [
'determineRouteBeforeAppMiddleware' => true,
'displayErrorDetails' => true,
'db' => [
'dsn' => 'sqlite:' . __DIR__ . '/../data/tmp/db.sq3',
'user' => null,
'pass' => null,
'options' => [
],
],
];
<?php
/**
* gredu_labs.
*
* 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\Application\Authentication;
class RedbeanListener
{
public function __invoke(callable $stop, $identity, $credential)
{
if (!$identity || !$credential) {
return false;
}
$stop();
}
}
return [
'authentication' => [
'bcrypt' => [
'salt' => '',
'cost' => 14,
],
],
];
\ No newline at end of file
......@@ -27,7 +27,4 @@ return [
'tryUsernameSplit' => true,
'networkTimeout' => null,
],
'sso' => [
'allowed' => '&(physicaldeliveryofficename=ΕΠΙΣΗΜΟΣ ΛΟΓΑΡΙΑΣΜΟΣ)(umdobject=account)',
],
];
\ No newline at end of file
......@@ -48,18 +48,5 @@ return [
// ],
],
'main-right' => [
'login' => [
'label' => 'Σύνδεση',
'route' => 'user.login',
'icon' => 'unlock',
],
'logout' => [
'label' => 'Αποσύνδεση',
'route' => 'user.logout',
'id' => 'nav-logout',
'icon' => 'lock',
],
],
],
];
......@@ -8,18 +8,35 @@
*/
return [
'phpcas' => [
'serverVersion' => 'S1',
'proxy' => false,
'serverHostname' => '',
'serverPort' => 443,
'serverUri' => '/cas',
'changeSessionId' => false,
'handleLogoutRequests' => true,
'allowedClients' => [],
'lang' => 'CAS_Languages_Greek',
'casServerCaCert' => '',
'casServerCnValidate' => '',
'noCasServerValidation' => true,
'sso' => [
'phpcas' => [
'serverVersion' => 'S1',
'proxy' => false,
'serverHostname' => '',
'serverPort' => 443,
'serverUri' => '/cas',
'changeSessionId' => false,
'handleLogoutRequests' => true,
'allowedClients' => [],
'lang' => 'CAS_Languages_Greek',
'casServerCaCert' => '',
'casServerCnValidate' => '',
'noCasServerValidation' => true,
],
'allowed' => [
[
'physicaldeliveryofficename' => '/^ΕΠΙΣΗΜΟΣ ΛΟΓΑΡΙΑΣΜΟΣ$/i',
'umdobject' => '/^account$/i',
],
],
],
'acl' => [
'guards' => [
'routes' => [
['/user/logout/sso', ['guest'], ['get']],
],
],
],
];
\ No newline at end of file
......@@ -10,7 +10,9 @@
return [
'view' => [
'template_path' => 'module/application/templates',
'template_path' => [
'module/application/templates',
],
'twig' => [
'cache' => 'data/cache/templates',
'debug' => false,
......
......@@ -58,36 +58,10 @@ return function (Slim\App $app) {
return $logger;
};
$container['authenticate_redbean_listener'] = function ($c) {
return new GrEduLabs\Application\Authentication\RedbeanListener();
};
$container['csrf'] = function ($c) {
return new \Slim\Csrf\Guard();
};
$events = $container['events'];
$events('on', 'bootstrap', function () use ($container) {
session_name('GrEduLabs');
session_start();
// setup RedbeanPHP
RedBeanPHP\R::setup(
$container['settings']['db']['dsn'],
$container['settings']['db']['user'],
$container['settings']['db']['pass']
);
define('REDBEAN_MODEL_PREFIX', 'GrEduLabs\\Application\\Model\\');
}, 10000000);
$events('on', 'bootstrap', function () use ($container) {
$container['router']->getNamedRoute('user.login')->add('csrf');
});
$events('on', 'authenticate', $container['authenticate_redbean_listener']);
$container['GrEduLabs\\Application\\Action\\Index'] = function ($c) {
return new GrEduLabs\Application\Action\Index($c['view']);
......@@ -109,6 +83,32 @@ return function (Slim\App $app) {
return new GrEduLabs\Application\Action\School\Assets($c->get('view'));
};
$events = $container['events'];
$events('on', 'bootstrap', function () use ($container) {
session_name('GrEduLabs');
session_start();
// setup RedbeanPHP
RedBeanPHP\R::setup(
$container['settings']['db']['dsn'],
$container['settings']['db']['user'],
$container['settings']['db']['pass']
);
define('REDBEAN_MODEL_PREFIX', 'GrEduLabs\\Application\\Model\\');
}, 10000000);
$events('on', 'bootstrap', function () use ($container) {
try {
$container['router']->getNamedRoute('user.login')->add('csrf');
} catch (\RuntimeException $e) {
// eat it
}
});
$app->get('/', 'GrEduLabs\\Application\\Action\\Index')->setName('index');
$app->group('/school', function () {
$this->get('', 'GrEduLabs\\Application\\Action\\School\\Index')->setName('school');
......
......@@ -96,9 +96,13 @@ class Navigation extends Twig_Extension
/**
*
*/
public function nav($root = null)
public function nav($root)
{
$navigation = (null !== $root) ? $this->navigation[$root] : $this->navigation;
if (!isset($this->navigation[$root]) || !is_array($this->navigation[$root])) {
return [];
}
$navigation = $this->navigation[$root];
$aclFilter = function ($page) {
if (!$this->acl) {
......
{% for class, messages in flash() %}
<ul class="list-unstyled alert alert-{{ class }}" role="alert">
<ul class="list-unstyled alert alert-{{ class }} alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true"><i class="fa fa-close"></i></span>
</button>
{% for message in messages %}
<li>{{ message }}</li>
<li>{{ message|raw }}</li>
{% endfor %}
</ul>
{% endfor %}
......@@ -3,8 +3,5 @@
<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
......@@ -28,7 +28,7 @@
{% endfor %}
</ul>
<ul class="nav navbar-nav navbar-right">
{% for page in nav('main-right') %}
{% for page in nav('authentication') %}
{{ macros.li(page)}}
{% endfor %}
</ul>
......
......@@ -20,7 +20,11 @@ return function (Slim\App $app) {
};
$container['authentication_adapter'] = function ($c) {
return new GrEduLabs\Authentication\Adapter\EventsAdapter($c['events']);
return new GrEduLabs\Authentication\Adapter\RedBeanPHP(
$c['events'],
$c['authentication_identity_class'],
$c['authentication_crypt']
);
};
$container['authentication_service'] = function ($c) {
......@@ -34,8 +38,21 @@ return function (Slim\App $app) {
return GrEduLabs\Authentication\Identity::class;
};
$container['authentication_crypt'] = function ($c) {
$service = new Zend\Crypt\Password\Bcrypt();
if (isset($c['settings']['authentication']['bcrypt']['salt'])) {
$service->setSalt($c->settings['authentication']['bcrypt']['salt']);
}
if (isset($c['settings']['authentication']['bcrypt']['cost'])) {
$service->setCost($c->settings['authentication']['bcrypt']['cost']);
}
return $service;
};
$events('on', 'bootstrap', function () use ($container) {
$container->extend('view', function ($view, $c) {
$view->getEnvironment()->getLoader()->prependPath(__DIR__ . '/templates');
$view->addExtension(new GrEduLabs\Authentication\Twig\Extension\Identity(
$c['authentication_service']
));
......@@ -44,7 +61,17 @@ return function (Slim\App $app) {
});
});
$container['GrEduLabs\\Authentication\\Action\\User\\Login'] = function ($c) {
$events('on', 'authenticate.success', function ($stop, $identity) use ($container) {
if (isset($container['logger'])) {
$container['logger']->info(sprintf(
'Authentication through %s for %s',
$identity->authenticationSource,
$identity->mail
));
}
});
$container[GrEduLabs\Authentication\Action\User\Login::class] = function ($c) {
return new GrEduLabs\Authentication\Action\User\Login(
$c['view'],
......@@ -55,7 +82,7 @@ return function (Slim\App $app) {
);
};
$container['GrEduLabs\\Authentication\\Action\\User\\Logout'] = function ($c) {
$container[GrEduLabs\Authentication\Action\User\Logout::class] = function ($c) {
return new GrEduLabs\Authentication\Action\User\Logout(
$c['authentication_service'],
$c['events'],
......@@ -64,10 +91,26 @@ return function (Slim\App $app) {
};
$app->group('/user', function () {
$this->map(['GET', 'POST'], '/login', 'GrEduLabs\\Authentication\\Action\\User\\Login')
$this->map(['GET', 'POST'], '/login', GrEduLabs\Authentication\Action\User\Login::class)
->setName('user.login');
$this->get('/logout', 'GrEduLabs\\Authentication\\Action\\User\\Logout')
$this->get('/logout', GrEduLabs\Authentication\Action\User\Logout::class)
->setName('user.logout');
});
$nav = $container['settings']->get('navigation');
$nav['authentication'] = [
'login' => [
'label' => 'Σύνδεση',
'route' => 'user.login',
'icon' => 'unlock',
],
'logout' => [
'label' => 'Αποσύνδεση',
'route' => 'user.logout',
'id' => 'nav-logout',
'icon' => 'lock',
],
];
$container['settings']->set('navigation', $nav);
};
<?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;
use Zend\Authentication\Adapter\AbstractAdapter;
use Zend\Authentication\Result;
class EventsAdapter extends AbstractAdapter
{
protected $events;
public function __construct(callable $events)
{
$this->events = $events;
}
public function authenticate()
{
$events = $this->events;
$result = $events('trigger', 'authenticate', $this->getIdentity(), $this->getCredential());
$last = end($result['results']);
if ($last instanceof Identity) {
$events('trigger', 'authenticate.success', $last);
return new Result(Result::SUCCESS, $last, ['Authentication success']);
}
$events('trigger', 'authenticate.failure');
return new Result(Result::FAILURE_UNCATEGORIZED, null, ['Authentication failure']);
}
}
<?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 RedBeanPHP\R;
use Zend\Authentication\Adapter\AbstractAdapter;
use Zend\Authentication\Result;
use Zend\Crypt\Password\PasswordInterface;
class RedBeanPHP extends AbstractAdapter
{
/**
* @var string
*/
private static $failMessage = 'Failed to login. Please check your email and password and try again';
/**
* @var callable
*/
protected $events;
/**
* @var string
*/
protected $identityClass;
/**
* @var PasswordInterface
*/
protected $crypt;
public function __construct(callable $events, $identityClass, PasswordInterface $crypt)
{
$this->events = $events;
$this->identityClass = (string) $identityClass;
$this->crypt = $crypt;
}
public function authenticate()
{
$events = $this->events;
$events('trigger', 'authenticate', $this);
$user = R::findOne('user', 'mail = ? AND authentication_source = ?', [
$this->getIdentity(),
'DB',
]);
if (!$user) {
return new Result(Result::FAILURE_IDENTITY_NOT_FOUND, null, [self::$failMessage]);
}
if (!$this->crypt->verify($this->getCredential(), $user->password)) {
return new Result(Result::FAILURE_CREDENTIAL_INVALID, null, [self::$failMessage]);
}
$identityClass = $this->identityClass;
$identity = new $identityClass(
$user->uid,
$user->mail,
$user->displayName,
$user->officeName,
'DB'
);
$events('trigger', 'authenticate.success', $identity);
return new Result(Result::SUCCESS, $identity, ['Authentication success']);
}
}
{% extends "layout.twig" %}
{% block content %}
<h1>Σύνδεση</h1>
<div class="row">
<div class="col-xs-12">
<p>Αν έχετε ήδη δημιουργήσει λογαριασμό στο σύστημα, συνδεθείτε εδώ.</p>
<form method="post" action="{{ path_for('user.login') }}" id="user-login-form" class="form-horizontal">
<div class="form-group">
<label class="hidden-sm col-sm-3 control-label">Email</label>
<div class="col-xs-12 col-sm-9 col-md-5 col-lg-4">
<div class="input-group">
<span class="input-group-addon">
<i class="fa fa-envelope-o fa-fw"></i>
</span>
<input name="identity" type="text" value="" class="form-control" required autocomplete="off">
</div>
</div>
</div>
<div class="form-group">
<label class="hidden-sm col-sm-3 control-label">Password</label>
<div class="col-xs-12 col-sm-9 col-md-5 col-lg-4">
<div class="input-group">
<span class="input-group-addon">
<i class="fa fa-key fa-fw"></i>
</span>
<input name="credential" type="password" value="" class="form-control" required autocomplete="off">
</div>
</div>
</div>
<hr>
<div class="form-group text-center">
<button name="dologin" type="submit" value="1" class="btn btn-primary">Σύνδεση</button>
</div>
<input type="hidden" name="{{ csrf_name_key }}" value="{{ csrf_name }}">
<input type="hidden" name="{{ csrf_value_key }}" value="{{ csrf_value }}">
</form>
</div>
</div>
{% endblock %}
\ No newline at end of file
......@@ -37,13 +37,16 @@ class RouteGuard
*/
public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
{
if (!$request->getAttribute('route')) {
return $response->withStatus(500);
}
$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())) {
if (is_string($request->getAttribute('route')->getCallable()) &&
$this->acl->hasResource('callable/' . $request->getAttribute('route')->getCallable())) {
$isAllowed = $isAllowed || $this->acl->isAllowed($this->currentUserRole, 'callable/' . $request->getAttribute('route')->getCallable());
}
......
......@@ -17,20 +17,4 @@ return function (Slim\App $app) {
return new Zend\Ldap\Ldap($settings);
};
$events = $container['events'];
$events('on', 'authenticate.success', function (callable $stop, $identity) use ($container) {
$ldap = $container['ldap'];
$filter = Zend\Ldap\Filter::equals('mail', $identity->mail)
->addAnd(new Zend\Ldap\Filter\StringFilter($container['settings']['sso']['allowed']));
$dn = Zend\Ldap\Dn::factory($ldap->getBaseDn())->prepend(['ou' => 'people']);
$result = $ldap->search($filter, $dn, Zend\Ldap\Ldap::SEARCH_SCOPE_ONE, ['dn']);
if (0 === $result->count()) {
$stop();
$container['authentication_service']->clearIdentity();
}
}, 1000);
};
......@@ -11,10 +11,11 @@
return function (Slim\App $app) {
$container = $app->getContainer();
$events = $container['events'];
$initCas = function () use ($container) {
$settings = $container['settings']['phpcas'];
$container['autoloader']->addPsr4('SchSSO\\', __DIR__ . '/src/');
$container['init_cas'] = $container->protect(function () use ($container) {
$settings = $container['settings']['sso']['phpcas'];
phpCAS::client(
$settings['serverVersion'],
$settings['serverHostname'],
......@@ -34,60 +35,74 @@ return function (Slim\App $app) {
phpCAS::setNoCasServerValidation();
}
phpCAS::handleLogoutRequests();
phpCAS::setDebug('data/log/phpCAS.log');
};
});
$events('on', 'bootstrap', function () use ($container) {
$container['router']->getNamedRoute('user.login')->add(function ($req, $res, $next) {
$method =