Exemplo 2: Controle de acesso multilocatário e RBAC definido pelo usuário com OPA e Rego - AWS Orientação prescritiva

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Exemplo 2: Controle de acesso multilocatário e RBAC definido pelo usuário com OPA e Rego

Este exemplo usa OPA e Rego para demonstrar como o controle de acesso pode ser implementado em uma API para um aplicativo multilocatário com funções personalizadas definidas pelos usuários locatários. Também demonstra como o acesso pode ser restrito com base em um inquilino. Esse modelo mostra como a OPA pode tomar decisões granulares de permissão com base nas informações fornecidas em uma função de alto nível.

RBAC definido pelo usuário com OPA e Rego

As funções dos inquilinos são armazenadas em dados externos (dados RBAC) que são usados para tomar decisões de acesso ao OPA:

{ "roles": { "tenant_a": { "all_access_role": ["viewData", "updateData"] }, "tenant_b": { "update_data_role": ["updateData"], "view_data_role": ["viewData"] } } }

Essas funções, quando definidas por um usuário inquilino, devem ser armazenadas em uma fonte de dados externa ou em um provedor de identidade (IdP) que possa atuar como uma fonte confiável ao mapear funções definidas pelo inquilino para as permissões e para o próprio inquilino. 

Este exemplo usa duas políticas no OPA para tomar decisões de autorização e examinar como essas políticas impõem o isolamento do inquilino. Essas políticas usam os dados RBAC definidos anteriormente.

default allowViewData = false allowViewData = true { input.method == "GET" input.path = ["viewData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "viewData") }

Para mostrar como essa regra funcionará, considere uma consulta OPA que tenha a seguinte entrada:

{ "tenant_id": "tenant_a", "role": "all_access_role", "path": ["viewData", "tenant_a"], "method": "GET" }

Uma decisão de autorização para essa chamada de API é feita da seguinte forma, combinando os dados do RBAC, as políticas de OPA e a entrada da consulta OPA:

  1. Um usuário de Tenant A faz uma chamada de API para/viewData/tenant_a.

  2. O microsserviço Data recebe a chamada e consulta a allowViewData regra, passando a entrada mostrada no exemplo de entrada da consulta OPA.

  3. A OPA usa a regra consultada nas políticas de OPA para avaliar a entrada fornecida. O OPA também usa os dados dos dados do RBAC para avaliar a entrada. A OPA faz o seguinte:

    1. Verifica se o método usado para fazer a chamada da API éGET.

    2. Verifica se o caminho solicitado éviewData.

    3. Verifica se o tenant_id no caminho é igual ao input.tenant_id associado ao usuário. Isso garante que o isolamento do inquilino seja mantido. Outro inquilino, mesmo com uma função idêntica, não pode ser autorizado a fazer essa chamada de API.

    4. Extrai uma lista de permissões de função dos dados externos das funções e as atribui à variável. role_permissions Essa lista é recuperada usando a função definida pelo inquilino associada ao usuário no input.role.

    5. Verifica role_permissions se ele contém a permissão viewData.

  4. A OPA retorna a seguinte decisão ao microsserviço Data:

{ "allowViewData": true }

Esse processo mostra como o RBAC e a conscientização do inquilino podem contribuir para a tomada de uma decisão de autorização com a OPA. Para ilustrar melhor esse ponto, considere uma chamada de API para /viewData/tenant_b com a seguinte entrada de consulta:

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["viewData", "tenant_b"], "method": "GET" }

Essa regra retornaria a mesma saída da entrada da consulta OPA, embora seja para um inquilino diferente que tenha uma função diferente. Isso ocorre porque essa chamada é para /tenant_b e os view_data_role dados do RBAC ainda têm a viewData permissão associada a ela. Para aplicar o mesmo tipo de controle de acesso/updateData, você pode usar uma regra de OPA semelhante:

default allowUpdateData = false allowUpdateData = true { input.method == "POST" input.path = ["updateData", tenant_id] input.tenant_id == tenant_id role_permissions := data.roles[input.tenant_id][input.role][_] contains(role_permissions, "updateData") }

Essa regra é funcionalmente igual à allowViewData regra, mas verifica um caminho e um método de entrada diferentes. A regra ainda garante o isolamento do inquilino e verifica se a função definida pelo inquilino concede permissão ao chamador da API. Para ver como isso pode ser aplicado, examine a seguinte entrada de consulta para uma chamada de API para/updateData/tenant_b:

{ "tenant_id": "tenant_b", "role": "view_data_role", "path": ["updateData", "tenant_b"], "method": "POST" }

Essa entrada de consulta, quando avaliada com a allowUpdateData regra, retorna a seguinte decisão de autorização:

{ "allowUpdateData": false }

Essa chamada não será autorizada. Embora o chamador da API esteja associado ao correto tenant_id e esteja chamando a API usando um método aprovado, ele input.role é definido pelo inquilinoview_data_role. view_data_roleNão tem updateData permissão; portanto, a chamada para não /updateData é autorizada. Essa chamada teria sido bem-sucedida para um tenant_b usuário que tem update_data_role o.