API REST basée sur les rôles?

27

Je construis une API REST pour laquelle plusieurs utilisateurs avec des rôles différents auront accès aux ressources qu'elle contient.

Pour garder la portée simple prenons le domaine "étudiant / enseignant / classe":

GET /students est la ressource à laquelle accéder.

Les utilisateurs peuvent avoir des rôles comme étudiant et / ou enseignant

Les étudiants n'auront accès qu'aux étudiants de leurs classes. Les enseignants auront accès aux étudiants des classes qu'ils enseignent. Certaines utilisations peuvent être étudiantes ET enseigner d'autres classes aussi. Ils doivent avoir accès aux étudiants de leurs classes ET aux étudiants des classes qu'ils enseignent.

Idéalement, je veux implémenter cela comme deux fonctions - une par rôle et ensuite "union" si un utilisateur a plusieurs rôles.

Ma question est: quel modèle dois-je utiliser pour l'implémenter?

Extérieurement

  • Dois-je diviser mon API par rôle? GET /teacher/studentset GET /student/studentscela ne me semble pas juste.
  • Gardez tout ce que je suis une ressource (préféré)

Intérieurement

Comment doit-il être mis en œuvre en interne?

  • Chaque méthode doit-elle commencer par un GRAND commutateur / si par rôle?
  • Dois-je implémenter un référentiel par rôle?
  • Existe-t-il un modèle de conception qui m'aidera à y parvenir?

En guise de commentaire secondaire: j'utilise l' API Web ASP.NET et Entity Framework 6 , mais cela n'a pas vraiment d'importance pour l'implémentation conceptuelle.

Casper Jensen
la source
3
"c'est une excellente question, j'aimerais savoir si vous avez trouvé une solution à cela, car j'essaie de faire quelque chose de similaire. , alors chaque client se connectera non pas directement à l'API mais à un proxy qui sera chargé de filtrer les données selon les rôles de cet utilisateur "
Cleiton

Réponses:

11

Vous devez structurer l'API autour des ressources, pas autour des rôles, par exemple:

/rest/students

devrait être accessible à toute personne ayant un rôle qui lui permet de voir les élèves.

En interne, vous implémentez la sécurité basée sur les rôles. La façon dont vous procédez dépend des détails de votre application, mais supposons que vous ayez une table de rôles, chaque personne a un ou plusieurs rôles, et ces rôles déterminent à quoi chaque personne peut accéder. Vous avez déjà énoncé les règles d'accès aux étudiants:

  • les étudiants peuvent accéder aux étudiants dans les classes qu'ils prennent
  • les enseignants peuvent accéder aux étudiants dans les classes qu'ils enseignent

Donc, quand une personne appelle:

/rest/students

vous appelez une méthode qui accède aux étudiants, en passant dans le rôle de la personne. Voici un pseudo code:

roles = person.roles; //array
students = getStudents( roles );
return students;

et dans cette méthode, vous pouvez obtenir les étudiants pour chaque rôle avec des appels séparés, par exemple:

factory = getFactory();
classes= [];
students = [];
for( role in roles ){
    service = factory.getService( role );
    // implementation details of how you get classes for student/teacher are hidden in the service
    classes = classes.merge( service.getClasses( person ) );
    // classes[] has class.students[]
    // loop on classes and add each student to students, or send back classes with nested students? depends on use case
  }
}

C'est une idée très approximative de ce que vous pourriez faire et ne répondra pas nécessairement à vos besoins spécifiques, mais cela devrait vous donner une idée des éléments impliqués. Si vous souhaitez retourner les cours avec chaque élève répertorié, c'est une bonne approche. Si vous voulez juste les étudiants, vous pouvez les extraire de chaque classe et les fusionner dans une collection d'étudiants.

Non, vous ne devriez pas avoir de référentiel distinct par rôle. Tout ce rôle consiste à déterminer comment vous obtenez les données, et peut-être ce que vous pouvez faire avec les données (par exemple, les enseignants peuvent entrer les notes des élèves). Les données elles-mêmes sont les mêmes.

En ce qui concerne les modèles, cette approche utilise Factory Pattern pour abstraire le service qui obtient les données en fonction du rôle. Il peut être approprié ou non d'avoir des services distincts par rôle. J'aime cette approche car elle minimise la quantité de code à chaque étape du programme et la rend plus lisible qu'un commutateur ou un bloc if.

Robert Munn
la source
1
Merci pour la réponse. J'ai fini par faire quelque chose comme ce que vous avez suggéré. En utilisant LINQ2SQL (C #), je pouvais transmettre la requête à chaque "rôle" et appliquer un où pour chaque rôle obtenu par l'utilisateur. Le résultat serait une instruction sql avec une condition "OU" pour chaque rôle auquel l'utilisateur a accès. Si aucun rôle n'est attribué à un utilisateur, je renvoie simplement Enumarable.Empty () à l'appelant.
Casper Jensen
0

Trouvez un stylo et un papier et commencez à modéliser votre système.

Vous constaterez que vous avez probablement besoin d'une entité de domaine appelée PERSON. Puisque les ÉTUDIANTS et le PROFESSEUR "est-une" PERSONNE, vous pouvez créer une entité abstraite appelée PERSONNE avec des attributs génériques comme prénom, nom de famille, etc. UN PROFESSEUR -> est-a -> Personne. Vous pouvez maintenant essayer de trouver des caractéristiques pour un ENSEIGNANT qui ne s'appliquent pas aux ÉTUDIANTS; Par exemple, UN ENSEIGNANT enseigne des classes concernant un ou plusieurs sujet (s).

L'application de la sécurité est considérée comme un aspect non fonctionnel de votre application. Il s'agit d'une préoccupation transversale qui doit être traitée en dehors de votre «logique métier». Comme le souligne @Robert Munn, le (s) RÔLE (s) doivent tous être conservés au même endroit. L'utilisation de rôles pour limiter l'accès à certaines fonctions est plutôt grossière et le concept est appelé contrôle d'accès basé sur les rôles (RBAC).

Pour vérifier si un enseignant doit être autorisé ou non à voir les notes des élèves, doit être exprimé dans votre modèle de domaine. Disons qu'un enseignant a un cours sur la programmation du sujet. Vous exprimeriez probablement dans votre modèle que les élèves suivent des cours dans différentes matières. C'est là que la logique d'application / métier entre en jeu. C'est une logique que vous pouvez vérifier à l'aide d'un développement piloté par les tests.

Vous devez partager vos ressources pour faire votre demande testable et modulaire.

Quoi qu'il en soit, la meilleure façon de vraiment montrer ce que je veux dire est de le montrer avec du code :) Voici une page GitHub: https://github.com/thomasandersen77/role-based-rest-api

Bonne chance :)

Thomas Andersen
la source
3
votre lien a disparu ...
Cleiton