web-dev-qa-db-fra.com

Affichage de différents contenus dans une seule vue en fonction du rôle de l'utilisateur

Supposons que nous ayons un menu dans mon application SPA angulaire. Maintenant, je veux que les options de base soient affichées pour tous les utilisateurs, tels que la maison, à propos de nous, les opportunités de transporteur, etc.

J'aimerais également disposer de plusieurs autres options, telles que gérer les utilisateurs, gérer les publications, etc., qui ne seront affichées que par un administrateur.

Supposons également que nous ayons un point d'accès API qui me fournit le rôle d'utilisateur ou, mieux encore, que le rôle d'utilisateur se trouve dans l'objet extrait de/api/users/me.

Quel serait le meilleur moyen d'encapsuler ces outils de gestion pour qu'ils ne soient pas vus par les utilisateurs habituels?.

Y a-t-il une sorte d'héritage entre les points de vue? comme dans Django?, existe-t-il un moyen de masquer les éléments DOM à l'utilisateur non autorisé? (oui, je sais que c'est du côté client).

Je préférerais vraiment ne pas utiliser différentes vues pour le menu, car il est supposé être un composant générique.

Je suppose que si la réponse à toutes mes questions précédentes est non, la question qui reste est la suivante: quelle est la meilleure mise en œuvre pour cela? une directive personnalisée ("E" + "A") dire: 

<limitedAccss admin>Edit page</limitedAccess>
 <limitedAccss user>view page</limitedAccess>

ou peut-être simplement en utilisant le ng-show normal avec une condition sur l'objet utilisateur?.

29
Oleg Belousov

La solution est dans ce violon:

http://jsfiddle.net/BmQuY/3/

var app = angular.module('myApp', []);

app.service('authService', function(){

  var user = {};
  user.role = 'guest';
  return{
    getUser: function(){
      return user;
    },
    generateRoleData: function(){
      /* this is resolved before the 
         router loads the view and model.
         It needs to return a promise. */
      /* ... */
    }
  }
});

app.directive('restrict', function(authService){
    return{
        restrict: 'A',
        priority: 100000,
        scope: false,
        compile:  function(element, attr, linker){
            var accessDenied = true;
            var user = authService.getUser();

            var attributes = attr.access.split(" ");
            for(var i in attributes){
                if(user.role == attributes[i]){
                    accessDenied = false;
                }
            }


            if(accessDenied){
                element.children().remove();
                element.remove();           
            }


            return function linkFn() {
                /* Optional */
            }
        }
    }
});

si vous souhaitez utiliser cette directive avec IE 7 ou 8, vous devrez supprimer les enfants de l'élément manuellement, sinon une erreur sera renvoyée:

  angular.forEach(element.children(), function(Elm){
    try{
      Elm.remove();
    }
    catch(ignore){}
  });

Exemple d'utilisation possible:

<div data-restrict access='superuser admin moderator'><a href='#'>Administrative options</a></div>

Test unitaire avec Karma + Jasmine: Attention: la fonction de rappel done n’est disponible que pour Jasmine 2.0. Si vous utilisez la version 1.3, vous devez utiliser waitsFor .

  describe('restrict-remove', function(){
    var scope, compile, html, elem, authService, timeout;
    html = '<span data-restrict data-access="admin recruiter scouter"></span>';
    beforeEach(function(){
      module('myApp.directives');
      module('myApp.services');
      inject(function($compile, $rootScope, $injector){
        authService = $injector.get('authService');
        authService.setRole('guest');
        scope = $rootScope.$new();
        // compile = $compile;
        timeout = $injector.get('$timeout');
        elem = $compile(html)(scope);
        elem.scope().$apply();
      });
    });
    it('should allow basic role-based content discretion', function(done){
        timeout(function(){
          expect(elem).toBeUndefined(); 
          done(); //might need a longer timeout;
        }, 0);
    });
  });
  describe('restrict-keep', function(){
    var scope, compile, html, elem, authService, timeout;
    html = '<span data-restrict data-access="admin recruiter">';
    beforeEach(function(){
      module('myApp.directives');
      module('myApp.services');
      inject(function($compile, $rootScope, $injector){
        authService = $injector.get('authService');
        timeout = $injector.get('$timeout');
        authService.setRole('admin');
        scope = $rootScope.$new();
        elem = $compile(html)(scope);
        elem.scope().$apply();
      });
    });

    it('should allow users with sufficient priviledsges to view role-restricted content', function(done){
      timeout(function(){
        expect(elem).toBeDefined();
        expect(elem.length).toEqual(1);
        done(); //might need a longer timeout;
      }, 0)
    })
  });

Une directive de contrôle d'accès générique pour les éléments, sans utiliser ng-if (uniquement depuis V1.2 - actuellement instable), ou ng-show qui ne supprime pas réellement l'élément du DOM.

38
Oleg Belousov

ng-ifest définitivement ce que je ferais! Il suffit de placer les outils de modération dans la vue à laquelle ils appartiennent et ils apparaîtront si l’utilisateur les avait. ng-show/ng-hide sont également acceptables si vous utilisez une version de angular antérieure à 1.1.5.

Démo en direct! (cliquez ici)

Il est TRES important de s’assurer que votre backend/serveur/api n’acceptera pas une requête simplement parce que votre js a lancé un appel à une action de modérateur !! Toujours demander au serveur de valider son autorisation pour chaque appel.

3
m59

ng-if ou ng-hide sont acceptables, 

le html est juste une vue et ne devrait pas être responsable de la gestion de la sécurité, votre sécurité

Avoir un objet permissions associé à l'utilisateur peut être utile . Quand je récupère les données utilisateur, j'obtiens une permission json puis sur l'angle 

<div ng-if="user.permissions.restrictedArea">Very restricted area</div>

Et l'utilisateur Json ressemble à ceci:

{name:"some one", permissions:{restrictedArea:false,sandbox:true}} //and so on...
0
Jonathan