Cedar Policy Language Guide
Learn the fundamentals of Cedar, the high-performance policy language from AWS, for building secure and auditable application authorization.
What You'll Learn
📋 Prerequisites
- Understanding of authorization concepts (principal, action, resource)
- Familiarity with JSON data structures
- A basic understanding of application security
- Access to a command line/terminal
🎯 What You'll Learn
- Cedar's core design principles: performance, safety, and analysis
- The Principal, Action, Resource, Context (PARC) authorization model
- How to write
permitandforbidpolicies using Cedar's syntax - Using a schema to validate policies and prevent errors
- How to test and evaluate policies using the Cedar CLI
- How Cedar integrates with AWS Verified Permissions
🏷️ Topics Covered
Cedar Policy Language Tutorial: AWS Verified Permissions Guide
Cedar is an open-source language for defining access control policies. Developed by AWS, it's designed to be ergonomic for developers, fast enough for critical authorization paths, and safe enough for security reviews. Unlike IAM, which governs access to AWS resources, Cedar is designed for developers to control access within their own applications.
It is the engine behind AWS Verified Permissions and is built on a simple but powerful model: "permit principals to perform actions on resources when specified conditions are met."
Human-Readable Policies
Cedar policies are designed to be intuitive and easy to read, resembling natural language sentences. This simplifies writing, reviewing, and auditing.
Schema-Based Validation
You can define a schema for your application's entities and attributes. Cedar's validator uses this schema to catch errors in your policies before they are ever evaluated.
Formal Verification
Cedar's design is backed by automated reasoning and formal verification, providing mathematical assurances about its behavior, such as "a deny policy always overrides a permit."
How to Write Cedar Policies: Syntax and Examples for Beginners
Cedar is built on two core concepts: the policy structure and the entity-attribute model.
The Policy Structure: permit and forbid
A Cedar policy either grants (permit) or denies (forbid) permission. The structure is straightforward:
basic-policy.cedar
// A 'permit' policy grants access
permit (
principal,
action,
resource
)
when { // Optional conditions
//...
}
unless { // Optional exceptions
//...
};
// A 'forbid' policy explicitly denies access.
// It overrides any and all 'permit' policies.
forbid (
principal,
action,
resource
);Entities and Attributes
Everything in Cedar is an entity, which has a type and a unique ID (e.g., User::"alice"). Entities can also have attributes, which are key-value pairs (e.g., department: "finance").
🔧 Core Entity Types
- Principal: The entity requesting to perform an action (e.g., a user, a service).
- Action: The action the principal wants to perform (e.g.,
Action::"viewPhoto",Action::"deleteAlbum"). - Resource: The entity being acted upon (e.g., a photo, a file).
- Context: Optional request-time information, like the source IP address or time of day.
AWS Verified Permissions Cedar Guide: Step-by-Step Tutorial
Let's write policies for a simple photo-sharing application. First, we define a schema.
Define a Schema
The schema describes our application's entity types and the attributes they can have. This allows the Cedar validator to catch typos and other errors.
Write the Policies
Create authorization logic in a .cedar file using permit and forbid statements.
1. Define a Schema
photo_app_schema.json
{
"PhotoApp": {
"entityTypes": {
"User": {
"shape": {
"type": "Record",
"attributes": {
"department": {"type": "String"},
"jobLevel": {"type": "Long"}
}
}
},
"Photo": {
"shape": {
"type": "Record",
"attributes": {
"isPrivate": {"type": "Boolean"},
"owner": {"type": "Entity", "name": "User"},
"album": {"type": "Entity", "name": "Album"}
}
}
},
"Album": {
"memberOfTypes": ["User"]
}
},
"actions": {
"view": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Photo"] } },
"delete": { "appliesTo": { "principalTypes": ["User"], "resourceTypes": ["Photo"] } }
}
}
}2. Write the Policies
Now we write our authorization logic in a .cedar file.
policies.cedar
// Policy 1: Users can view their own photos.
permit(
principal,
action == Action::"view",
resource
)
when {
principal == resource.owner
};
// Policy 2: Users can view any photo in an album they are a member of.
permit(
principal,
action == Action::"view",
resource
)
when {
principal in resource.album
};
// Policy 3: Forbid viewing of private photos, unless the viewer is the owner.
// This is a powerful safety guardrail.
forbid(
principal,
action == Action::"view",
resource
)
when {
resource.isPrivate == true
}
unless {
principal == resource.owner
};
// Policy 4: Users with job level 5 or higher can delete any photo.
permit(
principal,
action == Action::"delete",
resource
)
when {
principal.jobLevel >= 5
};Cedar Policy Language Examples: Testing and Validation Best Practices
We can use the Cedar CLI to test our work. First, install the cedar-policy Rust crate.
Install Cedar CLI
cargo install cedar-policy-cli1. Validate Policies Against the Schema
This command checks for correctness, such as ensuring we didn't misspell an attribute (e.g., resource.ownr instead of resource.owner).
Validate Policies
cedar validate --schema photo_app_schema.json --policies policies.cedar2. Test an Authorization Request
Let's ask Cedar for a decision. We need to provide the request context and a file describing our entities (who owns what).
entities.json
[
{
"uid": {"type": "User", "id": "alice"},
"attrs": {"department": "eng", "jobLevel": 6}
},
{
"uid": {"type": "User", "id": "bob"},
"attrs": {"department": "sales", "jobLevel": 3}
},
{
"uid": {"type": "Album", "id": "vacation_pics"},
"parents": [{"type": "User", "id": "bob"}]
},
{
"uid": {"type": "Photo", "id": "photo123"},
"attrs": {
"isPrivate": true,
"owner": {"type": "User", "id": "alice"},
"album": {"type": "Album", "id": "vacation_pics"}
}
}
]Now, let's see if Bob can view Alice's private photo (he shouldn't be able to).
Authorize Request
cedar authorize \
--principal 'User::"bob"' \
--action 'Action::"view"' \
--resource 'Photo::"photo123"' \
--policies policies.cedar \
--entities entities.json
# OPA Response:
# DENYThe decision is DENY. Even though Bob is a member of the album (Policy 2), the forbid policy (Policy 3) for private photos takes precedence.
Cedar Policy Language Syntax: Production Best Practices Guide
Schema First
Always start by defining a schema. It enables static validation that catches errors early and serves as clear documentation.
Use forbid for Guardrails
Use permit policies to grant permissions broadly and forbid policies to create specific, non-negotiable security boundaries.
Attribute-Based Control (ABAC)
Leverage attributes on your principal and resource entities to create flexible policies that don't need to be updated when a new user or resource is added.
Policy Templates
For recurring patterns, use policy templates. These are policies with placeholders (e.g., ?principal, ?resource) that can be instantiated with specific entities.
🎉 Congratulations!
Cedar Mastery Achieved
You have learned the core concepts of the Cedar policy language. You are now able to:
Understand Cedar's Design
Comprehend Cedar's safety-first design principles and formal verification approach.
Write Effective Policies
Create permit and forbid policies using entities and attributes effectively.
Validate with Schema
Use schemas to validate your policies and prevent common errors before deployment.
Test Authorization
Evaluate authorization requests to get reliable Allow or Deny decisions.