diff --git a/Module.php b/Module.php index c0b969fd183df36b48ca80eceb222015e3aa19e5..340f48138218760533ef21827e14c6167f997652 100644 --- a/Module.php +++ b/Module.php @@ -122,11 +122,12 @@ class Module implements ConfigProviderInterface, ViewHelperProviderInterface, Se { return array( 'factories' => array( - 'userConnection' => 'UnicaenAuth\View\Helper\UserConnectionFactory', - 'userCurrent' => 'UnicaenAuth\View\Helper\UserCurrentFactory', - 'userStatus' => 'UnicaenAuth\View\Helper\UserStatusFactory', - 'userProfile' => 'UnicaenAuth\View\Helper\UserProfileFactory', - 'userInfo' => 'UnicaenAuth\View\Helper\UserInfoFactory', + 'userConnection' => 'UnicaenAuth\View\Helper\UserConnectionFactory', + 'userCurrent' => 'UnicaenAuth\View\Helper\UserCurrentFactory', + 'userStatus' => 'UnicaenAuth\View\Helper\UserStatusFactory', + 'userProfile' => 'UnicaenAuth\View\Helper\UserProfileFactory', + 'userInfo' => 'UnicaenAuth\View\Helper\UserInfoFactory', + 'userProfileSelect' => 'UnicaenAuth\View\Helper\UserProfileSelectFactory', ), 'invokables' => array( 'appConnection' => 'UnicaenAuth\View\Helper\AppConnection', diff --git a/config/module.config.php b/config/module.config.php index cb7bec104386b8f2768df286d4bbaa8a8598d091..5bad3942a1d893602392b9d148ea5436a5046637 100644 --- a/config/module.config.php +++ b/config/module.config.php @@ -91,8 +91,8 @@ $bjyauthorize = array( * - le rôle 'user', c'est le rôle de tout utilisateur authentifié. */ 'UnicaenAuth\Provider\Role\Config' => array( - 'guest' => array('name' => "Non authentifié(e)", 'children' => array( - 'user' => array('name' => "Authentifié(e)") + 'guest' => array('name' => "Non authentifié(e)", 'selectable' => false, 'children' => array( + 'user' => array('name' => "Authentifié(e)", 'selectable' => false) )), ), /** @@ -128,12 +128,13 @@ $bjyauthorize = array( array('controller' => 'zfcuser', 'roles' => array()), array('controller' => 'Application\Controller\Index', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'etab', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'apropos', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'contact', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'plan', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'mentions-legales', 'roles' => array()), - array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'informatique-et-libertes', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'etab', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'apropos', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'contact', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'plan', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'mentions-legales', 'roles' => array()), + array('controller' => 'UnicaenApp\Controller\Application', 'action' => 'informatique-et-libertes', 'roles' => array()), + array('controller' => 'UnicaenAuth\Controller\Utilisateur', 'action' => 'selectionner-profil', 'roles' => array('user')), ), ), ); @@ -197,7 +198,7 @@ return array( ), 'controllers' => array( 'invokables' => array( - + 'UnicaenAuth\Controller\Utilisateur' => 'UnicaenAuth\Controller\UtilisateurController', ), ), 'view_manager' => array( @@ -263,6 +264,33 @@ return array( ), ), ), + 'utilisateur' => array( + 'type' => 'Literal', + 'options' => array( + 'route' => '/utilisateur', + 'defaults' => array( + '__NAMESPACE__' => 'UnicaenAuth\Controller', + 'controller' => 'Utilisateur', + 'action' => 'index', + ), + ), + 'may_terminate' => true, + 'child_routes' => array( + 'default' => array( + 'type' => 'Segment', + 'options' => array( + 'route' => '/:action[/:id]', + 'constraints' => array( + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + 'id' => '[0-9]*', + ), + 'defaults' => array( + 'action' => 'index', + ), + ), + ), + ), + ), ), ), // All navigation-related configuration is collected in the 'navigation' key diff --git a/src/UnicaenAuth/Controller/UtilisateurController.php b/src/UnicaenAuth/Controller/UtilisateurController.php new file mode 100644 index 0000000000000000000000000000000000000000..12e8d1f7fd3c8b78fd0dae457105bfbbaee2dc9d --- /dev/null +++ b/src/UnicaenAuth/Controller/UtilisateurController.php @@ -0,0 +1,36 @@ +<?php + +namespace UnicaenAuth\Controller; + +use Zend\Mvc\Controller\AbstractActionController; + +/** + * Description of UtilisateurController + * + * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr> + */ +class UtilisateurController extends AbstractActionController +{ + /** + * Traite les requêtes AJAX POST de sélection d'un profil utilisateur. + * La sélection est mémorisé en session par le service AuthUserContext. + */ + public function selectionnerProfilAction() + { + if (!$this->getRequest()->isXmlHttpRequest()) { + return $this->url()->fromRoute('home'); + } + if (($role = $this->getRequest()->getPost('role'))) { + $this->getAuthUserContextService()->setSelectedIdentityRole($role); + } + exit; + } + + /** + * @return \UnicaenAuth\Service\UserContext + */ + protected function getAuthUserContextService() + { + return $this->getServiceLocator()->get('AuthUserContext'); + } +} \ No newline at end of file diff --git a/src/UnicaenAuth/Provider/Role/Config.php b/src/UnicaenAuth/Provider/Role/Config.php index dda5aa708828b3bf25c62f06a223ff1ad5c60fcd..20c480808ef38d5c0dca0bd679994008c6ac7930 100644 --- a/src/UnicaenAuth/Provider/Role/Config.php +++ b/src/UnicaenAuth/Provider/Role/Config.php @@ -64,8 +64,22 @@ class Config extends \BjyAuthorize\Provider\Role\Config $children = array(); } + if (isset($options['description'])) { + $description = (bool) $options['description']; + } + else { + $description = false; + } + + if (isset($options['selectable'])) { + $selectable = (bool) $options['selectable']; + } + else { + $selectable = true; + } + $roles = array(); - $role = new NamedRole($name, $parent, $roleName); + $role = new NamedRole($name, $parent, $roleName, $description, $selectable); $roles[] = $role; foreach ($children as $key => $value) { diff --git a/src/UnicaenAuth/Service/UserContext.php b/src/UnicaenAuth/Service/UserContext.php index 6ff5f02b32f24b1436f8b5cc4e325f6050ffb431..c09cac240644857c172c9fbaff57bf1a64ef4abb 100644 --- a/src/UnicaenAuth/Service/UserContext.php +++ b/src/UnicaenAuth/Service/UserContext.php @@ -3,47 +3,34 @@ namespace UnicaenAuth\Service; use Zend\ServiceManager\ServiceLocatorAwareInterface; -use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\ServiceLocatorAwareTrait; +use Zend\Session\Container as SessionContainer; use ZfcUser\Entity\UserInterface; use UnicaenAuth\Entity\Ldap\People; /** - * + * Service centralisant des méthodes utiles concernant l'utilisateur authentifié. * * @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr> */ class UserContext implements ServiceLocatorAwareInterface { - - /** - * @var ServiceLocatorInterface - */ - protected $sl; + use ServiceLocatorAwareTrait; /** * @var mixed */ protected $identity; - - - - - + /** - * - * @return mixed + * @var array */ - protected function getIdentity() - { - if (null === $this->identity) { - $authenticationService = $this->sl->get('Zend\Authentication\AuthenticationService'); - if ($authenticationService->hasIdentity()) { - $this->identity = $authenticationService->getIdentity(); - } - } - - return $this->identity; - } + protected $identityRoles; + + /** + * @var SessionContainer + */ + protected $sessionContainer; /** * Retourne l'utilisateur BDD courant @@ -76,25 +63,112 @@ class UserContext implements ServiceLocatorAwareInterface } /** - * Set service locator + * Retourne l'identité correspondant à l'utilisateur courant. * - * @param ServiceLocatorInterface $serviceLocator - * @return self + * @return mixed */ - public function setServiceLocator(ServiceLocatorInterface $serviceLocator) + protected function getIdentity() { - $this->sl = $serviceLocator; - + if (null === $this->identity) { + $authenticationService = $this->getServiceLocator()->get('Zend\Authentication\AuthenticationService'); + if ($authenticationService->hasIdentity()) { + $this->identity = $authenticationService->getIdentity(); + } + } + return $this->identity; + } + + /** + * Retourne les rôles de l'utilisateur courant. + * + * @return array + */ + public function getIdentityRoles() + { + if (null === $this->identityRoles) { + $authorize = $this->getServiceLocator()->get('BjyAuthorize\Service\Authorize'); + $identityProvider = $authorize->getIdentityProvider(); + $this->identityRoles = $identityProvider->getIdentityRoles(); + } + return $this->identityRoles; + } + + /** + * Retourne les rôles de l'utilisateur courant qui peuvent être sélectionnés. + * + * @return array + */ + public function getSelectableIdentityRoles() + { + return array_filter( + $this->getIdentityRoles(), + function($r) { return !($r instanceof \UnicaenAuth\Acl\NamedRole && !$r->getSelectable()); }); + } + + /** + * Retourne le rôle utilisateur sélectionné, ou le premier sélectionnable si aucun n'a été sléectionné. + * + * @return mixed + */ + public function getSelectedIdentityRole() + { + if (null === $this->getSessionContainer()->selectedIdentityRole) { + $roles = $this->getSelectableIdentityRoles(); + $this->getSessionContainer()->selectedIdentityRole = reset($roles) ?: null; + } + return $this->getSessionContainer()->selectedIdentityRole; + } + + /** + * Mémorise le rôle courant de l'utilisateur. + * + * @param mixed $role + * @return \UnicaenAuth\Service\UserContext + */ + public function setSelectedIdentityRole($role) + { + if ($role) { + if (!$this->isRoleValid($role)) { + throw new \Common\Exception\RuntimeException("Rôle spécifié invalide."); + } + $this->getSessionContainer()->selectedIdentityRole = $role; + } + else { + unset($this->getSessionContainer()->selectedIdentityRole); + } return $this; } - + /** - * Get service locator - * - * @return ServiceLocatorInterface + * Teste si le rôle spécifié fait partie des rôles disponibles. + * + * @param mixed $role + * @return boolean */ - public function getServiceLocator() + protected function isRoleValid($role) { - return $this->sl; + foreach ($this->getIdentityRoles() as $r) { + if ($r instanceof \Zend\Permissions\Acl\Role\RoleInterface) { + $r = $r->getRoleId(); + } + if ($role === $r) { + return true; + } + } + return false; + } + + /** + * Retourne le stockage en session utilisé pour mémoriser le profil + * sélectionné par l'utilsateur. + * + * @return SessionContainer + */ + protected function getSessionContainer() + { + if (null === $this->sessionContainer) { + $this->sessionContainer = new SessionContainer(get_class()); + } + return $this->sessionContainer; } } \ No newline at end of file diff --git a/src/UnicaenAuth/View/Helper/UserCurrent.php b/src/UnicaenAuth/View/Helper/UserCurrent.php index 149cc232626f2af81d36bbc6fd4e6b8b89bd83c2..af4b8f8a5855587b39fc6d7368652b264ebfc8a5 100644 --- a/src/UnicaenAuth/View/Helper/UserCurrent.php +++ b/src/UnicaenAuth/View/Helper/UserCurrent.php @@ -43,7 +43,10 @@ class UserCurrent extends UserAbstract if ($this->getIdentity()) { $userProfileHelper = $this->getView()->plugin('userProfile'); /* @var $userProfileHelper \UnicaenAuth\View\Helper\UserProfile */ - $userInfoHelper = $this->getView()->plugin('userInfo'); /* @var $userInfoHelper \UnicaenAuth\View\Helper\UserInfo */ + $userProfileHelper->setUserProfileSelectable(); + + $userInfoHelper = $this->getView()->plugin('userInfo'); /* @var $userInfoHelper \UnicaenAuth\View\Helper\UserInfo */ + $content = $userProfileHelper . $userInfoHelper($this->getAffectationFineSiDispo()); } else { @@ -53,7 +56,7 @@ class UserCurrent extends UserAbstract } } - $content = preg_replace('/\r\n|\n|\r/', '', $content); + $content = htmlspecialchars(preg_replace('/\r\n|\n|\r/', '', $content)); $title = _("Utilisateur connecté à l'application"); if ($this->getTranslator()) { diff --git a/src/UnicaenAuth/View/Helper/UserProfile.php b/src/UnicaenAuth/View/Helper/UserProfile.php index 796aebaa91e01bd46d1edeca264d5dd0a501576b..f3d917085e4268f7051ff71dc5c58cec465900ea 100644 --- a/src/UnicaenAuth/View/Helper/UserProfile.php +++ b/src/UnicaenAuth/View/Helper/UserProfile.php @@ -1,7 +1,6 @@ <?php namespace UnicaenAuth\View\Helper; -use BjyAuthorize\Provider\Identity\ProviderInterface; use UnicaenAuth\Acl\NamedRole; use Zend\Permissions\Acl\Role\RoleInterface; @@ -13,17 +12,32 @@ use Zend\Permissions\Acl\Role\RoleInterface; class UserProfile extends UserAbstract { /** - * @var ProviderInterface + * @var \UnicaenAuth\Service\UserContext */ - protected $identityProvider; + protected $userContextService; + + /** + * @var bool + */ + protected $userProfileSelectable = false; + + /** + * @var array + */ + protected $identityRoles; /** * Point d'entrée. * + * @param bool $userProfileSelectable Spécifie s'il faut afficher les profils + * de l'utilisateur sous forme d'une liste déroulante ou de boutons radios, permettant + * ainsi à l'utilisateur de changer de profil courant. * @return self */ - public function __invoke() + public function __invoke($userProfileSelectable = false) { + $this->userProfileSelectable = $userProfileSelectable; + return $this; } @@ -32,7 +46,7 @@ class UserProfile extends UserAbstract * * @return string */ - public function __toString() + public function render() { $title = _("Profil utilisateur"); $unknown = _("Inconnu"); @@ -44,9 +58,58 @@ class UserProfile extends UserAbstract $none = $this->getTranslator()->translate($none, $this->getTranslatorTextDomain()); } - $roles = array(); - $identityRoles = $this->getIdentityProvider() ? $this->getIdentityProvider()->getIdentityRoles() : array(); - foreach ($identityRoles as $role) { + $roles = $this->getIdentityRolesAsOptions(); + + if (!$roles) { + $roles[] = $none; + } + + $html = "<strong>$title :</strong>" . PHP_EOL; + + if ($this->userProfileSelectable) { + $html .= $this->getView()->userProfileSelect(false); + } + else { + $html .= $this->getView()->htmlList($roles); + } + + return $html; + } + + /** + * Retourne le code HTML généré par cette aide de vue. + * + * @return string + */ + public function __toString() + { + return $this->render(); + } + + /** + * Retourne les rôles de l'utilisateur courant. + * + * @return array + */ + protected function getIdentityRoles() + { + if (null === $this->identityRoles) { + $this->identityRoles = $this->getUserContextService()->getIdentityRoles(); + } + return $this->identityRoles; + } + + /** + * Retourne les rôles de l'utilisateur courant. + * + * @return array + */ + protected function getIdentityRolesAsOptions() + { + $identityRoles = $this->getIdentityRoles(); + $roles = array(); + + foreach ($identityRoles as $id => $role) { $lib = ''; if ($role instanceof NamedRole) { $lib = $role->getRoleName(); @@ -65,36 +128,46 @@ class UserProfile extends UserAbstract if ($this->getTranslator()) { $lib = $this->getTranslator()->translate($lib, $this->getTranslatorTextDomain()); } - $roles[] = $lib; + $roles[$id] = $lib; } } - if (!$roles) { - $roles[] = $none; - } - - $html = "<strong>$title :</strong>" . PHP_EOL; - $html .= $this->getView()->htmlList($roles); - - return $html; + + return $roles; } /** * - * @return ProviderInterface + * @return \UnicaenAuth\Service\UserContext */ - public function getIdentityProvider() + public function getUserContextService() { - return $this->identityProvider; + return $this->userContextService; } /** * - * @param ProviderInterface $identityProvider + * @param \UnicaenAuth\Service\UserContext $userContextService * @return self */ - public function setIdentityProvider(ProviderInterface $identityProvider = null) + public function setUserContextService(\UnicaenAuth\Service\UserContext $userContextService = null) { - $this->identityProvider = $identityProvider; + $this->userContextService = $userContextService; return $this; } + + /** + * Spécifie s'il faut afficher les profils + * de l'utilisateur sous forme d'une liste déroulante ou de boutons radios, permettant + * ainsi à l'utilisateur de changer de profil courant. + * + * @param bool $userProfileSelectable + * @return \UnicaenAuth\View\Helper\UserProfile + */ + public function setUserProfileSelectable($userProfileSelectable = true) + { + $this->userProfileSelectable = $userProfileSelectable; + return $this; + } + + } \ No newline at end of file diff --git a/src/UnicaenAuth/View/Helper/UserProfileFactory.php b/src/UnicaenAuth/View/Helper/UserProfileFactory.php index cdbcb65007c0653ef4cf634a2c98a7f391472bb5..225ad963e9c073d84d22537b2d4d9acb2d9bc81c 100644 --- a/src/UnicaenAuth/View/Helper/UserProfileFactory.php +++ b/src/UnicaenAuth/View/Helper/UserProfileFactory.php @@ -20,13 +20,12 @@ class UserProfileFactory implements FactoryInterface */ public function createService(ServiceLocatorInterface $helperPluginManager) { - $serviceLocator = $helperPluginManager->getServiceLocator(); - $authService = $serviceLocator->get('zfcuser_auth_service'); - $authorize = $serviceLocator->get('BjyAuthorize\Service\Authorize'); - $identityProvider = $authorize->getIdentityProvider(); + $serviceLocator = $helperPluginManager->getServiceLocator(); + $authService = $serviceLocator->get('zfcuser_auth_service'); + $userContextService = $serviceLocator->get('AuthUserContext'); $helper = new UserProfile($authService); - $helper->setIdentityProvider($identityProvider); + $helper->setUserContextService($userContextService); return $helper; } diff --git a/src/UnicaenAuth/View/Helper/UserProfileSelect.php b/src/UnicaenAuth/View/Helper/UserProfileSelect.php new file mode 100644 index 0000000000000000000000000000000000000000..d88009278ca9dd9feac69bab218736f56f5598e1 --- /dev/null +++ b/src/UnicaenAuth/View/Helper/UserProfileSelect.php @@ -0,0 +1,164 @@ +<?php +namespace UnicaenAuth\View\Helper; + +use BjyAuthorize\Provider\Identity\ProviderInterface; +use UnicaenAuth\Acl\NamedRole; +use Zend\Permissions\Acl\Role\RoleInterface; + +/** + * Aide de vue permettant à l'utilisateur de sélectionner son profil courant parmi + * les différents profils qu'il possède. + * + * Si l'utilisateur possède moins de 2 rôles, rien ne s'affiche. + * + * @author Bertrand GAUTHIER <bertrand.gauthier@unicaen.fr> + */ +class UserProfileSelect extends UserProfile +{ + /** + * @var string + */ + protected $formClass; + + /** + * @var bool + */ + protected $asSelect = false; + + /** + * Retourne le code HTML généré par cette aide de vue. + * + * @return string + */ + public function render() + { + $roles = $this->getIdentityRolesAsOptions(); + + if (!$roles) { + return ''; + } + + $formClass = 'user-profile-select-form'; + $inputClass = 'user-profile-select-input'; + + $form = new \Zend\Form\Form(); + $form->setAttribute('class', "$formClass " . $this->formClass); + + $html = $this->getView()->form()->openTag($form); + + // rendu sous forme d'un select + if ($this->asSelect) { + $select = new \Zend\Form\Element\Select('role'); + $select + ->setValueOptions($roles) + ->setAttributes(array( + 'class' => $inputClass, + 'value' => $this->getSelectedIdentityRole(), + )); + + $html .= $this->getView()->formControlGroup($select); + } + // rendu sous forme de radios + else { + foreach ($roles as $id => $role) { + $radio = new \Zend\Form\Element\Radio('role'); + $radio + ->setValueOptions(array($id => $role)) + ->setAttribute('class', $inputClass) + ->setAttribute('title', "Cliquez pour changer de profil courant") + ->setValue($id === $this->getSelectedIdentityRole() ? $id : null); + $html .= '<div class="radio">' . $this->getView()->formRadio($radio) . '</div>'; + } + } + + $html .= $this->getView()->form()->closeTag(); + + $url = $this->getView()->url('utilisateur/default', array('action' => 'selectionner-profil')); + + $html .= <<<EOS +<script> + $(function() { + $("input.$inputClass").change(function() { + $("body *").css('cursor', 'wait'); + $.post("$url", $(".$formClass").serializeArray(), function() { location.reload(); }); } ); + }); +</script> +EOS; + + return $html; + } + + /** + * Interroge le service pour abtenir le rôle sélectionné. + * + * @return mixed + */ + protected function getSelectedIdentityRole() + { + $role = $this->getUserContextService()->getSelectedIdentityRole(); + if ($role instanceof RoleInterface) { + return $role->getRoleId(); + } + return $role; + } + + /** + * Formatte et traduit les rôles. + * + * @return array + */ + protected function getIdentityRolesAsOptions() + { + $roles = $this->getSelectableRoles(); + + if (count($roles) < 2) { + return array(); + } + + foreach ($roles as $id => $role) { + $lib = ''; + if ($role instanceof NamedRole) { + $lib = $role->getRoleName(); + } + elseif ($role instanceof RoleInterface) { + $lib = $role->getRoleId(); + } + else { + $fallback = null; + set_error_handler(function() use (&$fallback, $unknown) { $fallback = $unknown; }); + $lib = (string) $role; + restore_error_handler(); + $lib = $lib ?: $fallback; + } + if ($lib) { + if ($this->getTranslator()) { + $lib = $this->getTranslator()->translate($lib, $this->getTranslatorTextDomain()); + } + $roles[$id] = $lib; + } + } + + return $roles; + } + + /** + * + * @return array + */ + protected function getSelectableRoles() + { + return $this->getUserContextService()->getSelectableIdentityRoles(); + } + + public function setFormClass($formClass) + { + $this->formClass = $formClass; + return $this; + } + + public function setAsSelect($asSelect) + { + $this->asSelect = $asSelect; + return $this; + } +} \ No newline at end of file diff --git a/src/UnicaenAuth/View/Helper/UserProfileSelectFactory.php b/src/UnicaenAuth/View/Helper/UserProfileSelectFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..76072f0734b06422b93546a4f43d006c8dbdc6e0 --- /dev/null +++ b/src/UnicaenAuth/View/Helper/UserProfileSelectFactory.php @@ -0,0 +1,31 @@ +<?php + +namespace UnicaenAuth\View\Helper; + +use Zend\ServiceManager\ServiceLocatorInterface; + +/** + * Description of UserProfileFactory + * + * @author Bertrand GAUTHIER <bertrand.gauthier at unicaen.fr> + */ +class UserProfileSelectFactory extends \UnicaenApp\View\Helper\UserProfileSelectFactory +{ + /** + * Create service + * + * @param ServiceLocatorInterface $helperPluginManager + * @return UserProfile + */ + public function createService(ServiceLocatorInterface $helperPluginManager) + { + $serviceLocator = $helperPluginManager->getServiceLocator(); + $authService = $serviceLocator->get('zfcuser_auth_service'); + $userContextService = $serviceLocator->get('AuthUserContext'); + + $helper = new UserProfileSelect($authService); + $helper->setUserContextService($userContextService); + + return $helper; + } +} \ No newline at end of file