User authentication

- sso
- logout
- cas logout middleware
parent 7e22895c
......@@ -109,6 +109,13 @@ $container['authentication_service'] = function ($c) {
);
};
$container['authentication_cas_logout_middleware'] = function ($c) {
return new GrEduLabs\Middleware\CasLogout(
$c->get('authentication_service'),
$c->get('authentication_cas_adapter')
);
};
$container['maybe_identity'] = function ($c) {
return function ($call) use ($c) {
......
......@@ -24,13 +24,18 @@ $app->get('/', function ($request, $response, $args) {
// authentication
$app->group('/user', function () {
$this->map(['GET', 'POST'], '/login', 'GrEduLabs\\Action\\User\\Login')
->setName('user.login')
->add('csrf');
$this->get('/login-sso', 'GrEduLabs\\Action\\User\\LoginSso')
->setName('user.loginSso');
$this->get('/logout', 'GrEduLabs\\Action\\User\\Logout')
->setName('user.logout');
->setName('user.logout')
->add('authentication_cas_logout_middleware');
$this->get('/profile', 'GrEduLabs\\Action\\User\\Profile')
->setName('user.profile');
});
......@@ -9,10 +9,10 @@
namespace GrEduLabs\Action\User;
use Psr\Http\Message\ResponseInterface;
use Slim\Csrf\Guard;
use Slim\Flash\Messages;
use Slim\Http\Request;
use Slim\Http\Response;
use Slim\Views\Twig;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Authentication\Adapter\ValidatableAdapterInterface;
......@@ -77,7 +77,7 @@ class Login
}
}
public function __invoke(Request $req, ResponseInterface $res)
public function __invoke(Request $req, Response $res)
{
if ($req->isPost()) {
if ($this->authAdapter instanceof ValidatableAdapterInterface) {
......
......@@ -9,9 +9,9 @@
namespace GrEduLabs\Action\User;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Flash\Messages;
use Slim\Http\Response;
use Zend\Authentication\AuthenticationServiceInterface;
class LoginSso
......@@ -56,7 +56,7 @@ class LoginSso
public function __invoke(
ServerRequestInterface $req,
ResponseInterface $res
Response $res
) {
$result = $this->authService->authenticate();
if (!$result->isValid()) {
......
......@@ -9,8 +9,8 @@
namespace GrEduLabs\Action\User;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Http\Response;
use Zend\Authentication\AuthenticationServiceInterface;
class Logout
......@@ -33,7 +33,7 @@ class Logout
$this->redirectUrl = $redirectUrl;
}
public function __invoke(ServerRequestInterface $req, ResponseInterface $res, array $args = [])
public function __invoke(ServerRequestInterface $req, Response $res)
{
if ($this->authService->hasIdentity()) {
$this->authService->clearIdentity();
......
......@@ -17,32 +17,25 @@ use Zend\Authentication\Result;
class Cas implements AdapterInterface
{
public function __construct(array $settings = [])
{
phpCAS::client(
$settings['serverVersion'],
$settings['serverHostname'],
$settings['serverPort'],
$settings['serverUri'],
$settings['changeSessionId']
);
/**
* @var bool
*/
protected $__init__ = false;
if (($casServerCaCert = $settings['casServerCaCert'])) {
if ($settings['casServerCnValidate']) {
phpCAS::setCasServerCACert($casServerCaCert, true);
} else {
phpCAS::setCasServerCACert($casServerCaCert, false);
}
}
/**
* @var array
*/
protected $settings;
if ($settings['noCasServerValidation']) {
phpCAS::setNoCasServerValidation();
}
public function __construct(array $settings = [])
{
$this->settings = $settings;
}
public function authenticate()
{
try {
$this->init();
phpCAS::handleLogoutRequests();
phpCAS::forceAuthentication();
if (!phpCAS::isAuthenticated()) {
......@@ -59,6 +52,19 @@ class Cas implements AdapterInterface
}
}
public function logout($redirect = null)
{
$this->init();
if (!phpCAS::isAuthenticated()) {
return;
}
if ($redirect) {
phpCAS::logoutWithRedirectService((string) $redirect);
}
phpCAS::logout();
}
private static function identityFormCasAttributes()
{
$attributes = phpCAS::getAttributes();
......@@ -80,7 +86,35 @@ class Cas implements AdapterInterface
$identity,
$filterAttribute('mail'),
$filterAttribute('cn'),
$filterAttribute('ou')
$filterAttribute('ou'),
'CAS'
);
}
private function init()
{
if (!$this->__init__) {
$settings = $this->settings;
phpCAS::client(
$settings['serverVersion'],
$settings['serverHostname'],
$settings['serverPort'],
$settings['serverUri'],
$settings['changeSessionId']
);
if (($casServerCaCert = $settings['casServerCaCert'])) {
if ($settings['casServerCnValidate']) {
phpCAS::setCasServerCACert($casServerCaCert, true);
} else {
phpCAS::setCasServerCACert($casServerCaCert, false);
}
}
if ($settings['noCasServerValidation']) {
phpCAS::setNoCasServerValidation();
}
$this->__init__ = true;
}
}
}
......@@ -21,12 +21,15 @@ class Identity implements JsonSerializable
protected $officeName;
public function __construct($uid, $mail, $displayName, $officeName)
protected $authenticationSource;
public function __construct($uid, $mail, $displayName, $officeName, $authenticationSource)
{
$this->uid = $uid;
$this->mail = $mail;
$this->displayName = $displayName;
$this->officeName = $officeName;
$this->uid = $uid;
$this->mail = $mail;
$this->displayName = $displayName;
$this->officeName = $officeName;
$this->authenticationSource = $authenticationSource;
}
public function __get($name)
......@@ -51,10 +54,11 @@ class Identity implements JsonSerializable
public function toArray()
{
return [
'uid' => $this->uid,
'mail' => $this->mail,
'displayName' => $this->displayName,
'officeName' => $this->officeName,
'uid' => $this->uid,
'mail' => $this->mail,
'displayName' => $this->displayName,
'officeName' => $this->officeName,
'authenticationSource' => $this->authenticationSource,
];
}
......
<?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\Middleware;
use GrEduLabs\Authentication\Adapter\Cas;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Authentication\AuthenticationServiceInterface;
class CasLogout
{
/**
* @var AuthenticationServiceInterface
*/
protected $authService;
/**
* @var Cas
*/
protected $adapter;
public function __construct(AuthenticationServiceInterface $authService, Cas $adapter)
{
$this->authService = $authService;
$this->adapter = $adapter;
}
public function __invoke(ServerRequestInterface $req, ResponseInterface $res, callable $next)
{
if ($this->authService->hasIdentity()) {
$identity = $this->authService->getIdentity();
}
$res = $next($req, $res);
if (isset($identity) && 'CAS' === $identity->authenticationSource) {
$this->adapter->logout($req->getUri());
}
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 GrEduLabsTest\Action\User;
use GrEduLabs\Action\User\Logout;
use Slim\Http\Response;
class LogoutTest extends \PHPUnit_Framework_TestCase
{
private $request;
private $response;
private $authService;
private $clearIdentityFlag;
private $redirectUrl = '/some/success/url';
protected function setUp()
{
$this->request = $this->getMock('\\Psr\\Http\\Message\\ServerRequestInterface');
$this->response = new Response();
$this->authService = $this->getMock('\\Zend\\Authentication\\AuthenticationServiceInterface');
$this->authService->expects($this->any())
->method('hasIdentity')
->will($this->returnValue(true));
$this->authService->expects($this->any())
->method('clearIdentity')
->will($this->returnCallback(function () {
$this->clearIdentityFlag = true;
}));
$this->clearIdentityFlag = false;
$this->action = new Logout(
$this->authService,
$this->redirectUrl
);
}
public function testConstructorSetsDependencies()
{
$this->assertAttributeSame($this->authService, 'authService', $this->action);
$this->assertAttributeSame($this->redirectUrl, 'redirectUrl', $this->action);
}
public function testInvokeClearsIdentity()
{
$action = $this->action;
$actionResponse = $action($this->request, $this->response);
$this->assertTrue($this->clearIdentityFlag);
}
public function testInvokeRedirects()
{
$action = $this->action;
$actionResponse = $action($this->request, $this->response);
$location = $actionResponse->getHeader('Location');
$this->assertContains($this->redirectUrl, $location);
}
}
......@@ -20,7 +20,8 @@ class IdentityTest extends \PHPUnit_Framework_TestCase
'someUid',
'some@mail.com',
'Jonh Doe',
'Office'
'Office',
'authSource'
);
}
......@@ -31,6 +32,7 @@ class IdentityTest extends \PHPUnit_Framework_TestCase
$this->assertAttributeSame('some@mail.com', 'mail', $this->identity);
$this->assertAttributeSame('Jonh Doe', 'displayName', $this->identity);
$this->assertAttributeSame('Office', 'officeName', $this->identity);
$this->assertAttributeSame('authSource', 'authenticationSource', $this->identity);
}
public function testMagicGet()
......@@ -39,6 +41,7 @@ class IdentityTest extends \PHPUnit_Framework_TestCase
$this->assertSame('some@mail.com', $this->identity->mail);
$this->assertSame('Jonh Doe', $this->identity->displayName);
$this->assertSame('Office', $this->identity->officeName);
$this->assertSame('authSource', $this->identity->authenticationSource);
}
public function testMagicGetReturnsNullIfNoProperty()
......@@ -59,10 +62,11 @@ class IdentityTest extends \PHPUnit_Framework_TestCase
public function testToArray()
{
$this->assertEquals([
'uid' => 'someUid',
'mail' => 'some@mail.com',
'displayName' => 'Jonh Doe',
'officeName' => 'Office',
'uid' => 'someUid',
'mail' => 'some@mail.com',
'displayName' => 'Jonh Doe',
'officeName' => 'Office',
'authenticationSource' => 'authSource',
], $this->identity->toArray());
}
......
<?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 GrEduLabsTest\Middleware;
use GrEduLabs\Middleware\CasLogout;
use Slim\Http\Response;
class CasLogoutTest extends \PHPUnit_Framework_TestCase
{
protected $authService;
protected $middleware;
protected $adapter;
protected $idenityStub;
protected $logoutFlag = false;
protected function setUp()
{
$this->request = $this->getMock('\\Psr\\Http\\Message\\ServerRequestInterface');
$this->response = new Response();
$this->idenityStub = $this->getMockBuilder('GrEduLabs\Authentication\Identity')
->disableOriginalConstructor()
->getMock();
$this->authService = $this->getMock('\\Zend\\Authentication\\AuthenticationServiceInterface');
$this->authService->expects($this->any())
->method('hasIdentity')
->will($this->returnValue(true));
$this->authService->expects($this->any())
->method('getIdentity')
->will($this->returnValue($this->idenityStub));
$this->adapter = $this->getMockBuilder('\\GrEduLabs\\Authentication\\Adapter\\Cas')
->disableOriginalConstructor()
->getMock();
$this->adapter->expects($this->any())
->method('logout')
->with($this->anything())
->will($this->returnCallback(function () {
$this->logoutFlag = true;
}));
$this->logoutFlag = false;
$this->middleware = new CasLogout($this->authService, $this->adapter);
}
public function testInvokeCallsCasLogoutIfIdentitySourceIsCAS()
{
$this->idenityStub->expects($this->any())
->method('__get')
->with($this->equalTo('authenticationSource'))
->will($this->returnValue('CAS'));
$middleware = $this->middleware;
$middleware($this->request, $this->response, function ($req, $res) {
return $res;
});
$this->assertTrue($this->logoutFlag);
}
public function testInvokeNotCallsCasLogoutIfIdentitySourceIsNotCAS()
{
$this->idenityStub->expects($this->any())
->method('__get')
->with($this->equalTo('authenticationSource'))
->will($this->returnValue('SomeOtherSource'));
$middleware = $this->middleware;
$middleware($this->request, $this->response, function ($req, $res) {
return $res;
});
$this->assertFalse($this->logoutFlag);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment