diff --git a/VERSIONS_HOTLINE/160049 b/VERSIONS_HOTLINE/160049 new file mode 100644 index 0000000000000000000000000000000000000000..f7477d881798cc729083b7c1d1ff5645c19f3326 --- /dev/null +++ b/VERSIONS_HOTLINE/160049 @@ -0,0 +1 @@ + - correctif #160049 : Administration : correction de la prise en charge de la redirection dans le formulaire d'authentification de l'administration. \ No newline at end of file diff --git a/application/modules/admin/controllers/AuthController.php b/application/modules/admin/controllers/AuthController.php index 677bb7f40e6ec0fb3676e502e26d27d18fe8983c..0a04215f32c50c0eda174a86d1738513c998143c 100644 --- a/application/modules/admin/controllers/AuthController.php +++ b/application/modules/admin/controllers/AuthController.php @@ -19,7 +19,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Admin_AuthController extends Zend_Controller_Action { +class Admin_AuthController extends ZendAfi_Controller_Action { + use Trait_Translator; function init() { @@ -33,31 +34,35 @@ class Admin_AuthController extends Zend_Controller_Action { } - function loginAction() { - $this->view->message = ''; - $this->view->redirect = $this->_processRedirect(); + public function loginAction() { + $this->view->titre = $this->_('Entrez votre identité S.V.P.'); + $redirect = $this->_processRedirect(); if ( Class_Users::isCurrentUserCanAccesBackend() ) - return $this->_redirect($this->view->redirect); + return $this->_redirect($redirect); + + $this->view->form = (new ZendAfi_Form_Admin_Login) + ->setAction(Class_Url::relative(['module' => 'admin', + 'controller' => 'auth', + 'action' => 'login', + 'redirect' => $redirect])); - if (!$this->_request->isPost()) + if ( ! $this->_request->isPost()) return; $f = new Zend_Filter_StripTags(); $username = $f->filter($this->_request->getPost('username')); $password = $f->filter($this->_request->getPost('password')); - if (empty($username)) { - $this->view->message = $this->_("Entrez votre nom d'utilisateur puis validez S.V.P."); + if ( ! $this->view->form->isValid(['username' => $username, + 'password' => $password])) return; - } $auth = ZendAfi_Auth::getInstance(); - if (!$auth->authenticateLoginPassword($username, $password, [$auth->newAuthDb()])) - return; + if ( ! $auth->authenticateLoginPassword($username, $password, [$auth->newAuthDb()])) + return $this->view->form->addError($this->_('Échec de connexion : identifiant ou mot de passe invalide.')); - if ($this->view->redirect) - return $this->_redirect($this->view->redirect); + $this->_redirect($redirect); } diff --git a/application/modules/admin/views/scripts/auth/login.phtml b/application/modules/admin/views/scripts/auth/login.phtml index dc05c6252f061f208330c0a0878787ffad6fce0f..0b57b92d9a27393738075e15cd9116d474c72081 100644 --- a/application/modules/admin/views/scripts/auth/login.phtml +++ b/application/modules/admin/views/scripts/auth/login.phtml @@ -1,30 +1,5 @@ -<form name="form" action="<?php echo BASE_URL ?>/admin/auth/login" method="post"> - <?php - if ($this->redirect) - echo $this->formHidden('redirect', $this->redirect); - ?> - <center> - <div class="login" align="center"> - <table cellpadding="0" cellspacing="0" > - <tr class ="dark"> - <td colspan="2" align="center" height="60"> - <span id="abonne_erreur"><?php echo $this->message; ?><br /></span> - <?php echo $this->_("Entrez votre identité S.V.P.") ?></td> - </tr> - <tr class="light"> - <td width="50%" height="54" align="right"><label for="username"><?php echo $this->_("Identifiant") ?></label> </td> - <td width="50%" height="54" align="left" valign="middle"> <input type="text" name="username" value=""/></td> - </tr> - <tr class="light"> - <td width="50%" height="55" align="right"><label for="password"><?php echo $this->_("Mot de passe") ?></label> </td> - <td width="50%" height="55" align="left" valign="middle"> <input type="password" name="password" onkeypress="if (event.keyCode == 13) {javascript:PicToolbarOver( getElementById('menu_item975'), 'menu_item975');this.form.submit();return false;}" value=""/></td> - </tr> - <tr class="dark"> - <td colspan="2" width="50%" height="66" align="center"> - <?php echo $this->Button_Submit(); ?> - </td> - </tr> - </table> - </div> - </center> -</form> +<?php +if ( $this->form->isErrors() ) + echo $this->tagError(implode(BR, $this->form->getErrorMessages())); + +echo $this->renderForm($this->form); diff --git a/library/ZendAfi/Form/Admin/Login.php b/library/ZendAfi/Form/Admin/Login.php new file mode 100644 index 0000000000000000000000000000000000000000..02036629035b9b4526e80d8b1a1068209b945459 --- /dev/null +++ b/library/ZendAfi/Form/Admin/Login.php @@ -0,0 +1,57 @@ +<?php +/** + * 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 + * 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_Login extends ZendAfi_Form { + public function init() { + parent::init(); + + $this + ->setMethod(Zend_Form::METHOD_POST) + ->setAttrib('class', 'admin-form admin_login_form') + + ->addElement('text', + 'username', + ['label' => $this->_('Identifiant'), + 'aria-label' => $this->_('Votre identifiant pour accéder à l\'administration'), + 'required' => true, + 'allowEmpty' => false]) + + ->addSummary($this->_('Formulaire d\'authentification')) + + ->addElement('password', + 'password', + ['label' => $this->_('Mot de passe'), + 'aria-label' => $this->_('Un super mot de passe !'), + 'required' => true, + 'allowEmpty' => false]) + + ->addUniqDisplayGroup('login'); + + return $this; + } + + + public function beWrong() : self { + + return $this; + } +} diff --git a/public/admin/skins/bokeh74/form.css b/public/admin/skins/bokeh74/form.css index eb1aa3b7f3af3d23f97e5c8e186cab0018173938..4b757982c35550bc6c1749491a7d2ea2a7392d11 100644 --- a/public/admin/skins/bokeh74/form.css +++ b/public/admin/skins/bokeh74/form.css @@ -192,3 +192,23 @@ body:not(.template_HISTORIC) #fieldset-style_group tr.boite > td.gauche > * { white-space: nowrap; min-height: 1em; } + +form.admin_login_form + div > button.back { + display: none; +} + +form.admin_login_form tr { + line-height: 3em; +} + +form.admin_login_form td input { + width: 15em; + padding: 5px; + margin: 0; + box-shadow: 0 0 1px var(--border-highlight); + border: 0; +} + +form.admin_login_form { + overflow-x: auto; +} diff --git a/public/admin/skins/bokeh74/global.css b/public/admin/skins/bokeh74/global.css index 9531d3fea6827570a755bf9ddefa96b4b8900af5..bfea7fe345c3686ae842436bf10ca31a782eb243 100755 --- a/public/admin/skins/bokeh74/global.css +++ b/public/admin/skins/bokeh74/global.css @@ -419,7 +419,12 @@ table#suggestions td:last-child a { display: block; float: none; margin: 6em auto; - width: 50%; + max-width: 500px; +} + +[data-logged="false"] .main > .modules p.error, +[data-logged="false"] .main > .modules h1 { + text-align: center; } .main > .left .titre { diff --git a/tests/application/modules/admin/controllers/AdminAuthControllerTest.php b/tests/application/modules/admin/controllers/AdminAuthControllerTest.php index 8bf89571aa1732560c0ed1b2617230c2428cfab8..205a77bd4d2765fd5e26eaf411b34eedf6d4dea6 100644 --- a/tests/application/modules/admin/controllers/AdminAuthControllerTest.php +++ b/tests/application/modules/admin/controllers/AdminAuthControllerTest.php @@ -19,36 +19,21 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class AdminAuthControllerNobodyLoggedTest extends Admin_AbstractControllerTestCase { - protected - $_storm_default_to_volatile = true, - $_auth, - $_auth_db_adapter; + +class AdminAuthControllerNobodyLoggedTest extends AbstractControllerTestCase { public function setUp() { parent::setUp(); - $this->_auth_db_adapter = $this->mock(); - $this->_auth = $this->mock() - ->whenCalled('authenticateLoginPassword')->answers(false) - ->whenCalled('hasIdentity')->answers(false) - ->whenCalled('getIdentity')->answers(null) - ->whenCalled('newAuthDb')->answers($this->_auth_db_adapter); - - ZendAfi_Auth::setInstance($this->_auth); - } - - - public function tearDown() { - ZendAfi_Auth::setInstance(null); - parent::tearDown(); + ZendAfi_Auth::getInstance()->clearIdentity();; } /** @test */ - public function whileNotConnectedShouldIncludeHiddenInputWithRedirectToDesiredUrl() { - $this->dispatch('/admin/cms/edit/id/3', true); - $this->assertXPath('//input[@type="hidden"][@name="redirect"][@value="' . Class_Url::absolute('/admin/cms/edit/id/3"]')); + public function whileNotConnectedShouldAddCmsEditIdThreeToFormAction() { + $this->dispatch('/admin/cms/edit/id/3'); + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute('/admin/cms/edit/id/3')), + $this->_response->getBody()); } @@ -64,7 +49,7 @@ class AdminAuthControllerNobodyLoggedTest extends Admin_AbstractControllerTestCa /** @test */ public function withNoUsernameShouldDisplayMessageEntrezVotreNom() { $this->postDispatch('/admin/auth/login', []); - $this->assertXPathContentContains('//span', 'Entrez votre nom d\'utilisateur'); + $this->assertXPathContentContains('//ul[@class="errors"]/li', 'Une valeur est requise'); } @@ -72,7 +57,7 @@ class AdminAuthControllerNobodyLoggedTest extends Admin_AbstractControllerTestCa public function withNoUserNameShouldContainsLinkToGoBackToProfilOne() { $this->dispatch('/admin/auth/login'); $this->assertXPathContentContains('//a[contains(@href, "index/id_profil/1")]', 'Retour au site'); - } + } } @@ -80,53 +65,33 @@ class AdminAuthControllerNobodyLoggedTest extends Admin_AbstractControllerTestCa class AdminAuthControllerSuccessfulLoggedTest extends AbstractControllerTestCase { - protected - $_storm_default_to_volatile = true, - $_auth, - $_auth_db_adapter; - public function setUp() { parent::setUp(); - Class_Users::newInstanceWithId(2, - ['nom' => 'Marcel','login' =>'foo', 'password' => 'bar']) - ->beAdminPortail() - ->assertSave(); - - $this->_auth = $this->mock() - ->whenCalled('authenticateLoginPassword') - ->with('foo', 'bar', [$this->_auth_db_adapter]) - ->answers(true) - ->whenCalled('getIdentity') - ->answers(Class_Users::find(2)) - ->whenCalled('hasIdentity') - ->answers(true) - ->whenCalled('newAuthDb')->answers($this->_auth_db_adapter); - - ZendAfi_Auth::setInstance($this->_auth); - $this->postDispatch('/admin/auth/login', - ['username' => 'foo', 'password' => 'bar']); - } - - - public function tearDown() { - ZendAfi_Auth::setInstance(null); - parent::tearDown(); + $this->fixture(Class_Users::class, + ['id' => 2, + 'nom' => 'Marcel', + 'login' =>'foo', + 'password' => 'bar']) + ->beAdminPortail() + ->assertSave(); } /** @test */ public function withAuthenticationSuccessfullShouldRedirectToAdmin() { + $this->postDispatch('/admin/auth/login', + ['username' => 'foo', 'password' => 'bar']); + $this->assertRedirectTo('/admin/'); } /** @test */ public function withAuthenticationSuccessfullAndRedirectShouldRedirectToIt() { - $this->postDispatch('/admin/auth/login', + $this->postDispatch('/admin/auth/login/redirect/' . urlencode('/admin/newsletter'), ['username' => 'foo', - 'password' => 'bar', - 'redirect' => '/admin/newsletter']); + 'password' => 'bar']); $this->assertRedirectTo('/admin/newsletter'); } @@ -136,27 +101,27 @@ class AdminAuthControllerSuccessfulLoggedTest extends AbstractControllerTestCase class AdminAuthControllerWithRestrictedProfilTest extends AbstractControllerTestCase { - protected $_storm_default_to_volatile = true; + public function setup() { parent::setUp(); ZendAfi_Auth::getInstance()->clearIdentity(); - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 1, 'access_level' => ZendAfi_Acl_AdminControllerRoles::INVITE, 'login_page' => null, 'cfg_menus' => '']); - $this->fixture('Class_Profil', + $this->fixture(Class_Profil::class, ['id' => 2, 'access_level' => ZendAfi_Acl_AdminControllerRoles::ADMIN_BIB, 'login_page' => null, 'cfg_menus' => '']); ZendAfi_Auth::getInstance()->logUser( - $this->fixture('Class_Users', + $this->fixture(Class_Users::class, ['id' => 5, 'login' => 'tom', 'password'=>'tom1', @@ -172,12 +137,6 @@ class AdminAuthControllerWithRestrictedProfilTest extends AbstractControllerTest $this->dispatch('/admin'); $this->assertRedirectTo('/opac/index/index/id_profil/1'); } - - - public function tearDown() { - ZendAfi_Auth::setInstance(null); - parent::tearDown(); - } } @@ -346,3 +305,56 @@ class AdminAuthControllerLoggedAsAdminTest extends Admin_AbstractControllerTestC $this->assertRedirectTo('/admin/'); } } + + + + +class AdminAuthControllerNotLoggedPostDispatchTest extends AbstractControllerTestCase { + public function setUp() { + parent::setUp(); + ZendAfi_Auth::getInstance()->clearIdentity(); + } + + + /** @test */ + public function dispatchAdminActionShouldRenderAdminAuthLoginFormWithRedirectToAdminCmsEditIdOneTwo() { + $this->dispatch('/admin/cms/edit/id/12'); + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute('/admin/cms/edit/id/12')), + $this->_response->getBody()); + } + + + /** @test */ + public function postingWrongDataShouldRenderAdminAuthLoginFormWithRedirectToAdminCmsEditIdOneTwo() { + $this->postDispatch('/admin/cms/edit/id/12', + ['username' => 'pro', + 'password' => 'lo']); + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute('/admin/cms/edit/id/12')), + $this->_response->getBody()); + } + + + /** @test */ + public function postingWrongDataShouldDisplayError() { + $this->postDispatch('/admin/cms/edit/id/12', + ['username' => 'pro', + 'password' => 'lo']); + $this->assertXPathContentContains('//p[@class="error"]', 'Échec de connexion : identifiant ou mot de passe invalide.', $this->_response->getBody()); + } + + + /** @test */ + public function postingGoodDataShouldRedirectToAdimCmsEditIdOneTwo() { + $this->fixture(Class_Users::class, + ['id' => 89, + 'login' => 'Zeboss', + 'password' => 'copyrights', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ADMIN_PORTAIL]); + + $this->postDispatch('/admin/cms/edit/id/12', + ['username' => 'Zeboss', + 'password' => 'copyrights']); + + $this->assertRedirectTo(Class_Url::absolute('/admin/cms/edit/id/12')); + } +} \ No newline at end of file diff --git a/tests/application/modules/admin/controllers/CmsControllerTest.php b/tests/application/modules/admin/controllers/CmsControllerTest.php index 4e906c3f0b0688971cd16d752151869fb71bd580..b4690f2a64e8b37864409e618604c6aa8a13e147 100644 --- a/tests/application/modules/admin/controllers/CmsControllerTest.php +++ b/tests/application/modules/admin/controllers/CmsControllerTest.php @@ -615,16 +615,18 @@ class CmsControllerArticleDuplicateActionTest extends CmsControllerWithPermissio + class CmsControllerArticleIndexWithContextTest extends CmsControllerWithPermissionTestCase { - public function setUp() { + + public function setUp() { parent::setUp(); - $root = $this->fixture('Class_ArticleCategorie', - ['id' => 801, 'libelle' => 'Root']); + $root = $this->fixture(Class_ArticleCategorie::class, + ['id' => 801, 'libelle' => 'Root']); - $news= $this->fixture('Class_ArticleCategorie', - ['id' => 330, - 'libelle' => 'News', - 'parent_categorie' => $root]); + $news= $this->fixture(Class_ArticleCategorie::class, + ['id' => 330, + 'libelle' => 'News', + 'parent_categorie' => $root]); $this->cran_gevrier->setArticleCategories([$root]) ->assertSave(); @@ -633,28 +635,29 @@ class CmsControllerArticleIndexWithContextTest extends CmsControllerWithPermissi } - /** @test */ - public function treeViewShouldBeOpenOnLibraryCranGevrierAndCategory330WhenCatIdInDispatch() { - $this->dispatch('/admin/cms/index/id_cat/330', true); - $this->assertXPathContentContains('//script','var treeViewSelectedCategory = 330;',$this->_response->getBody()); - $this->assertXPathContentContains('//script','var treeViewSelectedLibrary = 3;',$this->_response->getBody()); - } - + /** @test */ + public function treeViewShouldBeOpenOnLibraryCranGevrierAndCategory330WhenCatIdInDispatch() { + $this->dispatch('/admin/cms/index/id_cat/330'); + $this->assertXPathContentContains('//script', 'var treeViewSelectedCategory = 330;'); + $this->assertXPathContentContains('//script', 'var treeViewSelectedLibrary = 3;'); + } - /** @test */ - public function treeViewShouldBeOpenOnLibraryCranGevrierAndCategory801WhenIdArticleInDispatch() { - $this->dispatch('/admin/cms/index/id/4', true); - $this->assertXPathContentContains('//script','var treeViewSelectedCategory = 801;',$this->_response->getBody()); - $this->assertXPathContentContains('//script','var treeViewSelectedLibrary = 3;',$this->_response->getBody()); - } + /** @test */ + public function treeViewShouldBeOpenOnLibraryCranGevrierAndCategory801WhenIdArticleInDispatch() { + $this->dispatch('/admin/cms/index/id/4'); + $this->assertXPathContentContains('//script', 'var treeViewSelectedCategory = 801;'); + $this->assertXPathContentContains('//script', 'var treeViewSelectedLibrary = 3;'); + } } + + class CmsControllerArticleConcertEditActionTest extends CmsControllerWithPermissionTestCase { public function setUp() { parent::setUp(); - $this->dispatch('/admin/cms/edit/id/4', true); + $this->dispatch('/admin/cms/edit/id/4'); } @@ -3276,4 +3279,4 @@ class CmsControllerEditMultipleArticleUnindexTest extends CmsControllertestCase public function NoticesShouldBeEmpty() { $this->assertEmpty(Class_Notice::findAllBy([])); } -} +} \ No newline at end of file diff --git a/tests/application/modules/opac/controllers/IndexControllerTest.php b/tests/application/modules/opac/controllers/IndexControllerTest.php index 34779a66ac9a55974ee678559118fb6f02944c35..719c95fdc9216b099867c1453706f40c7f0e4c6d 100644 --- a/tests/application/modules/opac/controllers/IndexControllerTest.php +++ b/tests/application/modules/opac/controllers/IndexControllerTest.php @@ -366,7 +366,7 @@ class IndexControllerSitemapTest extends AbstractControllerTestCase { class IndexControllerAccessLevelWithLoginPageTest extends AbstractControllerTestCase { - protected $_storm_default_to_volatile = true; + public function setUp() { parent::setUp(); @@ -375,14 +375,14 @@ class IndexControllerAccessLevelWithLoginPageTest extends AbstractControllerTest } - /** @test */ + /** @test */ public function sameLoginPageShouldNotRedirect() { Class_Profil::getPortail() ->setAccessLevel(ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB) ->setLoginPage(1) ->assertSave(); - $this->dispatch('/recherche/simple', true); + $this->dispatch('/recherche/simple'); $this->assertNotRedirect($this->getResponseLocation()); $this->assertController('auth'); @@ -402,7 +402,7 @@ class IndexControllerAccessLevelWithLoginPageTest extends AbstractControllerTest ->setLoginPage(12) ->assertSave(); - $this->dispatch('/recherche/avancee', true); + $this->dispatch('/recherche/avancee'); $this->assertRedirectTo(Class_Url::absolute('/auth/login/id_profil/12?redirect=' . urlencode(Class_Url::absolute('/recherche/avancee/id_profil/1'))), @@ -410,14 +410,14 @@ class IndexControllerAccessLevelWithLoginPageTest extends AbstractControllerTest } - /** @test */ + /** @test */ public function noLoginPageShouldNotRedirectToAdmin() { Class_Profil::getPortail() ->setAccessLevel(ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB) ->setLoginPage(null) ->assertSave(); - $this->dispatch('/recherche/simple', true); + $this->dispatch('/recherche/simple'); $this->assertNotRedirect($this->getResponseLocation()); $this->assertController('auth'); @@ -425,7 +425,9 @@ class IndexControllerAccessLevelWithLoginPageTest extends AbstractControllerTest $this->assertModule('admin'); $absolute_url = Class_Url::absolute('/recherche/simple/id_profil/1'); - $this->assertXPath('//input[@name="redirect"][@value="'.$absolute_url.'"]'); + + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute('/recherche/simple')), + $this->_response->getBody()); } } @@ -480,11 +482,12 @@ class IndexControllerPrivateProfilesRewriteUrlWithoutLoginTest * @dataProvider datas */ public function shouldDisplayLoginScreenWithRedirect($url) { - $this->dispatch($url, true); + $this->dispatch($url); $this->assertModule('admin'); $this->assertController('auth'); $this->assertAction('login'); - $this->assertXPath('//input[@name="redirect"][contains(@value, "'. $url . '")]'); + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute($url)), + $this->_response->getBody()); } } @@ -547,11 +550,12 @@ class IndexControllerPrivateProfilesRewriteUrlWithDeletedLoginPageTest * @dataProvider datas */ public function shouldDisplayLoginScreenWithRedirect($url) { - $this->dispatch($url, true); + $this->dispatch($url); $this->assertModule('admin'); $this->assertController('auth'); $this->assertAction('login'); - $this->assertXPath('//input[@name="redirect"][contains(@value, "'. $url . '")]'); + $this->assertContains('<form enctype="application/x-www-form-urlencoded" method="post" class="admin-form admin_login_form" action="/admin/auth/login/redirect/' . urlencode(Class_Url::absolute($url)), + $this->_response->getBody()); } }