Prácticas estándar para el control de acceso (patrón de diseño)

9

Estoy mirando el diseño de mi interfaz y estoy luchando para decidir cuál es la forma más "correcta" de implementar el control de acceso basado en roles, dado a usery a los subjectque userles gustaría acceder.


Por lo que puedo ver, tengo tres opciones principales (una cuarta es una bastardicación de las tres primeras y una quinta es una modificación de la cuarta):

  1. Consulta subjectcon una lista de permisos que usertiene:subject.allowAccess(user.getPermissionSet)
  2. Consulte usercon una lista de permisos que subjectrequiere:user.hasPermissionTo(subject.getRequiredPermissions())
  3. Consultar a un tercero para ubicar las intersecciones de permisos: accessController.doPermissionSetsIntersect(subject.permissionSet, user.getPermissionSet())
  4. Consulte el subject/ user, mientras delega la "decisión" a una clase de terceros
  5. Intente useracceder subjecty arroje un error si no se permite el acceso

Me estoy inclinando hacia la opción cuatro: que subjectcontenga un accessControllercampo, donde las llamadas para subject.userMayAccess(User user)delegar la operación a la:

class Subject {
    public function display(user) {
        if(!accessController.doPermissionSetsIntersect(this.permissionSet, user.getPermissionSet())) {
            display403(); //Or other.. eg, throw an error..
        }
    }
}

.. pero esto plantea más preguntas:

  • ¿Debería accessControllerser un campo frente a una clase estática?
  • ¿Debería subject saber qué permisos se requieren para poder verlo?
  • ¿Dónde entra en juego aquí el principio de menor conocimiento, con respecto al llamado subject.display()? ¿Deben las personas que llaman subject.display()saber que el control de acceso está vigente? (donde subject.display()es un "método de plantilla" final)
  • ¿ha subject.display()administrado el control de acceso, lanzando una excepción donde el usuario no tiene el permiso requerido?

¿Qué se consideraría "mejor práctica" en esta situación? ¿Dónde debería ocurrir la responsabilidad de realizar las verificaciones?

Como esto es algo tanto un ejercicio académico que luego progresará en la implementación, se agradecerán las referencias a los patrones de diseño.

kwah
fuente

Respuestas:

7

La mejor práctica es usar algo conocido como el patrón Interceptor para interceptar llamadas a áreas protegidas.

Esto se puede lograr mediante el uso de AOP o preocupaciones transversales aplicadas a sus puntos de entrada de acceso.

El sujeto nunca debe saber quién puede verlo. Esto complica el código de tema innecesariamente y no hay ninguna razón para que se requiera, a menos que, sin darse cuenta, proporcione un mecanismo de acceso directo a la misma función.

Preferiblemente, la persona que llama y la persona que llama no deben saber sobre el acceso, aparte de manejar los rechazos. Sin embargo, el problema dependerá del sistema que esté implementando y de cómo obtenga acceso a las credenciales / principal de seguridad para la persona que llama. Por ejemplo, en los sistemas SOAP, esta información se agrega al encabezado de un mensaje SOAP, mientras que en un sistema Windows estaría disponible a través del mecanismo de autenticación de Windows.

Si utiliza el enfoque de patrón de interceptor o AOP, arrojará las excepciones necesarias, y dependerá del cliente (llamante) manejar las excepciones lanzadas.

De esta manera, mantiene su cliente, servicio y código de autenticación separados sin mezclar conocimiento o funcionalidad.

sweetfa
fuente
2

Creo que su opción 3 es la más cercana, pero en lugar de interrogar sobre usery subjectsobre sus conjuntos de permisos, debe pasar el usery subjectal controlador de acceso.

class Subject {
    public function display(user) {
        if(!accessController.checkAccess(this, user, AccessControl.Read)) {
            display403(); //Or other.. eg, throw an error..
        }
    }
}

El controlador de acceso debe ser responsable tanto de obtener los conjuntos de permisos como de verificar si el acceso es suficiente. De esa manera, aísla tanto la lógica de almacenamiento como la lógica de verificación en su controlador de acceso, separadas del usuario y del sujeto.

El otro elemento que posiblemente falta en su ejemplo es qué operación se está realizando. Algunos usuarios pueden tener el derecho de leer algunos datos pero no a actualizar, eliminar, ejecutar, etc. Por lo que podría tener un checkAccessmétodo en el controlador de acceso con tres parámetros: user, subject, operation. También es posible que desee proporcionar alguna información adicional checkAccesspara devolver información sobre por qué no se otorgó el acceso.

Por ejemplo, delegar todo esto al controlador de acceso más adelante le permite reemplazar la forma en que se representan sus permisos. Puede comenzar con el control de acceso basado en roles y pasar a los basados ​​en notificaciones más adelante. Puede almacenar permisos en una estructura simple para comenzar y luego agregar grupos / roles jerárquicos y operaciones permitidas sobre diferentes tipos de temas. No poner sus conjuntos de permisos en la interfaz ayuda a habilitar esto.

Si usa AOP o algún código repetitivo para conectar esto, en mi opinión, es menos importante.

Rory
fuente