mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-23 18:03:17 +00:00
Use subscriber to check session idle time
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Phraseanet
|
||||
*
|
||||
* (c) 2005-2014 Alchemy
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Alchemy\Phrasea\Core\Event\Subscriber;
|
||||
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Entities\SessionModule;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||
|
||||
/**log real human activity on application, to keep session alive*/
|
||||
class SessionManagerSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private $app;
|
||||
|
||||
public function __construct(Application $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return array(
|
||||
KernelEvents::REQUEST => array('onKernelRequest', -112),
|
||||
);
|
||||
}
|
||||
|
||||
public function onKernelRequest(GetResponseEvent $event)
|
||||
{
|
||||
$modulesIds = array(
|
||||
"prod" => 1,
|
||||
"client" => 2,
|
||||
"admin" => 3,
|
||||
"thesaurus" => 5,
|
||||
"report" => 10,
|
||||
"lightbox" => 6,
|
||||
);
|
||||
|
||||
$pathInfo = array_filter(explode('/', $event->getRequest()->getPathInfo()));
|
||||
|
||||
if(count($pathInfo) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$moduleName = strtolower($pathInfo[1]);
|
||||
if (!array_key_exists($moduleName, $modulesIds) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this route is polled by js in admin/databox to refresh infos (progress bar...)
|
||||
if (preg_match("#^/admin/databox/[0-9]+/informations/documents/#", $event->getRequest()->getPathInfo()) == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// this route is polled by js in admin/tasks to refresh tasks status
|
||||
if ($event->getRequest()->getPathInfo() == "/admin/task-manager/tasks/" && $event->getRequest()->getContentType() == 'json') {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we are already disconnected (ex. from another window), quit immediatly
|
||||
if (!($this->app['authentication']->isAuthenticated())) {
|
||||
if ($event->getRequest()->isXmlHttpRequest()) {
|
||||
$response = new Response("End-Session", 403);
|
||||
} else {
|
||||
$response = new RedirectResponse($this->app["url_generator"]->generate("homepage", array("redirect"=>'..' . $event->getRequest()->getPathInfo())));
|
||||
}
|
||||
$response->headers->set('X-Phraseanet-End-Session', '1');
|
||||
|
||||
$event->setResponse($response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$session = $this->app['EM']->find('Entities\Session', $this->app['session']->get('session_id'));
|
||||
|
||||
$idle = 0;
|
||||
if (isset($this->app["phraseanet.configuration"]["session"]["idle"])) {
|
||||
$idle = (int)($this->app["phraseanet.configuration"]["session"]["idle"]);
|
||||
}
|
||||
$now = new \DateTime();
|
||||
$dt = $now->getTimestamp() - $session->getUpdated()->getTimestamp();
|
||||
if ($idle > 0 && $dt > $idle) {
|
||||
// we must disconnet due to idletime
|
||||
$this->app['authentication']->closeAccount();
|
||||
if ($event->getRequest()->isXmlHttpRequest()) {
|
||||
$response = new Response("End-Session", 403);
|
||||
} else {
|
||||
$response = new RedirectResponse($this->app["url_generator"]->generate("homepage", array("redirect"=>'..' . $event->getRequest()->getPathInfo())));
|
||||
}
|
||||
$response->headers->set('X-Phraseanet-End-Session', '1');
|
||||
|
||||
$event->setResponse($response);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$moduleId = $modulesIds[$moduleName];
|
||||
|
||||
$session->setUpdated(new \DateTime());
|
||||
|
||||
if (!$session->hasModuleId($moduleId)) {
|
||||
$module = new SessionModule();
|
||||
$module->setModuleId($moduleId);
|
||||
$module->setSession($session);
|
||||
$session->addModule($module);
|
||||
|
||||
$this->app['EM']->persist($module);
|
||||
} else {
|
||||
$this->app['EM']->persist($session->getModuleById($moduleId)->setUpdated(new \DateTime()));
|
||||
}
|
||||
|
||||
$this->app['EM']->persist($session);
|
||||
$this->app['EM']->flush();
|
||||
}
|
||||
}
|
@@ -45,14 +45,22 @@ class PhraseaAuthenticationForm extends AbstractType
|
||||
),
|
||||
));
|
||||
|
||||
$builder->add('remember-me', $this->app['phraseanet.configuration']['session']['idle'] < 1 ? 'checkbox' : 'hidden', array(
|
||||
'label' => $this->app['phraseanet.configuration']['session']['idle'] < 1 ? _('Remember me') : "",
|
||||
if ($this->app['phraseanet.configuration']['session']['idle'] < 1) {
|
||||
$builder->add('remember-me', 'checkbox' , array(
|
||||
'label' => _('Remember me'),
|
||||
'mapped' => false,
|
||||
'required' => false,
|
||||
'attr' => array(
|
||||
'value' => '1',
|
||||
)
|
||||
));
|
||||
} else {
|
||||
$builder->add('remember-me', 'hidden' , array(
|
||||
'label' => '',
|
||||
'mapped' => false,
|
||||
'required' => false
|
||||
));
|
||||
}
|
||||
|
||||
$builder->add('redirect', 'hidden', array(
|
||||
'required' => false,
|
||||
|
@@ -1738,6 +1738,34 @@ class LoginTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
||||
$this->assertSame(200, self::$DI['client']->getResponse()->getStatusCode());
|
||||
}
|
||||
|
||||
public function testLoginPageWithIdleSessionTime()
|
||||
{
|
||||
$this->logout(self::$DI['app']);
|
||||
self::$DI['app']['phraseanet.configuration']['session'] = array(
|
||||
'idle' =>10,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
|
||||
$crawler = self::$DI['client']->request('GET', '/login/');
|
||||
|
||||
$this->assertSame(200, self::$DI['client']->getResponse()->getStatusCode());
|
||||
$this->assertEquals('hidden', $crawler->filter('input[name="remember-me"]')->attr('type'));
|
||||
}
|
||||
|
||||
public function testLoginPageWithNoIdleSessionTime()
|
||||
{
|
||||
$this->logout(self::$DI['app']);
|
||||
self::$DI['app']['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 0,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
|
||||
$crawler = self::$DI['client']->request('GET', '/login/');
|
||||
|
||||
$this->assertSame(200, self::$DI['client']->getResponse()->getStatusCode());
|
||||
$this->assertEquals('checkbox', $crawler->filter('input[name="remember-me"]')->attr('type'));
|
||||
}
|
||||
|
||||
private function addUsrAuthDoctrineEntitySupport($id, $out, $participants = false)
|
||||
{
|
||||
$repo = $this->getMockBuilder('Doctrine\ORM\EntityRepository\UsrAuthProviderRepository')
|
||||
|
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
namespace Alchemy\Tests\Phrasea\Core\Event\Subscriber;
|
||||
|
||||
use Alchemy\Phrasea\Core\Event\Subscriber\SessionManagerSubscriber;
|
||||
use Alchemy\Phrasea\Application;
|
||||
use Entities\Session;
|
||||
use Symfony\Component\HttpKernel\Client;
|
||||
|
||||
class SessionManagerSubscriberTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
|
||||
{
|
||||
public function testEndSession()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 0,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/prod', function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/prod');
|
||||
|
||||
$this->assertTrue($client->getResponse()->isRedirect());
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('x-phraseanet-end-session'));
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('location'));
|
||||
$this->assertEquals('/login?redirect=..%2Fprod', $client->getResponse()->headers->get('location'));
|
||||
}
|
||||
|
||||
public function testEndSessionXmlXhttpRequest()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 0,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/prod', function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/prod', array(), array(), array(
|
||||
'HTTP_ACCEPT' => 'application/json',
|
||||
'HTTP_X-Requested-With' => 'XMLHttpRequest',
|
||||
|
||||
));
|
||||
|
||||
$this->assertTrue($client->getResponse()->isClientError());
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('x-phraseanet-end-session'));
|
||||
}
|
||||
|
||||
public function testEndSessionAuthenticated()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['authentication'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Authenticator')->disableOriginalConstructor()->getMock();
|
||||
$app['authentication']->expects($this->any())->method('isAuthenticated')->will($this->returnValue(true));
|
||||
|
||||
$session = new Session();
|
||||
$session->setUpdated(new \DateTime());
|
||||
|
||||
$app['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')->disableOriginalConstructor()->getMock();
|
||||
$app['EM']->expects($this->once())->method('find')->with($this->equalTo('Entities\Session'))->will($this->returnValue($session));
|
||||
$app['EM']->expects($this->exactly(2))->method('persist')->will($this->returnValue(null));
|
||||
$app['EM']->expects($this->once())->method('flush')->will($this->returnValue(null));
|
||||
|
||||
$app['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 0,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/prod', function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/prod');
|
||||
|
||||
$this->assertTrue($client->getResponse()->isOK());
|
||||
}
|
||||
|
||||
public function testEndSessionAuthenticatedWithOutdatedIdle()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['authentication'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Authenticator')->disableOriginalConstructor()->getMock();
|
||||
$app['authentication']->expects($this->any())->method('isAuthenticated')->will($this->returnValue(true));
|
||||
$app['authentication']->expects($this->once())->method('closeAccount')->will($this->returnValue(null));
|
||||
|
||||
$session = new Session();
|
||||
$session->setUpdated(new \DateTime('-1 hour'));
|
||||
|
||||
$app['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')->disableOriginalConstructor()->getMock();
|
||||
$app['EM']->expects($this->once())->method('find')->with($this->equalTo('Entities\Session'))->will($this->returnValue($session));
|
||||
|
||||
$app['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 10,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/prod', function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/prod');
|
||||
|
||||
$this->assertTrue($client->getResponse()->isRedirect());
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('x-phraseanet-end-session'));
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('location'));
|
||||
$this->assertEquals('/login?redirect=..%2Fprod', $client->getResponse()->headers->get('location'));
|
||||
}
|
||||
|
||||
public function testEndSessionAuthenticatedWithOutdatedIdleXmlHttpRequest()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['authentication'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Authenticator')->disableOriginalConstructor()->getMock();
|
||||
$app['authentication']->expects($this->any())->method('isAuthenticated')->will($this->returnValue(true));
|
||||
$app['authentication']->expects($this->once())->method('closeAccount')->will($this->returnValue(null));
|
||||
|
||||
$session = new Session();
|
||||
$session->setUpdated(new \DateTime('-1 hour'));
|
||||
|
||||
$app['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')->disableOriginalConstructor()->getMock();
|
||||
$app['EM']->expects($this->once())->method('find')->with($this->equalTo('Entities\Session'))->will($this->returnValue($session));
|
||||
|
||||
$app['phraseanet.configuration']['session'] = array(
|
||||
'idle' => 10,
|
||||
'lifetime' => 60475,
|
||||
);
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/prod', function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/prod', array(), array(), array(
|
||||
'HTTP_ACCEPT' => 'application/json',
|
||||
'HTTP_X-Requested-With' => 'XMLHttpRequest',
|
||||
));
|
||||
|
||||
$this->assertTrue($client->getResponse()->isClientError());
|
||||
$this->assertNotNUll($client->getResponse()->headers->get('x-phraseanet-end-session'));
|
||||
}
|
||||
|
||||
public function testUndefinedModule()
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get('/undefined-module', function () {
|
||||
return 'undefined-module';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', '/undefined-module');
|
||||
|
||||
$this->assertTrue($client->getResponse()->isOk());
|
||||
$this->assertEquals('undefined-module', $client->getResponse()->getContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider forbiddenRouteProvider
|
||||
*/
|
||||
public function testForbiddenRoutes($route)
|
||||
{
|
||||
$app = new Application('test');
|
||||
$app['dispatcher']->addSubscriber(new SessionManagerSubscriber($app));
|
||||
$app['authentication'] = $this->getMockBuilder('Alchemy\Phrasea\Authentication\Authenticator')->disableOriginalConstructor()->getMock();
|
||||
$app['authentication']->expects($this->never())->method('isAuthenticated');
|
||||
|
||||
$app['EM'] = $this->getMockBuilder('Doctrine\ORM\EntityManager')->disableOriginalConstructor()->getMock();
|
||||
$app['EM']->expects($this->never())->method('flush');
|
||||
|
||||
|
||||
$app->get('/login', function () {
|
||||
return '';
|
||||
})->bind("homepage");
|
||||
|
||||
$app->get($route, function () {
|
||||
return '';
|
||||
});
|
||||
|
||||
$client = new Client($app);
|
||||
$client->request('GET', $route, array(), array(), array(
|
||||
'HTTP_CONTENT-TYPE' => 'application/json',
|
||||
'HTTP_ACCEPT' => 'application/json',
|
||||
'HTTP_X-Requested-With' => 'XMLHttpRequest',
|
||||
));
|
||||
}
|
||||
|
||||
public function forbiddenRouteProvider()
|
||||
{
|
||||
return array(
|
||||
array('/admin/databox/17/informations/documents/'),
|
||||
array('/admin/task-manager/tasks/'),
|
||||
);
|
||||
}
|
||||
}
|
@@ -9,6 +9,6 @@ class PhraseaAuthenticationFormTest extends FormTestCase
|
||||
{
|
||||
protected function getForm()
|
||||
{
|
||||
return new PhraseaAuthenticationForm();
|
||||
return new PhraseaAuthenticationForm(self::$DI['app']);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user