Pregunta

Estoy construyendo un sitio basado en la comunidad en Rails para los miembros de una organización del mundo real. Estoy tratando de adherirme a las mejores prácticas de diseño RESTful, y la mayor parte es más o menos por el libro. El problema que hace que mi cerebro funcione en círculos RESTful es el de la autorización. Autenticación es un problema fácil y de larga resolución con soluciones RESTful ampliamente aceptadas, pero la autorización RESTful parece ser un poco un arte negro. Estoy tratando de encontrar el enfoque que proporcione el marco más general y flexible para controlar el acceso a los recursos a la vez que sea lo más simple posible, todo mientras se ajusta a una arquitectura RESTful. (Además, un pony).

Consideraciones:

  1. Necesito controlar el acceso a una variedad de recursos, como Usuarios, Páginas, Publicaciones, etc.
  2. La autorización para un recurso dado debe ser más precisa que la simple CRUD.
  3. Deseo permitirme a mí mismo y a otros editar las reglas de autorización desde la aplicación.
  4. Se debe permitir que las reglas de autorización dependan de predicados, como (conceptualmente) Propietario (Usuario, Recurso) o Bloqueado (Tema)

La consideración (2) es la que más me preocupa. Parece haber un desajuste de impedancia entre mi concepción de permisos y la concepción RESTful de acciones. Por ejemplo, tome publicaciones (como en un tablero de mensajes). REST dicta la existencia de cuatro operaciones en el recurso Publicar: Crear, Leer, Actualizar y Eliminar. Es simple decir que un usuario debería poder actualizar sus propias publicaciones, pero solo ciertos usuarios (o roles o grupos) deberían poder bloquearlas. La forma tradicional de representar el bloqueo es dentro del estado de la publicación, pero eso lleva al olor de que un usuario en las mismas condiciones puede o no puede actualizar una publicación dependiendo de los valores (completamente válidos) que proporciona. Me parece claro que en realidad hay dos acciones diferentes para cambiar el estado del Correo, y calzarlas es simplemente disfrazar una violación de los principios RESTful.

