2011-05-03 21 views
25

Deseo crear un esquema para una ACL; sin embargo, estoy dividido entre un par de formas de implementarlo.Esquema de la base de datos para la ACL

Estoy bastante seguro de que no quiero lidiar con los permisos en cascada ya que eso genera mucha confusión en el servidor y para los administradores del sitio.

Creo que también puedo vivir con usuarios que solo cumplen una función a la vez. Una configuración como esta permitirá que los roles y permisos se agreguen según sea necesario a medida que el sitio crece sin afectar los roles/reglas existentes.

Al principio iba a normalizar los datos y tener tres tablas para representar las relaciones.

ROLES { id, name } 
RESOURCES { id, name } 
PERMISSIONS { id, role_id, resource_id } 

una consulta para averiguar si un usuario se le permitió algún lugar se vería así:

SELECT id FROM resources WHERE name = ? 
SELECT * FROM permissions WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id) 

Entonces me di cuenta de que sólo tendrá alrededor de 20 recursos, cada uno con hasta 5 acciones (crear , actualizar, ver, etc.) y tal vez otros 8 roles. Esto significa que puedo ejercer un flagrante desprecio por la normalización de datos ya que nunca tendré más de un par de cientos de registros posibles.

Entonces, tal vez un esquema como este tenga más sentido.

ROLES { id, name } 
PERMISSIONS { id, role_id, resource_name } 

que me permitiría para buscar registros en una sola consulta

SELECT * FROM permissions WHERE role_id = ? AND permission = ? ($user_role_id, 'post.update') 

Entonces, ¿cuál de ellos es más correcto? ¿Hay otros diseños de esquema para ACL?

+4

Puede buscar permisos en una sola consulta con el esquema correcto, correcto y normalizado también. Use un 'JOIN'. –

+2

Mi experiencia directa con los permisos en cascada es que fue difícil para mí (el desarrollador/mantenedor del software) hacer las cosas bien, pero la vida fue mucho mejor para los administradores. En parte porque incluí en la UI de permiso la capacidad de ver lo que tenía permiso para hacer y * por qué * tenía permiso para hacerlo. Para que pudieran ver fácilmente de dónde provenía el permiso. Esto fue bastante intuitivo para ellos. – btilly

Respuesta

28

En mi experiencia, la pregunta real sobre todo si se rompe a cualquier cantidad de Acceso restricción específica del usuario o no va a ocurrir.

Supongamos, por ejemplo, que está diseñando el esquema de una comunidad y que permite a los usuarios alternar la visibilidad de su perfil.

Una opción es adherirse a un indicador de perfil público/privado y atenerse a amplios controles de permiso preventivos: 'users.view' (vistas de usuarios públicos) vs, digamos, 'users.view_all' (vistas de todos los usuarios , para los moderadores).

Otro implica permisos más refinados, es posible que desee que puedan configurar las cosas para que puedan (a) ser visibles para todos, (b) visibles para sus amigos, (c) mantenerse privadas por completo, y tal vez (d) visible para todos excepto sus bozos seleccionados a mano. En este caso, debe almacenar los datos relacionados con el propietario/acceso para filas individuales, y deberá abstraer en gran medida algunas de estas cosas para evitar materializar el cierre transitivo de un gráfico denso orientado.

Con uno u otro enfoque, he encontrado que añade complejidad en función de edición/asignación se compensa con la facilidad/flexibilidad que resulta en asignar permisos a piezas individuales de datos, y que el siguiente para mejor trabajado:

  1. los usuarios pueden tener múltiples funciones
  2. Roles y permisos se fusionaron en la misma mesa con una bandera para distinguir los dos (roles/permanentes durante la edición de útiles)
  3. roles pueden asignar otras funciones, y las funciones y las permanentes pueden asignar permisos (pero los permisos no pueden asno roles de ign), desde dentro de la misma tabla.

El gráfico orientado resultante se puede extraer en dos consultas, compiladas de una vez por todas en un tiempo razonable utilizando el idioma que esté utilizando, y se almacenan en memoria caché en Memcache o similar para su uso posterior.

A partir de ahí, extraer los permisos de un usuario es una cuestión de verificar qué roles tiene y procesarlos usando el gráfico de permisos para obtener los permisos finales. Compruebe los permisos verificando que un usuario tenga el rol/permiso especificado o no. Y luego ejecute su consulta/problema un error basado en esa verificación de permiso.

Puede ampliar la comprobación de nodos individuales (es decir, check_perms($user, 'users.edit', $node) para "editar este nodo" contra check_perms($user, 'users.edit') para "puede editar un nodo") si es necesario, y tendrá algo muy flexible/fácil de usar para los usuarios finales.

