

# Implementing a PDP by using Amazon Verified Permissions
<a name="avp"></a>

Amazon Verified Permissions is a scalable, fine-grained permissions management and authorization service that you can use to implement a policy decision point (PDP). As a policy engine, it can help your application verify user actions in real time and highlight permissions that are overly privileged or invalid. It helps your developers build more secure applications faster by externalizing authorization and centralizing policy management and administration. By separating authorization logic from application logic, Verified Permissions supports policy decoupling. 

By using Verified Permissions to implement a PDP and implementing least privilege and continual verification within applications, developers can align their application access with [Zero Trust](https://aws.amazon.com/security/zero-trust/) principles. Additionally, security and audit teams can better analyze and audit who has access to which resources within an application. Verified Permissions uses [Cedar](https://www.cedarpolicy.com/en), which is a purpose-built and security-first, open source policy language, to define policy-based access controls based on role-based access control (RBAC) and attribute-based access control (ABAC) for more granular, context-aware access control.

Verified Permissions provides some useful features for SaaS applications, such as the ability to enable multi-tenant authorization by using multiple identity providers such as Amazon Cognito, Google, and Facebook. Another Verified Permissions feature that is particularly helpful for SaaS applications is support for custom roles on a per-tenant basis. If you're designing a customer relationship management (CRM) system, one tenant might define granularity of access by sales opportunities based on one particular set of criteria. Another tenant might have another definition. The underlying permissions systems in Verified Permissions can support these variations, which makes it an excellent candidate for SaaS use cases. Verified Permissions also supports the ability to write policies that apply to all tenants, so it is straightforward to apply guardrail policies to prevent unauthorized access as a SaaS provider.

![\[Using Amazon Verified Permissions to implement a PDP\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp.png)


**Why use Verified Permissions?**

Use Verified Permissions with an identity provider such as [Amazon Cognito](https://aws.amazon.com/cognito/) for a more dynamic, policy-based access management solution for your applications. You can build applications that help users share information and collaborate while maintaining the security, confidentiality, and privacy of their data. Verified Permissions helps reduce operational costs by providing you with a fine-grained authorization system to enforce access based on the roles and attributes of your identities and resources. You can define your policy model, create and store policies in a central location, and evaluate access requests in milliseconds.

In Verified Permissions, you can express permissions by using a simple, human-readable declarative language called Cedar. Policies that are written in Cedar can be shared across teams regardless of the programming language used by each team's application.

**What to consider when you use Verified Permissions**

In Verified Permissions, you can create policies and automate them as part of provisioning. You can also create policies at runtime as part of application logic. As a best practice, you should use a continuous integration and continuous deployment (CI/CD) pipeline to administer, modify, and track policy versions when you create policies as part of tenant onboarding and provisioning. Alternatively, an application can administer, modify, and track policy versions; however, application logic doesn't inherently perform this functionality. To support these capabilities in your application, you must explicitly design your application to implement this functionality.

If it's necessary to provide external data from other sources to reach an authorization decision, this data must be retrieved and provided to Verified Permissions as part of the authorization request. Additional context, entities, and attributes are not retrieved by default with this service.

# Cedar overview
<a name="cedar"></a>

Cedar is a flexible, extensible, and scalable policy-based access control language that helps developers express application permissions as policies. Administrators and developers can define policies that permit or forbid users to act on application resources. Multiple policies can be attached to a single resource. When a user of your application tries to perform an action on a resource, your application requests authorization from the Cedar policy engine. Cedar evaluates the applicable policies and returns an `ALLOW` or `DENY` decision. Cedar supports authorization rules for any type of principal and resource, allows for role-based access control (RBAC) and attribute-based access control (ABAC), and supports analysis through automated reasoning tools.

Cedar lets you separate your business logic from the authorization logic. When you make requests from your application's code, you call Cedar's authorization engine to determine whether the request is authorized. If it's authorized (the decision is `ALLOW`), your application can perform the requested operation. If it isn't authorized (the decision is `DENY`), your application can return an error message. Major features of Cedar include:
+ **Expressiveness** – Cedar is purpose-built to support authorization use cases and was developed with human readability in mind.
+ **Performance** – Cedar supports indexing policies for quick retrieval, and provides fast and scalable real-time evaluation with bounded latency.
+ **Analysis** – Cedar supports analysis tools that can optimize your policies and verify your security model.

For more information, see the [Cedar website](https://www.cedarpolicy.com/).

# Example 1: Basic ABAC with Verified Permissions and Cedar
<a name="avp-basic-abac-examples"></a>

In this example scenario, Amazon Verified Permissions is used to determine which users are allowed to access information in a fictional Payroll microservice. This section includes Cedar code snippets to demonstrate how you can use Cedar to render access control decisions. These examples aren't intended to provide a full exploration of the capabilities provided by Cedar and Verified Permissions. For a more thorough overview of Cedar, see the [Cedar documentation](https://docs.cedarpolicy.com/).

In the following diagram, we would like to enforce two general business rules that are associated with the `viewSalary` `GET` method: *Employees can view their own salary* and *Employees can view the salary of anyone who reports to them.* You can enforce these business rules by using Verified Permissions policies.

![\[Example of a basic ABAC implementation with Amazon Verified Permissions and Cedar to implement a PDP\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-1.png)


*Employees can view their own salary.*

In Cedar, the basic construct is an *entity*, which represents a principal, action, or a resource. To make an authorization request and start an evaluation with a Verified Permissions policy, you need to provide a *principal,* an *action*, a *resource,* and a* list of entities.*
+ The principal (`principal`) is the logged in user or role.
+ The action (`action`) is the operation that is evaluated by the request.
+ The resource (`resource`) is the component that the action is accessing.
+ The list of entities (`entityList`) contains all the required entities needed to evaluate the request.

To satisfy the business rule *Employees can view their own salary*, you can provide a Verified Permissions policy such as the following.

```
permit (
    principal,
    action == Action::"viewSalary",
    resource
)
when {
    principal == resource.owner
};
```

This policy evaluates to `ALLOW` if the `Action` is `viewSalary` and the resource in the request has an attribute owner that is equal to the principal. For example, if Bob is the logged in user who requested the salary report and is also the owner of the salary report, the policy evaluates to `ALLOW`.

The following authorization request is submitted to Verified Permissions to be evaluated by the sample policy. In this example, Bob is the logged in user who makes the `viewSalary` request. Therefore, Bob is the principal of the entity type `Employee`. The action Bob is trying to perform is `viewSalary,` and the resource that `viewSalary` will display is `Salary-Bob` with the type `Salary`. In order to evaluate if Bob can view the `Salary-Bob` resource, you need to provide an entity structure that links the type `Employee` with a value of `Bob` (the principal) to the owner attribute of the resource that has the type `Salary` . You provide this structure in an `entityList`, where the attributes associated with `Salary` include an owner, which specifies an `entityIdentifier` that contains the type `Employee` and value `Bob`. Verified Permissions compares the `principal` provided in the authorization request to the `owner` attribute that is associated with the `Salary` resource to make a decision.

```
{
  "policyStoreId": "PAYROLLAPP_POLICYSTOREID",
  "principal": {
    "entityType": "PayrollApp::Employee",
    "entityId": "Bob"
  },
  "action": {
    "actionType": "PayrollApp::Action",
    "actionId": "viewSalary"
  },
  "resource": {
    "entityType": "PayrollApp::Salary",
    "entityId": "Salary-Bob"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
          "entityType": "PayrollApp::Salary",
          "entityId": "Salary-Bob"
        },
        "attributes": {
          "owner": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Bob"
            }
          }
        }
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Bob"
        },
        "attributes": {}
      }
    ]
  }
}
```

The authorization request to Verified Permissions returns the following as output, where the attribute `decision` is either `ALLOW` or `DENY`.

```
{
    "determiningPolicies": 
        [ 
            {
                "determiningPolicyId": "PAYROLLAPP_POLICYSTOREID" 
            }
        ],
    "decision": "ALLOW",
    "errors": [] 
}
```

In this case, because Bob was trying to view his own salary, the authorization request sent to Verified Permissions evaluates to `ALLOW`. However, our objective was to use Verified Permissions to enforce two business rules. The business rule that states the following should also be true:

*Employees can view the salary of anyone who reports to them.*

To satisfy this business rule, you can provide another policy. The following policy evaluates to `ALLOW` if the action is `viewSalary` and the resource in the request has an attribute `owner.manager` that is equal to the principal. For example, if Alice is the logged in user who requested the salary report and Alice is the manager of the report's owner, the policy evaluates to `ALLOW`.

```
permit (
    principal,
    action == Action::"viewSalary",
    resource
)
when {
    principal == resource.owner.manager
};
```

The following authorization request is submitted to Verified Permissions to be evaluated by the sample policy. In this example, Alice is the logged in user who makes the `viewSalary` request. Therefore Alice is the principal and the entity is of the type `Employee`. The action Alice is trying to perform is `viewSalary`, and the resource that `viewSalary` will display is of the type `Salary` with a value of `Salary-Bob`. In order to evaluate if Alice can view the `Salary-Bob` resource, you need to provide an entity structure that links the type `Employee` with a value of `Alice` to the `manager` attribute, which must then be associated with the `owner` attribute of the type `Salary` with a value of `Salary-Bob`. You provide this structure in an `entityList`, where the attributes associated with `Salary` include an owner, which specifies an `entityIdentifier` that contains the type `Employee` and value `Bob`. Verified Permissions first checks the `owner` attribute, which evaluates to the type `Employee` and the value `Bob`. Then, Verified Permissions evaluates the `manager` attribute that's associated with `Employee` and compares it to the provided principal to make an authorization decision. In this case, the decision is `ALLOW` because the `principal `and `resource.owner.manager` attributes are equivalent.

```
{
  "policyStoreId": "PAYROLLAPP_POLICYSTOREID",
  "principal": {
    "entityType": "PayrollApp::Employee",
    "entityId": "Alice"
  },
  "action": {
    "actionType": "PayrollApp::Action",
    "actionId": "viewSalary"
  },
  "resource": {
    "entityType": "PayrollApp::Salary",
    "entityId": "Salary-Bob"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Alice"
        },
        "attributes": {
          "manager": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "None"
            }
          }
        },
        "parents": []
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Salary",
          "entityId": "Salary-Bob"
        },
        "attributes": {
          "owner": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Bob"
            }
          }
        },
        "parents": []
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Bob"
        },
        "attributes": {
          "manager": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Alice"
            }
          }
        },
       "parents": []
      }
    ]
  }
}
```

So far in this example, we provided the two business rules associated with the `viewSalary` method, *Employees can view their own salary* and *Employees can view the salary of anyone who reports to them*, to Verified Permissions as policies to satisfy the conditions of each business rule independently. You can also use a single Verified Permissions policy to satisfy the conditions of both business rules:

*Employees can view their own salary and the salary of anyone who reports to them.*

When you use the previous authorization request, the following policy evaluates to `ALLOW` if the action is `viewSalary` and the resource in the request has an attribute `owner.manager` that is equal to the `principal`, or an attribute `owner` that is equal to the `principal`. 

```
permit (
    principal,
    action == PayrollApp::Action::"viewSalary",
    resource
)
when {
    principal == resource.owner.manager ||
    principal == resource.owner
};
```

For example, if Alice is the logged in user who requests the salary report, and if Alice is either the manager of the owner or the owner of the report, then the policy evaluates to `ALLOW`.

For more information about using logical operators with Cedar policies, see the [Cedar documentation](https://docs.cedarpolicy.com/policies/syntax-operators.html).

# Example 2: Basic RBAC with Verified Permissions and Cedar
<a name="avp-basic-rbac-examples"></a>

This example uses Verified Permissions and Cedar to demonstrate basic RBAC. As mentioned previously, Cedar's basic construct is an entity. Developers define their own entities and can optionally create relationships between entities. The following example includes three type of entities: `Users`, `Roles`, and `Problems`. `Students` and `Teachers` can be considered entities of the type `Role,` and each `User` can be associated with zero or any of the `Roles`.

![\[Example of a basic RBAC implementation with Amazon Verified Permissions and Cedar to implement a PDP\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-2.png)


In Cedar, these relationships are expressed by linking the `Role` `Student` to the `User` `Bob` as its parent. This association logically groups all the student users in one group. For more information about grouping in Cedar, see the [Cedar documentation](https://docs.cedarpolicy.com/overview/terminology.html#term-group).

The following policy evaluates to the decision `ALLOW` for the action `submitProblem,` for all principals that are linked to the logical group `Students` of the type `Role`.

```
permit (
    principal in ElearningApp::Role::"Students",
    action == ElearningApp::Action::"submitProblem",
    resource
);
```

The following policy evaluates to the decision `ALLOW` for the action `submitProblem` or `answerProblem`, for all principals that are linked to the logical group `Teachers` of the type `Role`.

```
permit (
    principal in ElearningApp::Role::"Teachers",
    action in [
        ElearningApp::Action::"submitProblem",
        ElearningApp::Action::"answerProblem"
    ],
    resource
);
```

In order to evaluate requests with these policies, the evaluation engine needs to know whether the principal referenced within the authorization request is a member of the appropriate group. Therefore, the application has to pass relevant group membership information to the evaluation engine as part of the authorization request. This is done through the `entities` property, which enables you to provide the Cedar evaluation engine with attribute and group membership data for the principal and resource involved in the authorization call. In the following code, group membership is indicated by defining `User::"Bob"` as having a parent called `Role::"Students"`.

```
{
  "policyStoreId": "ELEARNING_POLICYSTOREID",
  "principal": {
    "entityType": "ElearningApp::User",
    "entityId": "Bob"
  },
  "action": {
    "actionType": "ElearningApp::Action",
    "actionId": "answerProblem"
  },
  "resource": {
    "entityType": "ElearningApp::Problem",
    "entityId": "SomeProblem"
  },
  "entities": {
    "entityList": [
        {
            "identifier": {
                "entityType": "ElearningApp::User",
                "entityId": "Bob"
            },
            "attributes": {},
            "parents": [
                {
                    "entityType": "ElearningApp::Role",
                    "entityId": "Students"
                } 
            ]
        },
        {
          "identifier": {
            "entityType": "ElearningApp::Problem",
            "entityId": "SomeProblem"
          },
          "attributes": {},
          "parents": []
        }
      ]
  }
}
```

In this example, Bob is the logged in user who makes the `answerProblem` request. Therefore, Bob is the principal and the entity is of the type `User`. The action Bob is trying to perform is `answerProblem`. In order to evaluate if Bob can perform the `answerProblem` action, you need to provide an entity structure that links the entity `User` with a value of `Bob` and assigns his group membership by listing a parent entity as `Role::"Students"`. Because entities in the user group `Role::"Students"` are allowed only to perform the action `submitProblem`, this authorization request evaluates to `DENY`.

On the other hand, if the type `User` that has a value of `Alice` and is a part of the group `Role::"Teachers"`  tries to perform the `answerProblem` action, the authorization request evaluates to `ALLOW`, because the policy dictates that principals in the group `Role::"Teachers"` are allowed to perform the action `answerProblem` on all resources. The following code shows this type of  authorization request that evaluates to `ALLOW`.

```
{
  "policyStoreId": "ELEARNING_POLICYSTOREID",
  "principal": {
    "entityType": "ElearningApp::User",
    "entityId": "Alice"
  },
  "action": {
    "actionType": "ElearningApp::Action",
    "actionId": "answerProblem"
  },
  "resource": {
    "entityType": "ElearningApp::Problem",
    "entityId": "SomeProblem"
  },
  "entities": {
    "entityList": [
        {
            "identifier": {
                "entityType": "ElearningApp::User",
                "entityId": "Alice"
            },
            "attributes": {},
            "parents": [
                {
                    "entityType": "ElearningApp::Role",
                    "entityId": "Teachers"
                } 
            ]
        },
        {
            "identifier": {
                "entityType": "ElearningApp::Problem",
                "entityId": "SomeProblem"
            },
            "attributes": {},
            "parents": []
        }
      ]
  }
}
```

# Example 3: Multi-tenant access control with RBAC
<a name="avp-mt-abac-examples"></a>

To elaborate on the previous RBAC example, you can expand your requirements to include SaaS multi-tenancy, which is a common requirement for SaaS providers. In multi-tenant solutions, resource access is always provided on behalf of a given tenant. That is, users of Tenant A cannot view the data of Tenant B, even if that data is logically or physically collocated in a system. The following example illustrates how you can implement tenant isolation by using multiple [Verified Permissions policy stores](https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/policy-stores.html), and how you can employ user roles to define permissions within the tenant. 

Using the Per Tenant Policy Store design pattern is a best practice for maintaining tenant isolation while implementing access control with Verified Permissions. In this scenario, Tenant A and Tenant B user requests are verified against separate policy stores, `DATAMICROSERVICE_POLICYSTORE_A` and `DATAMICROSERVICE_POLICYSTORE_B`, respectively. For more information about Verified Permissions design considerations for multi-tenant SaaS applications, see the [Verified Permissions multi-tenant design considerations](avp-design-considerations.md) section.

![\[Example of multi-tenant access control with RBAC, Amazon Verified Permissions, and Cedar\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-3.png)


The following policy resides in the `DATAMICROSERVICE_POLICYSTORE_A` policy store. It verifies that the principal will be a part of the group `allAccessRole` of type `Role`. In this case, the principal will be allowed to perform the `viewData` and `updateData` actions on all resources that are associated with Tenant A.

```
permit (
    principal in MultitenantApp::Role::"allAccessRole",
    action in [
        MultitenantApp::Action::"viewData",
        MultitenantApp::Action::"updateData"
    ],
    resource
);
```

The following policies reside in the `DATAMICROSERVICE_POLICYSTORE_B` policy store. The first policy verifies that the principal is part of the `updateDataRole` group of type `Role`. Assuming that is the case, it gives permission to principals to perform the `updateData` action on resources that are associated with Tenant B.

```
permit (
    principal in MultitenantApp::Role::"updateDataRole",
    action == MultitenantApp::Action::"updateData",
    resource
);
```

This second policy mandates that principals that are a part of the `viewDataRole` group of type `Role` should be allowed to perform the `viewData` action on resources that are associated with Tenant B.

```
permit (
    principal in MultitenantApp::Role::"viewDataRole",
    action == MultitenantApp::Action::"viewData",
    resource
);
```

The authorization request made from Tenant A needs to be sent to the `DATAMICROSERVICE_POLICYSTORE_A` policy store and verified by the policies that belong to that store. In this case, it's verified by the first policy discussed earlier as part of this example. In this authorization request, the principal of type `User` with a value of `Alice` is requesting to perform the `viewData` action. The principal belongs to the group `allAccessRole` of type `Role`. Alice is trying to perform the `viewData` action on the `SampleData` resource. Because Alice has the `allAccessRole` role, this evaluation results in an `ALLOW` decision.

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_A",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "viewData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

If, instead, you view a request made from Tenant B by `User Bob`, you will see something like the following authorization request. The request is sent to the `DATAMICROSERVICE_POLICYSTORE_B` policy store because it originates from Tenant B. In this request, the principal `Bob` wants to perform the action `updateData` on the resource `SampleData`. However, `Bob` is not a part of a group that has access to the action `updateData` on that resource. Therefore, the request results in a `DENY` decision.

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_B",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Bob"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "updateData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Bob"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "viewDataRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

In this third example, `User Alice `tries to perform the `viewData` action on the resource `SampleData`. This request is directed to the `DATAMICROSERVICE_POLICYSTORE_A` policy store because the principal `Alice` belongs to Tenant A. `Alice` is a part of the group `allAccessRole` of the type `Role`, which permits her to perform the `viewData` action on resources. As such, the request results in an `ALLOW` decision.

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_A",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "viewData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

# Example 4: Multi-tenant access control with RBAC and ABAC
<a name="avp-mt-abac-rbac-examples"></a>

To enhance the RBAC example in the previous section, you can add attributes to users to create a RBAC-ABAC hybrid approach for multi-tenant access control. This example includes the same roles from the previous example, but adds the user attribute `account_lockout_flag` and the context parameter `uses_mfa`. The example also takes a different approach to implementing multi-tenant access control by using both RBAC and ABAC, and uses one shared policy store instead of a different policy store for each tenant. 

![\[Example of multi-tenant access control with RBAC, ABAC, Amazon Verified Permissions, and Cedar\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-4.png)


This example represents a multi-tenant SaaS solution in which you need to provide authorization decisions for Tenant A and Tenant B, similar to the previous example.

To implement the user lock feature, the example adds the attribute `account_lockout_flag` to the `User` entity principal in the authorization request. This flag locks user access to the system and will `DENY` all privileges to the locked out user. The `account_lockout_flag` attribute is associated with the `User` entity and is in effect for the `User` until the flag is actively revoked across multiple sessions. The example uses the `when` condition to evaluate `account_lockout_flag`.

The example also adds details about the request and session. The context information specifies that the session has been authenticated by using multi-factor authentication. To implement this validation, the example uses the `when` condition to evaluate the `uses_mfa` flag as part of the context field. For more information about best practices for adding context, see the [Cedar documentation](https://docs.cedarpolicy.com/auth/entities-syntax.html).

```
permit (
    principal in MultitenantApp::Role::"allAccessRole",
    action in [
        MultitenantApp::Action::"viewData",
        MultitenantApp::Action::"updateData"
    ],
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

This policy prevents access to resources unless the resource is in the same group as the requesting principal's `Tenant` attribute. This approach to maintaining tenant isolation is referred to as the *One Shared Multi-Tenant Policy Store* approach. For more information about Verified Permissions design considerations for multi-tenant SaaS applications, see the [Verified Permissions multi-tenant design considerations](avp-design-considerations.md) section.

The policy also ensures that the principal is a member of `allAccessRole` and restricts actions to `viewData` and `updateData`. Additionally, this policy verifies that `account_lockout_flag` is `false` and that the context value for `uses_mfa` evaluates to `true`.

Similarly, the following policy ensures that both the principal and resource are associated with the same tenant, which prevents cross-tenant access. This policy also ensures that the principal is a member of `viewDataRole` and restricts actions to `viewData`. Additionally, it verifies that the `account_lockout_flag` is `false` and that the context value for `uses_mfa` evaluates to `true`.

```
permit (
    principal in MultitenantApp::Role::"viewDataRole",
    action == MultitenantApp::Action::"viewData",
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

The third policy is similar to the previous one. The policy requires the resource to be a member of the group that corresponds to the entity that's represented by `principal.Tenant`. This ensures that both the principal and resource are associated with Tenant B, which prevents cross-tenant access. This policy ensures that the principal is a member of `updateDataRole` and restricts actions to `updateData`. Additionally, this policy verifies that the `account_lockout_flag` is `false` and that the context value for `uses_mfa` evaluates to `true`.

```
permit (
    principal in MultitenantApp::Role::"updateDataRole",
    action == MultitenantApp::Action::"updateData",
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

The following authorization request is evaluated by the three policies discussed earlier in this section. In this authorization request, the principal of type `User` and with a value of `Alice` makes an `updateData` request with the role `allAccessRole`. `Alice` has the attribute `Tenant` whose value is `Tenant::"TenantA"`. The action `Alice` is trying to perform is `updateData,` and the resource it will be applied to is `SampleData` of the type `Data`. `SampleData` has `TenantA` as a parent entity. 

According to the first policy in the `<DATAMICROSERVICE_POLICYSTOREID>` policy store, `Alice` can perform the `updateData` action on the resource, assuming that the conditions in the `when` clause of the policy are met. The first condition requires the `principal.Tenant` attribute to evaluate to `TenantA`. The second condition requires the principal's attribute `account_lockout_flag` to be `false`. The final condition requires the context `uses_mfa` to be `true`. Because all three conditions are met, the request returns an `ALLOW` decision.

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "updateData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "context": {
    "contextMap": {
        "uses_mfa": {
            "boolean": true
        }
    }
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {
            {
                "account_lockout_flag": {
                    "boolean": false
                },
                "Tenant": {
                   "entityIdentifier": {
                        "entityType":"MultitenantApp::Tenant",
                        "entityId":"TenantA"
                   }
                }
            }
        },
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
        },
     {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Tenant",
                "entityId": "TenantA"
            }
        ]
      }
    ]
  }
}
```

# Example 5: UI filtering with Verified Permissions and Cedar
<a name="avp-ui-filtering-examples"></a>

You can also use Verified Permissions to implement RBAC filtering of UI elements based on authorized actions. This is extremely valuable for applications that have context-sensitive UI elements that might be associated with specific users or tenants in the case of a multi-tenant SaaS application.

In the following example, `Users` of the `Role` `viewer` are not allowed to perform updates. For these users, the UI should not render any update buttons.

![\[Example of UI filtering with Amazon Verified Permissions and Cedar\]](http://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-5.png)


In this example, a single-page web application has four buttons. Which buttons are visible depends on the `Role` of the user who is currently logged in to the application. As the single-page web application renders the UI, it queries Verified Permissions to determine which actions the user is authorized to perform, and then generates the buttons based on the authorization decision.

The following policy specifies that the type `Role` with a value of `viewer` can view both users and data. An `ALLOW` authorization decision for this policy requires a `viewData` or `viewUsers` action, and also requires a resource to be associated with the type `Data` or `Users`. An `ALLOW` decision permits the UI to render two buttons: `viewDataButton` and `viewUsersButton`.

```
permit (
    principal in GuiAPP::Role::"viewer",
    action in [GuiAPP::Action::"viewData", GuiAPP::Action::"viewUsers"],
    resource 
)
when {
   resource in [GuiAPP::Type::"Data", GuiAPP::Type::"Users"]
};
```

The following policy specifies that the type `Role` with a value of `viewerDataOnly` can only view data. An `ALLOW` authorization decision for this policy requires a `viewData` action, and also requires a resource to be associated with the type `Data`. An `ALLOW` decision permits the UI to render the button `viewDataButton`.

```
permit (
    principal in GuiApp::Role::"viewerDataOnly",
    action in [GuiApp::Action::"viewData"],
    resource in [GuiApp::Type::"Data"] 
);
```

The following policy specifies that the type `Role` with a value of `admin` can edit and view data and users. An `ALLOW` authorization decision for this policy requires an action of `updateData`, `updateUsers`, `viewData,` or `viewUsers`, and also requires a resource to be associated with the type `Data` or `Users`. An `ALLOW` decision permits the UI to render all four buttons: `updateDataButton`, `updateUsersButton`, `viewDataButton`, and `viewUsersButton`.

```
permit (
    principal in GuiApp::Role::"admin",
    action in [
        GuiApp::Action::"updateData",
        GuiApp::Action::"updateUsers",
        GuiApp::Action::"viewData", 
        GuiApp::Action::"viewUsers"
       ],
    resource 
)
when {
   resource in [GuiApp::Type::"Data", GuiApp::Type::"Users"]
};
```