API REST basada en roles?

27

Estoy creando una API REST para la cual varios usuarios con diferentes roles tendrán acceso a los recursos que contiene.

Para mantener el alcance simple, tomemos el dominio "alumno / profesor / clase":

GET /students es el recurso para acceder.

Los usuarios pueden tener roles como Estudiante y / o Profesor

Los estudiantes solo tendrán acceso a los estudiantes de sus clases. Los maestros tendrán acceso a los estudiantes de las clases que imparten. Algunos usos pueden ser estudiantes Y enseñar otras clases también. Deben tener acceso a los alumnos de sus clases Y a los alumnos de las clases que imparten.

Idealmente, quiero implementar esto como dos funciones: una por rol y luego "unión" si un usuario tiene múltiples roles.

Mi pregunta es: ¿Qué patrón debo usar para implementar esto?

Externamente

  • ¿Debería dividir mi API por rol? GET /teacher/studentsy GET /student/studentsno me parece bien
  • Mantenlo todo, soy un recurso (preferido)

Internamente

¿Cómo debe implementarse internamente?

  • ¿Debería comenzar cada método con un interruptor GRANDE / si es por función?
  • ¿Debo implementar un repositorio por rol?
  • ¿Existe un patrón de diseño que me ayudará a lograr esto?

Como comentario adicional: estoy usando ASP.NET Web API y Entity Framework 6 , pero realmente no importa para la implementación conceptual.

Casper Jensen
fuente
3
"Esta es una gran pregunta, me gustaría saber si encontraste una solución para eso, porque estoy tratando de hacer algo similar. Lo que creo que debería ser: Primero, implementaremos una API que devuelva todos los datos necesarios , cada cliente se conectará no directamente a la API, sino a un proxy que será responsable de filtrar los datos según los roles de ese usuario "
Cleiton

Respuestas:

11

Debe diseñar la API en torno a los recursos, no en torno a los roles, por ejemplo:

/rest/students

debe ser accesible para cualquier persona con un rol que les permita ver a los estudiantes.

Internamente, está implementando seguridad basada en roles. La forma en que lo haga depende de los detalles de su aplicación, pero supongamos que tiene una tabla de roles, cada persona tiene uno o más roles y esos roles determinan a qué puede acceder cada persona. Ya ha indicado las reglas para acceder a los estudiantes:

  • los estudiantes pueden acceder a los estudiantes en las clases que toman
  • los maestros pueden acceder a los estudiantes en las clases que imparten

Entonces, cuando una persona llama:

/rest/students

llama a un método que accede a los estudiantes, pasando el papel de la persona. Aquí hay un pseudocódigo:

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

y en ese método, podría obtener los estudiantes para cada rol con llamadas separadas, por ejemplo:

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
  }
}

Esa es una idea muy aproximada de lo que podría hacer y no necesariamente se ajustará a sus necesidades específicas, pero debería darle una idea de las piezas involucradas. Si desea regresar las clases con cada estudiante en la lista, este es un buen enfoque. Si solo quiere los estudiantes, puede extraerlos de cada clase y fusionarlos en una colección de estudiantes.

No, no debe tener un repositorio separado por rol. Todo lo que hace el papel es determinar cómo obtiene los datos, y tal vez qué puede hacer con los datos (por ejemplo, los maestros pueden ingresar las calificaciones de los estudiantes). Los datos en sí son los mismos.

En cuanto a los patrones, este enfoque está utilizando Factory Pattern para abstraer el servicio que obtiene datos en función del rol. Puede o no ser apropiado tener servicios separados por rol. Me gusta este enfoque porque minimiza la cantidad de código en cada etapa del programa y lo hace más legible que un interruptor o un bloque.

Robert Munn
fuente
1
Gracias por la respuesta. Terminé haciendo algo como lo que sugeriste. Al usar LINQ2SQL (C #) podría pasar la consulta a cada "rol" y aplicar un lugar para cada rol que obtuvo el usuario. El resultado sería una instrucción sql con una condición "OR" para cada rol al que el usuario tenga acceso. Si no se asignan roles a un usuario, simplemente devuelvo Enumarable.Empty () la persona que llama.
Casper Jensen
0

Encuentre un bolígrafo y un papel y comience a modelar su sistema.

Descubrirá que probablemente necesita una entidad de dominio llamada PERSON. Dado que tanto los ESTUDIANTES como el PROFESOR "es-una" PERSONA, puede crear una entidad abstracta llamada PERSONA con atributos genéricos como nombre, apellido, etc. UN PROFESOR -> es-una -> Persona. Ahora puede intentar encontrar características para un PROFESOR que no se apliquen a ESTUDIANTES; Por ejemplo, un MAESTRO enseña CLASE (s) con respecto a uno o más ASUNTO (s).

Hacer cumplir la seguridad se considera un aspecto no funcional de su aplicación. Es una preocupación transversal que debe manejarse fuera de su "lógica de negocios". Como señala @Robert Munn, todos los ROLES deben mantenerse en un solo lugar. El uso de roles para limitar el acceso a ciertas funciones es bastante tosco, y el concepto se llama control de acceso basado en roles (RBAC).

Para verificar si un maestro debe o no poder ver las calificaciones de un estudiante, debe expresarse en su modelo de dominio. Digamos que un maestro tiene una clase sobre la programación de materias Probablemente exprese en su modelo que los estudiantes asisten a clases para diferentes materias. Aquí es donde entra en juego la lógica de la aplicación / negocio. Esta es la lógica que puede verificar utilizando un desarrollo basado en pruebas.

Usted debe dividir sus recursos para hacer su aplicación comprobable y modular.

De todos modos, la mejor manera de mostrar realmente lo que quiero decir es mostrarlo con código :) Aquí hay una página de GitHub: https://github.com/thomasandersen77/role-based-rest-api

Buena suerte :)

Thomas Andersen
fuente
3
su enlace se ha ido ...
Cleiton