Como lo ilustrará el ejemplo de apertura, tenga cuidado de dirigir demasiado hacia los permisos de nivel de fila. El cuello de botella de rendimiento es menor al verificar los permisos de un nodo individual que al extraer una lista de nodos válidos (es decir, solo aquellos que el usuario puede ver o editar).Aconsejo que no vaya más allá de los indicadores y los campos user_id dentro de las filas si no está (muy) bien versado en la optimización de consultas.

+0

Gracias por ese buen resumen de las relaciones más complejas. Me imagino que este es el tipo de gráfico de permisos que usaría algo como Facebook. Recuerdo haber tomado con un chico sobre roles hace un tiempo y mencionó muchos casos extremos que requerirían tener múltiples roles. Lo único que me preocupa es que el tamaño del objeto/matriz que se requeriría para almacenar este gráfico en la memoria sería de al menos un megabyte. Parece que solo consultar las partes que necesita y dejar el gráfico en la base de datos ahorraría mucha RAM. – Xeoncross

+0

Para los gráficos de permisos que me he encontrado, los resultados fueron en realidad bastante pequeños. Pero entonces, nunca tuve que administrar cientos de roles tampoco. :-) –

+0

En realidad, estaba tratando de pensar en algunos ejemplos donde los objetos individuales también necesitarían permisos, pero no puedo pensar en un momento en el que deberían pertenecer al gráfico. Si fue un permiso de perfil de usuario como el que dijo, acepto que la fila de perfil debe contener una columna de bit. Si fue un foro privado para un cierto rol, creo que también debería ir en el objeto del foro. ¿Tiene algún otro ejemplo de situaciones que no funcionaría solo con roles? – Xeoncross

0

Puede usar un SET para asignar las funciones.

CREATE TABLE permission (
    id integer primary key autoincrement 
    ,name varchar 
    ,perm SET('create', 'edit', 'delete', 'view') 
    ,resource_id integer); 
+0

Las reglas no se conocen de antemano. Si bien es cierto que las reglas básicas de CRUD son necesarias en la mayoría de los recursos, puede haber otras reglas personalizadas, por lo que un SET no funcionará, ya que no se pueden definir al inicio del proyecto. – Xeoncross

+0

Puede expandir el conjunto luego usando 'ALTER TABLE'. – Johan

+0

Bueno, el problema es que las reglas son exclusivas de algunos módulos. Tal vez "Revise" es un permiso de artículos y "reembolso" es un permiso del sistema de pago. Además, ALTER TABLE no es muy rápido en grandes conjuntos de datos y es molesto cada vez que necesita agregar un nuevo permiso. – Xeoncross

7

Esto significa que puedo ejercer flagrante indiferencia para la normalización de datos, ya que nunca tener más de un par cientos de registros posibles.

El número de filas que espera no es un criterio para elegir qué forma normal apuntar. La normalización se refiere a la integridad de los datos. Por lo general, aumenta la integridad de los datos al reducir la redundancia.

La verdadera pregunta a realizar no es "¿Cuántas filas tendré?", Sino "¿Qué tan importante es para la base de datos darme siempre las respuestas correctas?" Para una base de datos que se utilizará para implementar una ACL, diría "Bastante peligroso".

En todo caso, un número bajo de filas sugiere que no necesita preocuparse por el rendimiento, por lo que 5NF debería ser una elección fácil de realizar. Deberá presionar 5NF antes de agregar cualquier número de identificación.

una consulta para averiguar si un usuario se permitió algún lugar se vería como esto:

SELECT id FROM resources WHERE name = ? 
SELECT * FROM permissions 
WHERE role_id = ? AND resource_id = ? ($user_role_id, $resource->id) 

que escribió que, como dos consultas en lugar de utilizar una combinación interna sugiere que usted podría estar por encima de tu cabeza (Eso es una observación, no una crítica.)

SELECT p.* 
FROM permissions p 
INNER JOIN resources r ON (r.id = p.resource_id AND 
          r.name = ?) 
+0

¿Cómo sería un esquema de ACL en 5NF? – Xeoncross

+0

Completamente de acuerdo, lo más importante es que las tablas deben mantenerse claras. Si desea optimizaciones en términos de velocidad, debe ver bitfileds en las bases de datos, algunas aplicaciones con elementos ACL fijos utilizan bitfields y operaciones de bits para realizar las comprobaciones de ACL, pero esto es mucho menos legible que algunas tablas con relaciones claras y futuras extensiones disponible. En términos de velocidad, la aplicación puede almacenar en caché el objeto ACL después de una carga inicial completa de todas las tablas ACL en un objeto a nivel de aplicación y realizar la verificación con el lenguaje de la aplicación y no con SQL. – regilero

Cuestiones relacionadas