DEV Community

Cover image for Authorization and Amazon Verified Permissions - A New Way to Manage Permissions Part XIII: Cloudformation
Daniel Aniszkiewicz for AWS Community Builders

Posted on • Edited on

Authorization and Amazon Verified Permissions - A New Way to Manage Permissions Part XIII: Cloudformation

Welcome back to my blog post series dedicated to building authorization using Cedar and Amazon Verified Permissions. In a previous blogpost we've learned about usage AVP with IaC Terraform. Today, we will cover the topic of how to use AVP with Cloudformation.

Introduction

Cloudformation (IaC) does not need to be introduced to anyone, plus if you read the previous blogpost, the terraform provider (CC) we used is based on Cloudformation. Moreover, you will notice a lot of similarities, after all, we are implementing the same scenario, but with a different tool.

All the Cloudformation references you can check here.

Let's assume that we would like to recreate one of the scenarios from avp-cli in Cloudformation, for example, the documents scenario. This is a basic scenario with a document management platform schema and two policies (Allow all users to view all documents and Forbid user X from viewing any documents).

To recreate this, we need:

policy store

A policy store, which is a container for our policies. In such a container, we keep all policies, as well as the schema. It's important to remember that later when using AVP for authorization requests, we send requests against a specific policy store.

A schema is the structure of all available entities for a given policy store. In short, it defines what entities are available for our authorization system, including available actions, principals, and resources. The schema not only informs us about the available entities but also validates the correctness of authorization requests.

Finally, we will also need to add our policies.

Let's build!

The code for today's blog post can be found here.

The example we will create is not intended for production use; it has been created for educational purposes. However, in the future, In the next weeks, I will try to recreate all scenarios from avp-cli to Cloudformation. It will be in this repository, as well as a couple of more advanced, production-wise examples.

For our example, we will need two types of resources from the Cloudformation:

Now that we know everything, we can start building the project with Cloudformation.

Let's start with empty avp.yaml for our Cloudformation configuration.

AWSTemplateFormatVersion: "2010-09-09"

Description: >
  IaC for AVP

Resources:
Enter fullscreen mode Exit fullscreen mode

We've started from adding AWSTemplateFormatVersion to tell CloudFormation which template version we're adhering to. The Description part refers to the overall description of the template.

Now we can focus on the Resources section where we declare the AWS resources you want CloudFormation to create or manage.

Create a policy store

Now our focus will be to add a configuration for a policy store. First, we need to define the validation_settings. It specifies the validation setting for this policy store. In our use-case, we would like to be strict as we will add schema and check the validation of our authorization requests.

  PolicyStore:
    Type: AWS::VerifiedPermissions::PolicyStore
    Properties:
      Description: "Policy store for the documents scenario"
      ValidationSettings:
        Mode: STRICT
Enter fullscreen mode Exit fullscreen mode

We're declaring a resource of type AWS::VerifiedPermissions::PolicyStore, which represents a container in AVP where we will store policies. The Description nested within Properties gives our policy store a description. Moreover, we've added validation settings setup as strict.

Now if we deploy it, we will have an empty policy store with a validation mode setup as strict.

Adding Schema

Now we want to add a schema, which you can find here. As you can see, it is a JSON file that we need to have. Let's update the configuration

...
  PolicyStore:
    Type: AWS::VerifiedPermissions::PolicyStore
    Properties:
      Description: "Policy store for the documents scenario"
      ValidationSettings:
        Mode: STRICT
      Schema:
        CedarJson: |
          {
            "DocumentManagementPlatform": {
              "entityTypes": {
                "User": {
                  "shape": {
                    "type": "Record",
                    "attributes": {}
                  }
                },
                "Document": {
                  "shape": {
                    "type": "Record",
                    "attributes": {}
                  }
                }
              },
              "actions": {
                "View": {
                  "appliesTo": {
                    "principalTypes": ["User"],
                    "resourceTypes": ["Document"]
                  }
                },
                "Edit": {
                  "appliesTo": {
                    "principalTypes": ["User"],
                    "resourceTypes": ["Document"]
                  }
                }
              }
            }
          }
Enter fullscreen mode Exit fullscreen mode

This way, we've added a schema to our policy store.

Policy Store ID

Before we jump into the policies, we need to tackle one important thing in our template. We would like to know the ID of our policy store, so we can easily find it, and check our Policy Store, or pass it to the resources that will use it (for instance Lambda function which will make requests to AVP later on).

So to have it, we need to use Cloudformation Outputs which specifies the values that are returned whenever you view your stack's properties.

We can easily add it like:

...
Outputs:
  PolicyStoreId:
    Description: "Identifier of the Amazon Verified Permissions policy store."
    Value: !Ref PolicyStore
Enter fullscreen mode Exit fullscreen mode

