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 tels que Élève 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
GET /teacher/students
et GET /student/students
Ça ne me semble pas juste.En interne
Comment doit-il être mis en œuvre en interne?
En guise de commentaire secondaire: j'utilise API Web ASP.NET et Entity Framework 6 , mais cela n'a pas vraiment d'importance pour l'implémentation conceptuelle.
Vous devez structurer l'API autour des ressources, pas autour des rôles, par exemple:
/rest/students
devrait être accessible à toute personne dont le rôle 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:
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 le 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 de disposer de 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.
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-une -> 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. C'est une préoccupation transversale qui doit être géré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 diviser vos ressources pour rendre votre application 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 :)