(Debo señalar que este problema es bastante distinto del problema de una actualización fallida debido a datos inválidos o inconsistentes & # 8212; una solicitud de bloqueo de un usuario no privilegiado es en principio bastante válida, simplemente no permitido.)

  

¿No es descomposición otra palabra para pudrirse?

Esto puede superarse descomponiendo la publicación: un bloqueo es un recurso secundario de una publicación en particular, y para crear o destruir puede tener permisos separados. Esta solución tiene el sonido de REST, pero trae consigo dificultades teóricas y prácticas. Si factorizo ??las cerraduras, ¿qué pasa con otros atributos? ¿Supongo que decido, en un ataque de capricho, que solo un miembro del Administrador debe poder modificar el título de la publicación? ¡Un simple cambio en la autorización requeriría una reestructuración de la base de datos para acomodarla! Esto no es una gran solución. Permitir este tipo de flexibilidad bajo una estrategia de descomposición requeriría que cada atributo sea un recurso. Esto presenta un pequeño dilema. Mi suposición implícita ha sido que un recurso está representado en la base de datos como una tabla. Bajo este supuesto, un recurso para cada atributo significa una tabla para cada atributo. Claramente, esto no es práctico. Sin embargo, eliminar esta suposición presenta un desajuste de impedancia entre las tablas y los recursos, lo que podría abrir su propia lata de gusanos. Usar este enfoque requeriría una consideración mucho más profunda de la que le he dado. Por un lado, los usuarios esperan razonablemente poder editar múltiples atributos a la vez. ¿A dónde va la solicitud? ¿Al recurso más pequeño que contiene todos los atributos? ¿Para cada recurso individual en paralelo? ¿A la luna?

  

Algunas de estas cosas no son como las demás & # 8230;

Supongamos entonces que no descompongo atributos. La alternativa parece ser definir un conjunto de privilegios para cada recurso. En este punto, sin embargo, se pierde la homogeneidad de REST. Para definir reglas de acceso para un recurso, el sistema debe tener un conocimiento específico de las capacidades de ese recurso. Además, ahora es imposible propagar de forma genérica los permisos a los recursos descendientes & # 8212; incluso si un recurso hijo tuviera un privilegio del mismo nombre, no existe una conexión semántica inherente entre los privilegios. Definir un conjunto de privilegios estándar tipo REST me parece el peor de los dos mundos, por lo que estoy atascado con una jerarquía de permisos separada para cada tipo de recurso.

  

Bueno, hicimos la nariz. Y el sombrero ¡Pero es un recurso!

Una sugerencia que he visto que mitiga algunas de las desventajas del enfoque anterior es definir permisos como crear / eliminar en recursos y leer / escribir en atributos . Este sistema es un compromiso entre los atributos como recursos y los privilegios por recurso: uno solo se queda con CRUD, pero a efectos de autorización, Leer y Actualizar pertenecen a los atributos, que podrían considerarse como pseudo recursos. Esto proporciona muchos de los beneficios prácticos del enfoque de atributos como recursos, aunque la integridad conceptual está, hasta cierto punto, comprometida. Los permisos aún pueden propagarse de un recurso a otro y de un recurso a un pseudo recurso, pero nunca de un pseudo recurso. No he explorado completamente las ramificaciones de esta estrategia, pero parece que puede ser prometedora. Se me ocurre que dicho sistema funcionaría mejor como una parte integral del Modelo. En Rails, por ejemplo, podría ser una actualización de ActiveRecord . Esto me parece bastante drástico, pero la autorización es una preocupación transversal tan fundamental que puede justificarse.

  

Ah, y no te olvides del pony

Todo esto ignora el problema de los permisos predicativos. Obviamente, un usuario debería poder editar sus propias publicaciones, pero las de nadie más. Obviamente, la tabla de permisos escritos por el administrador no debe tener registros separados para cada usuario. Esto no es un requisito poco común & # 8212; el truco es hacerlo genérico. Creo que se puede obtener toda la funcionalidad que necesito haciendo solo las reglas predicativas, de modo que la aplicabilidad de la regla se pueda decidir de forma rápida e inmediata. Una regla " permite al Usuario escribir Publicación donde Autor (Usuario, Publicación) " se traduciría a " para todos los Usuarios, Publicar de manera tal que Autor (Usuario, Publicación), permita al Usuario escribir Publicación " ;, y " denegará toda Publicación de Escritura donde esté Bloqueado (Publicar) " a " para todas las Publicaciones de manera que Bloqueado (Publicar), denegar todas las Publicaciones de escritura " ;. (Sería grandioso si todos esos predicados pudieran expresarse en términos de un Usuario y un Recurso.) El resultado conceptual " final " las reglas serían no predicativas. Esto plantea la cuestión de cómo implementar dicho sistema. Los predicados deberían ser miembros de las clases Modelo, pero no estoy seguro de cómo uno podría referirse a ellos con gracia en el contexto de las reglas. Hacerlo de manera segura requeriría algún tipo de reflexión. Aquí nuevamente tengo la sensación de que esto requeriría una actualización de la implementación del Modelo.

  

¿Cómo se deletrea eso nuevamente?

La pregunta final es cómo representar mejor estas reglas de autorización como datos. Una tabla de base de datos podría hacer el truco, con columnas de enumeración para permitir / denegar y C / R / U / D (o tal vez bits CRUD? O tal vez {C, R, U, D} & # 215; {permitir, denegar, heredar }?), y una columna de recursos con una ruta. Quizás, por conveniencia, un "heredar" poco. Estoy perdido en cuanto a predicados. Mesa separada? Ciertamente, mucha memoria caché para evitar que sea demasiado impíamente lenta.


Supongo que es mucho pedir. Traté de hacer mi tarea antes de hacer la pregunta, pero en este punto realmente necesito una perspectiva externa. Agradecería cualquier aporte que cualquiera de ustedes pueda tener sobre el problema.

¿Fue útil?

Solución

Lo siento, no tengo tiempo para hacer justicia a esta pregunta, pero es bueno ver algunas preguntas bien pensadas sobre SO. Aquí hay algunos comentarios:

No caigas en la trampa de mapear los verbos HTTP a CRUD. Sí, OBTENER y ELIMINAR un mapa bastante limpio, pero PUT puede crear y actualizar (pero solo reemplazo completo) y POST es un verbo comodín. POST es realmente para manejar todo lo que no encaja en GET, PUT y BORRAR.

El uso de atributos para representar el estado de un objeto es solo un enfoque para la gestión del estado. Supongo que puedes imaginar lo que podría hacer la siguiente solicitud:

POST /LockedPosts?url=/Post/2010

Un subrecurso también es un enfoque válido para administrar el estado actual de un recurso. No me sentiría obligado a tratar el "estado" de un recurso atributos y sus " datos " atributos de manera consistente.

Intentar asignar recursos directamente a las tablas te limitará seriamente. No olvides que cuando sigues las restricciones REST, de repente estás muy limitado en los verbos que tienes disponibles. Necesita poder compensar eso al ser creativo en los recursos que utiliza. Limitarse a un recurso equivale a una tabla limitará severamente la funcionalidad de su aplicación final.

Regularmente vemos usuarios de Rails, ASP.NET MVC y WCF Rest publicando preguntas aquí en StackOverflow sobre cómo hacer ciertas cosas dentro de las restricciones de REST. El problema a menudo no es una restricción de REST sino las limitaciones del marco en su soporte para aplicaciones RESTful. Creo que es esencial encontrar primero una solución RESTful a un problema y luego ver si eso se puede asignar a su marco de elección.

En cuanto a la creación de un modelo de permisos que existe en un grano más fino que el recurso en sí. Recuerde que una de las principales restricciones REST es hipermedia. Hypermedia puede usarse para algo más que solo encontrar entidades relacionadas, también puede usarse para representar transiciones de estado válidas / permitidas. Si devuelve una representación que contiene enlaces incrustados, condicionalmente basados ??en permisos, puede controlar qué acciones puede realizar quién. es decir, si un usuario tiene permisos para desbloquear POST 342, puede devolver el siguiente enlace incrustado en la representación:

<Link href="/UnlockedPosts?url=/Post/342" method="POST"/>

Si no tienen ese permiso, no devuelva el enlace.

Creo que una de sus dificultades aquí es que está tratando de masticar un problema demasiado grande a la vez. Creo que debe mirar la interfaz RESTful que está tratando de exponer al cliente como un problema distinto de cómo va a administrar los permisos y predicados para administrar la autorización en su modelo de dominio.

Me doy cuenta de que no he respondido directamente ninguna de sus preguntas, pero espero haber proporcionado algunos puntos de vista que pueden ayudar de alguna manera.

Otros consejos

Recientemente descubrí una solución de autenticación que parece abordar la mayoría de mis preocupaciones. Si le ha gustado esta pregunta, podría ser de su interés:

https://github.com/stffn/declarative_authorization

Como señaló Darrel: REST no es CRUDO. Si encuentra que sus recursos identificados son demasiado gruesos que la interfaz uniforme no proporciona suficiente control, divida su recurso en sub-recursos y use el recurso original como una 'colección' de hipervínculos a sus componentes.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top