The actual !Ref directive within Value is an intrinsic function used to retrieve the ID of a resource defined in the same template. In our case, !Ref PolicyStore gets the ID of the PolicyStore resource created by the stack.

Adding policies

Now, it's time for the final step, adding policies:

Now we can add:

  AllowPolicy:
    Type: AWS::VerifiedPermissions::Policy
    Properties:
      PolicyStoreId: !Ref PolicyStore
      Definition:
        Static:
          Description: "Allow all users to view all documents"
          Statement: |
            permit (
                principal,
                action in [DocumentManagementPlatform::Action::"View"],
                resource
            );

  DenyPolicy:
    Type: AWS::VerifiedPermissions::Policy
    Properties:
      PolicyStoreId: !Ref PolicyStore
      Definition:
        Static:
          Description: "Forbid user X from viewing any documents"
          Statement: |
            forbid(
              principal == DocumentManagementPlatform::User::"xyz",
              action in [DocumentManagementPlatform::Action::"View"],
              resource
            );
Enter fullscreen mode Exit fullscreen mode
  • The Type: AWS::VerifiedPermissions::Policy is used for creating two policies.
  • As any policy is part of that policy store, we needed to reference the Policy Store ID.
  • For the Definition, we've used static, which indicates that the policy is static – it's not dynamically created from a template but is rather a fixed definition.
  • For the Statement we just need to pass the definition in Cedar language.

The whole file will look like this:

AWSTemplateFormatVersion: "2010-09-09"

Description: >
  IaC for AVP

Resources:
  PolicyStore:
    Type: AWS::VerifiedPermissions::PolicyStore
    Properties:
      Description: "Policy store for the documents scenario"
      ValidationSettings:
        Mode: STRICT
      Schema:
        CedarJson: |
          {
            "DocumentManagementPlatform": {
              "entityTypes": {
                "User": {
                  "shape": {
                    "type": "Record",
                    "attributes": {}
                  }
                },
                "Document": {
                  "shape": {
                    "type": "Record",
                    "attributes": {}
                  }
                }
              },
              "actions": {
                "View": {
                  "appliesTo": {
                    "principalTypes": ["User"],
                    "resourceTypes": ["Document"]
                  }
                },
                "Edit": {
                  "appliesTo": {
                    "principalTypes": ["User"],
                    "resourceTypes": ["Document"]
                  }
                }
              }
            }
          }

  AllowPolicy:
    Type: AWS::VerifiedPermissions::Policy
    Properties:
      PolicyStoreId: !Ref PolicyStore
      Definition:
        Static:
          Description: "Allow all users to view all documents"
          Statement: |
            permit (
                principal,
                action in [DocumentManagementPlatform::Action::"View"],
                resource
            );

  DenyPolicy:
    Type: AWS::VerifiedPermissions::Policy
    Properties:
      PolicyStoreId: !Ref PolicyStore
      Definition:
        Static:
          Description: "Forbid user X from viewing any documents"
          Statement: |
            forbid(
              principal == DocumentManagementPlatform::User::"xyz",
              action in [DocumentManagementPlatform::Action::"View"],
              resource
            );

Outputs:
  PolicyStoreId:
    Description: "Identifier of the Amazon Verified Permissions policy store."
    Value: !Ref PolicyStore
Enter fullscreen mode Exit fullscreen mode

Deployment

Now that we have everything, let's try to deploy our template.

Navigate to the AWS console, and open Cloudformation service. Inside the Cloudformation service in the AWS console, press Create a new stack with the option With existing resources (import resources). Use the Template is ready.

Overall, we have 3 options for deploying our Cloudformation, either by storing it in S3 (if you use SAM or Serverless Framework you will be aware of that), you can upload it (which we will use), or you can deploy it from your repo. Here is a great article on that.

This time, use the Upload a template file option.

  • Attach the avp.yaml file and then for Stack name create a name, you can leave the rest of the configuration unchanged.

upload

If we check the Designer option it will look like this:

Designer

  • Attach the avp.yaml file and then for Stack name create a name, you can leave the rest of the configuration unchanged.
  • Review a stack, accept capabilities, and deploy.

  • After a couple of minutes everything will be deployed. In the resources tab, you can see 3 resources (AVP policy store, and 2 access policies), and in the output section, you will AVP policy store ID, which you can grab and check the resources in the AVP service.

Resources

Outputs

Feel free to grab the Policy Store ID and check it in AVP.

AVP

Final words

As you can see, it's quite simple to create a basic project with AVP using Cloudformation.

If you're interested in more Cloudformation templates, I've recreated all of the AVP-CLI scenarios in CF, you can find them here. In the next blogpost we will tackle AVP getting started feature.

That's all for today. I encourage you to experiment on your own.

Top comments (0)