diff --git a/VERSIONS_HOTLINE/84068 b/VERSIONS_HOTLINE/84068 new file mode 100644 index 0000000000000000000000000000000000000000..08a9b982ecd5e86851d9867291cb9ae8cf98b18a --- /dev/null +++ b/VERSIONS_HOTLINE/84068 @@ -0,0 +1 @@ + - ticket #84068 : SIGB Orphee: correction du fonctionnement lorque le webservice n'utilise pas de mots de passe \ No newline at end of file diff --git a/library/Class/WebService/SIGB/AbstractRESTService.php b/library/Class/WebService/SIGB/AbstractRESTService.php index 3b681672b16c600d9c59f928b25140133e36db36..5cd7289ac0035152d4a90fc3967d639191320280 100644 --- a/library/Class/WebService/SIGB/AbstractRESTService.php +++ b/library/Class/WebService/SIGB/AbstractRESTService.php @@ -188,7 +188,6 @@ abstract class Class_WebService_SIGB_AbstractRESTService extends Class_WebServic public function ilsdiGetPatronInfo($params, $reader, $error_tag='error') { $emprunteur = Class_WebService_SIGB_Emprunteur::newInstance()->setService($this); $params = array_merge(array('service' => 'GetPatronInfo'), $params); - $xml = $this->httpGet($params); if (0 === strpos($xml, '<html>')) diff --git a/library/Class/WebService/SIGB/Orphee/Service.php b/library/Class/WebService/SIGB/Orphee/Service.php index 7f8dc166c1ca65efd15952a8f750b5dabe1c1e51..59a2fc9ff15add2353f6be50e779e60591ccb4f1 100644 --- a/library/Class/WebService/SIGB/Orphee/Service.php +++ b/library/Class/WebService/SIGB/Orphee/Service.php @@ -163,7 +163,7 @@ class Class_WebService_SIGB_Orphee_Service extends Class_WebService_SIGB_Abstrac protected function hasGetAdh() { - return $this->_search_client->hasFunction('GetAdh'); + return $this->_search_client && $this->_search_client->hasFunction('GetAdh'); } diff --git a/library/ZendAfi/Auth.php b/library/ZendAfi/Auth.php index e139e270595b09471ca9447c19acf61dc5c4c7fb..1d76b019015023c4d6a028699183f3c43ec1d00e 100644 --- a/library/ZendAfi/Auth.php +++ b/library/ZendAfi/Auth.php @@ -55,14 +55,9 @@ class ZendAfi_Auth extends Zend_Auth { public function newAuthDb() { - if ($this->_auth_db_adapter) - return $this->_auth_db_adapter; - - $authAdapter = new ZendAfi_Auth_Adapter_DbTable(Zend_Db_Table::getDefaultAdapter()); - $authAdapter->setTableName('bib_admin_users'); - $authAdapter->setIdentityColumn('LOGIN'); - $authAdapter->setCredentialColumn('PASSWORD'); - return $this->_auth_db_adapter = $authAdapter; + return $this->_auth_db_adapter + ? $this->_auth_db_adapter + : $this->_auth_db_adapter = new ZendAfi_Auth_Adapter_DbTable(); } diff --git a/library/ZendAfi/Auth/Adapter/Abstract.php b/library/ZendAfi/Auth/Adapter/Abstract.php new file mode 100644 index 0000000000000000000000000000000000000000..0f3b16eaf6cffc21ee6fe7210b2b257c0ca4d0c4 --- /dev/null +++ b/library/ZendAfi/Auth/Adapter/Abstract.php @@ -0,0 +1,60 @@ +<?php +/** + * Copyright (c) 2012-2018, 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 ZendAfi_Auth_Adapter_Abstract implements Zend_Auth_Adapter_Interface { + protected + $_identity, + $_credential, + $_authenticated_user; + + public function shouldBreakChain() { + return false; + } + + + /** + * @param string $identity + * @return Zend_Auth_Adapter_Abstract + */ + public function setIdentity($identity) { + $this->_identity = $identity; + return $this; + } + + + /** + * @param string $credential + * @return Zend_Auth_Adapter_Abstract + */ + public function setCredential($credential) { + $this->_credential = $credential; + return $this; + } + + + /** + * @return Std_Class + */ + public function getResultObject() { + return $this->_authenticated_user->toStdClass(); + } +} diff --git a/library/ZendAfi/Auth/Adapter/CommSigb.php b/library/ZendAfi/Auth/Adapter/CommSigb.php index 7e5287aa02a7307598e946a24c85180455eb363e..246b99cd9f64c0334136cd7686fdfcfd27f3bc71 100644 --- a/library/ZendAfi/Auth/Adapter/CommSigb.php +++ b/library/ZendAfi/Auth/Adapter/CommSigb.php @@ -19,34 +19,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class ZendAfi_Auth_Adapter_CommSigb implements Zend_Auth_Adapter_Interface { +class ZendAfi_Auth_Adapter_CommSigb extends ZendAfi_Auth_Adapter_Abstract { protected - $_identity = null, - $_credential = null, - $_authenticated_user = null, $_called_services = []; - - /** - * @param string $identity - * @return Zend_Auth_Adapter_CommSigb - */ - public function setIdentity($identity) { - $this->_identity = $identity; - return $this; - } - - - /** - * @param string $credential - * @return Zend_Auth_Adapter_CommSigb - */ - public function setCredential($credential) { - $this->_credential = $credential; - return $this; - } - - /** * @return Zend_Auth_Result */ @@ -145,6 +121,9 @@ class ZendAfi_Auth_Adapter_CommSigb implements Zend_Auth_Adapter_Interface { protected function _getUserFromSigbWithLibrary($user, $library) { $this->_called_services[] = $service = $library->getSIGBComm(); + if (!$service->providesAuthentication()) + return; + if (!$loaner = $service->getEmprunteur($user)) return; @@ -206,12 +185,4 @@ class ZendAfi_Auth_Adapter_CommSigb implements Zend_Auth_Adapter_Interface { $this->_authenticated_user = $user; return $result; } - - - /** - * @return Std_Class - */ - public function getResultObject() { - return $this->_authenticated_user->toStdClass(); - } } diff --git a/library/ZendAfi/Auth/Adapter/DbTable.php b/library/ZendAfi/Auth/Adapter/DbTable.php index 4c0c9395ae33d77deedd30ed6a1f2c64a441cc8b..f46c611f23411861975ff65a6be822066258dac0 100644 --- a/library/ZendAfi/Auth/Adapter/DbTable.php +++ b/library/ZendAfi/Auth/Adapter/DbTable.php @@ -20,43 +20,24 @@ */ -class ZendAfi_Auth_Adapter_DbTable extends Zend_Auth_Adapter_DbTable { - public function shouldBreakChain() { - return false; - } - +class ZendAfi_Auth_Adapter_DbTable extends ZendAfi_Auth_Adapter_Abstract { - /** - * _authenticateCreateSelect() - This method creates a Zend_Db_Select object that - * is completely configured to be queried against the database. - * - * @return Zend_Db_Select - */ - protected function _authenticateCreateSelect() { - if (empty($this->_credentialTreatment) - || (strpos($this->_credentialTreatment, "?") === false)) - $this->_credentialTreatment = '?'; + /** @return Zend_Auth_Result */ + public function authenticate() { + $type = ($this->_authenticated_user = Class_Users::findFirstBy(['login' => $this->_identity, + 'password' => $this->_credential, + 'password not' => ''])) + ? Zend_Auth_Result::SUCCESS + : Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; - $credentialExpression = new Zend_Db_Expr( - '(CASE WHEN ' . - $this->_zendDb->quoteInto( - $this->_zendDb->quoteIdentifier($this->_credentialColumn, true) - . ' = ' . $this->_credentialTreatment, $this->_credential - ) - . ' THEN 1 ELSE 0 END) AS ' - . $this->_zendDb->quoteIdentifier('zend_auth_credential_match') - ); - - return $this->_zendDb - ->select() - ->from($this->_tableName, array('*', $credentialExpression)) - ->where($this->_zendDb->quoteIdentifier($this->_identityColumn, true) . ' = ?', $this->_identity) - ->where($this->_zendDb->quoteIdentifier($this->_credentialColumn, true) . ' = ?', $this->_credential) - ->where($this->_zendDb->quoteIdentifier($this->_credentialColumn, true) . ' <> ?', ''); + return new Zend_Auth_Result($type, $this->_identity); } public function getResultObject() { - return $this->getResultRowObject(null,'password'); + $object = parent::getResultObject(); + unset($object->password); + + return $object; } } diff --git a/tests/application/modules/admin/controllers/ModoControllerTest.php b/tests/application/modules/admin/controllers/ModoControllerTest.php index 99ea0719571d9e5dbf5491478b9092998dbbe2ab..66aac78b3b91ab7a12bf6b95e646b55ec184bff6 100644 --- a/tests/application/modules/admin/controllers/ModoControllerTest.php +++ b/tests/application/modules/admin/controllers/ModoControllerTest.php @@ -1061,7 +1061,7 @@ class ModoControllerDeleteExpiredRegistrationsTest extends Admin_AbstractControl ['id' => 1, 'login' => 'pwd', 'password' => 'pwd', - 'date' => '2018-16-01', + 'date' => '2048-12-01', 'mail' => 'pwd@afi-sa.fr']); $this->fixture('Class_UsersNonValid', diff --git a/tests/application/modules/opac/controllers/AuthControllerTest.php b/tests/application/modules/opac/controllers/AuthControllerTest.php index 57ae9afff599c8baf456f1fb21c8ca1cb2776998..cf681457a8a5378df4ccbd9af01c4c2021720085 100644 --- a/tests/application/modules/opac/controllers/AuthControllerTest.php +++ b/tests/application/modules/opac/controllers/AuthControllerTest.php @@ -2496,6 +2496,9 @@ class AuthControllerPostWithSameIdSigbTest extends AbstractControllerTestCase { ->answers([]) ->whenCalled('isConnected') + ->answers(true) + + ->whenCalled('providesAuthentication') ->answers(true); $params = ['url_serveur' => 'http://mon-koha-de-test.org', @@ -2588,6 +2591,9 @@ class AuthControllerPostLoginWithDifferentIdIntBibTest ->answers($emprunteur) ->whenCalled('isConnected') + ->answers(true) + + ->whenCalled('providesAuthentication') ->answers(true); $params = ['url_serveur' => 'http://mon-koha-de-test.org', diff --git a/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php b/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php index 22ad1712f2bff501b5e10c8f965be2a4a6f6b7a4..9a6a8382b54da51154f51643a9047071e77ce449 100644 --- a/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php +++ b/tests/application/modules/opac/controllers/AuthControllerWithoutPasswordTest.php @@ -114,6 +114,9 @@ class AuthControllerWithoutPasswordKohaTest extends AuthControllerWithoutPasswor ->answers($this->_emprunteur) ->whenCalled('isConnected') + ->answers(true) + + ->whenCalled('providesAuthentication') ->answers(true); } diff --git a/tests/library/Class/WebService/SIGB/BiblixNetTest.php b/tests/library/Class/WebService/SIGB/BiblixNetTest.php index 597f9c842995f00c4ab3d34d11bde0c72d9eb5c1..908fbda5dea3cad5175e09398df75ddcf09e657f 100644 --- a/tests/library/Class/WebService/SIGB/BiblixNetTest.php +++ b/tests/library/Class/WebService/SIGB/BiblixNetTest.php @@ -366,7 +366,7 @@ class BiblixAuthenticateTest extends ModelTestCase { $mock = $this ->mock() ->whenCalled('open_url') - ->with('http://www.annecy.fr/webservice/biblix.ilsdi?service=GetPatronInfo&showLoans=1&showHolds=1') + ->with('http://www.annecy.fr/webservice/biblix.ilsdi?service=GetPatronInfo&patronId=28&showLoans=1&showHolds=1') ->answers(BiblixNetFixtures::xmlGetPatronJustinTicou()) ->beStrict(); @@ -375,38 +375,57 @@ class BiblixAuthenticateTest extends ModelTestCase { $service->setWebClient($mock); - $this->fixture('Class_IntBib', - ['id' => 94, - 'comm_sigb' => Class_IntBib::COM_BIBLIXNET, - 'comm_params' => ['url_serveur' => 'http://www.annecy.fr/webservice/biblix.ilsdi']]); - $logger = $this - ->mock() - ->whenCalled('log') - ->answers(true) - - ->whenCalled('logError') - ->willDo(function($url, $message) { - throw new RuntimeException($url . ' :: ' . $message); - }); - - Class_WebService_SIGB_AbstractService::setLogger($logger); + $this->fixture('Class_Users', + ['id' => 12, + 'login' => '10102003', + 'password' => 'secret', + 'idabon' => '87364', + 'id_sigb' => 28, + 'int_bib' => $this->fixture('Class_IntBib', + ['id' => 94, + 'comm_sigb' => Class_IntBib::COM_BIBLIXNET, + 'comm_params' => ['url_serveur' => 'http://www.annecy.fr/webservice/biblix.ilsdi']]), + 'bib' => $this->fixture('Class_Bib', + ['id' => 94, + 'id_site' => 94])]) + ->beAbonneSIGB() + ->assertSave(); - $errors = ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'secret'); + ZendAfi_Auth::getInstance()->clearIdentity(); } public function tearDown() { Class_WebService_SIGB_BiblixNet::reset(); - Class_WebService_SIGB_AbstractService::setLogger(null); parent::tearDown(); } /** @test */ - public function EvelyneShouldBeLogged() { + public function withRightCredentialsJustinShouldBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'secret'); + if (!$user = Class_Users::getIdentity()) return $this->fail(); + $this->assertEquals('10102003', $user->getLogin()); + return $user->getEmprunts(); + } + + + /** + * @depends withRightCredentialsJustinShouldBeLogged + * @test + */ + public function justinfShouldHaveTwoLoans($loans) { + $this->assertCount(2, $loans); + } + + + /** @test */ + public function withWrongCredentialsJustinShouldNotBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'oups'); + $this->assertEmpty(Class_Users::getIdentity()); } } \ No newline at end of file diff --git a/tests/library/Class/WebService/SIGB/OrpheeServiceTest.php b/tests/library/Class/WebService/SIGB/OrpheeServiceTest.php index defadebe3d46013d3fc99d2aa6026bb9414e13f1..e9db8d1bb51285f0d2c796bf4422255bae0a72d4 100644 --- a/tests/library/Class/WebService/SIGB/OrpheeServiceTest.php +++ b/tests/library/Class/WebService/SIGB/OrpheeServiceTest.php @@ -73,7 +73,6 @@ class OrpheeServiceGetServiceTest extends ModelTestCase { abstract class OrpheeServiceTestCase extends ModelTestCase { protected - $_storm_default_to_volatile = true, $_search_client, $_orphee, $_orphee_allow_hold_avail, @@ -93,16 +92,28 @@ abstract class OrpheeServiceTestCase extends ModelTestCase { ->answers(new EndSessionResponse()); $this->_beforeOrpheeServiceCreate(); - $this->_orphee = new Class_WebService_SIGB_Orphee_ServiceForTesting($this->_search_client); + $allow_hold_available_items=true; $this->_orphee_allow_hold_avail = Class_WebService_SIGB_Orphee_Service::getService('tests/fixtures/orphee.wsdl', null, $allow_hold_available_items); $this->_orphee_allow_hold_avail->setSearchClient($this->_search_client); $this->_orphee_allow_hold_avail->isConnected(); - $this->_henry_dupont = Class_Users::getLoader() - ->newInstanceWithId(2) - ->setLogin('10900000753') - ->setPassword('secret'); + $this->_orphee = new Class_WebService_SIGB_Orphee_ServiceForTesting($this->_search_client); + Class_WebService_SIGB_Orphee::setService($this->_orphee); + + $this->_henry_dupont = $this->fixture('Class_Users', + ['id' => 2, + 'login' => '10900000753', + 'idabon' => '100753', + 'password' => 'secret', + 'int_bib' => $this->fixture('Class_IntBib', + ['id' => 34, + 'comm_sigb' => Class_IntBib::COM_ORPHEE, + 'comm_params' => ['url_serveur' => 'tests/fixtures/orphee.wsdl', + 'allow_hold_available_items' => true]]), + 'bib' => $this->fixture('Class_Bib', + ['id' => 34])]); + $this->_henry_dupont->beAbonneSIGB()->assertSave(); } @@ -1545,3 +1556,73 @@ class OrpheeServiceChangePasswordWithoutClientTest extends ModelTestCase { parent::tearDown(); } } + + + + +class OrpheeAuthenticateLocallyTest extends OrpheeServiceTestCase { + public function setUp() { + parent::setUp(); + ZendAfi_Auth::getInstance()->clearIdentity(); + } + + + /** @test */ + public function withRightCredentialsHenryShouldBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10900000753', 'secret'); + $this->assertNotNull($user = Class_Users::getIdentity()); + $this->assertEquals('10900000753', $user->getLogin()); + } + + + /** @test */ + public function withWrongCredentialsJustinShouldNotBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10900000753', 'oups'); + $this->assertEmpty(Class_Users::getIdentity()); + } +} + + + + +class OrpheeAuthenticateThroughSigbTest extends OrpheeServiceTestCase { + public function setUp() { + parent::setUp(); + + Class_AdminVar::set('LOGIN_THROUGH_SIGB_ONLY', 1); + + $this->_search_client + ->whenCalled('hasFunction')->with('GetAdh')->answers(true) + + ->whenCalled('GetAdh') + ->willDo( + function($param) { + $response = ($param->carte == '10900000753' && $param->pwd == 'secret') + ? OrpheeFixtures::xmlGetAdhHenryDupont() + : ''; + + return $this->mock() + ->whenCalled('getXml') + ->answers($response); + }) + + ->whenCalled('EndSession')->with(new EndSession())->answers(true); + + ZendAfi_Auth::getInstance()->clearIdentity(); + } + + + /** @test */ + public function withRightCredentialsHenryShouldBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10900000753', 'secret'); + $this->assertNotNull($user = Class_Users::getIdentity()); + $this->assertEquals('10900000753', $user->getLogin()); + } + + + /** @test */ + public function withWrongCredentialsJustinShouldNotBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10900000753', 'oups'); + $this->assertEmpty(Class_Users::getIdentity()); + } +} \ No newline at end of file diff --git a/tests/library/Class/WebService/SIGB/VSmartAuthenticateTest.php b/tests/library/Class/WebService/SIGB/VSmartAuthenticateTest.php index 384aa3183d9dea6579ae6bdc12c85922c4bad756..1157f310d64ecafd1e9360a251220a6b0d5fd621 100644 --- a/tests/library/Class/WebService/SIGB/VSmartAuthenticateTest.php +++ b/tests/library/Class/WebService/SIGB/VSmartAuthenticateTest.php @@ -271,38 +271,58 @@ class VSmartAuthenticateTest extends ModelTestCase { $service->setWebClient($mock); - $this->fixture('Class_IntBib', - ['id' => 94, - 'comm_sigb' => Class_IntBib::COM_VSMART, - 'comm_params' => ['url_serveur' => 'http://www.moulins.fr/webservice/VubisSmartHttpApi.csp']]); - - $logger = $this - ->mock() - ->whenCalled('log') - ->answers(true) - - ->whenCalled('logError') - ->willDo(function($url, $message) { - throw new RuntimeException($url . ' :: ' . $message); - }); - - Class_WebService_SIGB_AbstractService::setLogger($logger); - - $errors = ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'secret'); + $this->fixture('Class_Users', + ['id' => 12, + 'login' => '10102003', + 'password' => 'secret', + 'idabon' => '87364', + 'id_sigb' => 28, + 'int_bib' => $this->fixture('Class_IntBib', + ['id' => 94, + 'comm_sigb' => Class_IntBib::COM_VSMART, + 'comm_params' => ['url_serveur' => 'http://www.moulins.fr/webservice/VubisSmartHttpApi.csp']]), + 'bib' => $this->fixture('Class_Bib', + ['id' => 94, + 'id_site' => 94])]) + ->beAbonneSIGB() + ->assertSave(); + + + ZendAfi_Auth::getInstance()->clearIdentity(); } public function tearDown() { Class_WebService_SIGB_VSmart::reset(); - Class_WebService_SIGB_AbstractService::setLogger(null); parent::tearDown(); } + /** @test */ - public function EvelyneShouldBeLogged() { + public function withRightCredentialsEvelyneShouldBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'secret'); + if (!$user = Class_Users::getIdentity()) return $this->fail(); + $this->assertEquals('10102003', $user->getLogin()); + return $user->getEmprunts(); + } + + + /** + * @depends withRightCredentialsEvelyneShouldBeLogged + * @test + */ + public function justinfShouldHaveThreeLoans($loans) { + $this->assertCount(3, $loans); + } + + + /** @test */ + public function withWrongCredentialsJustinShouldNotBeLogged() { + ZendAfi_Auth::getInstance()->authenticateLoginPassword('10102003', 'oups'); + $this->assertEmpty(Class_Users::getIdentity()); } } \ No newline at end of file diff --git a/tests/library/ZendAfi/Auth/Adapter/AuthCommSigbTest.php b/tests/library/ZendAfi/Auth/Adapter/AuthCommSigbTest.php index b13fb96221bb22941665a65f36ffc050f8705a41..2f53ffd10e425384a622753261830ca4d1c069d7 100644 --- a/tests/library/ZendAfi/Auth/Adapter/AuthCommSigbTest.php +++ b/tests/library/ZendAfi/Auth/Adapter/AuthCommSigbTest.php @@ -108,9 +108,16 @@ abstract class AuthCommSigbWithWebServicesAndAbonneZorkTestCase extends AuthComm 'libelle' => 'Some patrons', 'type_fichier' => Class_IntProfilDonnees::FT_PATRONS]); - Class_WebService_SIGB_Nanook::setService($comm_params, $this->nanook = $this->mock()); - Class_WebService_SIGB_Orphee::setService($this->orphee = $this->mock()); - Class_WebService_SIGB_Opsys::setService($this->opsys = $this->mock()); + Class_WebService_SIGB_Nanook::setService($comm_params, + $this->nanook = $this->mock() + ->whenCalled('providesAuthentication') + ->answers(true)); + Class_WebService_SIGB_Orphee::setService($this->orphee = $this->mock() + ->whenCalled('providesAuthentication') + ->answers(false)); + Class_WebService_SIGB_Opsys::setService($this->opsys = $this->mock() + ->whenCalled('providesAuthentication') + ->answers(true)); $this->nanook ->whenCalled('getEmprunteur') @@ -518,7 +525,9 @@ class AuthCommSigbWithWebServicesAndAbonneZorkAndMinimalResponseTest 'libelle' => 'Some patrons', 'type_fichier' => Class_IntProfilDonnees::FT_PATRONS]); - Class_WebService_SIGB_Opsys::setService($this->opsys = $this->mock()); + Class_WebService_SIGB_Opsys::setService($this->opsys = $this->mock() + ->whenCalled('providesAuthentication') + ->answers(true)); $annecy_library = $this->fixture('Class_Bib', ['id' => 43,