IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

comparaison de roles sur une liste de user [2.x]


Sujet :

Symfony PHP

  1. #1
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut comparaison de roles sur une liste de user
    Bonjour,

    Sur un site en symfony, j'utilise le bundle fosuser pour gérer mes utilisateur dans l'administration de mon site.

    Dans mon administration j'ai une route qui me permet de consulter la liste des utilisateurs sous forme de tableau.
    Sur chaque ligne de mon tableau j'ai un bouton éditer qui me permet d'éditer le profile de mon utilisateur.
    Voici un morceau de la vue
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
     
    				  <table class="table table-striped" cellspacing="0" cellpadding="0">
    		          	<thead>
    		                <tr>
    		                  <th>Login</th>
    		                  <th align="center">Droits</th>
    		                  <th align="left">Prénom et nom</th>
    		                  <th align="center">Email</th>
    		                  <th align="center">Actif</th>
    		                  <th align="center">Editer</th>
    		                </tr>
    		            </thead>
     
                		{% for user in users %}
                		<tr>
                			<td>{{ user.username }}</td>
                			<td>
                				<ul>
                				{% for role in user.roles %}
                					<li>{{ role }}</li>	
                				{% endfor %}
                				</ul>
                			</td>
                			<td>// prenom et nom</td>
                			<td>{{ user.email }}</td>
                			{% if is_granted(user.roles) %}
                			<td>
    	            			{% if user.enabled %}
    	            			<a class="btn btn-default btn-sm" href="#"><span class="glyphicon glyphicon-remove-sign"></span></a>
    	            			{% endif %}
                			</td>
                			<td>
                				<a class="btn btn-default btn-sm" href="" title="Editer"><span class="glyphicon glyphicon-pencil"></span></a>
                			</td>
                			{% endif %}
                		</tr>		
                		{% endfor %}
                	  </table>
    Depuis le contrôleur j'envoie l'objet users qui comporte l'ensemble de mes utilisateurs.

    Et en fait je voudrai que lorsqu'un utilisateur se connecte il ne puisse pas editer un utilisateur qui a un role supérieur aux siens.
    dans le security.xml j'ai 4 rôle :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    security:
        role_hierarchy:
     
            ROLE_REDACTEUR :  ROLE_USER
            ROLE_ADMIN:       ROLE_REDACTEUR
            ROLE_SUPER_ADMIN: ROLE_ADMIN
    Le problème c'est que lorsqu' on ajoute un utilisateur, il bénéficie automatiquement du role 'ROLE_USER'
    Par exemple :
    L'user1 a les rôles 'ROLE_SUPER_ADMIN' et 'ROLE_USER'
    L'user2 a les rôles 'ROLE_ADMIN' et 'ROLE_USER'

    Du coup l'user 2peut editer une fiche de l'user1 a cause de 'role_user' alors qu'il ne devrait pas.

    J'espère que vous pourrez m'aider a résoudre ce problème.
    merci

  2. #2
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    Slt,

    Normalement dans ton cas on utilise les Voter(s) tu trouveras un exemple d'implementation de base dans la doc en ligne de FOSUserBundle (https://github.com/FriendsOfSymfony/...suserbundle.md) et d'autres exemples en ligne plus parlant (http://kriswallsmith.net/post/159949...ecurity-voters - http://yoann.aparici.fr/post/les-voter-dans-symfony2).

    Je sais qu'il y'a des méthodes de barbares pour faire la même chose, mais je ne préfère pas en parler ....

  3. #3
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Oui ... et puis en même temps cette ligne elle ne veut pas dire grand chose.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {% if is_granted(user.roles) %}

  4. #4
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut
    Merci pour vos réponse.
    Concernant les voter j'ai regardé un peu je ne sais pas si ça pourra m'aider.
    Avec les voter on peut permettre à un utilisateur de pouvoir editer seulment ses fiches mais je ne sais pas si on peut permettre d'éditer les fiches d'un utilisateur qui a un role inférieur.

    Mois dans mon cas un super_admin peut tout editer.
    un admin peut editer les admin et les modérateur
    un modérateur peut éditer les modérateur et les simples utilisateurs.
    les simples utilisateurs peuvent éditer que leur fiche.

    En ce qui concerne la ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {% if is_granted(user.roles) %}
    en fait cette ligne est dans une boucle de user.
    Donc pour chaque user, je verifiait si l'utilisateur connectée pouvait editer.

    ex :user.roles = ROLE_ADMIN
    si l'utilisateur connecté a le role de role_moderateur, il pourrai pas éditer cette fiche.

    et en fait le problème c'est que tout le mode a le role 'role_user' du coup cette conditionnel tombe à l'eau.

    user.role est toujours égale à [ROLE_ADMIN,ROLE_USER] par exemple.

  5. #5
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    {% if is_granted(user.roles) %}
    La fonction is_granted attend une chaine de caractère : toi tu lui passes user.roles qui selon toute logique va te retourner un tableau de rôles, pas une chaine de caractères. Première erreur.
    Si un ROLE_USER ne peut éditer que sa fiche pourquoi ne pas présenter la condition telle quelle.


    Très approximativement ... ça donnerait ça
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    {% if is_granted('ROLE_SUPER_ADMIN') %}
      {# EDIT EVERYONE #}
    {% elseif is_granted('ROLE_ADMIN') and user.roles == {'ROLE_USER'} %}
      {# EDIT 'ROLE_USER' only users #}
    {% else %}
      {# EDIT himself only #}

  6. #6
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut
    Je ne sais pas si il attend obligatoirement une chaine, en tous cas ça ne me renvoie pas d'erreur.
    Sinon pour la conditionnel ça ne vas pas marcher tout le monde a le role_user.
    C'est un comportement par defaut renvoyée par symfony.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    {# EDIT 'ROLE_USER' only users #}
    Dans cette ligne justement il faudrait tous les utilisateurs qui ont le role admin,moderateur et user et pas les super_admin

  7. #7
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Oui justement si tout le monde a le rôle ROLE_USER, la deuxième condition vérifie que c'est le seul rôle sur le user possède (un tableau avec uniquement ce rôle) et le else signifie que ce n'est ni un SUPER ADMIN, NI UN ADMIN, donc à priori il n'a que le ROLE_USER.

    Je t'ai fait le if elseif approximativement, je ne connais pas tous tes rôles, mais ça respecte globalement ce que tu as dit. Si tu ne peux pas te baser sur le ROLE_USER qui est possédé par tous, alors par élimination, si un user n'a ni le rôle superadmin, ni le rôle admin, alors c'est un user basique.

  8. #8
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut
    J'ai trouvé une solution qui satisfait une partie de mes besoins, je ne sais pas si c'est propre de faire comme ceci, peut-être qu'il faut que je crée un helper.voici mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
                			{% set pass = true %}
                			{%for role in user.roles %}
                				{% if not is_granted(role) %}
                					{% set pass = false %} 
                				{% endif %}	
                			{% endfor %}
     
                			{% if pass==true %}
                                       //bouton edit user
                                    {%end if %}
    Qu-en pensez-vous?

  9. #9
    Expert éminent
    Avatar de pmithrandir
    Homme Profil pro
    Responsable d'équipe développement
    Inscrit en
    Mai 2004
    Messages
    2 418
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Responsable d'équipe développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 418
    Points : 7 295
    Points
    7 295
    Par défaut
    Je pense que ton problème vient du fait que role admin hérite de ROLE_USER.

    En gros, un admin est un user. Il a d'autres fonctions en plus, mais il reste user quand même.

    La solution fournie au dessus me semble la plus indiquée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    {% if is_granted('ROLE_SUPER_ADMIN') %}
      {# EDIT EVERYONE #}
    {% elseif is_granted('ROLE_ADMIN') and user.roles contains({'ROLE_USER'}) %}
      {# EDIT 'ROLE_USER' only users #}
    {% else %}
      {# EDIT himself only #}
    Sachant que tu as 3 roles, ce n'est pas complexe de lister toutes les possibilité comme ci dessus... et même peut être plus clair.

    Après, si tu as plus de roles, j'ajouterai un getter dans user qui me retourne le role level MAX d'un utilisateur. (dans ton get tu recupère les roles, et tu selectionne celui que tu considère le plus haut).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    class User{
     ROLE_SUPER_ADMIN = 1000
     ROLE_ADMIN = 100
     ROLE_USER = 10
    ...
     
     
    getMaxRoleLevel(){
      If ($this.roles CONTAINS ROLE_SUPER_ADMIN){
        return ROLE_SUPER_ADMIN;
      }
      else if ($this.roles CONTAINS ROLE_ADMIN){
        return ROLE_ADMIN;
      }
      else {
        return ROLE_USER;
      }
    }
    Tu pourras alors les comparer assez facilement.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    if($user1->getMaxRoleLevel()>$user2->getMaxRoleLevel(){
      //edit
    }

  10. #10
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    user.roles contains({'ROLE_USER'})
    Le problème de cette ligne c'est qu'un admin peut éditer un user possédant un rôle supérieur (super admin) au sien puisque tous les users possèdent le ROLE_USER.
    Et c'est logique si on tient compte de la hiérarchie des rôles proposée par Symfony. Un user, quel que soit son vrai rôle (au sens métier) est entre autre un user et possède ses droit de USER.

    C'est pour ça que ma solution vérifiait l'exclusivité de ce rôle en comparant le tableau de rôles tout entier en vérifiant qu'il ne possédait que celui-là.

    Par contre là ou je suis d'accord avec pmithrandir, c'est que pour simplifier le bloc de conditions dans ta template, tu peux rajouter une méthode (que ce soit au niveau de twig ou au niveau de ton objet user) te retournant uniquement son maxRoleLevel.

    La solution que tu as choisi n'est pas géniale puisqu'elle t'oblige à boucler sur tous les rôles (ce que fais la fonction contains) et si tu affectes ta variable dans la boucle, celle-ci te renverra systématiquement le résultat du dernier rôle (qui risque d'être le ROLE_USER car on ne connait l'ordre dans lequel c'est renvoyé).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    {%for role in user.roles %}
        {% if not is_granted(role) %}
            {% set pass = false %} 
        {% endif %}	
    {% endfor %}
    Imagine qu'un user ROLE_USER soit connecté et que les rôles du user sur lesquels tu boucles soient ROLE_ADMIN et le ROLE_USER.
    Premier passage dans la boucle : ROLE_ADMIN => not granted, pass = false
    Deuxième passage dans la boucle : ROLE_USER => granted, pass = true
    Sortie de la boucle : pass vaut true.

    Pour résoudre ta problématique, soit tu t'assures que seul le ROLE_USER est présent dans la collection de rôle (ma solution) soit tu vérifie le rôle max (solution de pmithrandir).

    ++

  11. #11
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut
    Le problème c'est qu'avec le bout code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    {% if is_granted('ROLE_SUPER_ADMIN') %}
      {# EDIT EVERYONE #}
    {% elseif is_granted('ROLE_ADMIN') and user.roles contains({'ROLE_USER'}) %}
      {# EDIT 'ROLE_USER' only users #}
    {% else %}
      {# EDIT himself only #}
    Si un jour je rajoute un role, je suis obligé d'aller modifier tout les morceau de code qui comporte ces conditionnels.du coup il faut quelque chose d'un peu plus automatisée pour réduire le risque de bug.

    Pour mon code avec la boucle vous avez du mal saisir le fonctionnement :

    puisque si par exemple je suis connectée avec l'user toto qui comporte les roles :'ROLE_ADMIN' et 'ROLE_USER'

    algo :
    variable pass à true => ce qui veut dire pour l'instant je peut editer
    ma boucle de tous les users commence :
    première ligne de ma boucle : 'user3' comporte 'ROLE_SUPER_ADMIN' et 'ROLE_USER'
    ensuite je boucle sur les roles de user3
    if is_granted('ROLE_USER') la boucle continue et la var pass reste à true
    deuxième tour if is_granted('ROLE_SUPER_ADMIN') la var passe devient false
    la boucle s'arrete
    donc pour l'user3 je ne peu pas editer

    en fait ce bout de code permet de dire que tous les roles doit etre satisfait.si il y en a au moins un qui es pas bon ça passe pas.

    Par contre en ce qui concerne le code de pmithrandir pour créer une fonction pour obtenir le niveau le plus haut et pour comparer c'est une bonne idée.

  12. #12
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Au temps pour moi, j'ai lu ton algo trop rapidement, il peut affecter false à la variable pass mais pas repasser à true. Du coup l'ordre des rôles n'a en effet plus d'importance et le fait de ne pas sortir de la boucle après le passage de pass à false n'impacte pas la suite. Rajoute peut-être une sortie de boucle après le set pass=false. Ca t'évitera de boucler pour rien par la suite.

    C'est une solution comme une autre : pas un must d'élégance mais si ça fonctionne et que ça te convient...
    C'est pas pire que de devoir modifier le morceau de code concerné lorsque tu rajoutes un rôle.

  13. #13
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Septembre 2014
    Messages
    20
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 20
    Points : 16
    Points
    16
    Par défaut
    Pour l'instant je vais laisser comme ça mais je vais surement par la suite overloader ma classe user ou alors crée un helper.

    En tous cas merçi d'avoir pris du temps pour m'aider.ça m'a permit de trouver des solutions.

    ++

  14. #14
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    Alors bande de barbares


    Je pense vraiment que le Voter est adapté au problème.

    Alors nous avons une liste d'utilisateurs et un lien permettant d'éditer chacun des utilisateurs si c'est lui ou si l'utilisateur a moins de droits que lui.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    {% extends '::base.html.twig' %}
    {% block body %}
    {% for user in users %}
        <div>
            {{ user.username }} {% if is_granted('USER_EDIT', user) %} <a href="{{ path('user_edit',{id: loop.index0 } ) }}">éditer</a>{% endif %}
        </div>
    {% endfor %}
    {% endblock %}
    le Voter qui va avec :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     
    <?php
     
    namespace App\DemoBundle\Authorization\Voter;
     
     
    use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
     
    class UserEditVoter implements VoterInterface
    {
        /**
         * @var array
         */
        protected $rolesHierarchy;
     
        public function __construct($roles)
        {
            $this->rolesHierarchy = $roles;
        }
     
        public function supportsAttribute($attribute)
        {
            return 'USER_EDIT' === $attribute;
        }
     
        public function supportsClass($class)
        {
            return $class instanceof \Symfony\Component\Security\Core\User\User;
        }
     
        protected function getChildRoles($role)
        {
            $userRoles = array();
            if (isset($this->rolesHierarchy[$role])) {
                foreach ($this->rolesHierarchy[$role] as $rolesHierarchy) {
                    $userRoles[] = $rolesHierarchy;
                    $userRoles = array_merge($userRoles, $this->getChildRoles($rolesHierarchy));
                }
            }
            return $userRoles;
        }
     
        public function vote(TokenInterface $token, $object, array $attributes)
        {
            foreach ($attributes as $attribute) {
                if ($this->supportsAttribute($attribute) && $this->supportsClass($object)) {
                    $user = $token->getUser();
     
                    if($user->getUsername() === $object->getUsername()){
                        return VoterInterface::ACCESS_GRANTED;
                    }
     
     
                    if (null !== $user) {
     
                        $roles = $token->getRoles();
     
                        $userRoles = array();
     
                        foreach ($roles as $role) {
                            $userRoles = array_merge($userRoles, $this->getChildRoles($role->getRole()));
                        }
     
                        foreach ($userRoles as $role) {
                            if (in_array($role, $object->getRoles())) {
                                return VoterInterface::ACCESS_GRANTED;
                            }
                        }
                    }
                }
            }
     
            return VoterInterface::ACCESS_DENIED;
        }
    }

    Il faut l'enregistrer dans la configuration du bundle (ou de l'application) :

    Resources/config/services.xml
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    <?xml version="1.0" ?>
     
    <container xmlns="http://symfony.com/schema/dic/services"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
     
        <services>
            <service id="demo.security.access.user_edit" class="App\DemoBundle\Authorization\Voter\UserEditVoter">
                <argument>%security.role_hierarchy.roles%</argument>
                <tag name="security.voter" />
            </service>
        </services>
    </container>

    J'ai rajouté un petit controller histoire de tester si l'utilisateur peut éditer une entité User avec if (!$this->get('security.context')->isGranted('USER_EDIT', $user)) {}

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
     
    <?php
     
    namespace App\DemoBundle\Controller;
     
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
    use Symfony\Component\HttpFoundation\Request;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Security\Core\Exception\AccessDeniedException;
    use Symfony\Component\Security\Core\SecurityContext;
     
    class SecurityController extends Controller
    {
        /**
         * Afficher les utilisateurs
         * @Route("/", name="user_list")
         * @Template()
         */
        public function indexAction()
        {
            $users = $this->getUsers();
     
            return array('users' => $users);
        }
     
        /**
         * Editer un utilisateur
         *
         * @param $id
         *
         * @return Response
         * @throws AccessDeniedException
         * @Route("/user/edit/{id}", name="user_edit")
         */
        public function editAction($id)
        {
            $users = $this->getUsers();
     
            if (isset($users[$id])) {
                $user = $users[$id];
            }
     
     
            if (!$this->get('security.context')->isGranted('USER_EDIT', $user)) {
                throw new AccessDeniedException();
            }
     
            return new Response('edit user : ' . $user->getUsername());
        }
     
        /**
         * Identifier un utilisateur
         *
         * @param Request $request
         *
         * @return \Symfony\Component\HttpFoundation\Response
         */
        public function loginAction(Request $request)
        {
            $session = $request->getSession();
            // get the login error if there is one
            if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
                $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
            } else {
                $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
                $session->remove(SecurityContext::AUTHENTICATION_ERROR);
            }
     
            return $this->render(
                'AppDemoBundle:Security:login.html.twig',
                array(
                    // last username entered by the user
                    'last_username' => $session->get(SecurityContext::LAST_USERNAME),
                    'error' => $error,
                )
            );
        }
     
        /**
         * Recuperer la liste des utilisateurs
         *
         * @return array
         */
        protected function getUsers()
        {
            $users = array();
            $users[] = new \Symfony\Component\Security\Core\User\User('admin', '', array('ROLE_ADMIN'));
            $users[] = new \Symfony\Component\Security\Core\User\User('superadmin', '', array('ROLE_SUPER_ADMIN'));
            $users[] = new \Symfony\Component\Security\Core\User\User('user', '', array('ROLE_REDACTEUR'));
            $users[] = new \Symfony\Component\Security\Core\User\User('userB', '', array('ROLE_REDACTEUR'));
     
            return $users;
        }
    }
    le routing pour le test :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    app_demo:
        resource: "@AppDemoBundle/Controller/"
        type:     annotation
        prefix:   /
     
    app_login:
        pattern:   /login
        defaults:  { _controller: AppDemoBundle:Security:login }
     
    app_login_check:
        pattern:   /login_check
     
    app_logout:
        pattern:   /logout
    la sécurité :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
     
    security:
        encoders:
            Symfony\Component\Security\Core\User\User:
                algorithm: plaintext
     
        role_hierarchy:
            ROLE_REDACTEUR :  ROLE_USER
            ROLE_ADMIN:       ROLE_REDACTEUR
            ROLE_SUPER_ADMIN: ROLE_ADMIN
     
        providers:
            administrators:
                memory:
                  users:
                    superadmin:
                      password: 12345
                      roles: [ ROLE_SUPER_ADMIN ]
                    admin:
                      password: 12345
                      roles: [ ROLE_ADMIN ]
                    user:
                      password: 12345
                      roles: [ ROLE_REDACTEUR ]
        firewalls:
            dev:
                pattern: ^/(_(profiler|wdt)|css|images|js)/
                security: false
     
            login:
                pattern: ^/login$
                security: false
     
            admin_area:
                pattern:    ^/
                form_login:
                    login_path:  /login
                    check_path:  /login_check
                logout:
                    path:   /logout
                    target: /
     
        access_control:
    Si qqn a besoin de plus d'info qu'il n'hésite pas

  15. #15
    Expert éminent
    Avatar de pmithrandir
    Homme Profil pro
    Responsable d'équipe développement
    Inscrit en
    Mai 2004
    Messages
    2 418
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Responsable d'équipe développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 418
    Points : 7 295
    Points
    7 295
    Par défaut
    @goabonga... je ne comprend pas dans ton code comment tu empêches l'ADMIN d'editer le rôle SUPER_ADMIN, mais en l'autorisant a éditer le role USER.

    Sachant que sur une même page, on peut imaginer une liste d'utilisateurs ayant différents roles, et donc de multiples combinaisons.(avec un nombre potentiellement infini de roles dans la hierarchie).

    Pour améliorer mon système, je rajouterai en fait une colonne dans mon objet user en BDD. maxRoleLevel.

    A l'aide d'un postpersist sur la colonne rôle, je forcerai l’exécution de la méthode définissant ce level max.
    Comme cela, on aurait qu'une seule exécution de la méthode getMaxLevel par changement de roles dans la BDD(ce qui revient a pas souvent) au lieux de le faire pour chaque user à chaque page.

    C'est de la dénormalisation, mais symfony a montré la voie avec leur système merdique de rôle encodés en json et pas uniques.(ils auraient pu tester à l'enregistrement si le role n'existait pas déjà, pour ne pas avoir ROLE_ADMIn, ROLE _USER si ROLE ADMIN inclue ROLE_USER... mais ca aurait peut être trop couplé la config et les données).

  16. #16
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    Citation Envoyé par pmithrandir Voir le message
    @goabonga... je ne comprend pas dans ton code comment tu empêches l'ADMIN d'editer le rôle SUPER_ADMIN, mais en l'autorisant a éditer le role USER.
    Ben je check les rôles principaux et je récupère les roles enfant pour avoir une liste complete des roles pour l'utilisateur. ( ce que tu ne peux pas faire

    Sachant que sur une même page, on peut imaginer une liste d'utilisateurs ayant différents roles, et donc de multiples combinaisons.(avec un nombre potentiellement infini de roles dans la hierarchie).
    Aucun problème puisque l'user super_admin et parent de admin qui est parent de user ....

    Pour améliorer mon système, je rajouterai en fait une colonne dans mon objet user en BDD. maxRoleLevel.

    A l'aide d'un postpersist sur la colonne rôle, je forcerai l’exécution de la méthode définissant ce level max.
    Comme cela, on aurait qu'une seule exécution de la méthode getMaxLevel par changement de roles dans la BDD(ce qui revient a pas souvent) au lieux de le faire pour chaque user à chaque page.
    C'est quoi cette histoire ?
    Pour la demo j'ai juste utilisé des simple \Symfony\Component\Security\Core\User\User de base de base ....
    Pourquoi prendre le risque de te retrouver avec un comportement qui ne fonctionnera pas avec un un autre provider ?


    C'est de la dénormalisation, mais symfony a montré la voie avec leur système merdique de rôle encodés en json et pas uniques.(ils auraient pu tester à l'enregistrement si le role n'existait pas déjà, pour ne pas avoir ROLE_ADMIn, ROLE _USER si ROLE ADMIN inclue ROLE_USER... mais ca aurait peut être trop couplé la config et les données).
    Je vois vraiment pas de quoi tu parles ? JSON ? ....

  17. #17
    Expert éminent
    Avatar de pmithrandir
    Homme Profil pro
    Responsable d'équipe développement
    Inscrit en
    Mai 2004
    Messages
    2 418
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Responsable d'équipe développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 418
    Points : 7 295
    Points
    7 295
    Par défaut
    @goabonga
    Je ne comprends toujours pas comment tu fais pour comparer les roles. a quel moment empêches-tu un admin d’éditer un super admin.

    Pour mon ajout de table, je pense que ca doit rester compatible avec l'user der base.
    Mais bon, c'était plus une optimisation de requête en base de données pour éviter de recharger les rôles de chaque utilisateur à chaque interrogation.

    Si je reprends l'exemple de base, avec un utilisateur ayant un role C, regardant une liste d'utilisateurs ayant chacun des roles définis.
    Par exemple :
    avec A>B>C, ...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    user1 ROLE_A 
    user2 ROLE_B 
    user3 ROLE_C Bouton EDITER
    user4 ROLE_D Bouton EDITER
    user5 ROLE_E Bouton EDITER
    user6 ROLE_F Bouton EDITER
    Dans mon premier système, je dois calculer pour chaque utilisateur que est son role max.
    Dans le second, ce sole est déjà calculé, donc j'ai l'info directement.
    Si j'affiche la liste ci dessus 10 fois avec le premier système, je devrais effectuer pour 6 utilisateurs (6 appels a la colonne role + 6 execution de mon algo ) * 10 itérations)
    Dans le second système, je fais 1 execution de mon algo par user, que ca soit 0 itération ou 200 J'y gagne assez vite.



    Pour la dé normalisation, je parle bien des rôles encodés en JSON. Mettre du JSON dans une BDD relationnelle, c'est une hérésie, et surement pas une BDD normalisée. Normalement, il aurait fallut une table role_type, et une table d'association user_role_type pour faire la relation n-n

  18. #18
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    168
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 168
    Points : 219
    Points
    219
    Par défaut
    C'est le but du voter de dire si l'utilisateur a la possibilité ou pas d'editer un autre utilisateur
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
                            if (in_array($role, $object->getRoles())) {
                                return VoterInterface::ACCESS_GRANTED;
                            }
    Comme je te disais je check le role parent de l'utilisateur et je récupère ces roles enfants :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    role_hierarchy:
            ROLE_REDACTEUR :  ROLE_USER
            ROLE_ADMIN:       ROLE_REDACTEUR
            ROLE_SUPER_ADMIN: ROLE_ADMIN

    Un SuperAdmin a pour enfant Admin qui a pour enfant Redacteur ..... mais un Admin n'est pas et n'a pas d'enfant SuperAdmin alors il ne peut pas l'éditer ....

    Ce Voter a le meme metier que le rolehierarchyvoter avec une verification du Owner en plus ( c'est une demo pour bien montrer que vous prenez une mauvaise route et des risque en essayant de faire des if if if ) ....


    Ton JSON m'intrigue (grave) comment as tu mis ton système en place, postes moi ton code, (les entités et si t'as un provider maison poste le avec) ....

  19. #19
    Expert éminent
    Avatar de pmithrandir
    Homme Profil pro
    Responsable d'équipe développement
    Inscrit en
    Mai 2004
    Messages
    2 418
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Responsable d'équipe développement
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2004
    Messages : 2 418
    Points : 7 295
    Points
    7 295
    Par défaut
    Merci, je pense avoir compris ta méthode.

    Pour le JSON, je parle juste de la colonne role dans la table user. Avoir plusieurs valeurs dans une même cellule, c'est de la dé normalisation. C'est tout ce que je voulais dire.

    De manière plus générale, je pense que ton système poserai également un problème dans le bas ou l'on a des branches de permission qui se mettrait en place avec des roles de même niveaux mais incompatible.

    Mais on sort du contexte de la question.

    merci pour l'echange en tout cas.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] Comparaison sur une liste contenu dans une classe d'objet
    Par jpbelmondo59 dans le forum C#
    Réponses: 8
    Dernier message: 02/09/2014, 11h45
  2. simuler un double click sur une liste dans un dbgrid
    Par bertrand_declerck dans le forum Bases de données
    Réponses: 1
    Dernier message: 01/09/2005, 10h45
  3. Réponses: 4
    Dernier message: 16/06/2005, 15h37
  4. Selectionnet tous ou faire un clear sur une liste
    Par Canou dans le forum Général JavaScript
    Réponses: 2
    Dernier message: 17/11/2004, 10h26
  5. [Débutant][jsp] évènement sur une liste
    Par phoebe dans le forum Servlets/JSP
    Réponses: 5
    Dernier message: 14/05/2004, 10h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo