Skip to content
Snippets Groups Projects
Commit b6c99b8f authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

Reprise en markdown de la doc du dokuwiki

parent f505c836
No related branches found
No related tags found
No related merge requests found
# UnicaenAuth
Ce module :
- ajoute à une application la possibilité d'identifier/authentifier l'utilisateur (LDAP, base de données ou CAS).
- fournit la possibilité à l'utilisateur de se créer un compte dans la base de données de l'application (option de config).
- fournit les fonctionnalités d'habilitation de l'utilisateur (ACL).
- Une bibliothèque de rôles éditable via une IHM
- Un système de gestion des droits avec des privilèges éditables via une IHM
- Un système d'assertions avancées pour gérer des cas complexes d'autorisations
- requiert les modules suivants :
- UnicaenApp
- ZfcUserDoctrineOrm
- phpCAS
- BjyAuthorize
## Documentation
- [Installation](./doc/installation.md)
- [Configuration](./doc/configuration.md)
- [Authentification](./doc/authentification.md)
- [Services](./doc/services.md)
- [Utilisation de la gestion des droits et privilèges](./doc/droits.md)
- [Aides de vue (view helpers)](./doc/helpers.md)
# Authentification
## Sources d'authentification
Les 3 sources suivantes sont sollicitées successivement jusqu'à ce que l'une d'entre elles valide l'authentification de l'utilisateur.
1/ Annuaire LDAP
- La connexion à l'annuaire LDAP est requise pour authentifier (ldap_bind) et récupérer les infos concernant l'utilisateur (cf. configuration du module UnicaenApp).
- Il est possible d'enregistrer systématiquement l'utilisateur authentifié dans la base de données de l'application.
2/ Table des utilisateurs
- Il peut arriver qu'une appli ait besoin d'authentifier des personnes n'existant pas dans l'annuaire LDAP.
- Pour donner accès à l'application à un nouvel utilisateur, 2 solutions :
- Un informaticien crée à la main l'utilisateur dans la table des utilisateurs ; le mot de passe doit être chiffré avec “Bcrypt”
(exemple en ligne de commande à la racine de votre projet : `php --run 'require "vendor/autoload.php"; $bcrypt = new Zend\Crypt\Password\Bcrypt(); var_dump($bcrypt->create("azerty"));'`).
- Si la fonctionnalité est activée (fournie par le module "zf-commons/zfc-user" dont dépend le module UnicaenAuth), l'utilisateur s'enregistre lui-même dans la table des utilisateurs via un formulaire de l'application (le lien figure sous le formulaire de connexion à l'appli).
3/ Serveur CAS
- L'authentification est déléguée au serveur CAS grâce au module jasig/phpcas (bibliothèque phpCAS).
- NB: La connexion à l'annuaire LDAP est tout de même requise pour récupérer les infos concernant l'utilisateur (cf. configuration du module UnicaenApp).
## Événement UserAuthenticatedEvent
Si vous avez activé l'enregistrement automatique de l'utilisateur authentifié dans la base de données de votre application, la classe abstraite UnicaenAuth\Event\Listener\AuthenticatedUserSavedAbstractListener peut vous intéresser.
Elle vous procure un moyen de “faire quelque chose” juste avant que l'entité utilisateur (fraîchement authentifié via LDAP) ne soit persistée. L'idée est d'écouter un événement particulier déclenché lors du processus d'authentification de l'utilisateur.
*Attention! Cet événement est déclenché par l'authentification LDAP, mais pas par l'authentification à partir d'une table locale en base de données.*
*Si vous avez mis en place (en plus ou à la place de l'authentification LDAP) une authentification à partir d'une table locale, écoutez plutôt l'événement authentication.success déclenché par le module ZfcUser une fois que l'authentification a réussi. Exemple :*
Module.php
public function onBootstrap(MvcEvent $e) {
//...
$e->getApplication()->getEventManager()->getSharedManager()->attach(
"*",
'authenticate.success',
array($this, 'onUserLogin'),
100
);
}
//...
public function onUserLogin( $e ) {
if (is_string($identity = $e->getIdentity())) {
// login de l'utilisateur authentifié
$username = $identity;
//...
} else {
// id de l'utilisateur authentifié dans la table
$id = $identity;
//...
}
//...
}
Exemple :
UserAuthenticatedEventListener.php
namespace Application\Auth;
use Application\Entity\Db\Role;
use Application\Entity\Db\Utilisateur;
use UnicaenAuth\Event\Listener\AuthenticatedUserSavedAbstractListener;
use UnicaenAuth\Event\UserAuthenticatedEvent;
use UnicaenAuth\Service\UserContext as UserContextService;
class UserAuthenticatedEventListener extends AuthenticatedUserSavedAbstractListener
{
/**
* @var Role
*/
private $defaultRole;
/**
* @param Role $defaultRole
*/
public function setDefaultRole(Role $defaultRole)
{
$this->defaultRole = $defaultRole;
}
/**
* @var UserContextService
*/
private $userContextService;
/**
* @param UserContextService $userContextService
*/
public function setAuthUserContextService(UserContextService $userContextService)
{
$this->userContextService = $userContextService;
}
/**
* @param UserAuthenticatedEvent $e
*/
public function onUserAuthenticatedPrePersist(UserAuthenticatedEvent $e)
{
/** @var Utilisateur $utilisateur */
$utilisateur = $e->getDbUser();
// Attribue le profil par défaut à l'utilisateur connecté s'il n'en a aucun.
if ($utilisateur->getRoles()->count() === 0 && $this->defaultRole) {
$role = $this->defaultRole;
$utilisateur->addRole($role);
}
// Le premier rôle trouvé sera celui endossé par l'utilisateur connecté.
if ($role = $utilisateur->getRoles()->first()) {
$this->userContextService->setNextSelectedIdentityRole($role);
}
}
}
UserAuthenticatedEventListenerFactory.php
namespace Application\Auth;
use Application\Entity\Db\Role;
use Doctrine\ORM\EntityManager;
use UnicaenAuth\Service\UserContext as UserContextService;
use Zend\ServiceManager\ServiceLocatorInterface;
class UserAuthenticatedEventListenerFactory
{
public function __invoke(ServiceLocatorInterface $serviceLocator)
{
/** @var EntityManager $em */
$em = $serviceLocator->get('doctrine.entitymanager.orm_default');
/** @var UserContextService $userContextService */
$userContextService = $serviceLocator->get('AuthUserContext');
/** @var Role $defaultRole */
$defaultRole = $em->getRepository(Role::class)->findOneBy(['isDefault' => true]);
$listener = new UserAuthenticatedEventListener();
$listener->setDefaultRole($defaultRole);
$listener->setAuthUserContextService($userContextService);
return $listener;
}
}
Module.php
namespace Application;
use Application\Auth\UserAuthenticatedEventListener;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$application = $e->getApplication();
$eventManager = $application->getEventManager();
//...
/** @var UserAuthenticatedEventListener $listener */
$listener = $sm->get('UserAuthenticatedEventListener');
$listener->attach($eventManager);
}
//...
}
module/Application/config/module.config.php
return array(
//...
'service_manager' => array(
'factories' => array(
//...
'UserAuthenticatedEventListener' => 'Application\Auth\UserAuthenticatedEventListenerFactory',
),
//...
),
//...
);
# Configuration
Il s'agit ici d'adapter certaines options de configuration des modules.
## Configuration globale
- Copier/coller/renommer le fichier config/unicaen-auth.global.php.dist du module vers config/autoload/unicaen-auth.global.php de votre projet.
- Adapter les informations suivantes à votre contexte (sans modifier le nom des clés)…
### Enregistrement automatique dans la base de l'appli de l'utilisateur authentifié via LDAP
Clé 'save_ldap_user_in_database' : flag indiquant si l'utilisateur authentifié avec succès via l'annuaire LDAP doit être enregistré/mis à jour dans la table des utilisateurs de l'appli (fonctionnalité du module ZfcUser).
*NB : Si vous activez cette fonctionnalité, vous devez spécifier dans la configuration locale du module (voir ci-dessous) les infos de connexion à la base de données d'authentification (“orm_auth”), soit spécifier dans la configuration locale d'un module quelconque les infos de connexion à la base de données principale de l'appli (“orm_default”).*
### Création de compte dans la base de l'appli par l'utilisateur lui-même
Clé 'enable_registration' : autorisation de la création d'un compte utilisateur par l'utilisateur lui-même dans la base de données de l'appli.
*NB : Si vous activez cette fonctionnalité, vous devez spécifier dans la configuration locale du module (voir ci-dessous) les infos de connexion à la base de données d'authentification (“orm_auth”), soit spécifier dans la configuration locale d'un module quelconque les infos de connexion à la base de données principale de l'appli (“orm_default”).*
### Système de gestion des privilèges
Le système de gestion des privilèges est activé par défaut. Il peut néanmoins être désactivé si
- il n'est pas utile à votre application
- si vous implémentez votre propre système de privilèges
La paramètre booléen unicaen-auth/enable_privileges en détermine l'activation.
### Désctivation d'autres parties d'UnicaenAuth
UnicaenAuth possède ses propres fournisseurs de rôles, d'identités (dans la configuration, rubrique respectives identity_provider et role_providers de bjyauthorize, pré-renseignées dans la configuration globale d'UnicaenAuth.
Tout comme le système de gestion des privilèges, il est possible de désactiver tout ou partie de ces systèmes pour les remplacer par les votres si nécessaire. Il suffit pour cela de commenter les lignes correspondantes dans le fichier de configuration global d'UnicaenAuth copié dans votre projet.
### Interface graphique de gestion (IHM)
Le système de gestion des privilèges d'UnicaenAuth est associé à une interface de gestion qui permet de :
- Gérer les rôles (ajout, modification et suppression de rôles) dynamiquement.
- Gérer l'association des rôles et des privilèges (les privilèges étant gérés directement en base de données car ces derniers sont liés au code source et ne sont pas dynamiques).
Un menu “Droits d'accès” est affiché par défaut dans votre barre de menu principale. Ceci peut bien entendu être modifié selon vos souhaits dans le fichier de configuration global d'UnicaenAuth placé dans votre projet.
## Configuration locale
- Copier/coller/renommer le fichier config/unicaen-auth.local.php.dist du module vers config/autoload/unicaen-auth.local.php de votre projet.
- Adapter les informations suivantes à votre contexte (sans modifier le nom des clés)…
### Authentification centralisée
Clé 'cas' : décommenter pour activer l'authentification CAS, commenter pour la désactiver, exemple :
unicaen-auth.local.php
'cas' => array(
'connection' => array(
'default' => array(
'params' => array(
'hostname' => 'cas.unicaen.fr',
'port' => 443,
'version' => "2.0",
'uri' => "",
'debug' => false,
),
),
),
),
### Usurpation d'identité
Clé 'usurpation_allowed_usernames' : liste des identifiants de connexion des utilisateurs (issus de l'annuaire LDAP) autorisés à se connecter à l'appli sous l'identité de n'importe quel utilisateur (issus de l'annuaire LDAP ou de la base de données d'authentification), exemple :
unicaen-auth.local.php
'usurpation_allowed_usernames' => array('gauthierb'),
D'après cet exemple, l'utilisateur “gauthierb” est habilité à saisir dans le formulaire de connexion à l'appli l'identifiant “gauthierb=fernagut” et son mot de passe habituel pour se faire passer pour l'utilisateur “fernagut”.
# Gestion des droits
## Généralités
Dans UnicaenAuth, les droits sont gérés (par défaut) de la manière suivante :
![alt text][systeme_de_gestion_des_droits]
### Utilisateurs
L'utilisateur s'authentifie. Une fois cela fait, 0 à n rôles lui sont associés (en base de données ou bien avec le LDAP). Si plusieurs sont possibles alors il pourra choisir d'en endosser un (en cliquant sur son nom en haut à droite de l'interface, une liste lui est proposée le cas échéant).
### Rôles
Les rôles sont définis dans la base de données (table user_role). Ils sont dynamiques, c'est-à-dire que rien dans le code de l'application ne doit faire référence à un rôle en particulier, hormis les rôles spéciaux guest et user (respectivement non authentifié et authentifié : les rôles de base).
Certains rôles sont proposés par défaut (gestionnaire, administrateur, etc). L'administrateur est par défaut le seul à accéder à la gestion des rôles et des privilèges.
Les rôles peuvent être gérés depuis une interface graphique. Elle est accessible si vous êtes administrateur (par défaut), sur “Droits d'accès ⇒ Rôles”.
Exemples de rôle :
- Administrateur
- Gestionnaire
- Responsable
- Personnel
- Etudiant
### Privilèges
Les privilèges représentent des fonctionnalités de votre logiciel (édition d'un contrat, visualisation d'un tableau de bord particulier, export CSV particulier, etc).
La liste des privilèges doit être renseignés dans la base de données (table privilege). Chaque privilège appartient à une catégorie, ce qui permet de mieux les identifier. Les catégories doivent être également créées par vos soins.
*Le système de gestion des droits gère lui-même ses accès, de la même manière que n'importe quelle autre fonctionnalité de votre logiciel.*
Exemples de privilège :
Catégorie | Privilège
--------- | -------------
Album | Visualisation
Album | Édition
### Utilisateurs => Rôles
Le lien entre utilisateur et rôle peut se faire de deux manières :
Soit l'association entre utilisateur et rôle est renseignée directement dans la base de données (table user/role/linker
Soit en fonction du filtre LDAP du rôle : si un rôle a un filtre LDAP renseigné et que le compte LDAP correspond à ce filtre alors il peut être associé au rôle.
### Rôles => privilèges
Le lien entre les rôles et les privilèges se fait au moyen de la table role_privilege. Une interface graphique permet d'éditer ces associations. Elle est accessible si vous êtes administrateur (par défaut), sur “Droits d'accès ⇒ Privilèges”.
## Interface
Voici un aperçu de l'IHM qui est proposée :
![alt text][roles]
Il est ici possible de visualiser la liste des rôles et de l'éditer. Vous avez également un exemple de filtres LDAP pour les étudiants et le personnel.
Et voici le tableau croisé des privilèges et des rôles.
![alt text][privileges]
Ces deux vues peuvent être accessibles en lecture seule uniquement, en fonction des privilèges accordés.
## Application
Concrètement, dans votre code source, aucune référence au rôle courant de l'utilisateur ne doit être faite, sous peine de perdre l'aspect dynamique et personnalisable de la liste des rôles. Les privilèges seront utilisés à la place.
### Quelques mots sur BjyAuthorize...
BjyAuthorize est un module de gestion des droits qui est utilisé par UnicaenAuth. Il exploite les ACL de Zend et fait le lien avec votre application par le biais :
- de directives de configuration des droits
- d'assertions pour aller plus loin si nécessaire
- de plugins et d'aides de vues (isAllowed) qui permettent à tout moment de tester si on a le droits de quelque chose ou non.
### Directives de configuration
#### Accès à la liste des privilèges
Pour
- bénéficier de l'auto-complétion,
- pouvoir recenser les usages d'un privilège dans votre application,
- éviter des erreurs (oubli de caractères, etc.),
il est nécessaire d'avoir une constante pour chaque privilège. Ces constantes peuvent être rassemblées dans un unique fichier Application/Provider/Privilege/Privileges.php avec un contenu similaire à :
<?php
namespace Application\Provider\Privilege;
/**
* Description of Privileges
*
* Liste des privilèges utilisables dans votre application
*
* @author UnicaenCode
*/
class Privileges extends \UnicaenAuth\Provider\Privilege\Privileges {
const ALBUM_VISUALISATION = 'album-visualisation';
const ALBUM_EDITION = 'album-edition';
}
*Les privilèges listés sont composés à la fois de la catégorie et du privilège.*
Ce code pourra être géréré automatiquement avec le générateur de code d'UnicaenCode (page GeneratePrivileges) et directement copié/collé dans votre projet. Lorsque vous ajouterez un privilège à votre base de données, l'opération sera à recommencer.
#### Guards
Les Guards servent à protéger les accès aux actions de contrôleurs. Concrètement, si un utilisateur n'a pas le droit d'accéder à une action, alors une erreur 403 lui est retournée.
Exemple de configuration de guards :
'bjyauthorize' => [
'guards' => [
/* Utilisation du système de privilèges d'UnicaenAuth pour les guards */
\UnicaenAuth\Guard\PrivilegeController::class => [
/* Global */
[
'controller' => 'Application\Controller\OffreFormation',
'action' => ['search-structures', 'search-niveaux'],
'privileges' => Privileges::ODF_VISUALISATION,
],
/* Éléments pédagogiques */
[
'controller' => 'Application\Controller\OffreFormation\ElementPedagogique',
'action' => ['voir', 'search', 'getPeriode'], // getPeriode est utilisé pour la saisie de service!!!
'privileges' => Privileges::ODF_ELEMENT_VISUALISATION,
],
[
'controller' => 'Application\Controller\OffreFormation\ElementPedagogique',
'action' => ['saisir', 'supprimer'],
'privileges' => Privileges::ODF_ELEMENT_EDITION,
// usage avancé d'une assertion pour préciser les droits
'assertion' => 'OffreDeFormationAssertion',
],
/* Rétro-compatibilité avec le système de BjyAuthorize : on peut aussi fournir des rôles... */
[
'controller' => 'Application\Controller\OffreFormation\OffreFormation',
'action' => ['index'],
'roles' => ['user'], // tous les utilisateurs connectés ont accès à cette page
],
],
],
],
privileges peut accueillir aussi un tableau.
*Attention à bien faire un use pour pouvoir utiliser simplement Privileges ou à défaut à utiliser \Application\Provider\Privilege\Privileges*
*Attention : les versions stables de BjyAuthorize ne gèrent pas les assertions dans des guards. Seule la version actuellement en développement (dev-master) le gère. Attention à bien configurer votre composer, donc, si vous voulez faire des assertions sur des guards!!*
#### Resources
##### Utilisation de ressources existantes
Avec BjyAuthorize, tous les contrôleurs et toutes les actions de contrôleurs sont identifiés comme des ressources.
La ressource d'une action de contrôleur est une chaîne de caractères qui peut être récupérée de la manière suivante :
$resourceAlbumIndex = \UnicaenAuth\Guard\PrivilegeController::getResourceId('Application\Controller\Album', 'index');
Avec UnicaenAuth, les privilèges sont aussi assimilables à des ressources. La ressource d'un privilège est une chaîne de caractères qui peut être récupérée de la manière suivante :
use Application\Provider\Privilege\Privileges;
$resourceAlbumEdition = Privileges::getResourceId(Privileges::ALBUM_EDITION);
##### Définition de vos propres ressources
Pour créer une ressource, il suffit de la déclarer dans votre configuration :
'bjyauthorize' => [
'resource_providers' => [
'BjyAuthorize\Provider\Resource\Config' => [
'ElementPedagogique' => [],
'Etape' => [],
],
],
],
Si vous voulez qu'une entité devienne une ressource, il faut qu'elle implémente ResourceInterface :
namespace Application\Entity\Db;
use Zend\Permissions\Acl\Resource\ResourceInterface;
class ElementPedagogique implements ResourceInterface
{
/**
* Returns the string identifier of the Resource
*
* @return string
*/
public function getResourceId()
{
return 'ElementPedagogique'; // à déclarer aussi dans votre config comme ci-dessus
}
}
Ceci est valable pour n'importe quelle classe. Il n'y a pas que les entités qui peuvent servir de ressources.
#### Rules
Les Rules sont des règles de gestion qui permettent de mettre en relation :
- un ou plusieurs privilèges ou pas
- une ou plusieurs ressources ou pas
- une assertion ou pas
- un ou plusieurs rôles ou pas (utile pour user et guest, pas recommandé pour d'autres rôles)
Exemple de configuration de Rules :
'bjyauthorize' => [
'rule_providers' => [
\UnicaenAuth\Provider\Rule\PrivilegeRuleProvider::class => [
'allow' => [
[
'privileges' => Privileges::ODF_ELEMENT_EDITION,
'resources' => ['ElementPedagogique', 'Structure'],
'assertion' => 'AssertionOffreDeFormation',
],
[
'privileges' => Privileges::ODF_ETAPE_EDITION,
'resources' => ['Etape', 'Structure'],
'assertion' => 'AssertionOffreDeFormation',
],
[
'privileges' => Privileges::ODF_CENTRES_COUT_EDITION,
'resources' => ['Etape', 'Structure', 'ElementPedagogique', 'CentreCoutEp'],
'assertion' => 'AssertionOffreDeFormation',
],
[
'privileges' => Privileges::ODF_MODULATEURS_EDITION,
'resources' => ['Etape', 'Structure', 'ElementPedagogique', 'ElementModulateur'],
'assertion' => 'AssertionOffreDeFormation',
],
],
],
],
],
*Attention : ici, la compatibilité avec Bjyauthorize n'est pas gérée pour le format de configuration : BjyAuthorize impose en effet un format tableau simple [roles,ressources,privileges,assertion] alors qu'avec le module UnicaenAuth une table de hachage est nécessaire (clés privileges, resources, etc) comme pour les guards.*
### Assertions
Une assertion est un service qui hérite de UnicaenAuth\Assertion\AbstractAssertion.
Elle doit être déclarée dans la configuration du ServiceManager, comme n'importe quel autre service réutilisable.
Son rôle est de répondre true ou false à une demande en fonction d'un certain nombre d'éléments qui lui sont transmis le cas échéant.
AbstractAssertion vous fournit l'accès
- au ServiceLocator,
- au gestionnaire d'événements MVC (Zend\Mvc\MvcEvent) pour éventuellement récupérer des infos sur les paramètres d'URL ou de routeur, etc (utile pour les assertions lancées par les guards),
- à l'ACL courante (getAcl()),
- au rôle courant (getRole()).
De plus, AbstractAssertion va vous pré-macher le travail en déterminant si l'assertion concerne un privilège, une action de contrôleur, une entité ou autre chose.
Il ne reste donc plus qu'à fournir une réponse à la question en héritant les méthodes suivantes (qui renvoient juste true par défaut) :
/**
* @param string $privilege
* @param string $subPrivilege
*
* @return boolean
*/
protected function assertPrivilege($privilege, $subPrivilege = null)
/**
* @param string $controller
* @param string $action
* @param string $privilege
*
* @return boolean
*/
protected function assertController($controller, $action = null, $privilege = null)
/**
* @param ResourceInterface $entity
* @param string $privilege
*
* @return boolean
*/
protected function assertEntity(ResourceInterface $entity, $privilege = null)
/**
* @param ResourceInterface $resource
* @param string $privilege
*
* @return boolean
*/
protected function assertOther(ResourceInterface $resource = null, $privilege = null)
*Vos méthodes devront systématiquement renvoyer true ou false, rien d'autre.*
Voici maintenant un exemple concret d'assertion :
<?php
namespace Application\Assertion;
use Application\Provider\Privilege\Privileges;
use Application\Entity\Db\CentreCoutEp;
use Application\Entity\Db\ElementModulateur;
use Application\Entity\Db\ElementPedagogique;
use Application\Entity\Db\Etape;
use Application\Entity\Db\Source;
use Application\Entity\Db\Structure;
use UnicaenAuth\Assertion\AbstractAssertion;
use Application\Acl\Role;
use Zend\Permissions\Acl\Resource\ResourceInterface;
/**
* Description of OffreDeFormationAssertion
*
* @author Laurent LÉCLUSE <laurent.lecluse at unicaen.fr>
*/
class OffreDeFormationAssertion extends AbstractAssertion
{
protected function assertEntity(ResourceInterface $entity = null, $privilege = null)
{
$role = $this->getRole();
// Si le rôle n'est pas renseigné alors on s'en va...
if (!$role instanceof Role) return false;
// pareil si le rôle ne possède pas le privilège adéquat
if (!$this->getAcl()->isAllowed($role, Privileges::getResourceId($privilege))) return false;
// Si c'est bon alors on affine...
switch(true){
case $entity instanceof ElementPedagogique:
switch ($privilege) {
case Privileges::ODF_ELEMENT_EDITION:
return $this->assertElementPedagogiqueSaisie($role,$entity);
case Privileges::ODF_CENTRES_COUT_EDITION:
return $this->assertElementPedagogiqueSaisieCentresCouts($role, $entity);
case Privileges::ODF_MODULATEURS_EDITION:
return $this->assertElementPedagogiqueSaisieModulateurs($role, $entity);
}
break;
case $entity instanceof Etape:
switch ($privilege) {
case Privileges::ODF_ETAPE_EDITION:
return $this->assertEtapeSaisie($role, $entity);
case Privileges::ODF_CENTRES_COUT_EDITION:
return $this->assertEtapeSaisieCentresCouts($role, $entity);
case Privileges::ODF_MODULATEURS_EDITION:
return $this->assertEtapeSaisieModulateurs($role, $entity);
}
break;
case $entity instanceof Structure:
switch ($privilege) {
case Privileges::ODF_ETAPE_EDITION:
case Privileges::ODF_ELEMENT_EDITION:
case Privileges::ODF_CENTRES_COUT_EDITION:
case Privileges::ODF_MODULATEURS_EDITION:
return $this->assertStructureSaisie($role, $entity);
}
break;
case $entity instanceof CentreCoutEp:
switch ($privilege) {
case Privileges::ODF_CENTRES_COUT_EDITION:
return $this->assertCentreCoutEpSaisieCentresCouts($role, $entity);
}
break;
case $entity instanceof ElementModulateur:
switch ($privilege) {
case Privileges::ODF_MODULATEURS_EDITION:
return $this->assertElementModulateurSaisieModulateurs($role, $entity);
}
break;
}
return true;
}
/* ---- Edition étapes & éléments ---- */
protected function assertElementPedagogiqueSaisie(Role $role, ElementPedagogique $elementPedagogique)
{
return $this->assertStructureSaisie($role, $elementPedagogique->getStructure())
&& $this->assertSourceSaisie($elementPedagogique->getSource());
}
protected function assertEtapeSaisie(Role $role, Etape $etape)
{
return $this->assertStructureSaisie($role, $etape->getStructure())
&& $this->assertSourceSaisie($etape->getSource());
}
/* ---- Centres de coûts ---- */
protected function assertEtapeSaisieCentresCouts(Role $role, Etape $etape)
{
return $this->assertStructureSaisie($role, $etape->getStructure())
&& $etape->getElementPedagogique()->count() > 0;
}
protected function assertCentreCoutEpSaisieCentresCouts(Role $role, CentreCoutEp $centreCoutEp)
{
return $this->assertElementPedagogiqueSaisieCentresCouts($role, $centreCoutEp->getElementPedagogique());
}
protected function assertElementPedagogiqueSaisieCentresCouts(Role $role, ElementPedagogique $elementPedagogique)
{
return $this->assertStructureSaisie($role, $elementPedagogique->getStructure());
}
/* ---- Modulateurs ---- */
protected function assertEtapeSaisieModulateurs(Role $role, Etape $etape)
{
return $this->assertStructureSaisie($role, $etape->getStructure())
&& $etape->getElementPedagogique()->count() > 0;
}
protected function assertElementPedagogiqueSaisieModulateurs(Role $role, ElementPedagogique $elementPedagogique)
{
return $this->assertStructureSaisie($role, $elementPedagogique->getStructure());
}
protected function assertElementModulateurSaisieModulateurs(Role $role, CentreCoutEp $centreCoutEp)
{
return $this->assertElementPedagogiqueSaisieCentresCouts($role, $centreCoutEp->getElementPedagogique());
}
/* ---- Globaux ---- */
protected function assertStructureSaisie(Role $role, Structure $structure)
{
// Dans Ose (d'où vient cette assertion), les rôles sont parfois liés à une structure. Pas dans UnicaenAuth
if ($rs = $role->getStructure()) {
return $rs === $structure;
}
return true;
}
protected function assertSourceSaisie(Source $source)
{
return $source->isOse();
}
}
Ici, seule assertEntity est utilisée. Les autres types d'assertions ne sont pas nécessaires. À première vue, les imbrications de switch/case d'AssertEntity peuvent paraître lourdes. Cependant cela permet de faire ruisseler les données jusqu'au test final, qui est accessible directement au besoin (si on fournit directement la structure plutôt qu'un élément pédagogique à isAllowed). AssertEntity en lui-même ne s'occupe que du routage. Cette manière d'architecturer votre assertion est recommandée car votre code pourra rester intelligible et adaptable y compris avec de nombreux tests.
## Usages avec IsAllowed
Maintenant que tout est défini, il ne reste plus qu'à faire les tests de droits dans notre code source.
IsAllowed est fait pour cela. C'est à la fois un plugin de contrôleur et une aide de vue. Donc aussi bien dans un contrôleur que dans une vue, $this->isAllowed(/* vos paramètres */); marchera.
Pour les guards, isAllowed est appelé implicitement par le GuardController. Inutile donc de répéter le test.
*Attention : les assertions peuvent ralentir votre application si vous en abusez. Je vous conseille de ne les utiliser qu'avec parcimonie ;-)*
Exemples :
$elementPedagogique = /* Récupération d'un élément pédagogique */
$canEditElement = $this->isAllowed($elementPedagogique,Privileges::ODF_ELEMENT_EDITION);
if ($canEditElement) echo 'On a le droit d\'éditer cet élément pédagogique particulier';
/* Depuis une aide de vue */
$canCreateEtape = $this->getView()->isAllowed(Privileges::getResourceId(Privileges::ODF_ETAPE_EDITION));
//utile pour afficher de manière conditionnelle un bouton d'ajout d'étape à partir du privilège correspondant
/* Savoir si on a le droit d'effectuer l'action de saisir un album */
$canGo = $this->isAllowed(\UnicaenAuth\Guard\PrivilegeController::getResourceId('Application\Controller\Album', 'saisie');
if ($canGo) echo '<a href="'.$this->url('album/saisie').'">Saisie d\'album</a>';
### Et si on n'est pas dans une vue ou un contrôleur ?
isAllowed est avant tout une méthode publique du service Authorize de BjyAuthorize. Il suffit de récupérer ce service avec le ServiceLocator et mancer la méthode :
$serviceAuthorize = $this->getServiceLocator()->get('BjyAuthorize\Service\Authorize');
/* @var $serviceAuthorize \BjyAuthorize\Service\Authorize */
$canDoThat = $serviceAuthorize->isAllowed(/* vos paramètres */);
## Autres cas
### Comment afficher un item de menu de manière conditionnelle ?
Dans la configuration, il suffit d'affecter une resource à un élément de navigation. L'accès à la ressource va conditionner la visibilité de l'élément.
'navigation' => [
'default' => [
'home' => [
'pages' => [
'of' => [
'label' => 'Offre de formation',
'title' => "Gestion de l'offre de formation",
'route' => 'of',
// exemple en récupérant le ressource d'une action de contrôleur
'resource' => \UnicaenAuth\Guard\PrivilegeController::getResourceId('Application\Controller\OffreFormation', 'index'),
],
],
],
],
],
[roles]: ./roles.png
[privileges]: ./privileges.png
[systeme_de_gestion_des_droits]: ./systeme_de_gestion_des_droits.png
# Aides de vue (view helpers)
## AppConnection
Aide de vue générant le lien et les infos concernant la connexion à l'application.
## UserConnection
Aide de vue générant le code HTML du lien de connexion ou de déconnexion à l'appli selon que l'utilisateur est connecté ou non.
## UserCurrent
Aide de vue générant le code HTML d'affichage de toutes les infos concernant l'utilisateur courant, à savoir :
- “Aucun” + lien de connexion OU BIEN nom de l'utilisateur connecté + lien de déconnexion
- profil de l'utilisateur connecté (ex: standard, gestionnaire, administrateur, etc.)
- infos administratives sur l'utilisateur (affectation, responsabilité)
## UserInfo
Aide de vue générant le code HTML d'affichage des infos administratives sur l'utilisateur (affectations, responsabilités).
## UserProfile
Aide de vue permettant d'afficher le profil de l'utilisateur connecté.
## UserStatus
Aide de vue de rendu des éléments concernant le statut de connexion à l'appli, à savoir :
- si un utilisateur est connecté : l'identité de l'utilisateur connecté et un lien pointant vers la demande de déconnexion
- sinon : un lien pointant vers le formulaire de connexion
# Installation
Cette page traite de l'installation du module UnicaenAuth au sein d'une application ne l'utilisant par encore.
## Module
- Éditez le fichier composer.json se trouvant à la racine de votre projet et assurez-vous que le “repository” suivant est bien présent :
composer.json
"repositories": [
{
"type": "composer",
"url": "http://dev.unicaen.fr/packagist"
}
],
- Ajoutez à présent la dépendance suivante :
composer.json
"require": {
...
"unicaen/unicaen-auth": "dev-master"
},
"minimum-stability": "dev"
- Placez-vous à la racine de votre projet et lancez la commande suivante dans un shell :
$ php ../composer.phar update
La commande ci-dessus fonctionne seulement si le binaire composer.phar se trouve dans le répertoire parent. Plus d'infos : http://getcomposer.org.
- Activez les modules suivants dans cet ordre dans le fichier config/application.config.php de l'application :
'modules' => array(
'Application',
'ZfcBase', 'DoctrineModule', 'DoctrineORMModule', 'ZfcUser', 'ZfcUserDoctrineORM', 'BjyAuthorize',
'UnicaenApp', 'AssetManager',
'UnicaenAuth',
// ...
),
## Base de données
Des tables doivent être créées/initialisées dans une base de données si vous prévoyez d'activer l'une des fonctionnalités suivantes :
- enregistrement / mise à jour de l'utilisateur authentifié dans la table des utilisateurs de l'appli (save_ldap_user_in_database) ;
- création d'un compte utilisateur par l'utilisateur lui-même (enable_registration) ;
- attribuer des rôles aux utilisateurs dans la base de données.
- gestion complète des rôles et privilèges
Voici le script à utiliser :
vendor/unicaen/unicaen-auth/data/schema.sql
Vous pouvez paramétrer l'accès à la base de données : par défaut 'doctrine.entitymanager.orm_default' est utilisé mais vous pouvez en choisir un autre (dans la config, modifier le paramètre unicaen-auth/entity_manager_name).
doc/privileges.png

49.9 KiB

doc/roles.png

39.8 KiB

# Liste des services proposés
Voici la liste des services proposées :
| Nom | Classe | Description |
| ----|--------|-------------|
| UnicaenAuth\Service\Privilege | UnicaenAuth\Service\PrivilegeService | Gère les privilèges (depuis la BDD)
| UnicaenAuth\Service\CategoriePrivilege | UnicaenAuth\Service\CategoriePrivilegeService | Gère les catégories de privilèges (depuis la BDD)
| UnicaenAuth\Service\Role | UnicaenAuth\Service\RoleService | Gère les rôles (depuis la BDD)
| UnicaenAuth\Service\UserContext | UnicaenAuth\Service\UserContext | Fournit un ensemble de méthodes liés à l'utilisateur
\ No newline at end of file
doc/systeme_de_gestion_des_droits.png

76 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment