Commit 476bfc52 authored by Laurent's avatar Laurent
Browse files

Merge branch 'dev#101989_gresivaudan_acces_ressources_numeriques_sso_seulement' into 'master'

dev #101989 add digital adapter for Syracuse SSO

See merge request !3399
parents 4323546e 16cc3213
Pipeline #9401 canceled with stage
in 27 minutes and 19 seconds
'101989' =>
['Label' => $this->_('SSO avec les portails Archimed Syracuse pour l'accès aux ressources numériques'),
'Desc' => $this->_('Connecteur de bibliothèque numérique pour accès par exemple aux ressources de la médiathèque départementale si elle utilise le système Syracuse'),
'Image' => '',
'Video' => '',
'Category' => $this->_('Ressources numériques'),
'Right' => function($feature_description, $user) {return true;},
'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php/Syracuse',
'Test' => '',
'Date' => '2020-02-06'],
\ No newline at end of file
- ticket #101989 : SSO avec les portails Archimed Syracuse pour l'accès aux ressources numériques
\ No newline at end of file
......@@ -22,30 +22,46 @@
class ZendAfi_Controller_Action_Helper_CasValidResponse extends Zend_Controller_Action_Helper_Abstract {
public function direct($user, $ticket, $attributes = []) {
$this->getActionController()->getHelper('ViewRenderer')->setNoRender();
$this->getResponse()->setHeader('Content-Type', 'application/xml;charset=utf-8');
$this->getResponse()->setBody("<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>
<cas:authenticationSuccess>
<cas:user>".$user->getId()."</cas:user>
<cas:proxyGrantingTicket>".$ticket."
</cas:proxyGrantingTicket>".
$this->renderAttributes($attributes).
"</cas:authenticationSuccess>
</cas:serviceResponse>");
$this->getResponse()
->setBody("<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>\n"
. " <cas:authenticationSuccess>\n"
. " <cas:user>".$user->getId()."</cas:user>\n"
. " <cas:proxyGrantingTicket>".$ticket."</cas:proxyGrantingTicket>\n"
. $this->renderAttributes($attributes)
. " </cas:authenticationSuccess>\n"
. "</cas:serviceResponse>");
}
public function renderAttributes($attributes) {
if (!$attributes)
return '';
$xml = '';
foreach($attributes as $key => $value) {
$xml .= '<cas:'.$key.'>'.$value.'</cas:'.$key.'>';
}
return '<cas:attributes>'.$xml.'</cas:attributes>';
}
$cas_attributes = [];
$push_attribute = function($name, $value) use (&$cas_attributes) {
$cas_attributes []= sprintf('<cas:%1$s>%2$s</cas:%1$s>',
$name,
$value);
};
array_walk($attributes,
function($value_or_array, $name) use ($push_attribute)
{
if (!is_array($value_or_array))
return $push_attribute($name, $value_or_array);
foreach($value_or_array as $value)
$push_attribute($name, $value);
});
return
" <cas:attributes>\n "
. implode("\n ", $cas_attributes)
. "\n </cas:attributes>\n";
}
}
?>
\ No newline at end of file
......@@ -93,17 +93,30 @@ class SkilleosModulesControllerUserWithGroupWithRightTest
}
/** @test */
public function validateWithValidUserTicketShouldAnswerSuccessAndLibraryName() {
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->dispatch('/Skilleos_Plugin/auth/servicevalidate/service/skilleos/ticket/' . $ticket,
true);
$this->assertContains('<cas:user>666</cas:user>',$this->_response->getBody());
$this->assertContains('<cas:sn>jumper</cas:sn><cas:mail>jolly@jumper.com</cas:mail><cas:givenName>jolly</cas:givenName><cas:bibId>Tombouctou</cas:bibId>',$this->_response->getBody());
public function expectedCasTags() {
return [
['user', 666],
['sn', 'jumper'],
['givenName', 'jolly'],
['mail', 'jolly@jumper.com'],
['bibId', 'Tombouctou'],
];
}
/**
* @dataProvider expectedCasTags
* @test
*/
public function validateWithValidUserTicketShouldAnswerSuccessAndAttributes($tag, $value) {
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->dispatch('/Skilleos_Plugin/auth/servicevalidate/service/skilleos/ticket/' . $ticket);
$this->assertContains('<cas:' . $tag .'>'
. (is_callable($value) ? $value() : $value)
. '</cas:' . $tag . '>',
$this->_response->getBody());
}
}
......
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Syracuse_Config extends Class_DigitalResource_Config {
protected function _getConfig() {
return ['Introduction' => $this->_('Connexion avec une médiathèque départementale utilisant Syracuse pour l\'accès aux ressources numériques'),
'AdminVars' => ['SSO_URL' => Class_AdminVar_Meta::newDefault($this->_('URL du serveur CAS Syracuse'))
->bePrivate()],
'PermissionLabel' => $this->_('Bibliothèque numérique: accéder aux ressources Syracuse'),
'SsoAction' => true,
'Harvesting' => false,
'HelpLink' => 'http://wiki.bokeh-library-portal.org/index.php/Syracuse',
'Url' => 'https://www.archimed.fr/module-solutions-pour-bibliotheque/bibliotheque-numerique/',
'Icon' => 'https://www.archimed.fr/wp-content/themes/wp-insita-bibdoc/images/logo.png',
'MenuLabel' => $this->_('Lien vers Syracuse'),
'ModuleMenu' => $this->withNameSpace('ModuleMenu'),
'NotAllowedMessage' => $this->_('Vous devez être abonné pour accéder à cette ressource.'),
];
}
public function getSsoUrl($user) {
return $this->getAdminVar('SSO_URL') . '&ticket='.(new Class_CasTicket())->getTicketForUser($user);
}
public function isEnabled() {
return '' != $this->getAdminVar('SSO_URL');
}
public function renderCustomDiagOn($view) {
return (new Syracuse_View_Helper_Dashboard)
->setView($view)
->dashboard();
}
public function getSsoValidateUrl() {
return Class_Url::absolute(['controller' => 'auth',
'action' => 'serviceValidate']);
}
public function validateUrlFor($user) {
return $this->getSsoValidateUrl()
. '?'
. http_build_query(['service' => 'bokeh-test',
'ticket' => (new Class_CasTicket())->getTicketForUser($user)]);
}
}
?>
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Syracuse_ModuleMenu extends Class_DigitalResource_ModuleMenu {}
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Syracuse_View_Helper_Dashboard extends ZendAfi_View_Helper_BaseHelper {
public function dashboard() {
return
$this->_tag('h3', $this->_('Informations à transmettre à Archimed'))
.
$this->_tag('h4', $this->_('Configuration du serveur CAS'))
.
$this->_tag('dl',
$this->_tag('dt', $this->_('URL d\'authentification'))
.
$this->_tag('dd',
$this->_tag('pre',
Class_Url::absolute(['module' => 'opac',
'controller' => 'cas-server',
'action' => 'login'])))
.
$this->_tag('dt', $this->_('URL de validation'))
.
$this->_tag('dd',
$this->_tag('pre',
Class_Url::absolute(['controller' => 'auth',
'action' => 'serviceValidate']))));
}
}
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Syracuse_Plugin_AuthController extends Class_DigitalResource_Controller {
public function servicevalidateAction() {
$service = $this->_request->getParam('service');
$ticket = $this->_request->getParam('ticket');
if (strlen($ticket)<1 || strlen($service)<1) {
return $this->_helper->casFailureResponse('INVALID_REQUEST');
}
if (!$user = (new Class_CasTicket())->userForTicket($ticket))
return $this->_helper->casFailureResponse('INVALID_TICKET',$ticket);
$attributes = ['lastname' => $user->getNom(),
'firstname' => $user->getPrenom(),
'mail' => $user->getMail(),
'expire_at' => $user->getDateFin(),
'card_number' => $user->getIdabon(),
'site_code' => $user->getLibraryId(),
'site_label' => $user->getLibelleBib(),
'birth_date' => $user->getNaissance(),
'postal_code' => $user->getCodePostal(),
'city' => $user->getVille(),
'affiliation' => array_map(
function($group) {
return $group->getLibelle();
},
$user->getUserGroups())
];
return $this->_helper->casValidResponse($user, $ticket, $attributes);
}
}
?>
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Syracuse_Plugin_IndexController extends Class_DigitalResource_Controller {}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2020, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
abstract class SyracuseModulesControllerTestCase extends AbstractControllerTestCase {
protected
$_storm_default_to_volatile = true,
$_user;
public function setUp() {
parent::setUp();
Storm_Cache::beVolatile();
Class_AdminVar::set('Syracuse_SSO_URL',
'https://mediatheque.departementale.fr/default.aspx?forceLogon=CG_CAS_BDP');
$this->fixture('Class_Bib',
['id' => 34,
'libelle' => 'Gresivaudan']);
$this->_user = $this->fixture('Class_Users',
['id' => 10,
'nom' => 'Pouce',
'prenom' => 'Tom',
'date_fin' => '2020-02-12',
'naissance' => '1978-08-02',
'code_postal' => '38123',
'ville' => 'Laval',
'idabon' => '123XPE',
'id_site' => 34,
'mail' => 'tom@pouce.fr',
'login' => 'Tom',
'password' => 'pwd']);
$this->_user
->addUserGroup($this->fixture('Class_UserGroup',
['id' => 1,
'libelle' => 'supremes']))
->addUserGroup($this->fixture('Class_UserGroup',
['id' => 2,
'libelle' => 'temptations']))
->assertSave();
ZendAfi_Auth::getInstance()->logUser($this->_user);
}
}
class SyracuseModulesControllerTest extends SyracuseModulesControllerTestCase {
/** @test */
public function userWithoutSIGBAccountShouldNotHaveAccessToSyracuse() {
$this->dispatch('/opac/modules/syracuse');
$this->assertFlashMessengerContentContains('Vous devez être abonné pour accéder à cette ressource.');
}
/** @test */
public function userWithoutRightsShouldBeRedirectedToSyracuse() {
$this->_user
->beAbonneSIGB()
->setDateFin('2099-02-12')
->setIdabon('34245')
->setBib($this->fixture('Class_Bib',
['id' => 2,
'libelle' => 'Chambe']))
->assertSave();
$this->dispatch('/opac/modules/syracuse');
$this->assertFlashMessengerContentContains('Vous devez être abonné pour accéder à cette ressource.');
}
/** @test */
public function userGroupPermissionsShouldContainsSyracuse() {
$this->assertContains('Bibliothèque numérique: accéder aux ressources Syracuse',
Class_DigitalResource::getInstance()->getPermissionsLabel());
}
/** @test */
public function validateAuthWithoutTicketShouldAnswerError() {
$this->dispatch('/Syracuse_Plugin/auth/servicevalidate');
$this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">',
$this->_response->getBody());
}
/** @test */
public function validateAuthWithValidTicketButNoServiceShouldAnswerErrorINVAID_REQUEST() {
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->dispatch('/Syracuse_Plugin/auth/servicevalidate/ticket/' . $ticket);
$this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">',
$this->_response->getBody());
}
/** @test */
public function validateAuthWithInvalidTicketShouldAnswerError() {
$this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/blabla/ticket/666');
$this->assertContains('<cas:authenticationFailure code="INVALID_TICKET"> Ticket 666 not recognized</cas:authenticationFailure>',
$this->_response->getBody());
}
public function expectedCasTags() {
return [
['user', 10],
['proxyGrantingTicket', function() { return (new Class_CasTicket())->getTicketForCurrentUser(); }],
['lastname', 'Pouce'],
['firstname', 'Tom'],
['mail', 'tom@pouce.fr'],
['expire_at', '2020-02-12'],
['card_number', '123XPE'],
['site_code', 34],
['site_label', 'Gresivaudan'],
['birth_date', '1978-08-02'],
['affiliation', 'supremes'],
['affiliation', 'temptations'],
['postal_code', '38123'],
['city', 'Laval']
];
}
/**
* @dataProvider expectedCasTags
* @test
*/
public function validateWithValidUserTicketShouldAnswerSuccessAndAttributes($tag, $value) {
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/syracuse/ticket/' . $ticket);
$this->assertContains('<cas:' . $tag .'>'
. (is_callable($value) ? $value() : $value)
. '</cas:' . $tag . '>',
$this->_response->getBody());
}
/** @test */
public function validateWithValidUserTicketShouldContainsFifteenCasAttributes() {
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->dispatch('/Syracuse_Plugin/auth/servicevalidate/service/syracuse/ticket/' . $ticket);
preg_match_all('/<cas:[^ >]+>/',
$this->_response->getBody(),
$matches);
$attributes = array_diff($matches[0], ['<cas:authenticationSuccess>']);
$this->assertCount(15, $attributes);
}
/** @test */
public function modulesMenuShouldBeAvailable() {
$menu = new Class_Systeme_ModulesMenu;
$this->assertEquals('Syracuse', $menu->getFonction('Syracuse')->getType());
}
/** @test */
public function dashboardShouldContainsUrlCasServiceValidate() {
$this->dispatch('/Syracuse_Plugin');
$this->assertXPathContentContains('//dl/dd/pre',
'http://localhost' . Class_Url::baseUrl() . '/Syracuse_Plugin/auth/serviceValidate',
$this->_response->getBody());
}
/** @test */
public function dashboardShouldContainsUrlCasLogin() {
$this->dispatch('/Syracuse_Plugin');
$this->assertXPathContentContains('//dl/dd/pre',
'http://localhost' . Class_Url::baseUrl() . '/cas-server/login',
$this->_response->getBody());
}
}
class SyracuseModulesControllerWithRightsTest extends SyracuseModulesControllerTestCase {
public function setUp() {
parent::setUp();
$group = $this->fixture('Class_UserGroup',
['id' => 3,
'libelle' => 'multimedia']);
$this->fixture('Class_Permission',
['id' => 1,
'code' => 'Syracuse'])
->permitTo($group, new Class_Entity());
$this->_user->beAbonneSIGB()
->setDateFin('2099-02-12')
->setIdabon('34245')
->setBib($this->fixture('Class_Bib',
['id' => 2,
'libelle' => 'Gresivaudan']))
->addUserGroup($group)
->assertSave();
}
/** @test */
public function userWithRightsShouldBeRedirectedToSyracuseWithCasTicket() {
$this->dispatch('/opac/modules/syracuse');
$ticket = (new Class_CasTicket())->getTicketForCurrentUser();
$this->assertXpathContentContains('//script', 'document.location.href="https://mediatheque.departementale.fr/default.aspx?forceLogon=CG_CAS_BDP&ticket=' . $ticket . '";');
}
/** @test */
public function pageShouldContainsUrlSsoTitle() {
$this->dispatch('/Syracuse_Plugin');
$this->assertXPathContentContains('//h4', 'URL SSO générée par /modules/syracuse pour l\'utilisateur "Syracuse_test_user"');
}
/** @test */
public function pageShouldContainsSsoUrlForServiceValidate() {
$this->dispatch('/Syracuse_Plugin');
$ticket = (new Class_CasTicket())->getTicketForUser(Class_Users::findFirstBy(['login' => 'Syracuse_test_user']));
$this->assertContains('<pre>http://localhost' . Class_Url::baseUrl() . '/Syracuse_Plugin/auth/serviceValidate?service=bokeh-test&ticket=' . $ticket . '</pre>',
$this->_response->getBody());
}
}
\ No newline at end of file