diff --git a/FEATURES/143970 b/FEATURES/143970 new file mode 100644 index 0000000000000000000000000000000000000000..d0aaee65f40ef1bcd82be7c64bf9d0cd665b5ee3 --- /dev/null +++ b/FEATURES/143970 @@ -0,0 +1,10 @@ + '143970' => + ['Label' => $this->_('Récupération des prêts PNB avec Baobab (drm LCP)'), + 'Desc' => 'Synchronisation des prêts PNB avec Baobab', + 'Image' => '', + 'Video' => '', + 'Category' => '', + 'Right' => function($feature_description, $user) {return true;}, + 'Wiki' => 'https://wiki.bokeh-library-portal.org/index.php?title=PNB_Baobab', + 'Test' => '', + 'Date' => '2022-06-23'], \ No newline at end of file diff --git a/VERSIONS_WIP/143970 b/VERSIONS_WIP/143970 new file mode 100644 index 0000000000000000000000000000000000000000..907349a680174ee90c23ca005c72bfa737e9c975 --- /dev/null +++ b/VERSIONS_WIP/143970 @@ -0,0 +1 @@ + - fonctionnalité #143970 : Implémentation de l'API-APP de Dilicom permettant de consulter ses prêts PNB sur l'appi Baobab, uniformisation et configuration des clients OAuth. Administration: journalisation des authentifications CAS et OAuth \ No newline at end of file diff --git a/application/modules/admin/controllers/IdentityClientsController.php b/application/modules/admin/controllers/IdentityClientsController.php new file mode 100644 index 0000000000000000000000000000000000000000..42a6b0d5a5404512c755747b0fef0fad86537a84 --- /dev/null +++ b/application/modules/admin/controllers/IdentityClientsController.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 Admin_IdentityClientsController extends ZendAfi_Controller_Action { + public function getPlugins() { + return [ZendAfi_Controller_Plugin_ResourceDefinition_IdentityClient::class, + ZendAfi_Controller_Plugin_Manager_IdentityClient::class]; + } + + public function indexAction() { + parent::indexAction(); + + $this->view->titre = $this->_('Clients d\'identité'); + } +} +?> diff --git a/application/modules/admin/controllers/IdentityProvidersController.php b/application/modules/admin/controllers/IdentityProvidersController.php index 8e51050a3d9306a9c1a5ee630dec912c14d36ad9..8f42f0881378b477bd027427022f68b5009e72f0 100644 --- a/application/modules/admin/controllers/IdentityProvidersController.php +++ b/application/modules/admin/controllers/IdentityProvidersController.php @@ -30,5 +30,10 @@ class Admin_IdentityProvidersController extends ZendAfi_Controller_Action { parent::indexAction(); $this->view->titre = $this->_('Fournisseurs d\'identité'); } + + public function federationAction() { + parent::indexAction(); + $this->view->titre = $this->_('Fédération d\'identités'); + } } -?> \ No newline at end of file +?> diff --git a/application/modules/admin/controllers/IndexController.php b/application/modules/admin/controllers/IndexController.php index d9ac1a0a5f25ff50d5ad68dca570d2464052c25d..2c5c1278b8504ea8b79da9e35e534ac7d77f30a6 100644 --- a/application/modules/admin/controllers/IndexController.php +++ b/application/modules/admin/controllers/IndexController.php @@ -91,6 +91,24 @@ class Admin_IndexController extends ZendAfi_Controller_Action { } + public function adminvarSetAction() { + if(!$id = $this->_getParam('cle')) { + $this->_helper->notify($this->_('Veuillez renseigner le paramètre "cle".')); + return $this->_redirectClose($this->_getReferer()); + } + + if(!$var = Class_AdminVar::find($id)) { + $this->_helper->notify($this->_('La clé "%s" n\'existe pas.', $id)); + return $this->_redirectClose($this->_getReferer()); + } + + $this->_saveVariable($var, $this->_getParam('valeur')); + + $this->view->admin_var = $var; + $this->_redirect($this->_getReferer()); + } + + protected function _saveVariable($var, $new_value) { $var->setValeur($new_value); $id = $var->getId(); @@ -101,7 +119,6 @@ class Admin_IndexController extends ZendAfi_Controller_Action { ? $this->_redirect($url) : $this->_redirectClose($url); } - $this->view->form->getElement('valeur')->addErrors($var->getErrors()); $this->_helper ->notify($this->_('Erreur(s) : %s, variable %s NON sauvegardée', diff --git a/application/modules/admin/controllers/UserApiTokensController.php b/application/modules/admin/controllers/UserApiTokensController.php new file mode 100644 index 0000000000000000000000000000000000000000..13be1bc2c46c4bb4bddec9ece32967e4b8cdca2b --- /dev/null +++ b/application/modules/admin/controllers/UserApiTokensController.php @@ -0,0 +1,44 @@ +<?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 + */ + + +class Admin_UserApiTokensController extends ZendAfi_Controller_Action { + public function getPlugins() { + return [ZendAfi_Controller_Plugin_ResourceDefinition_UserApiTokens::class, + ZendAfi_Controller_Plugin_Manager_UserApiTokens::class]; + } + + + public function showAction() { + $this->view->tokens = Class_User_ApiToken::findAllBy([ 'client_id' => $this->_getParam('client_id', 0)]); + } + + + public function indexAction() { + $this->view->tokens = Class_User_ApiToken::findAllBy([ 'order' => 'id desc' , 'limit' => 200 ]); + } + + + public function deleteAllAction() { + Class_User_ApiToken::deleteBy(['client_id' =>$this->_getParam('client_id')]); + $this->_redirectToIndex(); + } +} diff --git a/application/modules/admin/views/scripts/identity-clients/add.phtml b/application/modules/admin/views/scripts/identity-clients/add.phtml new file mode 100644 index 0000000000000000000000000000000000000000..c52ca489f905555642d9f8210fc3130ba4abfdcd --- /dev/null +++ b/application/modules/admin/views/scripts/identity-clients/add.phtml @@ -0,0 +1 @@ +<?php echo $this->renderForm($this->form); ?> diff --git a/application/modules/admin/views/scripts/identity-clients/edit.phtml b/application/modules/admin/views/scripts/identity-clients/edit.phtml new file mode 100644 index 0000000000000000000000000000000000000000..c52ca489f905555642d9f8210fc3130ba4abfdcd --- /dev/null +++ b/application/modules/admin/views/scripts/identity-clients/edit.phtml @@ -0,0 +1 @@ +<?php echo $this->renderForm($this->form); ?> diff --git a/application/modules/admin/views/scripts/identity-clients/index.phtml b/application/modules/admin/views/scripts/identity-clients/index.phtml new file mode 100644 index 0000000000000000000000000000000000000000..9e548b16f29b3c816ddb9fd4e3827ae7f2077e25 --- /dev/null +++ b/application/modules/admin/views/scripts/identity-clients/index.phtml @@ -0,0 +1,33 @@ +<?php + +echo $this->Button_New((new Class_Button) + ->setText($this->_('Ajouter un client d\'identité'))); + + +echo $this->Button((new Class_Button) + ->setText($this->_('Voir les fournisseurs d\'identité')) + ->setUrl($this->url(['controller' =>'identity-providers','action' => 'index']))); + + +echo $this->Button_ActivationLog(); + +if ( Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')) + echo $this->button((new Class_Button) + ->setText('Voir logs d\'authentification') + ->setUrl($this->url(['controller' =>'journal','action' => 'index', + 'order' => 'created_at+desc', + 'search_type' => + Class_Journal_OauthRequestType::MY_TYPE]))); + +echo $this + ->renderTable((new Class_TableDescription('servers')) + ->addColumn($this->_('Libellé'), ['attribute' => 'label']) + ->addColumn($this->_('Actif ?'), + function($model) + { + return $model->getActive() + ? $this->_('Oui') + : $this->_('Non'); + }) + ->addRowAction(function($model) { return $this->renderPluginsActions($model); }), + $this->servers); diff --git a/application/modules/admin/views/scripts/identity-providers/index.phtml b/application/modules/admin/views/scripts/identity-providers/index.phtml index 0ff46700631a4d627cb53de06d30998e1294b7b9..ae9e4305656cb72d5d8c597361adaec9fc74679e 100644 --- a/application/modules/admin/views/scripts/identity-providers/index.phtml +++ b/application/modules/admin/views/scripts/identity-providers/index.phtml @@ -3,6 +3,17 @@ echo $this->Button_New((new Class_Button) ->setText($this->_('Ajouter un fournisseur d\'identité'))); +echo $this->Button((new Class_Button) + ->setText($this->_('Voir les clients d\'identité')) + ->setUrl($this->url(['controller' =>'identity-clients','action' => 'index']))); + +echo $this->button((new Class_Button) + ->setText('Voir logs d\'authentification') + ->setUrl($this->url(['controller' =>'journal','action' => 'index', + 'order' => 'created_at+desc', + 'search_type' => + Class_Journal_CasRequestType::MY_TYPE]))); + echo $this ->renderTable((new Class_TableDescription('providers')) ->addColumn($this->_('Libellé'), ['attribute' => 'label']) diff --git a/application/modules/admin/views/scripts/journal/index.phtml b/application/modules/admin/views/scripts/journal/index.phtml index 61b0c6545a8243035b861bd65379c05c53590f28..fb4561599eee8152b4735484640d9a0f64b7e7d4 100644 --- a/application/modules/admin/views/scripts/journal/index.phtml +++ b/application/modules/admin/views/scripts/journal/index.phtml @@ -1,2 +1,5 @@ <?php + +echo $this->Button_ActivationLog(); + echo $this->searchJournal($this->search); diff --git a/application/modules/admin/views/scripts/user-api-tokens/index.phtml b/application/modules/admin/views/scripts/user-api-tokens/index.phtml new file mode 100644 index 0000000000000000000000000000000000000000..d3dca92569dcc7118b9da3df166dcd937795e7e9 --- /dev/null +++ b/application/modules/admin/views/scripts/user-api-tokens/index.phtml @@ -0,0 +1,16 @@ +<?php + + +echo $this->button((new Class_Button) + ->setText('Voir logs d\'authentification') + ->setUrl($this->url(['controller' =>'user-api-tokens','action' => 'logs']))); + +echo $this + ->renderTable((new Class_TableDescription('providers')) + ->addColumn($this->_('User'), ['attribute' => 'user_name']) + ->addColumn($this->_('Créé le'), ['attribute' => 'created_at']) + ->addColumn($this->_('Token'), ['attribute' => 'token']) + ->addColumn($this->_('Date d\'expiration'), ['attribute' => 'expired_at']) + ->addColumn($this->_('Refresh token'), ['attribute' => 'refresh_token']) + ->addRowAction(function($model) { return $this->renderPluginsActions($model); }), + $this->tokens); diff --git a/application/modules/admin/views/scripts/user-api-tokens/logs.phtml b/application/modules/admin/views/scripts/user-api-tokens/logs.phtml new file mode 100644 index 0000000000000000000000000000000000000000..381115b30428ca08a2553a172970177a32be8fca --- /dev/null +++ b/application/modules/admin/views/scripts/user-api-tokens/logs.phtml @@ -0,0 +1,25 @@ +<?php +$val = '1'; +$text = $this->_( 'Activer les logs'); + +if ( Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')) { +$val = '0'; +$text = $this->_( 'Desactiver les logs'); +} + +echo $this->button((new Class_Button) + ->setText($text) + ->setUrl($this->url(['module' => 'admin', + 'controller' => 'index', + 'action' => 'adminvar_set', + 'cle' => 'ACTIVATE_AUTH_LOG', + 'valeur' => $val, + 'redirect' =>'/admin/user-api-tokens/logs']))); + + +echo $this->button((new Class_Button) + ->setText( $this->_( 'Supprimer les logs')) + ->setUrl($this->url(['do' => 'clean']))); +echo '<br/>'; + +echo '<code>'. $this->log.'</code>'; diff --git a/application/modules/admin/views/scripts/user-api-tokens/show.phtml b/application/modules/admin/views/scripts/user-api-tokens/show.phtml new file mode 100644 index 0000000000000000000000000000000000000000..3d6d738b1792a13bc4fc4fcb80204558a651aeef --- /dev/null +++ b/application/modules/admin/views/scripts/user-api-tokens/show.phtml @@ -0,0 +1,18 @@ +<?php + + +echo $this->button((new Class_Button) + ->setText('Voir logs d\'authentification') + ->setUrl($this->url(['controller' =>'journal','action' => 'index', + 'order' => 'created_at+desc', + 'search_type' => + Class_Journal_CasRequestType::MY_TYPE]))); +echo $this + ->renderTable((new Class_TableDescription('providers')) + ->addColumn($this->_('User'), ['attribute' => 'user_name']) + ->addColumn($this->_('Créé le'), ['attribute' => 'created_at']) + ->addColumn($this->_('Token'), ['attribute' => 'token']) + ->addColumn($this->_('Date d\'expiration'), ['attribute' => 'expired_at']) + ->addColumn($this->_('Refresh token'), ['attribute' => 'refresh_token']) + ->addRowAction(function($model) { return $this->renderPluginsActions($model); }), + $this->tokens); diff --git a/application/modules/api/controllers/CatalogController.php b/application/modules/api/controllers/CatalogController.php index 2ce045a0e47c34f537fbdedd8587aa80caa9d657..a27ef30540a2e51223590b747e31658317a5fe2e 100644 --- a/application/modules/api/controllers/CatalogController.php +++ b/application/modules/api/controllers/CatalogController.php @@ -21,6 +21,20 @@ class Api_CatalogController extends ZendAfi_Controller_Action { + + public function discoverAction() { + $this->_helper->json([ + 'infos' => [ 'mail' => 'dev-opac@afi-sa.fr', + 'company' => 'AFI'], + 'authentication' => [ + 'get_token' => Class_Url::absolute('/auth/oauth'), + 'refresh_token' => Class_Url::absolute('/auth/refresh')], + 'resources' => [['code'=> 'loans', + 'endpoint' => Class_Url::absolute('/api/user/pnbloans'), + 'version' =>'1']]]); + } + + public function itemAction() { if (!$barcode = $this->_getParam('barcode')) return $this->_helper->throwHTTPError($this->_('Paramètre barcode obligatoire'), 403); @@ -31,4 +45,4 @@ class Api_CatalogController extends ZendAfi_Controller_Action { return $this->_helper->json($this->view->item($item)); } -} \ No newline at end of file +} diff --git a/application/modules/api/controllers/ErrorController.php b/application/modules/api/controllers/ErrorController.php index 13821b4bec0aed5126c92ee988f1e88865636829..ce3a623e1642605958115534459199648c54fb5d 100644 --- a/application/modules/api/controllers/ErrorController.php +++ b/application/modules/api/controllers/ErrorController.php @@ -22,10 +22,13 @@ class Api_ErrorController extends Zend_Controller_Action { public function errorAction() { $errors = $this->_getParam('error_handler'); + $json =['error' => 'invalid_request', + 'message' => $errors->exception->getMessage(), + 'error_description' => $errors->exception->getMessage() + ]; - $this->_helper->json(['error' => 'invalid_request', - 'message' => $errors->exception->getMessage()]); - + $this->_helper->json($json); + Class_Journal_RequestType::createWith( $this, json_encode($json), $errors->exception->getCode()); $this->_response->setHttpResponseCode($errors->exception->getCode()); } } diff --git a/application/modules/api/controllers/UserController.php b/application/modules/api/controllers/UserController.php index 416abf4d8c7d88252b5dcfa9e4cd0e1c4f38c61f..62eee011d02b630e82ae7adcc0be581eec176f70 100644 --- a/application/modules/api/controllers/UserController.php +++ b/application/modules/api/controllers/UserController.php @@ -23,6 +23,7 @@ class Api_UserController extends ZendAfi_Controller_Action { public function preDispatch() { parent::preDispatch(); + if (!Class_Users::hasIdentity()) $this->_authenticate(); } @@ -30,23 +31,49 @@ class Api_UserController extends ZendAfi_Controller_Action { public function accountAction() { $user = Class_Users::getIdentity(); + $json = ['account' => ['label' => $user->getNomAff(), + 'login' => $user->getLogin(), + 'card' => ['id'=> $user->getIdabon(), + 'expire_at' => $user->getDateFin()] + ]]; + + Class_Journal_RequestType::createWith($this, json_encode($json)); + $this->_helper - ->json(['account' => ['label' => $user->getNomAff(), - 'login' => $user->getLogin(), - 'card' => ['id'=> $user->getIdabon(), - 'expire_at' => $user->getDateFin()] - ]]); + ->json($json); } public function loansAction() { $this->_clearUserCache(); - $this->view->loans = $this->_userCards()->getLoans(); + $this->_helper->viewRenderer->setNoRender(); + $this->getResponse()->setHeader('Content-Type', 'application/json; charset=utf-8'); + + $json = '{ "loans":'.$this->view->loans($this->_userCards()->getLoans()).'}'; + Class_Journal_RequestType::createWith($this, $json); + $this->getResponse()->setBody($json); + } + + + public function pnbloansAction() { + + $this->_helper->viewRenderer->setNoRender(); + $this->getResponse()->setHeader('Content-Type', 'application/json; charset=utf-8'); + + $this->_clearUserCache(); + $this->view->loans = $this->_userCards()->getPNBLoans(); + $json='{ "loans":'.$this->view->pnbLoans($this->view->loans).'}'; + Class_Journal_RequestType::createWith($this, $json); + $this->getResponse()->setBody($json); } public function holdsAction() { - $this->view->holds = $this->_userCards()->getHolds(); + $this->_helper->viewRenderer->setNoRender(); + $this->getResponse()->setHeader('Content-Type', 'application/json; charset=utf-8'); + $json='{ "holds":'.$this->view->holds($this->_userCards()->getHolds()).'}'; + Class_Journal_RequestType::createWith($this, $json); + $this->getResponse()->setBody($json); } @@ -56,18 +83,23 @@ class Api_UserController extends ZendAfi_Controller_Action { $status = $cards->renewLoan($loan_id); - if ($status['statut'] == false) - return $this->_helper->json(['status' => 'error', - 'error' => $status['erreur']]); + if ($status['statut'] == false){ + $json = ['status' => 'error', + 'error' => $status['erreur']]; + Class_Journal_RequestType::createWith( $this, json_encode($json), '404'); + return $this->_helper->json($json); + } $loan = $cards->getLoans() ->detect(function($loan) use ($loan_id) { return $loan->getId() == $loan_id; }); + $json = ['status' => 'renewed', + 'date_due' => $loan->getDateRetourISO8601()]; + Class_Journal_RequestType::createWith( $this, json_encode($json)); - return $this->_helper->json(['status' => 'renewed', - 'date_due' => $loan->getDateRetourISO8601()]); + return $this->_helper->json($json); } @@ -95,6 +127,7 @@ class Api_UserController extends ZendAfi_Controller_Action { protected function _authenticate() { + if (Class_AdminVar_OAuthAcceptHTTP::shouldRejectRequest($this->_request)) return $this->_helper->throwHTTPError($this->_('Protocole HTTPS obligatoire'), 403); @@ -108,6 +141,10 @@ class Api_UserController extends ZendAfi_Controller_Action { if (!$token = Class_User_ApiToken::findFirstBy(['token' => $parts[1]])) return $this->_helper->throwHTTPError($this->_('Jeton d\'autorisation invalide'), 403); + if ($token->isExpired()) { + return $this->_helper->throwHTTPError(implode(',',array_unique($token->getErrors())), 403); + } + if (!$user = $token->getUser()) return $this->_helper->throwHTTPError($this->_('Utilisateur non trouvé'), 403); diff --git a/application/modules/api/views/scripts/user/holds.pjson b/application/modules/api/views/scripts/user/holds.pjson deleted file mode 100644 index 87732946fb1170d8d50fbf7a5b3c62d234ebddb1..0000000000000000000000000000000000000000 --- a/application/modules/api/views/scripts/user/holds.pjson +++ /dev/null @@ -1,3 +0,0 @@ -{ - "holds": <?php echo $this->holds($this->holds) ?> -} diff --git a/application/modules/api/views/scripts/user/loans.pjson b/application/modules/api/views/scripts/user/loans.pjson deleted file mode 100644 index 94d1684f134585f6fea7cbd946598ebe163f13d1..0000000000000000000000000000000000000000 --- a/application/modules/api/views/scripts/user/loans.pjson +++ /dev/null @@ -1,3 +0,0 @@ -{ - "loans": <?php echo $this->loans($this->loans) ?> -} diff --git a/application/modules/opac/controllers/AuthController.php b/application/modules/opac/controllers/AuthController.php index d38cbea151628a3dafbbd87c257d697ed7e1844a..3be3ae257e6c31aa12fbdf0b03cea261e992c963 100644 --- a/application/modules/opac/controllers/AuthController.php +++ b/application/modules/opac/controllers/AuthController.php @@ -236,13 +236,20 @@ class AuthController extends ZendAfi_Controller_Action { public function oauthAction() { $validator = new ZendAfi_Validate_Url(); - if (('code' !== $this->_getParam('response_type')) - || !$this->_getParam('client_id') - || !$this->_getParam('redirect_uri')) { - throw new Zend_Controller_Action_Exception($this->view->_('Désolé, requête incomplète'), 400); + if (!$client_id = $this->_getParam('client_id')) + throw $this->_helper->throwHTTPError($this->view->_('Désolé, requête incomplète'), 400); + + if (!$server = Class_IdentityClient::findFirstBy(['client_id' => $client_id])) + throw $this->_helper->throwHTTPError($this->view->_('Désolé, aucune configuration correspondante pour '.$client_id), 400); + + if (('code' == $this->_getParam('response_type')) + && ( !$this->_getParam('redirect_uri'))) { + Class_Journal_RequestType::createWith( $this, "Requete incomplete, missing redirect_uri", 400); + throw $this->_helper->throwHTTPError($this->view->_('Désolé, requête incomplète'), 400); } + $this->view->titre = $this->_('Authentifiez-vous pour autoriser "%s" à accéder à votre compte', $this->_getParam('client_id')); $preferences = Class_Profil::getCurrentProfil()->getCfgModulesPreferences('auth', 'login'); @@ -257,6 +264,19 @@ class AuthController extends ZendAfi_Controller_Action { $redirect_uri = $this->_getParam('redirect_uri'); $strategy = new Class_Auth_OAuth($this); $strategy->processLogin(); + + } + + public function refreshAction() { + + if (!$refresh_token = $this->_getParam('refresh_token')) { + $viewRenderer = $this->getHelper('ViewRenderer'); + $viewRenderer->setNoRender(); + + return $this->_helper->sendHttpErrorCode('invalid_request','Missing parameter',400); + } + $strategy = new Class_Auth_OAuth($this); + $strategy->refresh($refresh_token); } diff --git a/application/modules/opac/controllers/CasServerController.php b/application/modules/opac/controllers/CasServerController.php index 7b56e703dbddc711244a033e158dc5120c58bd36..02af67ed3febc9f0263f3ee2fa2a6a5b720f21d7 100644 --- a/application/modules/opac/controllers/CasServerController.php +++ b/application/modules/opac/controllers/CasServerController.php @@ -64,14 +64,13 @@ class CasServerController extends ZendAfi_Controller_Action { $this->getHelper('ViewRenderer')->setNoRender(); $service = $this->_getParam('service'); $ticket = $this->_getParam('ticket'); - if (!$ticket || !$service) { return $this->returnFailureTicketResponse(Class_CasTicket::CODE_INVALID_REQUEST); } $cas_ticket = $this->_getCasTicket($service); return ($user = $cas_ticket->userForTicket($ticket)) - ? $this->returnValidTicketResponse($user, $ticket) + ? $this->returnValidTicketResponse($user, $ticket) : $this->returnFailureTicketResponse($cas_ticket->getErrorCode(), $ticket, $cas_ticket->getService()); } @@ -124,6 +123,7 @@ class CasServerController extends ZendAfi_Controller_Action { public function loginAction() { $this->_addParamsOnRequest(); + Class_Journal_RequestType::createWith( $this, 'forward to auth/login'); $this->_forward('login', 'auth'); } @@ -134,9 +134,11 @@ class CasServerController extends ZendAfi_Controller_Action { public function logoutAction() { + ZendAfi_Auth::getInstance()->clearIdentity(); if ($url_redirect = $this->_getParam('url')) $this->_redirect($url_redirect); + Class_Journal_RequestType::createWith( $this, $url_redirect ? 'redirect to '. $url_redirect:'' ); } } ?> diff --git a/application/modules/opac/controllers/UserApiTokensController.php b/application/modules/opac/controllers/UserApiTokensController.php new file mode 100644 index 0000000000000000000000000000000000000000..0164cb49675eda026b4ca896693f778ac6042c01 --- /dev/null +++ b/application/modules/opac/controllers/UserApiTokensController.php @@ -0,0 +1,31 @@ +<?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 + */ + + +class Admin_UserApiTokensController extends ZendAfi_Controller_Action { + public function getPlugins() { + return ['ZendAfi_Controller_Plugin_ResourceDefinition_UserApiTokens', + 'ZendAfi_Controller_Plugin_Manager_UserApiTokens']; + } + + + +} diff --git a/cosmogramme/sql/patch/patch_440.php b/cosmogramme/sql/patch/patch_440.php new file mode 100644 index 0000000000000000000000000000000000000000..18df7f8e133acfb097ea4f206d3351a0c2c39943 --- /dev/null +++ b/cosmogramme/sql/patch/patch_440.php @@ -0,0 +1,48 @@ +<?php +$adapter = Zend_Db_Table_Abstract::getDefaultAdapter(); + +try { + $adapter->query('alter table user_api_tokens add column refresh_token varchar(255) default ""'); + $adapter->query('alter table user_api_tokens add column created_at datetime default NULL'); + $adapter->query('alter table user_api_tokens add column expired_at datetime default NULL'); + + +} +catch(Exception $e) { +} +try{ + + $adapter->query('CREATE TABLE if not exists `identity_client` ( ' + . 'id int(11) unsigned not null auto_increment,' + . 'type varchar(255),' + . 'client_id varchar(255),' + . 'protocol varchar(255),' + . 'active boolean default true,' + . 'label varchar(255) not null,' + . 'config text,' + . 'primary key (id)' + . ') engine=MyISAM default charset=utf8'); + +} catch(Exception $e) { + +} +try { + $adapter->query('delete from identity_client where client_id="MyBibApp"'); + $adapter->query('delete from identity_client where type="Class_IdentityClient_Dilicom"'); + $adapter->query('insert into identity_client (client_id, type, active,label, protocol) values ("MyBibApp","'.Class_IdentityClient_MyBibApp::class.'", true, "MyBibApp", "oauth2")'); + + $rows = $adapter->query('select distinct gln from bib_c_site where gln is not null and gln<>"" and gln not in(select distinct client_id from identity_client) ') + ->fetchAll(); + foreach ($rows as $row) + $adapter->query('insert into identity_client (client_id, type, active,label, protocol) values ("'.$row["gln"].'","'.Class_IdentityClient_Dilicom::class.'", true, "Dilicom '.$row["gln"].'", "oauth2") '); + + +} +catch(Exception $e) { + var_dump($e); +} +try { + $adapter->query('update loan_pnb set loan_link=REGEXP_REPLACE(loan_link, \'(\\\?userAgentId\\\=.*$)\', "")'); + +}catch(Exception $e) { +} diff --git a/library/Bluga/Webthumb/Job.php b/library/Bluga/Webthumb/Job.php index 0c8aa8b7dbefe584fa743a2c61591e1af7faa332..8c2fc0cae2d505d4af1d723cedee9177d79cc6bb 100644 --- a/library/Bluga/Webthumb/Job.php +++ b/library/Bluga/Webthumb/Job.php @@ -19,7 +19,7 @@ class Bluga_Webthumb_Job { 'excerpt', 'notify' )); - $this->options->setters['url'] = create_function('$url','return trim($url);'); + $this->options->setters['url'] = fn($url) => trim($url); $this->status = new Bluga_Propertybag(array( 'start_time', 'end_time', @@ -66,4 +66,3 @@ class Bluga_Webthumb_Job { return Bluga_Webthumb_Request::prettyPrint($this->render()); } } - diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index 409cc5b6963229ae95d8fd13127cbf1092197857..e4cbaf7b414c644f5f582a14c8a56610da899208 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -376,6 +376,7 @@ Pour vous désabonner de la lettre d\'information, merci de cliquer sur le lien . '<br/>' . $this->_('De plus, à la connexion, l\'enregistrement des mots de passes des abonnés est désactivé.')), 'OAUTH_ACCEPT_HTTP' => Class_AdminVar_Meta::newOnOff($this->_('Autoriser l\'accès aux API OAUTH via HTTP (non sécurisé - déconseillé)'), ['value' => 0]), + 'ACTIVATE_AUTH_LOG' => Class_AdminVar_Meta::newOnOff($this->_('Activer les log pour l\'authentification OAUTH ou CAS')), 'NB_AFFICH_AVIS_PAR_AUTEUR' => Class_AdminVar_Meta::newDefault($this->_('Nombre d\'avis maximum à afficher par utilisateur.')), 'REGISTER_OK' => Class_AdminVar_Meta::newEncodedData($this->_('Texte visible par l\'internaute après son inscription.')), 'RESA_CONDITION' => Class_AdminVar_Meta::newEncodedData($this->_('Texte visible après l\'envoi d\'e-mail de demande de réservation.')), diff --git a/library/Class/Auth/OAuth.php b/library/Class/Auth/OAuth.php index fe353a2834e0694531a13b3f9e95654eba08ee2e..5a19ea2fa5fb0cd04debecc634823e88268f492f 100644 --- a/library/Class/Auth/OAuth.php +++ b/library/Class/Auth/OAuth.php @@ -21,20 +21,101 @@ class Class_Auth_OAuth extends Class_Auth_NotLogged { - protected function _handlePost() { - parent::_handlePost(); - if (!$user = Class_Users::getIdentity()) - return function() {}; + public function refresh(string $refresh_token):self { + $token = Class_User_ApiToken::refreshToken($refresh_token); + if ($token->hasErrors()){ + $this->disable_redirect = true; + $this->controller->getHelperBroker()->sendHttpErrorCode('invalid_request', + implode(',',array_unique($token->getErrors())),401); + return $this; + } + + $json = [ + 'access_token' => $token->getToken(), + 'expires_in' => $token->getExpiresIn(), + 'token_type' => 'Bearer', + 'refresh_token' => $token->getRefreshToken()]; + Class_Journal_RequestType::createWith( $this->controller, json_encode($json)); + $this->controller->getHelperBroker()->json($json); + + return $this; + } + + + protected function _handlePostWithAuth() { + $response = $this->controller->getResponse(); + $view = $this->controller->view; $request = $this->controller->getRequest(); - $token = Class_User_ApiToken::findOrCreateForUserAndApplication($user, + $login = $request->getPost('username'); + $password = $request->getPost('password'); + + if ( !ZendAfi_Auth::getInstance()->authenticateLoginPassword($login, $password) ) { + $this->controller->getHelperBroker()->sendHttpErrorCode('invalid_grant','Authentication failure', 400); + return function() { + }; + } + $user = Class_Users::getIdentity(); + $token = Class_User_ApiToken::createForUserAndApplication($user, $request->getParam('client_id')); + if ($token->hasErrors()){ + $this->disable_redirect = true; + $this->controller->getHelperBroker()->sendHttpErrorCode('invalid_request',implode(',',array_unique($token->getErrors())),401); + return function() { + }; + } + + $json =['access_token' => $token->getToken(), + 'expires_in' => '86400', + 'token_type' => 'Bearer', + 'refresh_token' => $token->getRefreshToken()]; + + $this->disable_redirect = true; + Class_Journal_RequestType::createWith( $this->controller, json_encode($json)); + $this->controller->getHelperBroker()->json($json); + + return function() {}; + } + + + protected function _handlePost(){ + parent::_handlePost(); + $request = $this->controller->getRequest(); + + if ($request->getParam('response_type') != 'code') + return $this->_handlePostWithAuth(); + + if (!$user = Class_Users::getIdentity()) { + Class_Journal_RequestType::createWith( $this->controller, "Authentication failed, wrong identity"); + $this->disable_redirect = true; + return function() {}; + } + $redirect_uri = $request->getParam('redirect_uri', ''); + + $token = Class_User_ApiToken::createForUserAndApplication($user, + $request->getParam('client_id')); + + $token->checkRedirectUri($redirect_uri); + if ($token->hasErrors()){ + $this->disable_redirect = true; + $this->controller->getHelperBroker()->sendHttpErrorCode('invalid_request', + implode(',',array_unique($token->getErrors())),401); + return function() {}; + } + $this->redirect_url = sprintf('%s#token=%s', - $request->getParam('redirect_uri'), + $redirect_uri, $token->getToken()); + if (substr($redirect_uri,0,4) == 'http') + $this->redirect_url = sprintf('%s?token=%s', + $redirect_uri, + $token->getToken()); + + Class_Journal_RequestType::createWith( $this, "Authentication successful, redirected to :".$this->redirect_url); return function() {}; } + } diff --git a/library/Class/IdentityClient.php b/library/Class/IdentityClient.php new file mode 100644 index 0000000000000000000000000000000000000000..5f91232020f8bb18fd52ab2f59616f62fcf8a3c4 --- /dev/null +++ b/library/Class/IdentityClient.php @@ -0,0 +1,141 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 wxbill 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 IdentityClientLoader extends Storm_Model_Loader { + protected $_actives; + + public function findAllActiveServers() { + return Class_IdentityClient::findAllBy(['active' => 1]); + } +} + + + + +class Class_IdentityClient extends Storm_Model_Abstract{ + use Trait_Translator, Trait_LastMessage; + + protected + $_table_name = 'identity_client', + $_loader_class = IdentityClientLoader::class, + $_default_attribute_values = ['label' => '', + 'config' => '', + 'client_id'=>'', + 'active' => true, + 'type' => '', + 'protocol' => 'oauth2'], + + $_has_many = ['user_api_tokens' => ['model' => Class_User_ApiToken::class, + 'role' => 'client', + 'dependents' => 'delete'] ], + + $_config_fields = [ + 'client_secret', + 'redirect_uri', + 'allow_refresh_token', + 'token_expires_in', + 'disable_state', + 'disable_response_type', + 'scope'], + + $_config_as_array, + $_context, + $_identity_client_connector; + + public function validate() { + $this->checkAttribute('client_id', + $this->hasClientId() , + $this->_('Identifiant client est obligatoire')); + } + + + public function validateRequest($request) { + } + + + public function getLibelle() { + return $this->getLabel(); + } + + + public function getConnector() { + return isset($this->_identity_client_connector) + ? $this->_identity_client_connector + : $this->_identity_client_connector = (new Class_IdentityClient_Types)->newForServer($this); + + return new Class_IdentityClient_OAuth2; + } + + + public function acceptVisitor($visitor) { + foreach($this->getConfigAsArray() as $key => $value) + $visitor->visitParam($key, $value); + + return $this; + } + + + public function _get($key) { + if (in_array($key, $this->_config_fields)) + return $this->getConnector()->getParam($key); + return parent::_get($key); + } + + + public function _set($key, $value) { + if (in_array($key, $this->_config_fields)) + return $this->setConfigValue($key, $value); + return parent::_set($key, $value); + } + + + public function setConfigValue($key,$value) { + $config = $this->getConfigAsArray(); + $config[$key] = $value; + $this->setConfig(json_encode($config)); + $this->_config_as_array = $config; + return $this; + } + + + public function getConfigValue($key) { + $config = $this->getConfigAsArray(); + return isset($config[$key]) + ? $config[$key] + : ''; + } + + + public function getConfigAsArray() { + if (null !== $this->_config_as_array) + return $this->_config_as_array; + + $config = json_decode($this->getConfig(), true); + return $this->_config_as_array = $config ? $config : []; + } + + + public function setLoginSuccessRedirectUrl($url) { + $this->getConnector()->setLoginSuccessRedirectUrl($url); + return $this; + } +} diff --git a/library/Class/IdentityClient/Dilicom.php b/library/Class/IdentityClient/Dilicom.php new file mode 100644 index 0000000000000000000000000000000000000000..f29f85431a07de1c93684fc29502e587652829de --- /dev/null +++ b/library/Class/IdentityClient/Dilicom.php @@ -0,0 +1,34 @@ +<?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 Class_IdentityClient_Dilicom extends Class_IdentityClient_OAuth2 { + protected $_service_class = Class_Auth_OAuth::class; + + protected $_config = ['client_secret'=> '', + 'redirect_uri'=> '', + 'allow_refresh_token'=> true , + 'token_expires_in' => '86400', + 'disable_state' => true, + 'disable_response_type' => true]; + + +} diff --git a/library/Class/IdentityClient/MyBibApp.php b/library/Class/IdentityClient/MyBibApp.php new file mode 100644 index 0000000000000000000000000000000000000000..16baf26fe8425671c2844b58ab4c8c7a7505dd48 --- /dev/null +++ b/library/Class/IdentityClient/MyBibApp.php @@ -0,0 +1,34 @@ +<?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 Class_IdentityClient_MyBibApp extends Class_IdentityClient_OAuth2 { + protected $_service_class = Class_Auth_OAuth::class; + + protected $_config = ['client_secret'=> '', + 'redirect_url'=> 'bokeh://authorize', + 'allow_refresh_token'=> false, + 'token_expires_in' => 0, + 'disable_state' => true, + 'disable_response_type' => true]; + + +} diff --git a/library/Class/IdentityClient/OAuth2.php b/library/Class/IdentityClient/OAuth2.php new file mode 100644 index 0000000000000000000000000000000000000000..e93418d2d22c596a43ddfd757f84575bc8692d38 --- /dev/null +++ b/library/Class/IdentityClient/OAuth2.php @@ -0,0 +1,61 @@ +<?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 Class_IdentityClient_OAuth2 { + protected $_service_class = Class_Auth_OAuth::class; + + public function __construct($client) { + $this->_client = $client; + $this->_client->acceptVisitor($this); + } + + public function getType() { + return 'oauth'; + } + + public function visitParam($key, $value) { + if ($value) + $this->_config[$key] = $value; + + return $this; + } + + + public function getParam($key) { + return isset($this->_config[$key]) + ? $this->_config[$key] + : ''; + } + + + public function getService() { + $class_name = $this->_service_class; + + return $this->_service + ? $this->_service + : $this->_service = new $class_name($this->_client); + } + + public function setLoginSuccessRedirectUrl($url) { + return $this; + } +} diff --git a/library/Class/IdentityClient/Types.php b/library/Class/IdentityClient/Types.php new file mode 100644 index 0000000000000000000000000000000000000000..3f8b4d1603eca03f1f0187b07ceb4cf21d49c783 --- /dev/null +++ b/library/Class/IdentityClient/Types.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright (c) 2012-2019, 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 Class_IdentityClient_Types { + use Trait_Translator; + + const + OAUTH = Class_IdentityClient_OAuth2::class, + MYBIBAPP = Class_IdentityClient_MyBibApp::class, + DILICOM = Class_IdentityClient_Dilicom::class; + + + protected $_services; + + + public function asMultiOptions() { + return [static::OAUTH => $this->_('OAuth 2'), + static::MYBIBAPP => $this->_('MyBibApp OAuth'), + static::DILICOM => $this->_('Dilicom Baobab OAuth')]; + } + + + public function newForServer($server) { + $class_name = $server->getType(); + + return class_exists($class_name) + ? new $class_name($server) + : new Class_IdentityClient_OAuth2($server); + } + +} diff --git a/library/Class/Journal/CasRequestType.php b/library/Class/Journal/CasRequestType.php new file mode 100644 index 0000000000000000000000000000000000000000..cca94268f152dcb9b4cee72e11b3a12a8979aebe --- /dev/null +++ b/library/Class/Journal/CasRequestType.php @@ -0,0 +1,27 @@ +<?php +/** + * Copyright (c) 2022, 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 Class_Journal_CasRequestType extends Class_Journal_RequestType { + const + MY_TYPE = 'CAS_CALL'; + +} diff --git a/library/Class/Journal/DetailType.php b/library/Class/Journal/DetailType.php index 2bd9d1acb83a320bc048188dbff137eec9299d71..fd9746283ef53c9a0153ca4aa81fec908a5a7488 100644 --- a/library/Class/Journal/DetailType.php +++ b/library/Class/Journal/DetailType.php @@ -201,6 +201,16 @@ class Class_Journal_DetailType_NewValue extends Class_Journal_DetailType { +class Class_Journal_DetailType_BokehResponse extends Class_Journal_DetailType { + public function renderValueOn(Zend_View_Interface $view) : string { + return htmlentities($this->_detail->getValue()); + } + +} + + + + class Class_Journal_DetailType_PreviousValue extends Class_Journal_DetailType { public function getTypeLabel() : string { return $this->_('Ancienne valeur'); diff --git a/library/Class/Journal/OauthRequestType.php b/library/Class/Journal/OauthRequestType.php new file mode 100644 index 0000000000000000000000000000000000000000..cec938c88dd6f87ce188fb862b09812fe70fd597 --- /dev/null +++ b/library/Class/Journal/OauthRequestType.php @@ -0,0 +1,26 @@ +<?php +/** + * Copyright (c) 2022, 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 Class_Journal_OauthRequestType extends Class_Journal_RequestType { + const + MY_TYPE = 'OAUTH_CALL'; +} diff --git a/library/Class/Journal/RequestType.php b/library/Class/Journal/RequestType.php new file mode 100644 index 0000000000000000000000000000000000000000..52e6647bf2ca2746da3d75d0896506987d8ee2be --- /dev/null +++ b/library/Class/Journal/RequestType.php @@ -0,0 +1,128 @@ +<?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 Class_Journal_RequestType extends Class_Journal_Type { + + const + MY_TYPE = 'REQUEST_CALL', + BOKEH_RESPONSE = 'bokeh_response', + HTTP_RESPONSE_CODE='Http response code', + CLIENT_REQUEST = 'Client request', + REQUEST_URL = 'Request url'; + + public static function createWith( $controller, $response, $response_code = 200) { + if ( !Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')) + return; + $model = new Class_Journal_RequestResponse($controller, $response, $response_code); + $classname = $model->getType(); + $journal = new $classname((new Class_Journal)); + $journal->save($model); + return $journal; + } + + + /** + * @param $model Zend_Controller_Action + */ + public function save($model) { + if (!parent::save($model)) + return false; + $request = $model->getRequest(); + + $details = ['Request uri :' => $request->getRequestUri(), + 'Header Authorization' => $request->getHeader('Authorization'), + 'Header Content-Type' => $request->getHeader('Content_type')]; + + if (!empty($request->getPost())) + $details = array_merge($details,[ 'With params: ' => $request->getPost()]); + $this->_journal->addDetail(static::REQUEST_URL, $request->getRequestUri()); + $this->_journal->addDetail(static::HTTP_RESPONSE_CODE, $model->getResponseCode()); + $this->_journal->addDetail(static::CLIENT_REQUEST, json_encode($details)); + $this->_journal->addDetail(static::BOKEH_RESPONSE,$model->getResponseBody()); + + return true; + } + + + public function renderLabelOn(Zend_View_Interface $view) : string { + if (!$url_without_param=strstr($this->renderDetailValueOn(static::REQUEST_URL, $view),'?',TRUE)) + $url_without_param = $this->renderDetailValueOn(static::REQUEST_URL, $view); + + return $this->_('call for %s with response code %s',$url_without_param,$this->renderDetailValueOn(static::HTTP_RESPONSE_CODE, $view)); + } + + + public function getModelLabel() : string { + return $this->_('Variable'); + } +} + + + + +class Class_Journal_RequestResponse { + protected $_controller, + $_response_body; + + public function __construct($controller,$response_body ,$response_code) { + $this->_controller = $controller; + $this->_response_body = $response_body; + $this->_response_code = $response_code; + } + + + public function getController() { + return $this->_controller; + } + + + public function getRequest() { + return $this->_controller->getRequest(); + } + + + public function getType() : string { + if ((!$request = $this->getRequest()) || + (!$url = $request->getRequestUri())) + return Class_Journal_RequestType::class; + + if (strpos($url, '/cas-server') !== false) + return Class_Journal_CasRequestType::class; + + if ((strpos($url, '/oauth') !== false) || + (strpos($url, '/auth/refresh') !== false) || + (strpos($url, '/api/') !== false)) + return Class_Journal_OauthRequestType::class; + + return Class_Journal_RequestType::class; + } + + + public function getResponseBody() { + return $this->_response_body; + } + + + public function getResponseCode() { + return $this->_response_code; + } +} diff --git a/library/Class/Journal/Type.php b/library/Class/Journal/Type.php index 3d203b3b73b902ff5a7d34eb5e049b70e90a586a..eaea279abf53ea07518e3a7188aa28f5a4b4e5c3 100644 --- a/library/Class/Journal/Type.php +++ b/library/Class/Journal/Type.php @@ -30,7 +30,8 @@ class Class_Journal_Type { STACK = 'stack', PREVIOUS_VALUE = 'previous_value', NEW_VALUE = 'new_value', - MODEL_ID = 'model_id'; + MODEL_ID = 'model_id', + APPLY_ON = ''; protected $_journal; @@ -43,6 +44,11 @@ class Class_Journal_Type { } + public static function create() : self { + return new static(new Class_Journal); + } + + public static function canHandleEvent(Storm_Event_Abstract $event) : bool { return get_class($event->getModel()) === static::APPLY_ON; } diff --git a/library/Class/Journal/TypeMapping.php b/library/Class/Journal/TypeMapping.php index 2434f1c301ee4d3270ee5f240077f22d766ffcfa..c113a041ff7d9770f7b8704a1025cef6ab9db34e 100644 --- a/library/Class/Journal/TypeMapping.php +++ b/library/Class/Journal/TypeMapping.php @@ -31,7 +31,10 @@ class Class_Journal_TypeMapping { Class_Journal_CatalogueDeleteType::class => $this->_('Domaines : suppression'), Class_Journal_ProfileType::class => $this->_('Profils : mise à jour'), Class_Journal_ProfileDeleteType::class => $this->_('Profils : suppression'), - Class_Journal_ArticleType::class => $this->_('Articles') + Class_Journal_ArticleType::class => $this->_('Articles'), + Class_Journal_RequestType::class => $this->_('Requêtes'), + Class_Journal_CasRequestType::class => $this->_('Requêtes CAS'), + Class_Journal_OauthRequestType::class => $this->_('Requêtes OAUTH'), ]; } diff --git a/library/Class/Loan/Pnb.php b/library/Class/Loan/Pnb.php index 3a179a8b78a5b661d13ead5d0229d26656e084aa..efc94a204d0f1ab03caf36e3778943d8d54e5b0c 100644 --- a/library/Class/Loan/Pnb.php +++ b/library/Class/Loan/Pnb.php @@ -96,7 +96,6 @@ class Class_Loan_PnbLoader extends Storm_Model_Loader { } - protected function _applyOngoingAndDo($params, $closure) { $prefix = isset($params['where']) ? '(' . $params['where'] . ') and ' : ''; $params['where'] = $prefix . 'expected_return_date > "' . $this->_getDate() . '"'; @@ -111,12 +110,13 @@ class Class_Loan_PnbLoader extends Storm_Model_Loader { protected function _getCNILMaxRetentionDate() :string { - return Class_Loan_Pnb::addDaysToCurrentDate(static::CNIL_DAYS_MAX_RETENTION); + return Class_Loan_Pnb::addDaysToCurrentDateTime(static::CNIL_DAYS_MAX_RETENTION); } } + class Class_Loan_Pnb extends Storm_Model_Abstract { use Trait_TimeSource, Trait_AlbumDelegator; @@ -132,7 +132,6 @@ class Class_Loan_Pnb extends Storm_Model_Abstract { 'record_origin_id' => 0, 'options' => '']; - public function getAlbum() { return Class_Album::findFirstBy(['id_origine' => $this->getRecordOriginId()]); } @@ -199,6 +198,77 @@ class Class_Loan_Pnb extends Storm_Model_Abstract { } + protected function _addGenericFormatAudioBooksIfNotSet($formats) { + if ( (!in_array('AN', $formats) and !in_array('AJ', $formats)) + and in_array('A103', $formats)) + $formats[] ='AN'; + return $formats; + } + + + public function getFormats() : array { + if (!$album = $this->getAlbum()) + return []; + return $this->_addGenericFormatAudioBooksIfNotSet(explode(';', $album->getFormat())); + } + + + public function getFirstCollection() : string { + return ($album = $this->getAlbum()) + ? $album->getFirstCollection() + : ''; + } + + + public function getLanguage() : array { + return ($album = $this->getAlbum()) + ? $album->getIdLangue() + : []; + } + + + public function getSubject() : string { + return ($album = $this->getAlbum()) + ? $album->getMatiere() + : ''; + } + + + public function getCollection() : string { + return ($album = $this->getAlbum()) + ? $album->getCollection() + : ''; + } + + + public function getPoster() : string { + return ($album = $this->getAlbum()) + ? $album->getPoster() + : ''; + } + + + public function getDescription(): string { + return ($notice = $this->getNoticeOPAC()) + ? strip_tags($notice->getResume()) + :''; + } + + + public function getIsbnOrEan(): string { + return ($notice = $this->getNoticeOPAC()) + ? $notice->getIsbnOrEan() + :''; + } + + + public function getFirstEditor(): string { + return ($notice = $this->getNoticeOPAC()) + ? $notice->getFirstEditeur() + :''; + } + + protected function _withAlbumDo($callback) { return array_filter(array_map('trim', explode(';', $callback()))); } @@ -235,7 +305,6 @@ class Class_Loan_Pnb extends Storm_Model_Abstract { } - public function isRenewable() { return false; } diff --git a/library/Class/Log.php b/library/Class/Log.php index b47bc1cad0b634f4a7edefcebdb7cc817fa4dfcd..c99d4b93c3062a9c2e15b8300cfdecb582fc0fc0 100644 --- a/library/Class/Log.php +++ b/library/Class/Log.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved. + * Copyright (c) 2012-2022, 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 @@ -25,24 +25,24 @@ class Class_Log { protected $_messages = []; - public function log($message) { + public function log($message) : self { $this->_messages[] = $message; return $this; } - public function getLastMessage() { + public function getLastMessage() : string { return end($this->_messages); } - public function getMessages() { + public function getMessages() : array { return $this->_messages; } - public function reset() { + public function reset() : self { $this->_messages = []; return $this; } -} \ No newline at end of file +} diff --git a/library/Class/LogFile.php b/library/Class/LogFile.php new file mode 100644 index 0000000000000000000000000000000000000000..aa3713a82ea1c0b96b4e776d9ea10039aa0b6b60 --- /dev/null +++ b/library/Class/LogFile.php @@ -0,0 +1,91 @@ +<?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 + */ + + +class Class_LogFile extends Class_Log { + use Trait_StaticFileSystem, Trait_Singleton, Trait_TimeSource; + public static $LOG, + $MAX_FILE_SIZE = 100000 ; + protected $_messages = [], + $_file; + + public static function pushRequestMessage($request) { + Class_LogFile::pushMessage("\nRequest url",$request->getRequestUri()); + if (!empty($request->getPost())) + Class_LogFile::pushMessage("With params",$request->getPost()); + Class_LogFile::pushMessage("Header Authorization",$request->getHeader('Authorization')); + Class_LogFile::pushMessage("Header Content-Type",$request->getHeader('Content_type')); + } + + + public static function pushMessage($title, $message) { + if (is_array($message)) + return (self::getLog())->log('['. static::getCurrentDateTime().'] '.$title.':'.print_r($message,true )."\n"); + return (self::getLog())->log('['. static::getCurrentDateTime().'] '.$title.':'.$message."\n"); + } + + + public static function setLog(Class_Log $log) { + self::$LOG = $log; + } + + + public static function getLog() : Class_Log { + if (self::$LOG) + return self::$LOG; + self::$LOG = new Class_LogFile('temp/log_auth'); + return self::$LOG; + } + + + public function __construct( string $file_name ) { + $this->_file = $file_name; + } + + + public function log($message) :self{ + $this->_messages[] = $message; + if (static::getFileSystem()->filesize( $this->_file) > static::$MAX_FILE_SIZE) { + static::getFileSystem()->file_put_contents($this->_file,$message); + return $this; + } + + static::getFileSystem()->file_put_contents($this->_file,$message, FILE_APPEND); + return $this; + } + + + public function getLastMessage() : string { + return end($this->_messages); + } + + + public function getMessages() : array { + return explode("\n", htmlentities(static::getFileSystem()->file_get_contents($this->_file))); + } + + + public function reset() : self { + $this->_messages = []; + static::getFileSystem()->file_put_contents($this->_file,''); + return $this; + } +} diff --git a/library/Class/User/AdminMenu/Back.php b/library/Class/User/AdminMenu/Back.php index acc84256ae33cd80d5fbef251074c706303f2d58..9fcfb7f6decf1cb59fee31c18ed236492eaebf4d 100644 --- a/library/Class/User/AdminMenu/Back.php +++ b/library/Class/User/AdminMenu/Back.php @@ -214,9 +214,10 @@ class Class_User_AdminMenu_Back extends Class_User_AdminMenu_Abstract { 'icon' => 'computers'], ['url' => '/admin/identity-providers', - 'label' => $this->_('Fournisseurs d\'identités'), + 'label' => $this->_('Fédération d\'identités'), 'icon' => 'identity_providers']]; + return $this->_menuObjectsFromArray($items); } diff --git a/library/Class/User/AdminMenu/Default.php b/library/Class/User/AdminMenu/Default.php index 21916410c7e875bbca37fb0ab9a0281466463da9..56c8e1d1adc25f33fa8cfddf1281ea7358d585b9 100644 --- a/library/Class/User/AdminMenu/Default.php +++ b/library/Class/User/AdminMenu/Default.php @@ -124,6 +124,7 @@ class Class_User_AdminMenu_Default { '/admin/users', '/admin/usergroup', '/admin/url-manager', + '/admin/identity-providers', '/admin/index/adminvar']); return $this; diff --git a/library/Class/User/ApiToken.php b/library/Class/User/ApiToken.php index 341ec965c787168838e0e90a09878e82837d8457..93f4a33bd3d91a9f4ff7b0343d0728e3d065b9e7 100644 --- a/library/Class/User/ApiToken.php +++ b/library/Class/User/ApiToken.php @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class User_ApiTokenLoader extends Storm_Model_Loader { + use Trait_TimeSource; public function findOrCreateForUserAndApplication($user, $client_id) { if ($token = Class_User_ApiToken::findFirstBy(['role' => 'user', 'model' => $user, @@ -29,24 +30,158 @@ class User_ApiTokenLoader extends Storm_Model_Loader { $token ->setUser($user) ->setClientId($client_id) - ->setToken(md5(uniqid())) - ->save(); + ->setToken(md5(uniqid())); + if ( !$client = $token->getClient() ) + return $token; + + if ($client->getAllowRefreshToken()) + $token->setRefreshToken(md5(uniqid())); + $token->save(); return $token; } + + public function createForUserAndApplication($user, $client_id) { + if ($token = Class_User_ApiToken::findFirstBy(['role' => 'user', + 'model' => $user, + 'client_id' => $client_id])) + $token->delete(); + + $token = new Class_User_ApiToken(); + $token + ->setUser($user) + ->setClientId($client_id) + ->setToken(md5(uniqid())); + if ( !$client = $token->getClient() ) + return $token; + + if ($client->getAllowRefreshToken()) + $token->setRefreshToken(md5(uniqid())); + $token->save(); + + return $token; + } + + + public function refreshToken( string $refresh_token ) : Class_User_ApiToken { + if (!$token = Class_User_ApiToken::findFirstBy(['refresh_token' => $refresh_token])) + return (new Class_User_ApiToken())->addExpiredError(); + return $token->refresh(); + } } class Class_User_ApiToken extends Storm_Model_Abstract { - + use Trait_TimeSource; protected $_table_name = 'user_api_tokens', + $_client, $_loader_class = 'User_ApiTokenLoader', $_belongs_to = ['user' => ['model' => 'Class_Users', - 'role' => 'api_token']]; + 'role' => 'api_token']], + $_default_attribute_values =['expired_at' =>null, + 'client_id' => '', + 'refresh_token' =>'']; + public function __construct() { + parent::__construct(); + $this->setCreatedAt(static::getCurrentDateTime()); + } + + + public function isExpired() : bool{ + if (!$this->getClient()) + return true; + + if (!$this->getClient()->getTokenExpiresIn()) + return false; + + if ($expired=$this->getExpiredAt() && ($this->getCurrentTime() > strtotime($this->getExpiredAt()))) + $this->addExpiredError(); + + return $expired; + } + + public function getUserName() : string { + return $this->getUser() ? + $this->getUser()->getNomComplet() : ''; + } + + + public function getClient() { + if ($this->_client) + return $this->_client; + if ($this->_client = Class_IdentityClient::findFirstBy(['client_id'=> $this->getClientId(), + 'active' => true])) + return $this->_client; + $this->addError( 'Client configuration missing for '.$this->getClientId()); + return null; + } + + + public function addExpiredError() : self { + $this->addError( 'The access token has expired'); + return $this; + } + + + public function getExpiresIn() { + if ( $client = $this->getClient() ) + return $client->getTokenExpiresIn(); + return 0; + } + + + public function checkRedirectUri($redirect_uri) : self{ + if (!$client = $this->getClient()) { + return $this; + } + + if ( $client->getRedirectUri() == '' + || $client->getRedirectUri()==$redirect_uri) + return $this; + $this->addError(sprintf('redirect_uri %s differs of client config :%s', + $redirect_uri, + $client->getRedirectUri())); + return $this; + } + + + public function refresh() : self{ + if ( !$client = $this->getClient() ) { + return $this; + } + if (!$client->getAllowRefreshToken()){ + $this->addError( 'Client configuration is not allowed refresh token'); + return $this; + } + + $this->setRefreshToken(md5(uniqid())); + $this->setToken(md5(uniqid())); + $this->setExpiredAt(''); + if ( $expires_in = $client->getTokenExpiresIn()){ + $this->setExpiredAt( $this->addSecondsToCurrentDate($expires_in)); + } + + $this->save(); + return $this; + } + + + public function beforeSave() { + if ( !$client = $this->getClient() ) + return $this; + if ($client->getTokenExpiresIn()) + $this->setExpiredAt( $this->addSecondsToCurrentDate( $client->getTokenExpiresIn())); + return $this; + } + + + public function getLibelle() : string{ + return $this->getToken(); + } } -?> \ No newline at end of file +?> diff --git a/library/Class/WebService/BibNumerique/Dilicom/Book.php b/library/Class/WebService/BibNumerique/Dilicom/Book.php index f41ba06518c4c5d7f4d3a25d296e24b0913c6d5f..78660b21f706d38740f4e59c5f30263ded2141e9 100644 --- a/library/Class/WebService/BibNumerique/Dilicom/Book.php +++ b/library/Class/WebService/BibNumerique/Dilicom/Book.php @@ -25,6 +25,7 @@ class Class_WebService_BibNumerique_Dilicom_Book extends Class_WebService_BibNum protected $_isbn, $_subtitle, + $_formats, $_order_line_id, $_order_date, $_items = [], @@ -104,6 +105,7 @@ class Class_WebService_BibNumerique_Dilicom_Book extends Class_WebService_BibNum ->setISBN($this->_isbn) ->setSousTitre($this->_subtitle) ->addEditor($this->getEditeur()) + ->setFormat( implode(';',$this->_formats)) ->setDroits($this->_('Tous droits réservés')) ->setItems($this->_items); return $this; @@ -161,5 +163,15 @@ class Class_WebService_BibNumerique_Dilicom_Book extends Class_WebService_BibNum return 'Livre numérique (PNB)'; } + + public function addFormat($format) { + $this->_formats[] = $format; + return $this; + } + + + public function getFormats() { + return implode(';', array_unique($this->_formats)); + } } ?> diff --git a/library/Class/WebService/BibNumerique/Dilicom/Hub.php b/library/Class/WebService/BibNumerique/Dilicom/Hub.php index 1bfbc81221b21383520b8511131b74a6a12fed97..67a94e9840d0f7242f6cd8ede141bec48733d461 100644 --- a/library/Class/WebService/BibNumerique/Dilicom/Hub.php +++ b/library/Class/WebService/BibNumerique/Dilicom/Hub.php @@ -205,10 +205,7 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac } if (isset($content->link) && ($link = $content->link) && isset($link->url) && ($url = $link->url)) { - $loan->setLoanLink($url - .($user_agent - ? '?userAgentId='.$user_agent - :'')); + $loan->setLoanLink($url); if (isset($content->protection)) $loan->setOptions($content->protection)->save(); $loan->save(); diff --git a/library/Class/WebService/BibNumerique/Dilicom/ONIXFile.php b/library/Class/WebService/BibNumerique/Dilicom/ONIXFile.php index fb4d8659fad8d68a4c83cafc7e629189ecf91f24..0be0e4e2a08ad6ec50e1eecc1391d2b48389d433 100644 --- a/library/Class/WebService/BibNumerique/Dilicom/ONIXFile.php +++ b/library/Class/WebService/BibNumerique/Dilicom/ONIXFile.php @@ -38,6 +38,7 @@ class Class_WebService_BibNumerique_Dilicom_ONIXFile { protected $_parser, $_book, + $_formats = [], $_current_publishing_date_role, $_current_text_type, $_current_product_id_type, @@ -273,5 +274,15 @@ class Class_WebService_BibNumerique_Dilicom_ONIXFile { public function endEPubUsageUnit($content) { $this->_current_usage_unit = $content; } + + + public function endProductFormDetail($format) { + $this->_book->addFormat($format); + } + + public function endProductForm($format) { + $this->_book->addFormat($format); + } + } -?> \ No newline at end of file +?> diff --git a/library/Trait/TimeSource.php b/library/Trait/TimeSource.php index 5dac78b967a9654c56c053984651a7ed488fe643..0e2f04409eb17435a4340296239eab134e1a55d7 100644 --- a/library/Trait/TimeSource.php +++ b/library/Trait/TimeSource.php @@ -58,7 +58,17 @@ trait Trait_TimeSource { public static function addDaysToCurrentDate($days) { - return date('Y-m-d', strtotime((string)$days.' day', self::getTimeSource()->time())); + return date('Y-m-d', static::addIntervalToDate((string)$days.' day', null)); + } + + + public static function addDaysToCurrentDateTime($days) { + return date('Y-m-d H:i:s', static::addIntervalToDate((string)$days.' day', null)); + } + + + public static function addSecondsToCurrentDate($seconds) { + return date('Y-m-d H:i:s', static::addIntervalToDate((string)$seconds .' second' , null)); } diff --git a/library/ZendAfi/Acl/AdminControllerRoles.php b/library/ZendAfi/Acl/AdminControllerRoles.php index fc05362afea8b53f58d4e05f69fcc0fc9def4041..2094bf174e77091200e04424ee258d2147601161 100644 --- a/library/ZendAfi/Acl/AdminControllerRoles.php +++ b/library/ZendAfi/Acl/AdminControllerRoles.php @@ -118,6 +118,8 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl { $this->add(new Zend_Acl_Resource('rendez-vous')); $this->add(new Zend_Acl_Resource('journal')); $this->add(new Zend_Acl_Resource('identity-providers')); + $this->add(new Zend_Acl_Resource('identity-clients')); + $this->add(new Zend_Acl_Resource('user-api-tokens')); $this->add(new Zend_Acl_Resource('drive-checkout')); $this->add(new Zend_Acl_Resource('template')); $this->add(new Zend_Acl_Resource('multimedia')); diff --git a/library/ZendAfi/Controller/Action/Helper/CasFailureResponse.php b/library/ZendAfi/Controller/Action/Helper/CasFailureResponse.php index 4adb1c4b87dba660c7618234862eb1218676e536..870561b9cee200106fcbe0be96a8318153e921ed 100644 --- a/library/ZendAfi/Controller/Action/Helper/CasFailureResponse.php +++ b/library/ZendAfi/Controller/Action/Helper/CasFailureResponse.php @@ -45,6 +45,8 @@ class ZendAfi_Controller_Action_Helper_CasFailureResponse extends ZendAfi_Contro $this->_controller->getHelper('ViewRenderer')->setNoRender(); $this->_response->setHeader('Content-Type', 'application/xml;charset=utf-8'); $xml = $xml->_xmlString('cas:serviceResponse', implode('\n', $body), ' xmlns:cas="http://www.yale.edu/tp/cas"'); + + Class_Journal_RequestType::createWith( $this->_controller, $xml); $this->getResponse()->setBody($xml); } -} \ No newline at end of file +} diff --git a/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php b/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php index 540a0362d1ed185e3b60f3feaaea4698e0a2046d..a4bd01975f5d34eca15cc27d531cb7ad1a8366b8 100644 --- a/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php +++ b/library/ZendAfi/Controller/Action/Helper/CasValidResponse.php @@ -25,15 +25,16 @@ class ZendAfi_Controller_Action_Helper_CasValidResponse extends ZendAfi_Controll public function direct($user, $ticket, $attributes = []) { $this->_controller->getHelper('ViewRenderer')->setNoRender(); $this->_response->setHeader('Content-Type', 'application/xml;charset=utf-8'); - + $xml="<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>"; + Class_Journal_RequestType::createWith( $this->_controller, $xml); $this->_response - ->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>"); + ->setBody($xml); } @@ -64,4 +65,4 @@ class ZendAfi_Controller_Action_Helper_CasValidResponse extends ZendAfi_Controll . "\n </cas:attributes>\n"; } } -?> \ No newline at end of file +?> diff --git a/library/ZendAfi/Controller/Action/Helper/SendHttpErrorCode.php b/library/ZendAfi/Controller/Action/Helper/SendHttpErrorCode.php new file mode 100644 index 0000000000000000000000000000000000000000..0eb1db1d33cc7ff30fe5fae309bb6f8f25cd8f2e --- /dev/null +++ b/library/ZendAfi/Controller/Action/Helper/SendHttpErrorCode.php @@ -0,0 +1,42 @@ +<?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 + */ + +class ZendAfi_Controller_Action_Helper_SendHttpErrorCode extends ZendAfi_Controller_Action_Helper_Abstract { + + + public function sendHttpErrorCode($error,$error_description, $code = 400 ) { + $this->_controller->getHelper('ViewRenderer')->setNoRender(); + $json = [ + 'error' => $error, + 'error_description' => $error_description + ]; + Class_Journal_RequestType::createWith( $this->_controller, json_encode($json), $code); + $this->_response->setHttpResponseCode($code); + $this->_controller->getHelper('json')->direct($json); + + return true; + } + + + public function direct($error,$error_description, $code) { + return $this->sendHttpErrorCode($error,$error_description, $code); + } +} diff --git a/library/ZendAfi/Controller/Action/Helper/ThrowHTTPError.php b/library/ZendAfi/Controller/Action/Helper/ThrowHTTPError.php index af6898765ccf0272f24b7872d1000c6fbeb1ce30..6a72cfbc963f76ef5c0f5624d62dc22d078074b7 100644 --- a/library/ZendAfi/Controller/Action/Helper/ThrowHTTPError.php +++ b/library/ZendAfi/Controller/Action/Helper/ThrowHTTPError.php @@ -27,11 +27,11 @@ class ZendAfi_Controller_Action_Helper_ThrowHTTPError extends ZendAfi_Controller ->getFrontController() ->getPlugin('Zend_Controller_Plugin_ErrorHandler') ->setErrorHandlerModule($this->getRequest()->getModuleName()); - - throw new Zend_Controller_Action_Exception($message, $code); + Class_Journal_RequestType::createWith( $this->getFrontController(), $message, $code); + throw new Zend_Controller_Action_Exception($message , $code); } public function direct($message, $code) { return $this->throwHTTPError($message, $code); } -} \ No newline at end of file +} diff --git a/library/ZendAfi/Controller/Plugin/Manager/IdentityClient.php b/library/ZendAfi/Controller/Plugin/Manager/IdentityClient.php new file mode 100644 index 0000000000000000000000000000000000000000..8a8e7f094b0775975ea823e107bf2edb209d05c2 --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/Manager/IdentityClient.php @@ -0,0 +1,52 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 ZendAfi_Controller_Plugin_Manager_IdentityClient + extends ZendAfi_Controller_Plugin_Manager_Manager { + + public function getActions($model) { + return [ + ['url' => '/admin/identity-clients/edit/id/%s', + 'icon' => 'edit', + 'label' => $this->_('Modifier le client d\'identité')], + ['url' => '/admin/user-api-tokens/show/client_id/'.$model->getClientId(), + 'icon' => 'view', + 'label' => $this->_('Voir les connexions')], + + ['url' => '/admin/identity-clients/delete/id/%s', + 'icon' => 'delete', + 'label' => $this->_('Supprimer le client d\'identité')], + ]; + } + + + protected function _getFormValues($model) { + return array_merge($model->toArray(), + $model->getConfigAsArray()); + } + + + protected function _getFormWith($model, $custom_form) { + $form = parent::_getFormWith($model,$custom_form); + return $form; + } +} diff --git a/library/ZendAfi/Controller/Plugin/Manager/IdentityProvider.php b/library/ZendAfi/Controller/Plugin/Manager/IdentityProvider.php index c0d3d9cd99711f2d2fc1394aee9e494c58b3a8e4..ab73f67d8f2ee78d2e7dfb7735c4bd5e398e014e 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/IdentityProvider.php +++ b/library/ZendAfi/Controller/Plugin/Manager/IdentityProvider.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (c) 2012-2017, Agence Française Informatique (AFI). All rights reserved. + * 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 diff --git a/library/ZendAfi/Controller/Plugin/Manager/UserApiTokens.php b/library/ZendAfi/Controller/Plugin/Manager/UserApiTokens.php new file mode 100644 index 0000000000000000000000000000000000000000..6d97872b7fb730c635242ae89461a82640a5988f --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/Manager/UserApiTokens.php @@ -0,0 +1,34 @@ +<?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 + */ + + +class Zendafi_Controller_Plugin_Manager_UserApiTokens + extends ZendAfi_Controller_Plugin_Manager_Manager { + + public function getActions($model) { + return [ + ['url' => '/admin/user-api-tokens/delete/id/%s', + 'icon' => 'delete', + 'label' => $this->_('Révoquer')], + + ]; + } +} diff --git a/library/ZendAfi/Controller/Plugin/ResourceDefinition/IdentityClient.php b/library/ZendAfi/Controller/Plugin/ResourceDefinition/IdentityClient.php new file mode 100644 index 0000000000000000000000000000000000000000..d5e6b2e645815673784ec1804e3f5dacbc9aee10 --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/ResourceDefinition/IdentityClient.php @@ -0,0 +1,41 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 ZendAfi_Controller_Plugin_ResourceDefinition_IdentityClient + extends ZendAfi_Controller_Plugin_ResourceDefinition_Abstract { + + public function getDefinitions() { + return ['model' => ['class' => Class_IdentityClient::class, + 'name' => 'server', + 'order' => 'label'], + + 'messages' => ['successful_save' => $this->_('Client d\'identité %s sauvegardé'), + 'successful_add' => $this->_('Le client d\'identité %s a été sauvegardé'), + 'successful_delete' => $this->_('Client d\'identité %s supprimé')], + + 'actions' => ['add' => ['title' => $this->_('Ajouter un client d\'identité')], + 'edit' => ['title' => $this->_('Modifier le client d\'identité: %s')]], + + 'form_class_name' => 'ZendAfi_Form_Admin_IdentityClient' + ]; + } +} diff --git a/library/ZendAfi/Controller/Plugin/ResourceDefinition/UserApiTokens.php b/library/ZendAfi/Controller/Plugin/ResourceDefinition/UserApiTokens.php new file mode 100644 index 0000000000000000000000000000000000000000..fd396eae56982714d45cbf82fbaa194562b5204b --- /dev/null +++ b/library/ZendAfi/Controller/Plugin/ResourceDefinition/UserApiTokens.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 ZendAfi_Controller_Plugin_ResourceDefinition_UserApiTokens + extends ZendAfi_Controller_Plugin_ResourceDefinition_Abstract { + + public function getDefinitions() { + return ['model' => ['class' => 'Class_User_ApiToken', + 'name' => 'api_token', + 'order' => 'label'], + + 'messages' => [ 'successful_delete' => $this->_('Token %s supprimé')], + ]; + } +} diff --git a/library/ZendAfi/Form/Admin/IdentityClient.php b/library/ZendAfi/Form/Admin/IdentityClient.php new file mode 100644 index 0000000000000000000000000000000000000000..5753829d605535b43be8091c22811d2f47a00609 --- /dev/null +++ b/library/ZendAfi/Form/Admin/IdentityClient.php @@ -0,0 +1,84 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 ZendAfi_Form_Admin_IdentityClient extends ZendAfi_Form { + public function init() { + parent::init(); + + Class_ScriptLoader::getInstance() + ->addJQueryBackEnd('formSelectToggleVisibilityForElement("#type", $("#client_secret,#redirect_uri,#disable_state,#disable_response_type, #allow_refresh_token, #token_expires_in").closest("tr"), ["Class_IdentityClient_OAuth2"]);'); + + + $this + ->addElement('text', + 'label', + ['label' => $this->_('Libellé'), + 'size' => 50, + 'required' => true, + 'allowEmpty' => false]) + + ->addElement('checkbox', + 'active', + ['label' => $this->_('Actif')]) + + ->addElement('select', + 'type', + ['label' => $this->_('Type'), + 'multiOptions' => (new Class_IdentityClient_Types)->asMultiOptions()]) + + ->addElement('text', + 'client_id', + ['label' => $this->_('Identifiant client'), + 'size' => 50]) + + ->addElement('text', + 'client_secret', + ['label' => $this->_('Clé secrète'), + 'size' => 50]) + + ->addElement('url', + 'redirect_uri', + ['label' => $this->_('URL de redirection'), + 'size' => 50, + 'allowEmpty' => false, + 'title' => $this->_('URL de redirection')]) + + ->addElement('checkbox', + 'disable_response_type', + ['label' => $this->_('Désactiver la vérification du response_type')]) + + ->addElement('checkbox', + 'allow_refresh_token', + ['label' => $this->_('Autoriser la génération d\'un refresh token')]) + + ->addElement('text', + 'token_expires_in', + ['label' => $this->_('Temps d\'expiration d\'un token')]) + + + ->addElement('checkbox', + 'disable_state', + ['label' => $this->_('Désactiver la vérification du state')]) + + ->addUniqDisplayGroup('server'); + } +} diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php index c06115feae4987edf8e4a2df03ba0edf17adb089..0330820343cc171b2738b54da81ce40019164c30 100644 --- a/library/ZendAfi/View/Helper/Admin/HelpLink.php +++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php @@ -134,6 +134,8 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki { 'drive-checkout' => ['index' => 'Gérer_les_listes_de_rendez-vous_en_mode_drive', 'plan' => 'Prise_de_rendez-vous_par_les_professionnels'], 'identity-providers' => ['index' => 'Fournisseurs_d\'identités'], + 'identity-clients' => ['index' => 'Clients_d\'identités'], + 'identity-providers/federation' => ['index' => 'Fédération_d\'identités'], 'variable' => ['index' => 'Liste_des_variables'], 'pnb' => ['index' => 'PNB_Dilicom_tableau_de_bord'], 'journal' => ['index' => 'Journal_des_événements'] diff --git a/library/ZendAfi/View/Helper/Api/PnbLoans.php b/library/ZendAfi/View/Helper/Api/PnbLoans.php new file mode 100644 index 0000000000000000000000000000000000000000..04fa5485930815039984e3794a0421a93dfeffff --- /dev/null +++ b/library/ZendAfi/View/Helper/Api/PnbLoans.php @@ -0,0 +1,62 @@ +<?php +/** + * Copyright (c) 2012-2014, 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 ZendAfi_View_Helper_Api_PnbLoans extends ZendAfi_View_Helper_Api_Abstract { + public function pnbLoans(Storm_Collection $loans):string { + return json_encode( + $loans->collect(fn($loan) => $this->loanToArray($loan)) + ->getArrayCopy() + ); + } + + + public function loanToArray($loan) { + if (! $user = Class_Users::getIdentity()) + return [] ; + + $datas = [ + 'loanId' => $loan->getId(), + 'userId'=> $loan->getSubscriberId(), + 'orderLineId' => $loan->getOrderLineId(), + 'loanhLink' => $loan->getLoanLink(), + 'beginDate'=>date('Y-m-d\TH:i:s\Z',strtotime($loan->getLoanDate())), + + 'description' => $loan->getDescription(), + 'endDate'=> date('Y-m-d\TH:i:s\Z',strtotime($loan->getExpectedReturnDate())), + 'standardTitle' => strip_tags($loan->getTitre()), + 'frontCoverMedium' => $loan->getPoster(), + 'gtin13'=> $loan->getIsbnOrEan(), + 'loanerGln'=> $user->getBibGLN(), + 'epubTechnicalProtection' => $loan->getOptions(), + 'imprintName' => $loan->getFirstEditor(), + 'collection' => $loan->getFirstCollection(), + 'contributors'=>[], + 'categoryClil'=> $loan->getSubject(), + 'author' => $loan->getAuteur(), + 'productFormDetails' => $loan->getFormats() + ]; + + return $datas; + + } +} +?> diff --git a/library/ZendAfi/View/Helper/Button/ActivationLog.php b/library/ZendAfi/View/Helper/Button/ActivationLog.php new file mode 100644 index 0000000000000000000000000000000000000000..23250f99e1867e756ce157d84cf62eea0adde6e3 --- /dev/null +++ b/library/ZendAfi/View/Helper/Button/ActivationLog.php @@ -0,0 +1,42 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 ZendAfi_View_Helper_Button_ActivationLog extends ZendAfi_View_Helper_BaseHelper { + + public function Button_ActivationLog() { + $val = '1'; + $text = $this->_( 'Activer les logs sur les requêtes'); + + if ( Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')) { + $val = '0'; + $text = $this->_( 'Desactiver les logs sur les requêtes'); + } + + return $this->view->button((new Class_Button) + ->setText($text) + ->setUrl($this->view->url(['module' => 'admin', + 'controller' => 'index', + 'action' => 'adminvar_set', + 'cle' => 'ACTIVATE_AUTH_LOG', + 'valeur' => $val]))); + } +} diff --git a/library/templates/Intonation/Library/Record/DigitalResources.php b/library/templates/Intonation/Library/Record/DigitalResources.php index 717b795eeeb7cb85061fe7ca269651a7efb04deb..44f2df363034ad434df7fa46fa99f7d4d6872237 100644 --- a/library/templates/Intonation/Library/Record/DigitalResources.php +++ b/library/templates/Intonation/Library/Record/DigitalResources.php @@ -57,7 +57,9 @@ class Intonation_Library_Record_DigitalResources { ['class' => 'link_album', 'data-popup' => 'true']); + $html .= $this->_view->renderAlbum($this->_record->getAlbum()); + $html .= $this->_view->recordAlbums($this->_record); if ('' == $html) diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php index 61c99a18e85aced46f4941db2a1d07019ea29b5f..4e5cf71a55d0edcc10a30742ab53fc88ce15b7ce 100644 --- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php +++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php @@ -307,7 +307,6 @@ class AdminIndexControllerAdminVarEditActionTest extends Admin_AbstractControlle } - /** @test */ public function postWithStylesReloadPopupShouldReopenEditSameKey() { $this->postDispatch('/admin/index/adminvaredit/cle/FACETTE_GENRE_LIBELLE/styles_reload/1/render/popup', @@ -327,11 +326,13 @@ class AdminIndexControllerAdminVarEditCKEditorActionTest extends Admin_AbstractC parent::setUp(); } + /** @test */ public function defaultValueShouldBeSet() { $this->assertEquals('Votre compte sera mis à jour dans un délai de 15 minutes après le retour anticipé du document.',Class_AdminVar::getValueOrDefault('DILICOM_PNB_LOAN_WARNING_MESSAGE')); } + /** @test */ public function editPageShouldContainsTitlePNBLoanMessage() { $this->fixture('Class_AdminVar', @@ -344,6 +345,7 @@ class AdminIndexControllerAdminVarEditCKEditorActionTest extends Admin_AbstractC $this->_response->getBody()); } + /** @test */ public function postEnrichTextShouldNotRemoveTags() { $this->fixture('Class_AdminVar', @@ -357,8 +359,6 @@ class AdminIndexControllerAdminVarEditCKEditorActionTest extends Admin_AbstractC $this->dispatch('/admin/index/adminvaredit/cle/DILICOM_PNB_LOAN_WARNING_MESSAGE'); $this->assertEquals('<b>don\'t use this, drm are dangerous!</b>', Class_AdminVar::get('DILICOM_PNB_LOAN_WARNING_MESSAGE')); } - - } @@ -458,7 +458,6 @@ class AdminIndexControllerAdminVarEditWorkflowPostPermissionTest extends AdminIn } - protected function _fixDynamicWorkflowPermission($id, $status_id, $label) { $this->fixture('Class_Permission', ['id' => $id, 'code' => 'DYNAMIC_' . $status_id, @@ -470,6 +469,7 @@ class AdminIndexControllerAdminVarEditWorkflowPostPermissionTest extends AdminIn + class AdminIndexControllerAdminVarEditWorkflowWithUsedPostTest extends AdminIndexControllerAdminVarEditWithWorkflowTestCase { public function setUp() { parent::setUp(); @@ -496,6 +496,7 @@ class AdminIndexControllerAdminVarEditWorkflowWithUsedPostTest extends AdminInde + class AdminIndexControllerAdminVarEditWorkflowGoSimpleWithUsedPostTest extends AdminIndexControllerAdminVarEditWithWorkflowTestCase { public function setUp() { parent::setUp(); @@ -579,10 +580,10 @@ abstract class AdminIndexControllerDilicomPnbIpAdressesTestCase extends Admin_Ab RessourcesNumeriquesFixtures::activateDilicom(); - Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient($this->_http); } + public function tearDown() { Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient(null); RessourcesNumeriquesFixtures::deactivateDilicom(); @@ -636,6 +637,33 @@ class AdminIndexControllerAdminVarEditDilicomPnbIpAdressesTest extends AdminInde + +class AdminIndexControllerSetTestCase extends Admin_AbstractControllerTestCase{ + /** + * @test + */ + public function setVarShouldSetAdminVar(){ + Class_AdminVar::set('ACTIVATE_AUTH_LOG',1); + $this->assertEquals( 1 , Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')); + $this->dispatch('/admin/index/adminvar_set/cle/ACTIVATE_AUTH_LOG/valeur/0'); + $this->assertEquals( 0, Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')); + } + + + /** + * @test + */ + public function unsetVarShouldActivateAdminVar(){ + Class_AdminVar::set('ACTIVATE_AUTH_LOG',0); + $this->assertEquals( 0 , Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')); + $this->dispatch('/admin/index/adminvar_set/cle/ACTIVATE_AUTH_LOG/valeur/1/'); + $this->assertEquals( 1, Class_AdminVar::get( 'ACTIVATE_AUTH_LOG')); + } +} + + + + class AdminIndexControllerAdminVarDilicomDeclareIpPostTest extends AdminIndexControllerDilicomPnbIpAdressesTestCase { /** @test */ public function hubErrorMessageShouldAppearInNotification() { @@ -696,7 +724,6 @@ class AdminIndexControllerAdminVarDilicomDeclareIpPostTest extends AdminIndexCon } - /** @see http://forge.afi-sa.fr/issues/24383 */ class AdminIndexControllerAdminVarEditTextReplacementsActionTest extends Admin_AbstractControllerTestCase { @@ -776,6 +803,7 @@ class AdminIndexControllerAdminVarEditComboInvalidPostActionTest + class AdminIndexControllerDisplayModifierNomDomaineTest extends AbstractControllerTestCase { /** @test */ @@ -793,6 +821,7 @@ class AdminIndexControllerDisplayModifierNomDomaineTest extends AbstractControll $this->assertXpathContentContains('//div[@class="modules"]//a', 'Modifier'); } + /** @test */ public function ModifierNomDomaineShouldNotBePresent() { $user = $this->fixture('Class_Users', ['id' => 8, @@ -821,13 +850,12 @@ abstract class AdminIndexControllerAdminVarEditSearchAlsoInTestCase extends Abst ['id' => 'SEARCH_ALSO_IN', 'valeur' => json_encode(['site_label' => ['Jumel'], 'site_url' => ['http://testing.fr?q=%s']])]); - - } } + class AdminIndexControllerAdminVarEditSearchAlsoInTest extends AdminIndexControllerAdminVarEditSearchAlsoInTestCase { @@ -876,6 +904,7 @@ class AdminIndexControllerAdminVarEditSearchAlsoInTest + class AdminIndexControllerAdminVarEditSearchAlsoInPostTest extends AdminIndexControllerAdminVarEditSearchAlsoInTestCase { @@ -902,17 +931,16 @@ class AdminIndexControllerAdminVarEditSearchAlsoInPostTest + class AdminIndexControllerAdminVarEditHTTPSTest extends Admin_AbstractControllerTestCase { protected $_storm_default_to_volatile = true; protected $oldServerName; - public function setUp() { parent::setUp(); $this->oldServerName = $_SERVER['SERVER_NAME']; $_SERVER['SERVER_NAME'] = "MyWebsite"; - } @@ -922,6 +950,7 @@ class AdminIndexControllerAdminVarEditHTTPSTest $_SERVER['SERVER_NAME'] = $this->oldServerName; } + /** @test */ public function editAdminVarShouldPostHTTPS() { Class_AdminVar::set('FORCE_HTTPS', 0); @@ -935,10 +964,7 @@ class AdminIndexControllerAdminVarEditHTTPSTest Class_AdminVar_ForceHTTPS::$FORCE_HTTPS = true; $this->dispatch('/admin/index/adminvaredit/cle/FORCE_HTTPS', true); $this->assertXPath('//form[@action="https://MyWebsite/admin/index/adminvaredit/cle/FORCE_HTTPS"]',$this->_response->getBody()); - } - - } @@ -948,7 +974,6 @@ class AdminIndexControllerWithFormLinkTest extends Admin_AbstractControllerTestC protected $_storm_default_to_volatile = true; - public function setUp() { parent::setUp(); $super_admin = $this->fixture('Class_Users', diff --git a/tests/application/modules/opac/controllers/CasServerControllerTest.php b/tests/application/modules/opac/controllers/CasServerControllerTest.php index d8d9e4721001038f9d7c5b88b93b09c4655da254..bc0a894f581adaf077390034daff05a9457967c1 100644 --- a/tests/application/modules/opac/controllers/CasServerControllerTest.php +++ b/tests/application/modules/opac/controllers/CasServerControllerTest.php @@ -23,9 +23,11 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { protected $session_file_contents_logged; protected $session_file_contents_nologin; - + protected $_storm_default_to_volatile = true; public function setUp() { parent::setUp(); + Class_adminvar::set('ACTIVATE_AUTH_LOG', 1); + Storm_Cache::beVolatile(); $user = new StdClass(); $user->ID_USER=300; @@ -42,15 +44,17 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { /** @test */ public function requestWithNoServiceShouldRespondinvalidRequestFailureXML() { $this->dispatch('/opac/cas-server/validate?ticket=myticket'); - $this->assertContains('<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"><cas:authenticationFailure code="INVALID_REQUEST"></cas:authenticationFailure></cas:serviceResponse>', $this->_response->getBody()); + $this->assertContains('INVALID_REQUEST', + Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::BOKEH_RESPONSE)->getValue()); + } /** @test */ public function requestWithNoTicketShouldRespondinvalidRequestFailureXML() { $this->dispatch('/opac/cas-server/validate?service='.urlencode('http://test.com')); - $this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">', $this->_response->getBody()); + $this->assertContains('<cas:authenticationFailure code="INVALID_REQUEST">', Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_CasRequestType::BOKEH_RESPONSE)->getValue()); } @@ -58,6 +62,8 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { public function requestWithInvalidTicketShouldRespondInvalidTicketFailureXML() { $this->dispatch('/opac/cas-server/validate?ticket=STmarchepo&service=http://test.com', true); $this->assertContains('<cas:serviceResponse xmlns:cas="http://www.yale.edu/tp/cas"><cas:authenticationFailure code="INVALID_TICKET"><![CDATA[ Ticket STmarchepo not recognized]]></cas:authenticationFailure></cas:serviceResponse>', $this->_response->getBody()); + $this->assertContains('INVALID_TICKET', Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::BOKEH_RESPONSE)->getValue()); + } @@ -75,6 +81,8 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { $ticket, urlencode('http://test.com'))); $this->assertContains("<cas:serviceResponse xmlns:cas='http://www.yale.edu/tp/cas'>\n <cas:authenticationSuccess>\n <cas:user>300</cas:user>\n <cas:proxyGrantingTicket>".$ticket."</cas:proxyGrantingTicket>\n </cas:authenticationSuccess>\n</cas:serviceResponse>", $this->_response->getBody()); + $this->assertContains('<cas:authenticationSuccess>', Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::BOKEH_RESPONSE)->getValue()); + } @@ -158,6 +166,9 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { public function loginOnCasOneZeroWithoutOpenedSessionShouldDisplayLoginForm() { ZendAfi_Auth::getInstance()->clearIdentity(); $this->dispatch('/opac/cas-server-v10/login?service=http://test.com', true); + $this->assertContains('/opac/cas-server-v10/login?service=http://test.com', + Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::REQUEST_URL)->getValue()); + $this->assertXPath('//form//input[@name="password"]'); } @@ -166,6 +177,9 @@ class CasServerControllerValidateActionTest extends AbstractControllerTestCase { public function logoutOnCasOneZeroShouldClearIdentityAndDisplayThatYouHaveBeenDisconnected() { $this->dispatch('/opac/cas-server-v10/logout', true); $this->assertXPathContentContains('//p', 'Vous avez été déconnecté'); + $this->assertContains('/opac/cas-server-v10/logout', + Class_Journal::lastOf(Class_Journal_CasRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::REQUEST_URL)->getValue()); + $this->assertEmpty(ZendAfi_Auth::getInstance()->getIdentity()); } diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index acf9e303ec9aefee0279772a1921766612aaeb80..379433965a0973d05b47e8dc3835508c51af8b23 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -4824,6 +4824,7 @@ class UpgradeDB_437_Test extends UpgradeDBTestCase { + class UpgradeDB_438_Test extends UpgradeDBTestCase { public function prepare() { $this->silentQuery('ALTER TABLE `codif_thesaurus` drop column index_labels'); @@ -4850,3 +4851,76 @@ class UpgradeDB_439_Test extends UpgradeDBTestCase { } } + + + + +class UpgradeDB_440_Test extends UpgradeDBTestCase { + public function prepare() { + $this->silentQuery('alter table user_api_tokens drop column refresh_token'); + $this->silentQuery('alter table user_api_tokens drop column expirated_at'); + $this->silentQuery('alter table user_api_tokens drop column created_at'); + $this->silentQuery('alter table user_api_tokens drop column created_at'); + $this->query("insert into loan_pnb (record_origin_id, subscriber_id, user_id, loan_link) values('TestDilicom' , '1234','1','https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JZ.do?userAgentId=0932')"); + $this->query("insert into loan_pnb (record_origin_id, subscriber_id, user_id, loan_link) values('TestDilicom2' , '1234','1','https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JZ.do?userAgentId=0932&other=wedontcare')"); + $this->query("insert into loan_pnb (record_origin_id, subscriber_id, user_id, loan_link) values('TestDilicom3' , '1234','1','https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JuserAgentId=0932')"); + + } + + + public function tearDown() { + $this->query('delete from loan_pnb where record_origin_id="TestDilicom"'); + $this->query('delete from loan_pnb where record_origin_id="TestDilicom2"'); + $this->query('delete from loan_pnb where record_origin_id="TestDilicom3"'); + } + + + /** @test */ + public function userApiShouldHaveColumnRefreshToken() { + $this->assertFieldType('user_api_tokens', 'refresh_token', 'varchar(255)'); + } + + + /** @test */ + public function userApiShouldHaveColumnExpirationDate() { + $this->assertFieldType('user_api_tokens', 'expired_at', 'datetime'); + } + + + /** @test */ + public function userApiShouldHaveColumnCreatedAt() { + $this->assertFieldType('user_api_tokens', 'created_at', 'datetime'); + } + + + /** @test */ + public function identityShouldHaveColumnCreatedAt() { + $this->assertFieldType('identity_client', 'client_id', 'varchar(255)'); + } + + + /** @test */ + public function loanLinkShouldHaveRemovedUserAgentId() { + $row = $this->query('select loan_link from loan_pnb where record_origin_id="TestDilicom"') + ->fetch(); + $this->assertTrue("https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JZ.do" === $row['loan_link']); + } + + + /** @test */ + public function loan2LinkShouldHaveRemovedUserAgentId() { + $row = $this->query('select loan_link from loan_pnb where record_origin_id="TestDilicom2"') + ->fetch(); + $this->assertTrue("https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JZ.do" === $row['loan_link']); + + } + + + /** @test */ + public function loan3LinkShouldNotHaveRemovedUserAgentId() { + $row = $this->query('select loan_link from loan_pnb where record_origin_id="TestDilicom3"') + ->fetch(); + $this->assertTrue("https://pnb-dilicom.centprod.com/v2//link/3056000551506/LOAN/4/9782404003566-P4BRGR1CXJI4FZBRYLO64KEJZT7F02JuserAgentId=0932" === $row['loan_link']); + + } +} diff --git a/tests/fixtures/DilicomFixtures.php b/tests/fixtures/DilicomFixtures.php index 72e5197026f2adaa8373470b25d8e2d07134c057..eb48aff2fc7d7c848e5cdb6fceb73a3f1c10e8be 100644 --- a/tests/fixtures/DilicomFixtures.php +++ b/tests/fixtures/DilicomFixtures.php @@ -241,7 +241,7 @@ class DilicomFixtures { 'quantity' => 4, 'usage_constraints' => $constraints]); - return $this->fixture('Class_Album', + $album = $this->fixture(Class_Album::class, ['id' => 3, 'titre' => 'Totem et Thora', 'id_origine' => 'Dilicom-88817216', @@ -249,6 +249,9 @@ class DilicomFixtures { 'url_origine' => 'https://url_dilicom.org/ressource/id/1', 'type_doc_id' => Class_TypeDoc::DILICOM, 'items' => [$item]]) - ->addAuthor('Raphaël Draï'); + + ->addAuthor('Raphaël Draï'); + $album->save(); + return $album; } } diff --git a/tests/scenarios/AdminMenuComposition/AdminMenuCompositionTest.php b/tests/scenarios/AdminMenuComposition/AdminMenuCompositionTest.php index 4014735c97a50e98ac92b1fa92d8216eac197abc..0b7fbeadf00f315ed18cdaf55b7eba46fa52f2bd 100644 --- a/tests/scenarios/AdminMenuComposition/AdminMenuCompositionTest.php +++ b/tests/scenarios/AdminMenuComposition/AdminMenuCompositionTest.php @@ -227,7 +227,7 @@ class AdminMenuCompositionFormForSysadmTest extends AdminMenuCompositionFormTest ['Utilisateurs', '/admin/users'], ['Groupes', '/admin/usergroup'], ['Multimedia', '/admin/multimedia'], - ['Fournisseurs d\'identités', '/admin/identity-providers'], + ['Fédération d\'identités', '/admin/identity-providers'], ['Avis', '/admin/federation-reviews'], @@ -341,7 +341,7 @@ class AdminMenuCompositionFormForAdminPortailTest extends AdminMenuCompositionFo ['Utilisateurs', '/admin/users'], ['Groupes', '/admin/usergroup'], ['Multimedia', '/admin/multimedia'], - ['Fournisseurs d\'identités', '/admin/identity-providers'], + ['Fédération d\'identités', '/admin/identity-providers'], ['Avis', '/admin/federation-reviews'], @@ -389,7 +389,6 @@ class AdminMenuCompositionFormForAdminPortailTest extends AdminMenuCompositionFo ['Afficher les icones d\'administration', 'show_admin_icons'], ['Éditeur CSS', 'css_editor'], ['Niveau expert', 'admin_level'], - ['Synchronisation du CSS avec GIT', '/admin/index/update-skin'], ]; } @@ -1522,6 +1521,7 @@ class AdminMenuCompositionAddNewUsersTestCase extends Admin_AbstractControllerTe '/admin/users' => 'Utilisateurs', '/admin/usergroup' => 'Groupes', '/admin/url-manager' => 'Contrôle des URL', + '/admin/identity-providers' => 'Fédération d\'identité', '/admin/index/adminvar' => 'Variables'], ['/admin' => 'Accès pro', diff --git a/tests/scenarios/IdentityClient/IdentityClientAdminTest.php b/tests/scenarios/IdentityClient/IdentityClientAdminTest.php new file mode 100644 index 0000000000000000000000000000000000000000..9f8f46fee943cbb52a5f9e589f9a90c92aa6b7bc --- /dev/null +++ b/tests/scenarios/IdentityClient/IdentityClientAdminTest.php @@ -0,0 +1,209 @@ +<?php +/** + * Copyright (c) 2012-2017, 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 IdentityClientAdminIndexTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { + parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'oauth2', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + $this->dispatch('/admin/identity-clients/index'); + } + + + /** + * @test + */ + public function h1ShouldBeIdentityClient() { + $this->assertXPathContentContains('//h1', 'Clients d\'identité'); + } + + + /** + * @test + */ + public function mybibaddShouldBeDisplayed(){ + $this->assertXPathContentContains('//div','MyBibApp'); + } +} + + + + +class identityClientAdminAddTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { + parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'oauth2', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + $this->dispatch('/admin/identity-clients/add'); + } + + + /** + * @test + */ + public function clientIdShouldBeInput() { + $this->assertXPath('//input[@id="client_id"]',$this->_response->getBody()); + } +} + + + + +class identityClientAdminDeleteTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { + parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'oauth2', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + $this->dispatch('/admin/identity-clients/delete/id/1'); + } + + + /** + * @test + */ + public function clientShouldBeDeleted() { + $this->assertNull(Class_IdentityClient::find(1)); + $this->assertRedirect(); + } +} + + + + +class identityClientAdminUserApiTokensTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { + parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'oauth2', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + $this->fixture(Class_User_ApiToken::class, + ['id'=>'1', + 'user_id' => 1, + 'token'=>'1234679', + 'created_at' =>'2022-09-01 10:00:00', + 'expired_at' => '2022-09-03 10:00:00', + 'client_id'=> 'MyBibApp']); + + $this->dispatch('/admin/user-api-tokens/show/client_id/MyBibApp'); + } + + + /** + * @test + */ + public function tokenShouldBeDisplayed() { + $this->assertXPath('//td', '1234679'); + } + + + /** + * @test + */ + public function mybibappShouldBeDisplayed() { + $this->assertXPath('//td', 'MyBibApp'); + } + + + /** + * @test + */ + public function expiredAtShouldBeDisplayed() { + $this->assertXPath('//td', '2022-09-01 10:00:00'); + } + +} + + + + +class identityClientAdminUserApiTokensDeleteTest extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + public function setUp() { + parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'oauth2', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + $puppy = $this->fixture('Class_Users', + ['id' => 345, + 'pseudo' => 'Puppy', + 'date_fin' => '2018-02-12', + 'login' => 'puppy', + 'password' => 'opied', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => '234', + 'id_site' => 1]); + + $this->fixture('Class_User_ApiToken', + ['id' => 1, + 'token' => 'nonos', + 'client_id' => 'MyBibApp', + 'user' => $puppy]); + + $this->fixture('Class_User_ApiToken', + ['id' => 2, + 'token' => 'nonos', + 'client_id' => 'MyOtherApp', + 'user' => $puppy]); + + $this->dispatch('/admin/user-api-tokens/delete-all/client_id/MyBibApp'); + } + + + /** + * @test + */ + public function allTokenShouldBeDeletedForMyBibappButNotOtherApp() { + $this->assertEmpty(Class_User_ApiToken::findAllBy([ 'client_id' => 'MyBibApp'])); + $this->assertNotEmpty(Class_User_ApiToken::findAllBy([ 'client_id' => 'MyOtherApp'])); + } +} diff --git a/tests/scenarios/IdentityProvider/IdentityProviderAdminTest.php b/tests/scenarios/IdentityProvider/IdentityProviderAdminTest.php index fd74aa17e74531b4043b882ff5b401c0b6fa123f..8fdd4aa22f6bbbf3faf7865547b7709ee55d8c93 100644 --- a/tests/scenarios/IdentityProvider/IdentityProviderAdminTest.php +++ b/tests/scenarios/IdentityProvider/IdentityProviderAdminTest.php @@ -62,7 +62,7 @@ class IdentityProviderAdminIndexTest extends IdentityProviderAdminTestCase { /** @test */ - public function h1ShouldBeIdentityServer() { + public function h1ShouldBeIdentityClient() { $this->assertXPathContentContains('//h1', 'Fournisseurs d\'identité'); } diff --git a/tests/scenarios/MobileApplication/RestfulApiTest.php b/tests/scenarios/MobileApplication/RestfulApiTest.php index b1991a26d4f4ac5caa89bbdd3f01a2070d7543fd..500a0b06c1828c2966b199ab6eafe9a339a4587b 100644 --- a/tests/scenarios/MobileApplication/RestfulApiTest.php +++ b/tests/scenarios/MobileApplication/RestfulApiTest.php @@ -18,6 +18,9 @@ * 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/DilicomFixtures.php'; + abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends AbstractControllerTestCase { protected $_storm_default_to_volatile = true, @@ -26,13 +29,14 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends public function setUp() { parent::setUp(); - $_SERVER['HTTPS'] = 'on'; - + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); Class_CommSigb::setInstance($this->_sigb = $this->mock()); + Class_AdminVar::set('ACTIVATE_AUTH_LOG', '1'); $this->fixture('Class_Bib', ['id' => 1, + 'gln' => '3056032160004', 'libelle' => 'Vaise-sur-Ravoire']); $puppy = $this->fixture('Class_Users', @@ -44,11 +48,58 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, 'idabon' => '234', 'id_site' => 1]); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'Class_IdentityClient_MyBibApp', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{ + 'client_secret', + 'redirect_url', + 'disable_state', + 'disable_response_type', + 'scope'}" + ]); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 2, + 'client_id'=>'PNB', + 'type' => 'Class_IdentityClient_Dilicom', + 'label' => 'PNB', + 'active' => 1, + 'config' => "{ + 'client_secret', + 'redirect_url', + 'disable_state', + 'disable_response_type', + 'scope'}" + ]); + + $this->fixture( Class_IdentityClient::class, + [ 'id' => 3, + 'client_id'=>'pc', + 'type' => 'Class_IdentityClient_OAuth2', + 'label' => 'PortailCitoyen', + 'active' => 1, + 'config' => "{ + 'client_secret' => '',, + 'redirect_url' =>'myredirect', + 'disable_state' => true, + 'disable_response_type' => true, + 'scope'}" + ]); + $this->fixture('Class_User_ApiToken', ['id' => 1, 'token' => 'nonos', - 'client_id' => 'My mobile app', + 'client_id' => 'MyBibApp', + 'user' => $puppy]); + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'exptoken', + 'client_id' => 'MyBibApp', + 'expired_at' => '2022-01-01', 'user' => $puppy]); $this->_potter = new Class_WebService_SIGB_Emprunt('12', new Class_WebService_SIGB_Exemplaire(123)); @@ -64,23 +115,22 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends $alice = new Class_WebService_SIGB_Emprunt('13', new Class_WebService_SIGB_Exemplaire(456)); $alice - ->setDateRetour(date('d/m/Y', strtotime('tomorrow'))) + ->setDateRetour( '01-03-2022') ->getExemplaire() ->setTitre('Alice') ->setCodeBarre('A-1234'); - $afrodeezia = new Class_WebService_SIGB_Reservation('18', new Class_WebService_SIGB_Exemplaire(938)); $afrodeezia - ->setBibliotheque('Annecy') - ->setEtat('En attente') - ->getExemplaire() - ->setCodeBarre('M-456') - ->setNoticeOPAC($this->fixture('Class_Notice', - ['id' => 83, - 'url_vignette' => 'http://img.com/marcus.jpg', - 'titre_principal' => 'Afrodeezia', - 'auteur_principal' => 'Marcus Miller' ])); + ->setBibliotheque('Annecy') + ->setEtat('En attente') + ->getExemplaire() + ->setCodeBarre('M-456') + ->setNoticeOPAC($this->fixture('Class_Notice', + ['id' => 83, + 'url_vignette' => 'http://img.com/marcus.jpg', + 'titre_principal' => 'Afrodeezia', + 'auteur_principal' => 'Marcus Miller' ])); $this->fixture('Class_Exemplaire', ['id' => 2, @@ -88,10 +138,9 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends 'bib' => $this->fixture('Class_Bib', ['id' => 3, 'libelle' => 'Annecy']), 'id_notice' => 83]); - $emprunteur = (new Class_WebService_SIGB_Emprunteur(345, 'puppy')) - ->empruntsAddAll([$this->_potter, $alice]) - ->reservationsAddAll([$afrodeezia]); + ->empruntsAddAll([$this->_potter, $alice]) + ->reservationsAddAll([$afrodeezia]); Storm_Cache::beVolatile(); Class_WebService_SIGB_EmprunteurCache::newInstance()->save($puppy, $emprunteur); @@ -99,24 +148,43 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends ->setFicheSigb(['fiche' => $emprunteur ]) ->assertSave(); - Class_WebService_BibNumerique_Dilicom_Hub::setDefaultHttpClient($this->mock() ->whenCalled('open_url') ->answers('')); - $this->fixture('Class_Loan_Pnb', - ['id' => 5, - 'subscriber_id' => $puppy->getIdabon(), - 'ongoing' => true, - 'expected_return_date' => '2032-05-02T18:14:14+02:00', - 'loan_date' => '2016-06-13T15:10:02+02:00', - 'album' => $this->fixture('Class_Album', - ['id' => 4, - 'notice_id' => 5, - 'id_origine' => 'Dilicom-88817216', - 'titre' => 'Pinocchio'])->addAuthor('Collodi') - ]); - - + RessourcesNumeriquesFixtures::activateDilicom(); + $book = (new DilicomFixtures())->albumTotemThora(); + $book->setMatiere('Roman') + ->addEditor("Ed. de l'Olivier. Paris") + ->addCollection('Roman de gare') + ->addPosterURI('http://mylink/toetto.jpg'); + + $book->setFormat('A103;E200'); + $book->setISBN('9782823608489'); + $album =$this->fixture('Class_Album', + ['id' => 4, + 'notice_id' => 5, + 'id_origine' => 'Dilicom-88817216', + 'titre' => 'Pinocchio']) + ->addAuthor('Collodi'); + $album->save(); + + $book->setVisible(true) + ->setStatus(Class_Album::STATUS_VALIDATED) + ->assertSave(); + $book->index(); + + $loan = $this->fixture('Class_Loan_Pnb', + ['id' => 5, + 'subscriber_id' => $puppy->getIdabon(), + 'ongoing' => true, + 'order_line_id' => '1234', + 'expected_return_date' => '2032-05-02 18:14:14', + 'loan_date' => '2016-06-13 15:10:02', + 'options' =>'LCP', + 'loan_link'=> 'https://pnb-test.centprod.com/v3//link/3056000302801/LOAN/303/9791033159216-AZ585E8AKPX5HZZ86D4PUWPL6X5P9I7K.do', + ]); + $loan->setAlbum($book); + $loan->assertSave(); ZendAfi_Auth::getInstance()->clearIdentity(); } @@ -132,6 +200,83 @@ abstract class Scenario_MobileApplication_RestfulApi_UserAccountTestCase extends +class Scenario_MobileApplication_RestfulApi_UserPnbLoanWithTokenTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { + protected $_json, + $_storm_default_to_volatile=true; + public function setUp() { + parent::setUp(); + Class_Loan_pnb::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + } + + + /** + * @test + */ + public function apiLoanTokenWithoutValidConfigServer(){ + Class_User_ApiToken::getLoader()->setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + Class_IdentityClient::find(2)->setActive(false)->save(); + + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'mytoken', + 'refresh_token'=>'1234', + 'client_id' => 'PNB', + 'expiration_date' => '2027-01-01 20:00:00', + 'user_id' => 345]); + try{ + $this->dispatch( '/api/user/pnbloans', + true, + [ "Authorization" =>"Bearer mytoken" , + "Content-Type" => "application/json"]); + } catch(Zend_Controller_Action_Exception $e) { + + $this->assertEquals('Client configuration missing for PNB',Class_Journal::lastOf(Class_Journal_OauthRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::BOKEH_RESPONSE)->getValue()) ; + $this->assertEquals(403, $e->getCode()); + } + } + + + + /** @test */ + public function responseShouldContainsPnbLoan() { + $this->dispatch( '/api/user/pnbloans', + true, + [ "Authorization" =>"Bearer nonos" , + "Content-Type" => "application/json"]); + $this->_json = json_decode($this->_response->getBody(), true); + + $this->assertEquals(['loanId' => '345_5', + 'userId' => '234', + 'orderLineId' => '1234', + 'loanhLink'=> 'https://pnb-test.centprod.com/v3//link/3056000302801/LOAN/303/9791033159216-AZ585E8AKPX5HZZ86D4PUWPL6X5P9I7K.do', + 'description'=>'', + "standardTitle"=> "Totem et Thora", + 'author' => 'Raphaël Draï', + "frontCoverMedium"=> + "http://mylink/toetto.jpg", + "gtin13"=> "9782823608489", + "loanerGln"=> "3056032160004", + "epubTechnicalProtection"=> "LCP", + "imprintName"=> 'Ed. de l\'Olivier. Paris', + "collection"=> "Roman de gare", + "categoryClil"=> "Roman", + "contributors"=> [], + "productFormDetails"=> [ + "A103", + "E200", + "AN" + ], + 'beginDate' =>'2016-06-13T15:10:02Z', + 'endDate' => '2032-05-02T18:14:14Z', + + ], + $this->_json['loans'][0]); + } +} + + + + class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithTokenTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { protected $_json; @@ -164,25 +309,33 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithTokenTest extend 'loaned_by' => 'puppy', 'library' => 'Annecy', 'record' => [ 'id' => '34', - 'thumbnail' => 'http://img.com/potter.jpg' ] + 'thumbnail' => 'http://img.com/potter.jpg' ] ], - $this->_json['loans'][0]); + $this->_json['loans'][1]); } /** @test */ public function responseShouldContainsPinocchioPnbLoan() { $this->assertEquals(['id' => '345_5', - 'title' => 'Pinocchio', - 'author' => 'Collodi', + 'title' => 'Totem et Thora', + 'author' => 'Raphaël Draï', 'date_due' => '2032-05-02', 'loaned_by' => 'puppy', - 'library' => 'Vaise-sur-Ravoire' + 'library' => 'Vaise-sur-Ravoire', + 'record' => [ 'id' => 84] + ], $this->_json['loans'][2]); } + /** @test */ + public function jsonShouldBeValid() { + $this->assertTrue(json_last_error() == JSON_ERROR_NONE); + } + + /** @test */ public function responseHeaderContentTypeShouldBeApplicationJson() { $this->assertArraySubset(['name' => 'Content-Type', @@ -247,14 +400,11 @@ class Scenario_MobileApplication_RestfulApi_UserAccountRenewTest extends Scenari 'error' => 'Prêt introuvable'], json_decode($this->_response->getBody(), true)); } - } - - class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { /** @test */ public function withoutAuthorizationShouldAnswerInvalidRequest() { @@ -262,8 +412,10 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext false, ["Content-Type" => "application/json"]); + $this->assertEquals(['error' => 'invalid_request', - 'message' => 'Autorisation non spécifiée'], + 'message' => 'Autorisation non spécifiée', + 'error_description' => 'Autorisation non spécifiée'], json_decode($this->_response->getBody(), true)); } @@ -276,7 +428,8 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext "Content-Type" => "application/json"]); $this->assertEquals(['error' => 'invalid_request', - 'message' => 'Jeton d\'autorisation non fourni'], + 'message' => 'Jeton d\'autorisation non fourni', + 'error_description' => 'Jeton d\'autorisation non fourni'], json_decode($this->_response->getBody(), true)); } @@ -289,7 +442,9 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext "Content-Type" => "application/json"]); $this->assertEquals(['error' => 'invalid_request', - 'message' => 'Jeton d\'autorisation invalide'], + 'message' => 'Jeton d\'autorisation invalide', + 'error_description' => 'Jeton d\'autorisation invalide', + ], json_decode($this->_response->getBody(), true)); } @@ -299,15 +454,17 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext $this->fixture('Class_User_ApiToken', ['id' => 2, 'token' => 'veget@ble', + 'client_id' => 'MyBibApp', 'user_id' => 987]); $this->dispatch('/api/user/loans', false, ["Authorization" => 'Bearer veget@ble', "Content-Type" => "application/json"]); - + $this->assertEquals(403, $this->_response->getHttpResponseCode()); $this->assertEquals(['error' => 'invalid_request', - 'message' => 'Utilisateur non trouvé'], + 'message' => 'Utilisateur non trouvé', + 'error_description' => 'Utilisateur non trouvé'], json_decode($this->_response->getBody(), true)); } @@ -322,7 +479,8 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext "Content-Type" => "application/json"]); $this->assertEquals(['error' => 'invalid_request', - 'message' => 'Protocole HTTPS obligatoire'], + 'message' => 'Protocole HTTPS obligatoire', + 'error_description' => 'Protocole HTTPS obligatoire'], json_decode($this->_response->getBody(), true)); } @@ -343,7 +501,7 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext 'loaned_by' => 'puppy', 'library' => 'Annecy' ], - $loans['loans'][0]); + $loans['loans'][1]); } @@ -361,27 +519,28 @@ class Scenario_MobileApplication_RestfulApi_UserAccountLoansWithoutTokenTest ext 'loaned_by' => 'puppy', 'library' => 'Annecy' ], - $loans['loans'][0]); + $loans['loans'][1]); } } + class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginErrorsTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { public function wrongUrls() { return [ - ['/auth/oauth/response_type/code/client_id/My%20mobile%20app/redirect_uri/'], - ['/auth/oauth/response_type/code/client_id/My%20mobile%20app/'], - ['/auth/oauth/response_type/code/redirect_uri/http%3A%2F%2Fsomewhere.com'], - ['/auth/oauth/response_type/something/client_id/My%20mobile%20app/redirect_uri/http%3A%2F%2Fsomewhere.com'], + ['/auth/oauth/response_type/code/client_id/MyBibApp/redirect_uri/'], + ['/auth/oauth/response_type/code/client_id/MyBibApp/'], + ['/auth/oauth/response_type/code/redirect_uri/http%3A%2F%2Fsomewhere.com'] ]; } + /** * @dataProvider wrongUrls * @test */ - public function withIncompleUrlShouldError400BadRequest($url) { + public function withIncompleUrlForResponseTypeCodeShouldError400BadRequest($url) { try { $this->dispatch($url, true); } catch(Zend_Controller_Action_Exception $e) { @@ -398,7 +557,7 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginErrorsTest e class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { public function setUp() { parent::setUp(); - $this->dispatch('/auth/oauth/response_type/code/client_id/My%20mobile%20app?redirect_uri=' + $this->dispatch('/auth/oauth/response_type/code/client_id/MyBibApp?redirect_uri=' . urlencode('bokeh://authorize'), true); } @@ -413,7 +572,7 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginTest extends /** @test */ public function pageShouldContainsMyMobileAppWantsToConnect() { $this->assertXPathContentContains('//h1', - 'Authentifiez-vous pour autoriser "My mobile app" à accéder à votre compte'); + 'Authentifiez-vous pour autoriser "MyBibApp" à accéder à votre compte'); } @@ -430,11 +589,12 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginOnPhoneTest public function setUp() { parent::setUp(); $_SERVER['HTTP_USER_AGENT'] = 'iPhone'; + Class_AdminVar::set('ACTIVATE_AUTH_LOG', 1); Class_Profil::getCurrentProfil() ->beTelephone() ->assertSave(); - $this->dispatch('/auth/oauth/response_type/code/client_id/My%20mobile%20app?redirect_uri=' + $this->dispatch('/auth/oauth/response_type/code/client_id/MyBibApp?redirect_uri=' . urlencode('bokeh://authorize'), true); } @@ -461,8 +621,70 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthForLoginOnPhoneTest /** @test */ public function pageShouldContainsMyMobileAppWantsToConnect() { $this->assertXPathContentContains('//h1', - 'Authentifiez-vous pour autoriser "My mobile app" à accéder à votre compte'); + 'Authentifiez-vous pour autoriser "MyBibApp" à accéder à votre compte'); + } +} + + + +class Scenario_MobileApplication_RestfulApi_UserAccountOAuth2ForLoginOnPhoneTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { + public function setUp() { + parent::setUp(); + $_SERVER['HTTP_USER_AGENT'] = 'iPhone'; + Class_AdminVar::set('ACTIVATE_AUTH_LOG', 1); + Class_Profil::getCurrentProfil() + ->beTelephone() + ->assertSave(); + + $this->dispatch('/auth/oauth/response_type/code/client_id/pc?redirect_uri=' + . urlencode('http://myurl'), + true); + } + + + public function tearDown() { + unset($_SERVER['HTTP_USER_AGENT']); + parent::tearDown(); + } + + + /** @test */ + public function pageShouldDisplayLoginForm() { + $this->assertXPath('//form//input[@name="username"]'); + } + + + /** @test */ + public function formActionShouldBeEmpty() { + $this->assertXpath('//form[@action=""]'); + } + + + /** @test */ + public function pageShouldContainsMyMobileAppWantsToConnect() { + $this->assertXPathContentContains('//h1', + 'Authentifiez-vous pour autoriser "pc" à accéder à votre compte'); } + /** @test */ + public function responseShouldRedirectToBokehAuthorizeWithExistingToken() { + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/pc/redirect_uri/' . urlencode('http://myurl'), + ['username' => 'puppy', 'password' => 'opied'], true); + $token = Class_User_ApiToken::findFirstBy(['client_id'=>'pc']); + $this->assertRedirectTo('http://myurl?token='.$token->getToken()); + } + + + /** @test */ + public function responseShouldNotRedirectToBokehAuthorizeWithWrongId() { + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/pc/redirect_uri/' . urlencode('http://myurl'), + ['username' => 'puppy', 'password' => 'eopied'], true); + + $this->assertXPathContentContains('//h1', + 'Authentifiez-vous pour autoriser "pc" à accéder à votre compte'); + + } + + } @@ -504,10 +726,14 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthPostLoginSuccessTest /** @test */ public function responseShouldRedirectToBokehAuthorizeWithExistingToken() { - $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/My%20mobile%20app/redirect_uri/' . urlencode('bokeh://authorize'), + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-17 15:48:03')); + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/MyBibApp/redirect_uri/' . urlencode('bokeh://authorize'), ['username' => 'puppy', 'password' => 'opied'], true); + $token = Class_User_ApiToken::findFirstBy([ 'client_id' => 'MyBibApp', + 'user_id' => 345, + 'expired_at' =>'']); - $this->assertRedirectTo('bokeh://authorize#token=nonos'); + $this->assertRedirectTo('bokeh://authorize#token='. $token->getToken()); } @@ -515,7 +741,7 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthPostLoginSuccessTest public function tokenShouldBeCreatedIfNotExists() { Class_User_ApiToken::deleteBy([]); - $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/My%20mobile%20bokeh/redirect_uri/' . urlencode('bokeh://authorize'), + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/MyBibApp/redirect_uri/' . urlencode('bokeh://authorize'), ['username' => 'puppy', 'password' => 'opied'], true); $token = Class_User_ApiToken::find(1); @@ -524,12 +750,24 @@ class Scenario_MobileApplication_RestfulApi_UserAccountOAuthPostLoginSuccessTest } + /** @test */ + public function tokenShouldBeCreatedIfWrongExists() { + Class_User_ApiToken::deleteBy([]); + + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/MyBibApp/redirect_uri/' . urlencode('bokeh://authorize'), + ['username' => 'puppye', 'password' => 'opied'], true); + + $this->assertNotRedirect(''); + + } + + /** * @depends tokenShouldBeCreatedIfNotExists * @test */ public function tokenClientIdShouldBeMyMobileBokeh($token) { - $this->assertEquals('My mobile bokeh', $token->getClientId()); + $this->assertEquals('MyBibApp', $token->getClientId()); } } @@ -542,7 +780,8 @@ class Scenario_MobileApplication_RestfulApi_UserAccountWithTokenTest extends Sce public function setUp() { parent::setUp(); - + Class_AdminVar::set('ACTIVATE_AUTH_LOG', '1'); + Class_LogFile::setLog(new Class_LogFile('temp/log_auth')); $this->dispatch('/api/user/account', true, ["Authorization" => "Bearer nonos" , @@ -565,6 +804,38 @@ class Scenario_MobileApplication_RestfulApi_UserAccountWithTokenTest extends Sce + +class Scenario_MobileApplication_RestfulApi_UserAccountWithExpiredTokenNeverExpiredTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { + protected + $_json; + + public function setUp() { + parent::setUp(); + Class_AdminVar::set('ACTIVATE_AUTH_LOG', '1'); + Class_LogFile::setLog(new Class_LogFile('temp/log_auth')); + $this->dispatch('/api/user/account', + true, + ["Authorization" => "Bearer exptoken" , + "Content-Type" => "application/json"]); + $this->_json = json_decode($this->_response->getBody(), true); + } + + + /** @test */ + public function responseShouldContainsCardValidityAndLabel() { + $this->assertEquals(['label' => 'Puppy', + 'login' => 'puppy', + 'card' => [ + 'id' => '234', + 'expire_at' => '2018-02-12'] + ], + $this->_json['account']); + } +} + + + + class Scenario_MobileApplication_RestfulApi_UserAccountHoldsWithTokenTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { protected $_json; @@ -590,11 +861,10 @@ class Scenario_MobileApplication_RestfulApi_UserAccountHoldsWithTokenTest extend 'held_by' => 'puppy', 'library' => 'Annecy', 'record' => [ 'id' => '83', - 'thumbnail' => 'http://img.com/marcus.jpg' ] + 'thumbnail' => 'http://img.com/marcus.jpg' ] ], $this->_json['holds'][0]); } - } @@ -645,7 +915,6 @@ class Scenario_MobileApplication_RestfulApi_UserAccountCancelHoldTest extends Sc 'error' => 'Réservation introuvable'], json_decode($this->_response->getBody(), true)); } - } @@ -677,7 +946,6 @@ class Scenario_MobileApplication_RestfulApi_ItemByBarcodeTest extends Scenario_M '/api/catalog/item/barcode/345', true, ["Content-Type" => "application/json"]); - } catch(Zend_Controller_Action_Exception $e) { $this->assertEquals(404, $e->getCode()); return; @@ -693,7 +961,6 @@ class Scenario_MobileApplication_RestfulApi_ItemByBarcodeTest extends Scenario_M '/api/catalog/item', true, ["Content-Type" => "application/json"]); - } catch(Zend_Controller_Action_Exception $e) { $this->assertEquals(403, $e->getCode(), @@ -703,4 +970,230 @@ class Scenario_MobileApplication_RestfulApi_ItemByBarcodeTest extends Scenario_M $this->fail('should raise error 403 bad request'); } } -?> + + + + +class Scenario_MobileApplication_RestfulApi_DilicomSuccessTest extends Scenario_MobileApplication_RestfulApi_UserAccountTestCase { + protected $_auth; + + public function setUp() { + parent::setUp(); + $this->_auth = Storm_Test_ObjectWrapper::mock() + ->whenCalled('authenticateLoginPassword') + ->answers(false) + ->whenCalled('hasIdentity') + ->answers(false) + ->whenCalled('getIdentity') + ->answers(null); + + ZendAfi_Auth::setInstance($this->_auth); + + $this->_auth + ->whenCalled('authenticateLoginPassword') + ->with('puppy', 'opied') + ->willDo( + function() { + $user = new stdClass(); + $user->ID_USER = 345; + $this->_auth->whenCalled('getIdentity')->answers($user); + return true; + }); + } + + + public function tearDown() { + ZendAfi_Auth::setInstance(null); + parent::tearDown(); + } + + + /** @test */ + public function tokenShouldBeCreatedIfNotExistsAndShouldNotRedirect() { + Class_User_ApiToken::deleteBy([]); + $this->postDispatch('/opac/auth/oauth/', + ['grant_type' => 'password','client_id' => 'PNB','username' => 'puppy', 'password' => 'opied'], true); + + $token = Class_User_ApiToken::find(1); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->access_token , $token->getToken()); + $this->assertNotRedirect(); + return $token; + } + + + /** @test */ + public function tokenShouldBeCreatedAndUpdateIfExistsAndShouldNotRedirect() { + Class_User_ApiToken::deleteBy([]); + + $this->fixture('Class_User_ApiToken', + ['id' => 10, + 'token' => 'nonos', + 'client_id' => 'PNB', + 'user_id' => 345]); + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-17 15:48:03')); + + $token = Class_User_ApiToken::findFirstBy(['client_id'=> 'PNB']); + $this->postDispatch('/opac/auth/oauth/', + ['grant_type' => 'password','client_id' => 'PNB','username' => 'puppy', 'password' => 'opied'], true); + + $token2 = Class_User_ApiToken::findFirstBy(['client_id'=> 'PNB']); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->access_token , $token2->getToken()); + $this->assertNotEquals($json->access_token , $token->getToken()); + $this->assertEquals( '2019-03-18 15:48:03',$token2->getExpiredAt()); + $this->assertNotRedirect(); + return $token; + } + + + /** @test */ + public function invalidGrantIfWrongAuthentication() { + Class_User_ApiToken::deleteBy([]); + + $this->postDispatch('/opac/auth/oauth/', + ['client_id'=>'PNB','username' => 'puppy', 'password' => 'opiedee'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_grant' ); + $this->assertEquals($json->error_description, 'Authentication failure' ); + $this->assertEquals(302, $this->_response->getHttpResponseCode()); + } + + + /** @test */ + public function invalidGrantIfWrongRefreshToken() { + Class_User_ApiToken::deleteBy([]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_token' => 'wrong'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'The access token has expired' ); + $this->assertEquals(401, $this->_response->getHttpResponseCode()); + } + + + /** @test */ + public function invalidRequestIfNoRefreshToken() { + Class_User_ApiToken::deleteBy([]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_ejtoken' => 'wrong'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'Missing parameter' ); + $this->assertEquals(400, $this->_response->getHttpResponseCode()); + } + + + /** + * @test + */ + public function validRefreshTokenShouldReturnToken(){ + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + Class_User_ApiToken::getLoader()->setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'mytoken', + 'client_id'=>'PNB', + 'refresh_token'=>'1234', + 'expiration_date' => '2010-01-01 20:00:00', + 'user_id' => 987]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_token' => '1234'], true); + $json = json_decode($this->_response->getBody()); + + $this->assertEquals(Class_User_ApiToken::find(3)->getExpiredAt(), '2019-03-16 15:48:03'); + $this->assertEquals($json->token_type, 'Bearer'); + $this->assertNotEquals($json->refresh_token, '1234' ); + $this->assertEquals($json->expires_in, '86400' ); + $this->assertNotEquals($json->access_token, 'mytoken' ); + } + + + /** + * @test + */ + public function refreshTokenWithoutConfigServer(){ + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + Class_User_ApiToken::getLoader()->setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'mytoken', + 'refresh_token'=>'1234', + 'client_id' => 'ghost', + 'expiration_date' => '2010-01-01 20:00:00', + 'user_id' => 987]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_token' => '1234'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'Client configuration missing for ghost' ); + $this->assertEquals(401, $this->_response->getHttpResponseCode()); + } + + + /** + * @test + */ + public function refreshTokenWithoutValidConfigServer(){ + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + Class_User_ApiToken::getLoader()->setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + Class_IdentityClient::find(2)->setActive(false)->save(); + + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'mytoken', + 'refresh_token'=>'1234', + 'client_id' => 'PNB', + 'expiration_date' => '2010-01-01 20:00:00', + 'user_id' => 987]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_token' => '1234'], true); + $json = json_decode($this->_response->getBody()); + $this->assertContains('invalid_request', + Class_Journal::lastOf(Class_Journal_OauthRequestType::MY_TYPE)->getDetail(Class_Journal_RequestType::BOKEH_RESPONSE)->getValue()); + + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'Client configuration missing for PNB' ); + $this->assertEquals(401, $this->_response->getHttpResponseCode()); + } + + + /** + * @test + */ + public function refreshTokenNotAllowerdByConfigServer(){ + Class_User_ApiToken::setTimeSource(new TimeSourceForTest('2019-03-15 15:48:03')); + $this->fixture('Class_User_ApiToken', + ['id' => 3, + 'token' => 'mytoken', + 'refresh_token'=>'1234', + 'client_id' => 'MyBibApp', + 'expiration_date' => '2010-01-01 20:00:00', + 'user_id' => 987]); + + $this->postDispatch('/opac/auth/refresh', + ['refresh_token' => '1234'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'Client configuration is not allowed refresh token' ); + $this->assertEquals(401, $this->_response->getHttpResponseCode()); + } + + + /** + * @test + */ + public function discoverShoulReturnInformations(){ + $this->dispatch( '/api/catalog/discover'); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->infos->mail,'dev-opac@afi-sa.fr'); + $this->assertEquals($json->infos->company,'AFI'); + $this->assertEquals($json->authentication->get_token,( new Class_Url )->absoluteUrl('/auth/oauth')); + $this->assertEquals($json->resources[0]->endpoint,( new Class_Url )->absoluteUrl('/api/user/pnbloans')); + } +} diff --git a/tests/scenarios/PnbDilicom/PnbDilicomTest.php b/tests/scenarios/PnbDilicom/PnbDilicomTest.php index fbdb9a7c45560be39210ba1ca0dd3daf1ca268e7..299d1e7843669382ff594834fb4035c55af1607c 100644 --- a/tests/scenarios/PnbDilicom/PnbDilicomTest.php +++ b/tests/scenarios/PnbDilicom/PnbDilicomTest.php @@ -788,6 +788,14 @@ class PnbDilicomONIXParserTest extends ModelTestCase { } + /** + * @test + */ + public function formatShouldBeInFormat(){ + $this->assertEquals('EA;EC;ED;E127;E101', $this->_book->getFormats()); + } + + /** @test */ public function publisherShouldBePhebus() { $this->assertEquals('Phébus', $this->_book->getEditeur()); @@ -1754,7 +1762,7 @@ class PnbDilicomOldLoansQueryPageTest extends PnbDilicomLoansTestCase{ $this->assertEquals([['order_line_id' => 'x321', 'order' => 'expected_return_date desc', 'limit' => 5, - 'where' => 'expected_return_date <= "2022-06-10 10:23:10" and expected_return_date >= "2022-02-08"']],$this->_wrapper->getAttributesForLastCallOn('findAllBy')); + 'where' => 'expected_return_date <= "2022-06-10 10:23:10" and expected_return_date >= "2022-02-08 10:23:10"']],$this->_wrapper->getAttributesForLastCallOn('findAllBy')); } } diff --git a/tests/scenarios/Templates/MyBibAppTemplateTest.php b/tests/scenarios/Templates/MyBibAppTemplateTest.php index 67868cb47697f8dd2f77ec3a058e8a8966062beb..4cc5aab9f5435c7d9d552cb12b6fd2d632b14524 100644 --- a/tests/scenarios/Templates/MyBibAppTemplateTest.php +++ b/tests/scenarios/Templates/MyBibAppTemplateTest.php @@ -219,6 +219,7 @@ class MyBibAppTemplateEditTemplateTest extends Admin_AbstractControllerTestCase class MyBibAppTemplateOauthWithUserAgentTest extends MyBibAppTemplateTestCase { + public function setUp() { parent::setUp(); @@ -229,6 +230,14 @@ class MyBibAppTemplateOauthWithUserAgentTest extends MyBibAppTemplateTestCase { Class_AdminVar::set('MYBIBAPP_TEMPLATE', 1); Class_Profil::find(2)->beCurrentProfil(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'mybibapp', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); $this->dispatch('/opac/auth/oauth/response_type/code/id_profil/1/client_id/MyBibApp?redirect_uri=www.mon-bokeh.org'); } @@ -255,12 +264,29 @@ class MyBibAppTemplateOauthWithUserAgentTest extends MyBibAppTemplateTestCase { class MyBibAppTemplatePostDispatchOauthWithUserAgentTest extends MyBibAppTemplateTestCase { - + protected $_storm_default_to_volatile = true; protected $_auth; public function setUp() { parent::setUp(); + $this->fixture( Class_IdentityClient::class, + [ 'id' => 1, + 'client_id'=>'MyBibApp', + 'type' => 'mybibapp', + 'label' => 'MyBibApp', + 'active' => 1, + 'config' => "{}" + ]); + + $this->fixture( Class_IdentityClient::class, + [ 'id' => 10, + 'client_id'=>'My webapp', + 'type' => 'oauth2', + 'label' => 'Portail citoyen', + 'redirect_uri' => 'http://mon-portail.org/bokeh/oauth', + 'active' => 1, + ]); $puppy = $this->fixture('Class_Users', ['id' => 345, @@ -274,10 +300,17 @@ class MyBibAppTemplatePostDispatchOauthWithUserAgentTest extends MyBibAppTemplat $this->fixture('Class_User_ApiToken', ['id' => 1, + 'client_id'=>'MyBibApp', 'token' => 'nonos', - 'client_id' => 'My mobile app', 'user' => $puppy]); + $this->fixture('Class_User_ApiToken', + ['id' => 10, + 'client_id'=>'My webapp', + 'token' => '1234', + 'user' => $puppy]); + + $_SERVER['HTTP_USER_AGENT'] = 'MyBibApp/1.0.3 (Android)'; Class_AdminVar::set('MYBIBAPP_TEMPLATE', 1); @@ -304,8 +337,6 @@ class MyBibAppTemplatePostDispatchOauthWithUserAgentTest extends MyBibAppTemplat $this->_auth->whenCalled('getIdentity')->answers($user); return true; }); - - } @@ -317,10 +348,30 @@ class MyBibAppTemplatePostDispatchOauthWithUserAgentTest extends MyBibAppTemplat /** @test */ public function responseShouldRedirectToBokehAuthorizeWithExistingToken() { - $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/My%20mobile%20app/redirect_uri/' . urlencode('bokeh://authorize'), + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/MyBibApp/redirect_uri/' . urlencode('bokeh://authorize'), ['username' => 'puppy', 'password' => 'opied'], true); + $token = Class_User_ApiToken::findFirstBy(['client_id'=>'MyBibApp']); + $this->assertRedirectTo('bokeh://authorize#token='.$token->getToken()); + } + - $this->assertRedirectTo('bokeh://authorize#token=nonos'); + /** @test */ + public function responseShouldRedirectToMyWebappAuthorizeWithExistingToken() { + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/My%20webapp/redirect_uri/' . urlencode('http://mon-portail.org/bokeh/oauth'), + ['username' => 'puppy', 'password' => 'opied'], true); + $token = Class_User_ApiToken::findFirstBy(['client_id'=>'My webapp']); + $this->assertRedirectTo('http://mon-portail.org/bokeh/oauth?token='.$token->getToken()); + } + + + /** @test */ + public function responseShouldNotRedirectToMyWebappAuthorizeWithExistingToken() { + $this->postDispatch('/opac/auth/oauth/response_type/code/client_id/My%20webapp/redirect_uri/' . urlencode('http://mon-faux-portail.org/bokeh/oauth'), + ['username' => 'puppy', 'password' => 'opied'], true); + $json = json_decode($this->_response->getBody()); + $this->assertEquals($json->error, 'invalid_request'); + $this->assertEquals($json->error_description, 'redirect_uri http://mon-faux-portail.org/bokeh/oauth differs of client config :http://mon-portail.org/bokeh/oauth' ); + $this->assertEquals(401, $this->_response->getHttpResponseCode()); } }