Pergunta

Estou construindo um site comunitário em Rails para os membros de uma organização do mundo real. Estou tentando aderir às práticas recomendadas do design RESTful, e a maior parte é mais ou menos pelo livro. A questão que está fazendo meu cérebro funcionar em círculos repousantes é a da autorização. Autenticação é um problema fácil e de soluções longas, com soluções RESTful amplamente aceitas, mas a autorização repousante parece ser um pouco de arte negra. Estou tentando encontrar a abordagem que fornecerá a estrutura mais geral e flexível para controlar o acesso aos recursos, sendo o mais simples possível, enquanto está em conformidade com uma arquitetura repousante. (Além disso, um pônei.)

Considerações:

  1. Preciso controlar o acesso a uma variedade de recursos, como usuários, páginas, postagens, etc.
  2. A autorização para um determinado recurso deve ser mais refinada que o simples crud.
  3. Desejo permitir que eu e outras pessoas editem as regras de autorização de dentro do aplicativo.
  4. As regras de autorização devem poder depender de predicados, como (conceitual) proprietário (usuário, recurso) ou bloqueado (tópico)

A consideração (2) é a que mais me preocupa. Parece haver uma incompatibilidade de impedância entre minha concepção de permissões e a concepção de ações tranquilas. Por exemplo, faça postagens (como em um quadro de mensagens). O REST determina a existência de quatro operações no recurso de postagem: Criar, ler, atualizar e excluir. É simples dizer que um usuário deve ser capaz de atualizar suas próprias postagens, mas apenas certos usuários (ou funções ou grupos) devem ser autorizados a bloqueá -los. A maneira tradicional de representar o bloqueio está dentro do estado da postagem, mas isso leva ao cheiro que um usuário nas mesmas condições pode ou não ser capaz de atualizar uma postagem, dependendo dos valores (completamente válidos) que ele fornece. Parece -me claro que existem realmente duas ações diferentes para mudar o estado do post, e para calçar eles é apenas disfarçar uma violação dos princípios repousantes.

(Devo observar que esse problema é bastante distinto do problema de uma atualização falhando devido a inválido ou inconsistente Dados - uma solicitação de bloqueio de um usuário não privilegiada é, em princípio, bastante válido, simplesmente não permitido.)

A decomposição não é outra palavra para podridão?

Isso pode ser superado ao decompor a postagem: uma trava é um sub -resistência de um post específico e para criar ou destruir alguém pode ter permissões separadas. Esta solução tem o anel de descanso, mas é traz consigo dificuldades teóricas e práticas. Se eu levar os bloqueios, e os outros atributos? Suponha que eu decida, em um ataque de capricho, que apenas um membro do Administrador deve ter permissão para modificar o título do Post? Uma simples mudança na autorização exigiria uma reestruturação do banco de dados para acomodá -lo! Isso não é muita solução. Para permitir esse tipo de flexibilidade sob uma estratégia de decomposição, exigiria que todos os atributos fossem um recurso. Isso apresenta um pouco de dilema. Minha suposição implícita foi que um recurso é representado no banco de dados como uma tabela. Sob essa suposição, um recurso para cada atributo significa uma tabela para cada atributo. Claramente, isso não é prático. No entanto, para remover essa suposição apresenta uma incompatibilidade de impedância entre tabelas e recursos, o que poderia abrir sua própria lata de vermes. Usar essa abordagem exigiria uma consideração muito mais aprofundada do que eu o fiz. Por um lado, os usuários esperam razoavelmente poder editar vários atributos de uma só vez. Para onde vai o pedido? Para o menor recurso que contém todos os atributos? Para cada recurso individual em paralelo? Para a lua?

Algumas dessas coisas não são como as outras ...

Suponha então que eu não decomponha atributos. A alternativa parece estar definindo um conjunto de privilégios para cada recurso. Nesse ponto, no entanto, a homogeneidade do descanso é perdida. Para definir regras de acesso para um recurso, o sistema deve ter conhecimento específico dos recursos desse recurso. Além disso, agora é impossível propagar genericamente as permissões aos recursos descendentes - mesmo que um recurso infantil tivesse um privilégio de mesmo nome, não há conexão semântica inerente entre os privilégios. Definir um conjunto de privilégios padrão parece-me o pior dos dois mundos, por isso fico preso a uma hierarquia de permissões separada para cada tipo de recurso.

Bem, fizemos o nariz. E o chapéu. Mas é um recurso!

Uma sugestão que eu vi que mitiga algumas das desvantagens da abordagem acima é definir permissões como criar/excluir sobre Recursos e ler/escrever sobre atributos. Esse sistema é um compromisso entre os atributos como recursos e privilégios por resistência: ainda é deixado com apenas crud, mas para fins de autorização, leitura e atualização pertencem a atributos, que podem ser considerados como pseudo-respostas. Isso fornece muitos dos benefícios práticos da abordagem dos atributos como respostas, embora a integridade conceitual seja, até certo ponto, comprometida. As permissões ainda podem se propagar do recurso ao recurso e do recurso ao pseudo-resistência, mas nunca de uma pseudo-recursos. Não explorei completamente as ramificações dessa estratégia, mas parece que pode ser promissor. Ocorre -me que esse sistema funcionaria melhor como parte integrante do modelo. Em Rails, por exemplo, pode ser uma adaptação de ActiveRecord. Isso me parece bastante drástico, mas a autorização é uma preocupação cruzada tão fundamental de que isso pode ser justificado.

