diff --git a/VERSIONS_WIP/71949 b/VERSIONS_WIP/71949 new file mode 100644 index 0000000000000000000000000000000000000000..7526dd27210b90ab1865d4d395f39f86aded3367 --- /dev/null +++ b/VERSIONS_WIP/71949 @@ -0,0 +1,2 @@ + - ticket #71949 : Compte abonné : ajout d'un paramètre autorisant la connexion des abonnées uniquement via le webservice du SIGB. + \ No newline at end of file diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index 2fb6bc5ed7733bf857c3061aa44a27d4a221f7e2..60796c91e62b51a482601ffdf202dbac7190c0e7 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -286,6 +286,9 @@ class Class_AdminVarLoader extends Storm_Model_Loader { protected function _getGlobalVars() { return [ 'FORCE_HTTPS' => Class_AdminVar_Meta::newOnOff($this->_('Forcer l\'accès au site par le protocole HTTPS. Nécessite l\'installation et la configuration appropriée du serveur Web')), + 'LOGIN_THROUGH_SIGB_ONLY' => Class_AdminVar_Meta::newOnOff($this->_('Les abonnées peuvent se connecter uniquement via le webservice du SIGB.') + . '<br/>' + . $this->_('De plus, à la connexion, l\'enregistrement des mots de passes des abonnés est désactivé.'))->bePrivate(), 'OAUTH_ACCEPT_HTTP' => Class_AdminVar_Meta::newOnOff($this->_('Autoriser l\'accès aux API OAUTH via HTTP (non sécurisé - déconseillé)'), ['value' => 0]), 'NB_AFFICH_AVIS_PAR_AUTEUR' => Class_AdminVar_Meta::newDefault($this->_('Nombre d\'avis maximum à afficher par utilisateur.')), 'CLEF_GOOGLE_MAP' => Class_AdminVar_Meta::newDefault($this->_('Clef d\'activation pour le plan d\'accès google map. <a target="_blank" href="http://code.google.com/apis/maps/signup.html">Obtenir la clé google map</a>')), @@ -876,6 +879,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader { } + public static function isLoginThroughSigbOnlyEnabled() { + return Class_AdminVar::isModuleEnabled('LOGIN_THROUGH_SIGB_ONLY'); + } + + public function getBabelthequeId() { $mathes = []; if (preg_match('/bw_([^\.]+)\.js/', (string)Class_AdminVar::get('BABELTHEQUE_JS'), $matches)) diff --git a/library/Class/CommSigb.php b/library/Class/CommSigb.php index cf96df07565cc7f5ca0bf61c304201163785ba6f..8270b3565376b07a0a0a1f85e2bd0b5b93b93401 100644 --- a/library/Class/CommSigb.php +++ b/library/Class/CommSigb.php @@ -22,6 +22,7 @@ class Class_CommSigb { use Trait_Translator; protected static $_instance = null; + protected static $_logger; public static function getInstance() { @@ -36,6 +37,18 @@ class Class_CommSigb { } + public static function setLogger($logger) { + static::$_logger = $logger; + } + + + public static function logError($url, $message) { + if (!static::$_logger) + return; + static::$_logger->logError($url, $message); + } + + /** * @param array $exemplaires_to_check * @return array @@ -99,6 +112,7 @@ class Class_CommSigb { try { return ['fiche' => $cache->loadFromCacheOrSIGB($user, $sigb)]; } catch (Exception $e) { + static::logError('', $e->getMessage()); return $this->_error($e->getMessage()); } }; @@ -174,7 +188,7 @@ class Class_CommSigb { */ public function supprimerReservation($std_user, $id_reservation) { $supprimer = function ($user, $sigb) use ($id_reservation) { - return $sigb->supprimerReservation($user, $id_reservation); + return $sigb->supprimerReservation($user, $id_reservation); }; return $this->withUserAndSIGBDo($std_user, $supprimer); @@ -211,7 +225,7 @@ class Class_CommSigb { */ public function prolongerPret($std_user, $id_pret) { $prolonger = function($user, $sigb) use ($std_user, $id_pret) { - return $sigb->prolongerPret($user, $id_pret); + return $sigb->prolongerPret($user, $id_pret); }; return $this->withUserAndSIGBDo($std_user, $prolonger); diff --git a/library/Class/Users.php b/library/Class/Users.php index be6e2ddaac7b6360148d2ea241edf3903c409571..1626de1a33f69d7c40c00c90e2924949f85564ef 100644 --- a/library/Class/Users.php +++ b/library/Class/Users.php @@ -120,33 +120,44 @@ class UsersLoader extends Storm_Model_Loader { * @return bool */ public function hasIdentity() { - return null != $this->getIdentity(); + return null != Class_Users::getIdentity(); + } + + + public function isLogged($user) { + if(!$user) + return false; + + if(!$logged_user = Class_Users::getIdentity()) + return false; + + return $user->getId() == $logged_user->getId(); } public function isCurrentUserAdmin() { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->isAdmin(); } public function isCurrentUserSuperAdmin() { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->isSuperAdmin(); } public function isCurrentUserCanAccesBackend() { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->canAccessBackend(); } public function isCurrentUserCanConfigFront() { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->hasRightConfigFront(); } @@ -157,14 +168,14 @@ class UsersLoader extends Storm_Model_Loader { * @return bool */ public function isCurrentUserCanEditArticle($article) { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->canEditArticle($article); } public function isCurrentUserAllowedToEditLibrary($library) { - if(!$user = $this->getIdentity()) + if(!$user = Class_Users::getIdentity()) return false; return $user->hasRightsForLibrary($library->getId()); } @@ -174,7 +185,7 @@ class UsersLoader extends Storm_Model_Loader { if(!$profile) return false; - if(!$user = $this->getIdentity()) + if(!$user = Class_Users::getIdentity()) return false; if((!$user->isAdminBib() && !$user->hasRightConfigFront()) @@ -190,7 +201,7 @@ class UsersLoader extends Storm_Model_Loader { * @return bool */ public function isCurrentUserCanAccessAllBibs() { - if (!$user = $this->getIdentity()) + if (!$user = Class_Users::getIdentity()) return false; return $user->canAccessAllBibs(); } @@ -1009,19 +1020,18 @@ class Class_Users extends Storm_Model_Abstract { /* Hook appelé sur save */ public function validate() { $this->checkAttribute('login',$this->getLogin(), $this->_("Vous devez compléter le champ 'Identifiant'")); - $this->checkAttribute('password',$this->getPassword(), $this->_("Vous devez compléter le champ 'Mot de passe'")); + + $this->_checkPassword(); $this->checkAttribute('login',mb_strlen($this->getLogin(),'UTF-8') <= 50, $this->_("Le champ 'Identifiant' doit être inférieur à 50 caractères")); - if ($this->isNew()) { + if ($this->isNew()) $this->checkAttribute('login', $this->_isUnique(), $this->_("L'identifiant que vous avez choisi existe déjà .")); - } $this->checkAttribute('password', mb_strlen($this->getPassword(), 'UTF-8') <= 255, $this->_("Le champ 'Mot de passe' doit être inférieur à 255 caractères")); - if ($this->getRoleLevel() > 1 and $this->getRoleLevel() < 5 and $this->getIdSite() == 0) { $cls_role= new ZendAfi_Acl_AdminControllerRoles(); $this->addError($this->_("La bibliothèque est obligatoire pour le rôle : %s", @@ -1046,6 +1056,17 @@ class Class_Users extends Storm_Model_Abstract { } + protected function _checkPassword() { + if (Class_AdminVar::isLoginThroughSigbOnlyEnabled() + && $this->isAbonne()) + return; + + $this->checkAttribute('password', + $this->getPassword(), + $this->_("Vous devez compléter le champ 'Mot de passe'")); + } + + public function deleteUser($id_user) { if ($user = $this->getLoader()->find($id_user)) $user->delete(); diff --git a/library/Class/WebService/SIGB/Koha/Service.php b/library/Class/WebService/SIGB/Koha/Service.php index 1890f779e5a2d2d96d3bdb2fbf1bcb92d15601c9..3e417df0f41c0100b00d0eeddef41993925ba837 100644 --- a/library/Class/WebService/SIGB/Koha/Service.php +++ b/library/Class/WebService/SIGB/Koha/Service.php @@ -110,7 +110,17 @@ class Class_WebService_SIGB_Koha_Service extends Class_WebService_SIGB_AbstractR * @return Class_WebService_SIGB_Emprunteur */ public function getEmprunteur($user) { - if (!$patron_id = $this->_authenticateWebservice($user)) + if(!$user) + return Class_WebService_SIGB_Emprunteur::nullInstance(); + + $patron_id = Class_Users::isLogged($user) + ? $user->getIdSigb() + : ''; + + if(!$patron_id) + $patron_id = $this->_authenticateWebservice($user); + + if (!$patron_id) return Class_WebService_SIGB_Emprunteur::nullInstance(); $emprunteur = $this->getEmprunteurFor($patron_id); diff --git a/library/ZendAfi/Auth/Adapter/CommSigb.php b/library/ZendAfi/Auth/Adapter/CommSigb.php index c750efadcbcbacfb571a1e395ccb5198b706a9d6..dbf23edbae840a11a5d2e00cdb226224d27d753f 100644 --- a/library/ZendAfi/Auth/Adapter/CommSigb.php +++ b/library/ZendAfi/Auth/Adapter/CommSigb.php @@ -63,8 +63,10 @@ class ZendAfi_Auth_Adapter_CommSigb implements Zend_Auth_Adapter_Interface { $user_to_save = $this->_getUserToSave($user_from_sigb) ->beAbonneSIGB() - ->setLogin($this->_identity) - ->setPassword($this->_credential); + ->setLogin($this->_identity); + + if (!Class_AdminVar::isLoginThroughSigbOnlyEnabled()) + $user_to_save->setPassword($this->_credential); $this->_updateOptionalAttribs($user_from_sigb, $user_to_save); @@ -112,28 +114,43 @@ class ZendAfi_Auth_Adapter_CommSigb implements Zend_Auth_Adapter_Interface { protected function _getUserFromSigb($user) { $bibs = $this->_getBibsToAuthenticateTo($user); - foreach($bibs as $bib) { - if (!$emprunteur = $bib->getSIGBComm()->getEmprunteur($user)) - continue; + foreach($bibs as $bib) + if($user_found = $this->_getUserFromSigbWithLibrary($user, $bib)) + return $user_found; - if (!$emprunteur->isValid()) - continue; + return null; + } - if ($emprunteur->getPassword() != $user->getPassword()) - continue; - $emprunteur->updateUser($user); + protected function _getUserFromSigbWithLibrary($user, $library) { + if (!$loaner = $library->getSIGBComm()->getEmprunteur($user)) + return; - if(!$user->hasIdSite()) - $user->setIdSite($bib->getId()); + if (!$loaner->isValid()) + return; - if(!$user->hasIdIntBib()) - $user->setIdIntBib($bib->getId()); + if(!$loaner = $this->_loanerWithValidPassword($user, $loaner)) + return; - return $user; - } + $loaner->updateUser($user); - return null; + if(!$user->hasIdSite()) + $user->setIdSite($library->getId()); + + if(!$user->hasIdIntBib()) + $user->setIdIntBib($library->getId()); + + return $user; + } + + + protected function _loanerWithValidPassword($user, $loaner) { + if (Class_AdminVar::isLoginThroughSigbOnlyEnabled()) + return $loaner->setPassword(''); + + return ($loaner->getPassword() == $user->getPassword()) + ? $loaner + : null; } diff --git a/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php b/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php new file mode 100644 index 0000000000000000000000000000000000000000..22ad1712f2bff501b5e10c8f965be2a4a6f6b7a4 --- /dev/null +++ b/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php @@ -0,0 +1,174 @@ +<?php +/** + * Copyright (c) 2012, 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 + */ + +require_once 'tests/fixtures/KohaFixtures.php'; + +abstract class AuthControllerWithoutPasswordTestCase extends AbstractControllerTestCase { + protected + $_storm_default_to_volatile = true, + $_mock_web_client, + $_emprunteur, + $_service, + $_params; + + public function setUp() { + parent::setUp(); + + $logger = $this->mock() + ->whenCalled('log')->answers(true) + + ->whenCalled('logError') + ->willDo( + function($url, $message) { + throw new RuntimeException($url . ' :: ' . $message); + }); + + Class_CommSigb::setLogger($logger); + Class_WebService_SIGB_AbstractService::setLogger($logger); + + Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 1); + ZendAfi_Auth::getInstance()->clearIdentity(); + + $this->_emprunteur = Class_WebService_SIGB_Emprunteur::newInstance(789, 'koha') + ->setPassword('bar') + ->beValid(); + + $this->_params = ['url_serveur' => 'http://mon-koha-de-test.org', + 'id_bib' => 56, + 'type' => Class_IntBib::COM_KOHA]; + + $this->_setService(); + + Class_WebService_SIGB_Koha::setService($this->_params, + $this->_service); + + $this->fixture('Class_Bib', + ['id' => 56, + 'libelle' => 'Library']); + + $this->fixture('Class_IntBib', + ['id' => 56, + 'id_bib' => 56, + 'comm_sigb' => 5, + 'comm_params' => serialize($this->_params)]); + + $this->fixture('Class_Users', + ['id' => 5, + 'login' => 'foo', + 'password' => '', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => 'foo', + 'id_site' => 56, + 'id_int_bib' => 56, + 'id_sigb' => 789]); + } + + + protected function _setService() {} + + + public function tearDown() { + Class_CommSigb::setLogger(null); + Class_WebService_SIGB_AbstractService::setLogger(null); + Class_WebService_SIGB_Koha::reset(); + $this->_mock_web_client = null; + $this->_service = null; + $this->_emprunteur = null; + $this->_params = null; + parent::tearDown(); + } +} + + + +class AuthControllerWithoutPasswordKohaTest extends AuthControllerWithoutPasswordTestCase { + + public function setUp() { + parent::setUp(); + $this->postDispatch('/opac/auth/login', ['username' => 'foo', 'password' => 'bar']); + } + + + protected function _setService() { + $this->_service = $this->mock() + + ->whenCalled('getEmprunteur') + ->answers($this->_emprunteur) + + ->whenCalled('isConnected') + ->answers(true); + } + + + /** @test */ + public function userFooShouldBeLogged() { + $this->assertEquals('foo', Class_Users::getIdentity()->getLogin()); + } + + + /** @test */ + public function userFooPasswordShouldHaveNotBeenSet() { + $this->assertEquals('', Class_Users::getIdentity()->getPassword()); + } +} + + + + +class AuthControllerDispatchAbonnePretsWithoutPasswordKohaTest extends AuthControllerWithoutPasswordTestCase { + + public function setUp() { + parent::setUp(); + ZendAfi_Auth::getInstance()->logUser(Class_Users::find(5)); + $this->dispatch('/opac/abonne/prets', true); + } + + + protected function _setService() { + $this->_mock_web_client = $this->mock() + ->whenCalled('open_url') + ->with('http://mon-koha-de-test.org?service=GetPatronInfo&patron_id=789&show_contact=1&show_loans=1&show_holds=1') + ->answers('') + + ->beStrict(); + + $this->_service = Class_WebService_SIGB_Koha::getService($this->_params); + $this->_service->setWebClient($this->_mock_web_client); + } + + + /** @test */ + public function authenticateShouldNotBeCall() { + $this->assertFalse( + $this->_mock_web_client + ->methodHasBeenCalledWithParams('open_url', + ['http://mon-koha-de-test.org?service=AuthenticatePatron&username=foo&password='])); + } + + + /** @test */ + public function patronInfoShouldBeCallWithPatronId789() { + $this->assertTrue( + $this->_mock_web_client + ->methodHasBeenCalledWithParams('open_url', + ['http://mon-koha-de-test.org?service=GetPatronInfo&patron_id=789&show_contact=1&show_loans=1&show_holds=1'])); + } +} \ No newline at end of file