diff --git a/VERSIONS_HOTLINE/180943 b/VERSIONS_HOTLINE/180943 new file mode 100644 index 0000000000000000000000000000000000000000..fe06957dded29392460f2538c05e8dfbb3cffd2f --- /dev/null +++ b/VERSIONS_HOTLINE/180943 @@ -0,0 +1 @@ + - correctif #180943 : Compte lecteur : amélioration de la connexion via les fournisseurs d'identités. \ No newline at end of file diff --git a/application/modules/opac/controllers/AuthController.php b/application/modules/opac/controllers/AuthController.php index 7392c334e45d4afad94993b6b60944e322d25ea6..8a99004d76230fc2638d9982794bdf2d11190358 100644 --- a/application/modules/opac/controllers/AuthController.php +++ b/application/modules/opac/controllers/AuthController.php @@ -26,6 +26,7 @@ class AuthController extends ZendAfi_Controller_Action { LOST_PASS_NOUSER = 2, LOST_PASS_NOMAIL = 4; + public function init() { $this->view->locale = Zend_Registry::get('locale'); } diff --git a/library/Class/Auth/IdentityProvider.php b/library/Class/Auth/IdentityProvider.php index acc0ff33b4a731fe2d4e75fd7bcbf448c3957848..c49d4f2f0d0a664b721681343f910551ca92ff7f 100644 --- a/library/Class/Auth/IdentityProvider.php +++ b/library/Class/Auth/IdentityProvider.php @@ -21,16 +21,40 @@ class Class_Auth_IdentityProvider { - public static function newFor($controller) { - $request = $controller->getRequest(); + use Trait_IdentityProviderAware; + + protected static $controller; + + public function __construct($controller){ + static::$controller =$controller; + } - if (!$request->getParam('provider')) - return; - return Class_Users::hasIdentity() + public static function newFor(AuthController $controller) : ?Class_Auth_Strategy { + self::$controller = $controller; + + if ( ! ($controller->getRequest()->getParam('provider'))) + return null; + + return static::_isUserLogged() ? new Class_Auth_IdentityProviderLogged($controller) : new Class_Auth_IdentityProviderNotLogged($controller); } + + + protected static function _isUserLogged() :bool { + if ( ! $user = Class_Users::getIdentity()) + return false; + + if ( ! $provider = (new self(self::$controller))->getProvider(self::$controller)) + return true; + + if ( ! $provider->isCas()) + return true; + + return 0 < Class_User_Identity::isAssociatedToAnother($user, $provider, $provider->getRemoteUserId()); + + } } @@ -40,7 +64,7 @@ class Class_Auth_IdentityProviderLogged extends Class_Auth_Logged { use Trait_IdentityProviderAware; public function logout() { - if (!$provider = $this->_getProvider()) + if (!$provider = $this->getProvider($this->controller)) return parent::logout(); $redirect_url = $provider->logoutUrl($this->_getProfilRedirectUrl()); @@ -52,7 +76,7 @@ class Class_Auth_IdentityProviderLogged extends Class_Auth_Logged { protected function _handleRedirect() { - if (!$provider = $this->_getProvider()) + if (!$provider = $this->getProvider($this->controller)) return parent::_handleRedirect(); if (!$provider->associate(Class_Users::getIdentity()) @@ -79,7 +103,7 @@ class Class_Auth_IdentityProviderNotLogged extends Class_Auth_NotLogged { public function getMessage($view) { - if (!$provider = $this->_getProvider()) + if (!$provider = $this->getProvider($this->controller)) return parent::getMessage($view); if (!$provider->isRemotelyLogged()) @@ -97,7 +121,7 @@ class Class_Auth_IdentityProviderNotLogged extends Class_Auth_NotLogged { protected function _doOnLoginSuccess() { - if (!$provider = $this->_getProvider()) { + if (!$provider = $this->getProvider($this->controller)) { $this->controller->notify($this->_('Impossible d\'associer votre compte à un fournisseur inconnu')); return parent::_doOnLoginSuccess(); } @@ -124,19 +148,25 @@ class Class_Auth_IdentityProviderNotLogged extends Class_Auth_NotLogged { protected function _doOnLoginFail() { + if (!$this->getProvider($this->controller)) + return; + $this->redirect_url = ''; } public function getFormAction($id_module) { - return ($provider = $this->_getProvider()) + return ($provider = $this->getProvider($this->controller)) ? $provider->loginFormAction(parent::getFormAction($id_module)) : parent::getFormAction($id_module); } protected function _handleRedirect() { - $provider = $this->_getProvider(); + $provider = $this->getProvider($this->controller); + + if (!$provider) + return parent::_handleRedirect(); $user = $provider ? $provider->getRemotelyLoggedUser() diff --git a/library/Class/IdentityProvider.php b/library/Class/IdentityProvider.php index 6f355e17c67706a76d957806cfc4a38938a4c97e..8d717836d4b5750b69197d74de59faf13e329c49 100644 --- a/library/Class/IdentityProvider.php +++ b/library/Class/IdentityProvider.php @@ -91,15 +91,15 @@ class Class_IdentityProvider extends Storm_Model_Abstract{ public function validate() { $this->checkAttribute('client_id', - $this->hasClientId() || $this->_isCas(), + $this->hasClientId() || $this->isCas(), $this->_('Identifiant client est obligatoire')); $this->checkAttribute('client_secret', - $this->hasClientSecret() || $this->_isCas(), + $this->hasClientSecret() || $this->isCas(), $this->_('Clé secrète est obligatoire')); } - protected function _isCas() :bool { + public function isCas() :bool { return ($this->getType() == Class_IdentityProvider_Types::CAS2) || ($this->getType() == Class_IdentityProvider_Types::CAS3) || ($this->getType() == Class_IdentityProvider_Types::C3RB); diff --git a/library/Class/User/Identity.php b/library/Class/User/Identity.php index 9775541dad2b7c74fe6b372fbbb025818a6d64a0..7ff45e7d6d3f31cd0698939bba1161515a69a1d3 100644 --- a/library/Class/User/Identity.php +++ b/library/Class/User/Identity.php @@ -38,7 +38,7 @@ class Class_User_IdentityLoader extends Storm_Model_Loader { } - public function isAssociatedToAnother($user, $provider, $identifier) { + public function isAssociatedToAnother(?Class_Users $user, ?Class_IdentityProvider $provider, ?string $identifier) :bool { if (!$user || !$provider || !$identifier) return false; diff --git a/library/Trait/IdentityProviderAware.php b/library/Trait/IdentityProviderAware.php index 32c0336a695db3b8eca784d543eb29342becf021..aae9af55e87d5d42b660c0f93d3e66e531b8151f 100644 --- a/library/Trait/IdentityProviderAware.php +++ b/library/Trait/IdentityProviderAware.php @@ -21,20 +21,18 @@ trait Trait_IdentityProviderAware { - protected $_provider; + protected ?Class_Identityprovider $_provider; + + public function getProvider(?AuthController $controller) : ?Class_Identityprovider { + if (! $controller || ! $request = $controller->getRequest()) + return null; - protected function _getProvider() { if (isset($this->_provider)) return $this->_provider; - if ($this->_provider = Class_IdentityProvider::find((int)$this->_getParam('provider'))) - $this->_provider->setContext($this->_getRequest()); + if ($provider = Class_IdentityProvider::find((int)$request->getParam('provider'))) + $provider->setContext($request); - return $this->_provider; + return $this->_provider = $provider; } - - - abstract protected function _getParam($name); - - abstract protected function _getRequest(); } diff --git a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCas3Test.php b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCas3Test.php index f3f12aaa004f2a0e68740049924c1c8ed1467b98..cf096e11ae58dad0883a622de137cd0b87d9a491 100644 --- a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCas3Test.php +++ b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCas3Test.php @@ -127,6 +127,16 @@ class IdentityProviderAuthenticationCas3WrongMappingTest public function setUp() { parent::setUp(); + $tom = + $this->fixture(Class_Users::class, + ['id' => 12, + 'login' => 'Tom', + 'password' => 'uieuie', + 'role_level' => 1, + ]); + + ZendAfi_Auth::getInstance()->logUser($tom); + $this->dispatch('/auth/login/provider/1?ticket=testticket'); } @@ -179,6 +189,17 @@ class IdentityProviderAuthenticationCas3Bokeh2BokehWithUTCDateTest public function setUp() { parent::setUp(); + $tom = + $this->fixture(Class_Users::class, + ['id' => 12, + 'login' => 'Tom', + 'password' => 'uieuie', + 'role_level' => 1, + ]); + + ZendAfi_Auth::getInstance()->logUser($tom); + + $this->dispatch('/auth/login/provider/1?ticket=testticket'); } @@ -263,6 +284,58 @@ class IdentityProviderAuthenticationCas3Bokeh2BokehTest +class IdentityProviderAuthenticationCas3Bokeh2BokehWithAlreadyConnectedUserTest + extends IdentityProviderAuthenticationCas3BokehToBokehTestCase { + + public function setUp() { + parent::setUp(); + + $tom = + $this->fixture(Class_Users::class, + ['id' => 12, + 'login' => 'Tom', + 'password' => 'uieuie', + 'role_level' => 1, + ]); + + ZendAfi_Auth::getInstance()->logUser($tom); + + $this->dispatch('/auth/login/provider/1?ticket=ST-YetAnotherTicket'); + } + + + protected function _getAnswer() { + return file_get_contents(__DIR__ . '/cas3YetAnotherTicket.xml'); + } + + + public function mappingDatas() : array { + return [ + ['Nom', 'James'], + ['Prenom', 'P.D.'], + ['Mail', 'pdjames@black.fr'], + ['IdSigb', '9013'], + ['Idabon', '47777'], + ['NomRole', 'abonne'], + ['LibraryId', '3'], + ['LibelleBib', 'Annecy'], + ['DateFin', '2020-02-12'], + ['Naissance', '1978-08-02'], + ['CodePostal', '38123'], + ['Ville', 'Laval'], + ]; + } + + + /** @test */ + public function userShouldBeRemotelyLogged() { + $this->assertTrue($this->_provider->isRemotelyLogged()); + } +} + + + + class IdentityProviderAuthenticationCas3PMBToBokehTest extends IdentityProviderAuthenticationCas3BokehToBokehTestCase { diff --git a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCasTest.php b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCasTest.php index 487fd7db0c0a8c03b52c754e4a21ee6796c65dc4..0719ca64d92f32fbccbba48e8bc44636cf0a2f82 100644 --- a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCasTest.php +++ b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationCasTest.php @@ -30,7 +30,7 @@ abstract class IdentityProviderAuthenticationCasTestCase extends AbstractControl parent::setUp(); Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); - $this->_provider = $this->fixture('Class_IdentityProvider', + $this->_provider = $this->fixture(Class_IdentityProvider::class, ['id' => 1, 'label' => 'CAS 2.0', 'type' => 'cas2', @@ -225,7 +225,7 @@ class IdentityProviderAuthenticationCasAuthLoginIdentityAssociatedToAnotherTest Class_WebService_Cas2::loginWith('mysuperid'); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 803, 'provider_id' => 1, 'user_id' => 999, @@ -379,13 +379,15 @@ class IdentityProviderAuthenticationCasAuthLoginPostAutoAssociationWithDuplicate public function setUp() { parent::setUp(); - $this->fixture('Class_Users', ['id' => 999, - 'login' => 'mysuperid', - 'password' => 'infopassword']); + $this->fixture(Class_Users::class, + ['id' => 999, + 'login' => 'mysuperid', + 'password' => 'infopassword']); - $this->fixture('Class_Users', ['id' => 1000, - 'login' => 'mysuperid', - 'password' => 'infopassword']); + $this->fixture(Class_Users::class, + ['id' => 1000, + 'login' => 'mysuperid', + 'password' => 'infopassword']); $this->dispatch('/auth/login/provider/1?ticket=testticket'); } @@ -502,13 +504,13 @@ class IdentityProviderAuthenticationCasAuthLoginRemotelyLoggedAlreadyAssociatedT ZendAfi_Auth::getInstance()->clearIdentity(); - $this->fixture('Class_Users', + $this->fixture(Class_Users::class, ['id' => 33, 'login' => 'name@server.tld', 'password' => 'supersecret', ]); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -555,7 +557,7 @@ class IdentityProviderAuthenticationCasAuthLogoutRemotelyLoggedAlreadyAssociated public function setUp() { parent::setUp(); - $user = $this->fixture('Class_Users', + $user = $this->fixture(Class_Users::class, ['id' => 33, 'login' => 'name@server.tld', 'password' => 'supersecret', @@ -563,7 +565,7 @@ class IdentityProviderAuthenticationCasAuthLogoutRemotelyLoggedAlreadyAssociated ZendAfi_Auth::getInstance()->logUser($user); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -643,7 +645,7 @@ abstract class IdentityProviderAuthenticationCasBokehToBokehTestCase public function setUp() { parent::setUp(); - $this->_provider = $this->fixture('Class_IdentityProvider', + $this->_provider = $this->fixture(Class_IdentityProvider::class, ['id' => 1, 'label' => 'Médiathèque Deauville', 'type' => 'cas2', @@ -747,13 +749,13 @@ class IdentityProviderAuthenticationCasBokehWithExistingGuestTest extends Identi public function setUp() { parent::setUp(); - $user = $this->fixture('Class_Users', + $user = $this->fixture(Class_Users::class, ['id' => 10980908, 'login' => 'mysuperid', 'password' => 'mysuperid', 'role_level' => ZendAfi_Acl_AdminControllerRoles::INVITE]); - $group = $this->fixture('Class_UserGroup', + $group = $this->fixture(Class_UserGroup::class, ['id' => 12, 'libelle' => 'Quest for ship']); @@ -806,7 +808,7 @@ class IdentityProviderAuthenticationCasBokehTryingNormalLoginTest extends Identi $user = Class_Users::newInstance(['id' => 10980908, 'login' => 'mysuperid']); - $group = $this->fixture('Class_UserGroup', + $group = $this->fixture(Class_UserGroup::class, ['id' => 12, 'libelle' => 'Médiathèque Deauville', 'model_id' => 1, @@ -848,7 +850,7 @@ class IdentityProviderAuthenticationCasBokeh2BokehWithDuplicateIdentifierTest ex 'auto_create_users' => 1]) ]); - $user = $this->fixture('Class_Users', + $user = $this->fixture(Class_Users::class, ['id' => 123, 'login' => 'mysuperid', 'password' => 'mysuperid', diff --git a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationTest.php b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationTest.php index 6c97aaa569ca2c0310cffdca52fb25cfdc2049d4..c694370a0896675f837ad26dd22daafd69eb4c19 100644 --- a/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationTest.php +++ b/tests/scenarios/IdentityProvider/IdentityProviderAuthenticationTest.php @@ -34,7 +34,7 @@ class IdentityProviderAuthenticationDisabledBoxDisplayTest extends AbstractContr 'type_module' => 'IDENTITY_PROVIDER']]]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 1, 'label' => 'France connect', 'type' => 'franceconnect', @@ -59,7 +59,6 @@ class IdentityProviderAuthenticationDisabledBoxDisplayTest extends AbstractContr class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTestCase { - protected $_storm_default_to_volatile = true; public function setUp() { parent::setUp(); @@ -70,7 +69,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes ->setCfgAccueil(['modules' => ['1' => ['division' => '2', 'type_module' => 'IDENTITY_PROVIDER']]]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 1, 'label' => 'France connect', 'type' => 'franceconnect', @@ -80,7 +79,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes ]) ]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 2, 'label' => 'Biblibre', 'type' => '', @@ -89,7 +88,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes 'client_secret' => '9876']) ]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 3, 'label' => 'Google', 'type' => 'google', @@ -98,7 +97,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes 'client_secret' => '9876']) ]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 4, 'active' => false, 'label' => 'Gitlab', @@ -108,7 +107,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes 'client_secret' => '9876']) ]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 5, 'label' => 'Acheteza', 'type' => 'acheteza', @@ -117,7 +116,7 @@ class IdentityProviderAuthenticationBoxDisplayTest extends AbstractControllerTes 'client_secret' => '9876']) ]); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 6, 'label' => 'CAS 2.0', 'type' => 'cas2', @@ -306,7 +305,7 @@ class IdentityProviderControllerAuthenticationErrorsTest extends AbstractControl Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); $_SERVER['HTTP_REFERER'] = '/opac/index/index'; - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 2, 'label' => 'Error', 'type' => 'default', @@ -348,7 +347,7 @@ abstract class IdentityProviderAuthenticationCallbackAuthenticationTestCase Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); $this->_state = base64_encode('http://bokeh.org/mycurrenturl'); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 1, 'label' => 'FranceConnect', 'type' => 'franceconnect', @@ -402,7 +401,7 @@ abstract class IdentityProviderAuthenticationCallbackAuthenticationTestCase Class_WebService_OpenId::setWebClient($web_client); - $this->fixture('Class_Users', + $this->fixture(Class_Users::class, ['id' => 33, 'login' => 'Albator', 'password' => 'arc4di4', @@ -411,7 +410,7 @@ abstract class IdentityProviderAuthenticationCallbackAuthenticationTestCase protected function _associateUser() { - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -495,7 +494,7 @@ class IdentityProviderAuthenticationConnectedOpenidUserTest 'preferences' => []], ]]; - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -544,7 +543,7 @@ class IdentityProviderAuthenticationConnectedBokehNotConnectedOpenidUserTest 'preferences' => []], ]]; - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -673,7 +672,7 @@ class IdentityProviderFranceConnectAuthenticationCallbackAuthenticationFirstTime $session->nonce = $this->_nonce; $session->access_token = 1111; - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 12, 'label' => 'FranceConnect', 'type' => Class_IdentityProvider_Types::FRANCE_CONNECT, @@ -717,7 +716,7 @@ class IdentityProviderAuthenticationConnectedUser $session->state = $this->_state; $session->nonce = $this->_nonce; $session->userinfo = (object)['sub' => '9763', 'name' => 'Albator']; - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id'=>33, @@ -743,7 +742,7 @@ class IdentityProviderAuthenticationDissociateUserTest public function setUp() { parent::setUp(); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 1, 'user_id' => 33, @@ -789,7 +788,7 @@ class IdentityProviderAuthenticationCallbackAuthentication $session->nonce = $this->_nonce; $session->access_token = 1111; - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 12, 'label' => 'FranceConnect', 'type' => 'franceconnect', @@ -878,12 +877,9 @@ class IdentityProviderAuthenticationCallbackAuthentication class IdentityProviderAuthenticationAbonneListTest extends IdentityProviderAuthenticationCallbackAuthenticationTestCase { - protected $_storm_default_to_volatile = true; - - public function setUp() { parent::setUp(); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 2, 'label' => 'Afi', 'type' => 'default', @@ -948,9 +944,6 @@ class IdentityProviderAuthenticationAbonneListTest abstract class IdentityProviderAuthenticationAchetezaTestCase extends AbstractControllerTestCase { - protected $_storm_default_to_volatile = true; - - public function setUp() { parent::setUp(); Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); @@ -1003,7 +996,7 @@ class IdentityProviderAuthenticationConnectedAchetezaUserTest Class_WebService_Acheteza::loginWith('8988'); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 5, 'user_id' => 33, @@ -1392,9 +1385,9 @@ class IdentityProviderAuthenticationLogoutWithoutParamsTest extends AbstractCont public function setUp() { parent::setUp(); - Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); + Class_AdminVar::set('ENABLE_IDENTITY_PROVIDERS', 1); - $this->fixture('Class_IdentityProvider', + $this->fixture(Class_IdentityProvider::class, ['id' => 6, 'label' => 'CAS 2.0', 'type' => 'cas2', @@ -1406,7 +1399,7 @@ class IdentityProviderAuthenticationLogoutWithoutParamsTest extends AbstractCont 'login' => 'han.solo', 'password' => 'F@lc0n']); - $this->fixture('Class_User_Identity', + $this->fixture(Class_User_Identity::class, ['id' => 1, 'provider_id' => 6, 'user_id' => 314, @@ -1444,4 +1437,4 @@ class IdentityProviderAuthenticationLogoutWithoutParamsTest extends AbstractCont $this->dispatch('/auth/logout'); $this->assertRedirectContains('https://moncas.securelogin.com/logout?service=http'); } -} \ No newline at end of file +} diff --git a/tests/scenarios/IdentityProvider/cas3YetAnotherTicket.xml b/tests/scenarios/IdentityProvider/cas3YetAnotherTicket.xml new file mode 100644 index 0000000000000000000000000000000000000000..32a0e018e18f9168f529f95660a066ef4981f2bf --- /dev/null +++ b/tests/scenarios/IdentityProvider/cas3YetAnotherTicket.xml @@ -0,0 +1,19 @@ +<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"> + <cas:authenticationSuccess> + <cas:user>mysuperid</cas:user> + <cas:proxyGrantingTicket>ST-YetAnotherTicket</cas:proxyGrantingTicket> + <cas:attributes> + <cas:lastname>James</cas:lastname> + <cas:firstname>P.D.</cas:firstname> + <cas:mail>pdjames@black.fr</cas:mail> + <cas:expire_at>2020-02-12</cas:expire_at> + <cas:card_number>47777</cas:card_number> + <cas:ils_number>9013</cas:ils_number> + <cas:birth_date>1978-08-02</cas:birth_date> + <cas:postal_code>38123</cas:postal_code> + <cas:city>Laval</cas:city> + <cas:affiliation>supremes</cas:affiliation> + <cas:affiliation>temptations</cas:affiliation> + </cas:attributes> + </cas:authenticationSuccess> +</cas:serviceResponse>