Na minha experiência, a verdadeira questão se decompõe principalmente se alguma quantidade de restrição de acesso específica do usuário ocorrerá ou não.
Suponha, por exemplo, que você esteja projetando o esquema de uma comunidade e que permita que os usuários alternem a visibilidade de seu perfil.
Uma opção é manter um sinalizador de perfil público/privado e manter verificações de permissão amplas e preventivas:'users.view' (visualiza usuários públicos) vs, digamos, 'users.view_all' (visualiza todos os usuários, para moderadores) .
Outra envolve permissões mais refinadas, você pode querer que eles sejam capazes de configurar as coisas para que possam se tornar (a) visíveis para todos, (b) visíveis por seus amigos escolhidos a dedo, (c) mantidos totalmente privados e talvez (d) ) visível por todos, exceto seus bozos escolhidos a dedo. Nesse caso, você precisa armazenar dados relacionados ao proprietário/acesso para linhas individuais e precisará abstrair fortemente algumas dessas coisas para evitar a materialização do fechamento transitivo de um gráfico orientado e denso.
Com qualquer uma das abordagens, descobri que a complexidade adicional na edição/atribuição de funções é compensada pela facilidade/flexibilidade resultante na atribuição permissões para partes individuais de dados e que o seguinte funcionou melhor:
- Os usuários podem ter várias funções
- Funções e permissões mescladas na mesma tabela com um sinalizador para distinguir as duas (útil ao editar funções/permissões)
- As funções podem atribuir outras funções, e as funções e permissões podem atribuir permissões (mas as permissões não podem atribuir funções) de dentro da mesma tabela.
O gráfico orientado resultante pode então ser obtido em duas consultas, construídas de uma vez por todas em um período de tempo razoável usando qualquer linguagem que você esteja usando e armazenadas em cache no Memcache ou similar para uso subsequente.
A partir daí, extrair as permissões de um usuário é uma questão de verificar quais funções ele possui e processá-las usando o gráfico de permissões para obter as permissões finais. Verifique as permissões verificando se um usuário tem a função/permissão especificada ou não. E, em seguida, execute sua consulta/emita um erro com base nessa verificação de permissão.
Você pode estender a verificação para nós individuais (ou seja,
check_perms($user, 'users.edit', $node)
para "pode editar este nó" vs check_perms($user, 'users.edit')
para "pode editar um nó") se precisar, e você terá algo muito flexível/fácil de usar para usuários finais. Como o exemplo de abertura deve ilustrar, tenha cuidado ao direcionar muito para permissões em nível de linha. O gargalo de desempenho é menor na verificação das permissões de um nó individual do que na extração de uma lista de nós válidos (ou seja, apenas aqueles que o usuário pode visualizar ou editar). Eu aconselho contra qualquer coisa além de sinalizadores e campos user_id dentro das próprias linhas se você não for (muito) versado em otimização de consulta.