Práticas padrão para controle de acesso (padrão de design)

9

Estou olhando para o design da minha interface e estou lutando para decidir qual é a maneira mais "correta" de implementar o controle de acesso baseado em funções, dado a usere a subjectque usereles gostariam de acessar.


Tanto quanto posso ver, tenho três opções principais (com uma quarta sendo uma bastardização dos três primeiros e uma quinta sendo uma emenda do quarto):

  1. Consulte a subjectlista de permissões que userpossui -subject.allowAccess(user.getPermissionSet)
  2. Consulte o usercom uma lista de permissões que o subjectrequer -user.hasPermissionTo(subject.getRequiredPermissions())
  3. Consulte um terceiro para localizar as interseções de permissões - accessController.doPermissionSetsIntersect(subject.permissionSet, user.getPermissionSet())
  4. Consulte o subject/ userenquanto delega a "decisão" para uma classe de terceiros
  5. Tente useracessar subjecte gerar um erro se o acesso não for permitido

Estou inclinado para a opção quatro - subjectContenha um accessControllercampo, onde chamadas para subject.userMayAccess(User user)delegar a operação a la:

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

.. mas isso levanta outras questões:

  • deve accessControllerser um campo vs uma classe estática ..?
  • Deveria subject saber quais permissões são necessárias para poder visualizá-lo?
  • onde o princípio do menor conhecimento entra em jogo aqui, com relação ao chamado subject.display()? Os chamadores devem subject.display()saber que o controle de acesso está em vigor? (onde subject.display()é um "método de modelo" final)
  • ter subject.display()gerenciar o controle de acesso, lançar uma exceção, onde o usuário não tem a permissão necessária?

O que seria considerado "melhor prática" nessa situação? Onde deveria ocorrer a responsabilidade pela realização das verificações?

Como esse é um exercício acadêmico que progride para a implementação, as referências aos padrões de design seriam apreciadas.

kwah
fonte

Respostas:

7

A melhor prática é usar algo conhecido como padrão Interceptor para interceptar chamadas para áreas protegidas.

Isso pode ser alcançado pelo uso de AOP ou preocupações transversais aplicadas aos seus pontos de entrada de acesso.

O assunto nunca deve saber quem é capaz de vê-lo. Isso complica o código do assunto desnecessariamente e não há motivo para ser necessário, a menos que você inadvertidamente forneça um mecanismo de acesso direto à mesma função.

De preferência, o chamador e o destinatário não devem saber sobre o acesso, além de lidar com as rejeições. No entanto, o problema dependerá do sistema no qual você está implementando e como você obtém acesso às credenciais / principal de segurança do chamador. Por exemplo, nos sistemas SOAP, essas informações são adicionadas ao cabeçalho de uma mensagem SOAP, enquanto que no sistema Windows elas estavam disponíveis por meio do mecanismo de autenticação do Windows.

Se você usar a abordagem de padrão de interoperabilidade ou AOP, lançará quaisquer exceções necessárias e caberia ao cliente (responsável pela chamada) lidar com as exceções lançadas.

Dessa maneira, você mantém seu código de cliente, serviço e autenticação separado, sem mistura de conhecimento ou funcionalidade.

sweetfa
fonte
2

Penso que a sua opção 3 é o mais próximo, mas em vez de interrogar o usere subjectsobre os seus conjuntos de permissões que você deve passar o usere subjectao controlador de acesso.

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

O controlador de acesso deve ser responsável por obter os conjuntos de permissões e verificar se o acesso é suficiente. Dessa forma, você isola a lógica de armazenamento e a lógica de verificação em seu controlador de acesso, separadas do usuário e do assunto.

O outro elemento possivelmente ausente do seu exemplo é qual operação está sendo executada. Alguns usuários podem ter o direito de ler alguns dados, mas não atualizar, excluir, executar, etc Então, você pode ter um checkAccessmétodo no controlador de acesso com três parâmetros: user, subject, operation. Você também pode fornecer algumas informações adicionais checkAccesspara retornar informações sobre por que o acesso não foi concedido.

Por exemplo, delegar tudo isso ao controlador de acesso posteriormente permite substituir a maneira como suas permissões são representadas. Você pode começar com o controle de acesso baseado em função e passar para baseado em declarações posteriormente. Você pode armazenar permissões em uma estrutura simples para começar e depois adicionar grupos / funções hierárquicos e operações permitidas em diferentes tipos de assuntos. Não colocar seus conjuntos de permissões na interface ajuda a habilitar isso.

Se você usa AOP ou algum código padrão para conectar isso é, na minha opinião, menos importante.

Rory
fonte