Oh, e não se esqueça do pônei

Tudo isso ignora a questão das permissões predicativas. Obviamente, um usuário deve ser capaz de editar suas próprias postagens, mas ninguém mais. Igualmente, obviamente, a tabela de permissões escritas para administrador não deve ter registros separados para cada usuário. Esse não é um requisito incomum - o truque é torná -lo genérico. Eu acho que toda a funcionalidade que eu preciso pode ser obtida ao fazer apenas o as regras predicativo, para que a aplicabilidade da regra possa ser decidida de maneira rápida e imediatamente. Uma regra "allow User write Post where Author(User, Post)"Traduziria para"for all User, Post such that Author(User, Post), allow User write Post", e "deny all write Post where Locked(Post)" para "for all Post such that Locked(Post), deny all write Post". (Seria grande Se todos esses predicados pudessem ser expressos em termos de um usuário e um recurso.) As regras "final" conceitualmente resultantes seriam não predicativas. Isso levanta a questão de como implementar esse sistema. Os predicados devem ser membros das classes modelo, mas não tenho certeza de como alguém poderia se referir a eles graciosamente no contexto das regras. Fazer isso com segurança exigiria algum tipo de reflexão. Aqui, novamente, sinto que isso exigiria uma adaptação da implementação do modelo.

Como você soletra isso de novo?

A questão final é como melhor representar essas regras de autorização como dados. Uma tabela de banco de dados pode fazer o truque, com colunas enum para permitir/negar e c/r/u/d (ou talvez craudar bits? Ou talvez {c, r, u, d} × {permitir, negar, herdar}?)?) e uma coluna de recursos com um caminho. Talvez, como uma conveniência, uma parte "herdada". Estou perdido tanto quanto os predicados. Tabela separada? Certamente muito armazenamento em cache para impedir que ele seja também ímpio lento.


Eu acho que isso é muito para pedir. Tentei fazer meu dever de casa antes de fazer a pergunta, mas neste momento eu realmente preciso de uma perspectiva externa. Eu apreciaria qualquer contribuição que qualquer um de vocês possa ter sobre o problema.

Foi útil?

Solução

Desculpe, não tenho tempo para fazer justiça a essa pergunta, mas é bom ver algumas perguntas bem pensadas sobre isso. Aqui estão alguns comentários:

Não caia na armadilha de mapear os verbos HTTP para Crud. Sim, obtenha e exclua o mapa de maneira bastante limpa, mas a put pode criar e atualizar (mas apenas a substituição completa) e a postagem é um verbo curing. A postagem é realmente para lidar com tudo o que não se encaixa, colocar e excluir.

Usar atributos para representar o status de um objeto é apenas uma abordagem para o gerenciamento do estado. Acho que você pode imaginar o que a solicitação seguinte pode fazer:

POST /LockedPosts?url=/Post/2010

Uma sub -resistência também é uma abordagem válida para gerenciar o estado atual de um recurso. Eu não me sentiria obrigado a tratar os atributos de "estado" de um recurso e seus atributos "dados" de maneira consistente.

Tentar mapear recursos diretamente para tabelas vai restringi -lo seriamente. Não se esqueça que, quando você seguir as restrições de restrições, de repente é muito limitado nos verbos que tem disponível. Você precisa compensar isso em ser criativo nos recursos que usa. Limitar -se a um recurso é igual a uma tabela limitará severamente a funcionalidade do seu aplicativo final.

Vemos regularmente o Rails, o ASP.NET MVC e o WCF REST Usuários postando perguntas aqui no StackOverflow sobre como certas coisas dentro das restrições de descanso. O problema geralmente não é uma restrição de descanso, mas nas limitações da estrutura em seu suporte para aplicações RESTful. Eu acho que é essencial primeiro encontrar uma solução repousante para um problema e depois ver se isso pode ser mapeado de volta à sua estrutura de escolha.

Quanto a criar um modelo de permissões que existe em um grão mais fino que o próprio recurso. Lembre -se de que uma das principais restrições de descanso é a hipermídia. A hipermídia pode ser usada para mais do que apenas encontrar entidades relacionadas, também pode ser usado para representar transições de estado válidas/permitidas. Se você retornar uma representação que contém links incorporados, condicionalmente com base em permissões, poderá controlar quais ações podem ser executadas por quem. ou seja, se um usuário tiver permissões para desbloquear o post 342, você poderá retornar o seguinte link incorporado na representação:

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

Se eles não tiverem essa permissão, não retorne o link.

Eu acho que uma de suas dificuldades aqui é que você está tentando mastigar muito um problema de uma só vez. Eu acho que você precisa olhar para a interface RESTful que está tentando expor ao cliente como um problema distinto de como você gerenciará as permissões e predicados para gerenciar a autorização em seu modelo de domínio.

Percebo que não respondi diretamente a nenhuma das suas perguntas, mas espero ter fornecido alguns pontos de vista que possam ajudar de alguma forma.

Outras dicas

Descobri recentemente uma solução de autenticação que parece abordar a maioria das minhas preocupações. Se você favorito essa pergunta, pode ser de interesse para você:

https://github.com/stffn/declarative_authorization

Como Darrel apontou - o descanso não é Crud. Se você achar que seus recursos identificados são muito grossos graduados que a interface uniforme não fornece controle suficiente, divida seu recurso em sub-recursos e use o recurso original como uma 'coleção' de hiperlinks em seus componentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top