

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 示例 2：使用 OPA 和 Rego 进行多租户访问控制和用户定义的 RBAC
<a name="opa-rbac-examples"></a>

此示例使用 OPA 和 Rego 来演示如何通过租户用户定义的自定义角色在多租户应用程序的 API 上实现访问控制。它还演示了如何根据租户限制访问权限。此模型显示 OPA 如何根据高级角色中提供的信息做出精细的权限决策。

![带有 OPA 和 Rego 的用户定义的 RBAC](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/opa-example-2.png)


租户的角色存储在用于为 OPA 做出访问决策的外部数据（RBAC 数据）中：

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

这些角色由租户用户定义时，应存储在外部数据源或身份提供者 (IdP) 中，在将租户定义的角色映射到权限和租户本身时，身份提供者可以充当真实来源。 

此示例使用 OPA 中的两个策略来做出授权决策，并研究这些策略如何强制执行租户隔离。这些策略使用前面定义的 RBAC 数据。

```
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")
}
```

要显示此规则将如何运作，请考虑具有以下输入的 OPA 查询：

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

*通过组合 *RBAC 数据*、*OPA 策略和 OPA* 查询输入，可以按如下方式做出此 API 调用的授权决定：*

1. 来自的用户向`Tenant A`发出 API 调用`/viewData/tenant_a`。

1. 数据微服务接收调用并查询`allowViewData`规则，传递 OPA 查询输入示例中显示的输入。

1. OPA 使用 OPA 策略中的查询规则来评估所提供的输入。OPA 还使用 RBAC 数据中的数据来评估输入。OPA 会执行以下操作：

   1. 验证用于进行 API 调用的方法是否为`GET`。

   1. 验证请求的路径是否为。`viewData`

   1. 检查路径`tenant_id`中的是否等于与用户`input.tenant_id`关联的。这样可以确保保持租户隔离。另一个租户，即使角色相同，也无法获得进行此 API 调用的授权。

   1. 从角色的外部数据中提取角色权限列表并将其分配给变量。`role_permissions`此列表是通过使用与用户关联的租户定义角色来检索的 `input.role.`

   1. `role_permissions`检查它是否包含权限 `viewData.`

1. OPA 将以下决定返回给数据微服务：

```
{
    "allowViewData": true
}
```

此过程显示了 RBAC 和租户意识如何有助于通过 OPA 做出授权决定。为了进一步说明这一点，可以考虑使用以下查询输入`/viewData/tenant_b`进行 API 调用：

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

此规则将返回与 OPA 查询输入相同的输出，尽管它是针对具有不同角色的不同租户的。这是因为此调用是针对的，`/tenant_b`而 RBAC `view_data_role` 中的数据仍具有与之关联的`viewData`权限。要对强制执行相同类型的访问控制`/updateData`，可以使用类似的 OPA 规则：

```
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")
}
```

该规则在功能上与`allowViewData`规则相同，但它验证的是不同的路径和输入法。该规则仍可确保租户隔离，并检查租户定义的角色是否向 API 调用者授予权限。要了解如何强制执行此操作，请检查以下 API 调用的查询输入`/updateData/tenant_b`：

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

使用`allowUpdateData`规则评估此查询输入时，会返回以下授权决定：

```
{
    "allowUpdateData": false
}
```

此呼叫将不会获得授权。尽管 API 调用者关联了正确的，`tenant_id`并且正在使用经批准的方法调用 API，但还是租户`view_data_role`定义`input.role`的。`view_data_role`没有`updateData`权限；因此，对的调用`/updateData`是未经授权的。对于拥有. 的`tenant_b`用户来说，此调用本来是成功的`update_data_role`。