Basic policy construction in Cedar

A policy is a text document that includes the following elements:

  • Effect – The effect specifies the intent of the policy, to either permit or forbid any request that matches the scope and conditions specified in the policy.

  • Scope – The scope specifies the combination of principals, actions, and resources to which the policy applies. Inclusion of these elements is mandatory. A policy that has only a scope without additional conditions can be part of a role-based access control strategy.

  • Conditions(Optional) You can optionally provide additional conditions. These conditions must be satisfied for the policy to affect the evaluation of the authorization request. These conditions are expressed as when and unless clauses. You can use the conditions to evaluate the attributes of the principals, resources, and other elements that make up the context of the request. A policy that includes conditions can be part of an attribute-based access control strategy.

  • Annotations(Optional) An annotation is an arbitrary key-value pair that can be used by other services that read and process Cedar policies. An annotation has no impact on policy evaluation.

The policy must end with a semicolon character (;).

When the request exactly matches the scope, all of the when clauses evaluate to true, and all of the unless clauses evaluate to false, then that policy evaluates to true. This process repeats for all policies that are relevant to the principal and resources referenced by the request.

This guide includes examples that use simple entity identifiers, such as jane or bob for the name of an entity of type User. This is done to make the examples more readable. However, in a production system it is critical for security reasons that you use unique values that can’t be reused.

We recommend that you use values like universally unique identifiers (UUIDs). For example, if user jane leaves the company, and you later let someone else use the name jane, then that new user automatically gets access to everything granted by policies that still reference User::"jane".

Cedar can’t distinguish between the new user and the old. This applies to both principal and resource identifiers. Always use identifiers that are guaranteed unique and never reused to ensure that you don’t unintentionally grant access because of the presence of an old identifier in a policy.

Where you use a UUID for an entity, we recommend that you follow it with the // comment specifier and the ‘friendly’ name of your entity. This helps to make your policies easier to understand. For example:

principal == User::"a1b2c3d4-e5f6-a1b2-c3d4-EXAMPLE11111", // alice

Effect

The effect of the policy specifies whether Cedar should permit or forbid requests that evaluate as a match for the policy. The effect element can have one of the following values:

  • permit – If all elements in the policy match, then the policy results in an Allow.
  • forbid – If all elements in the policy match, then the policy results in a Deny.

After all policies in the policy store are evaluated, the results are combined as follows:

  • If at least one matching policy results in Allow and there are exactly zero policies that result in Deny, then the overall result of the evaluation is Allow.
  • If at least one matching policy results in Deny or if there are exactly zero policies that result in Allow, then the overall result of the evaluation is Deny.

The following are two key principles to remember that embody the previous two rules:

  • The default result, exemplified by an empty set of policies, is a Deny for the request, because there isn’t at least one policy that results in Allow. This is referred to as an implicit deny.
  • A Deny result for any policy evaluation results in an overall Deny for the request. This is referred to as an explicit deny. Important
    An explicit Deny for any one policy always overrides any Allow from other policies.

Effect examples

permit

The following permit example policy allows Alice to view a specific photo.

permit (
    principal == User::"alice", 
    action == Action::"view", 
    resource == Photo::"VacationPhoto94.jpg"
);

forbid

The following forbid example policy denies any action by any user except the resource’s owner on any resource that has the private attribute set to true, unless the principal making the request is the resource owner. This policy doesn’t explicitly allow anything; it only forbids when it matches. A resource owner making a request on a private resource must still have a separate policy that explicitly allows the action on the specified resource.

forbid (
    principal,
    action,
    resource
)
when {
    resource.private
}
unless {
    principal == resource.owner
};

Scope

A request always includes information that Cedar uses to answer the following three questions:

  • Principal –Who is making the request?
  • Action – What operation does the principal want to perform?
  • Resource – What does the principal want to perform the action on?

The scope section of a Cedar policy statement defines which values match the request.

principal

The principal element in a Cedar policy represents a role, user, service, or other identity that can make a request to perform an action on a resource in your application. If the principal making the request matches the principal defined in this policy statement, then this element matches.

The principal element must be present. If you specify only principal without an expression that constrains its scope, then the policy applies to any principal.

Examples of the principal element

//matches any principal entity of any type
principal

//matches only the one specified entity of type User
principal == User::"alice"

//matches any principal in the hierarchy of the specified Group
principal in Group::"alice_friends"

//matches any principal of type User
principal is User

//matches any principal of type User in the hierarchy of the specified Group
principal is User in Group::"alice_friends"

action

The action element in a Cedar policy is a list of the operations in your application for which this policy statement controls access. If the operation in the request matches one of the action items defined in this policy statement, then this element matches.

Examples of the action element

//matches any action
action

//matches only the one specified action
action == Action::"view"

//matches any of the listed actions
action in [Action::"listAlbums", Action::"listPhotos", Action::"view"]

//matches any action in the "admin" action group
action in Action::"admin"

resource

The resource element in a Cedar policy is a resource defined by your application that can be accessed or modified by the specified action.

The resource element must be present. If you specify only resource without an expression that constrains its scope, then the policy applies to any resource.

Examples of the resource element

//matches any resource
resource

//matches only the one specified resource of type Photo
resource == Photo::"VacationPhoto94.jpg"

//matches any resource that is in the hierarchy of the specified entity of type Album
resource in Album::"alice_vacation"

//matches any resource of type Photo
resource is Photo

//matches any resource of type Photo in the hierarchy of the specified Album
resource is Photo in Album::"alice_vacation"

Conditions

Conditions specify any additional constraints that Cedar must consider when deciding to allow or deny the request. Every when condition must evaluate to true and every unless condition must evaluate to false for the policy to match and contribute to the final decision. The conditions evaluate details that are unique to a particular access request. Consider a web service that accepts HTTP requests. The conditions for such a request might include things like the IP address from which the request originates, the HTTP headers in the request, the time of day that the request was sent, the user’s authentication posture, or detailed information about the query parameters in the HTTP request.

Conditions can also evaluate attributes of the principals and resources specified in the query. For example, a policy could contain a condition that specifies that principals can edit any photo that has an owner attribute with a value that matches the ID of the principal making the request.

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

The scope values of principal, action, and resource represent stable information in the system and must be present in every request. In comparison, context represents information about a point-in-time request and is required only for relevant request scenarios.

Condition elements take the following two forms:

when

The when clause causes the policy to match the request only if the embedded expression evaluates to true.

Examples of the when clause

The following when example matches the request if the principal making the request is a member of the HardwareEngineering department and has a jobLevel of at least 5. If either attribute is missing from the principal making the request, then the policy doesn’t match.

when {
    principal.department == "HardwareEngineering" 
    &&
    principal.jobLevel >= 5
}

The following when example matches the request if the request was submitted before the specified Unix time.

when {
  context.time.now < 1698423180
}

The following when example matches the request if the principal in the request is a member of the list of entities named sharedWith associated with the Album entity named "janeTrips".

when {
    principal in PhotoFlash::Album::"janeTrips".sharedWith
}

unless

The unless clause causes the policy to match the request only if the embedded expression evaluates to false.

Examples of the unless clause

The following unless clause does not match the request if the principal making the request is the resource’s owner.

unless {
  principal == resource.owner
}

The following policy example denies any request if the calling principal isn’t authenticated with multi-factor authentication (MFA). The policy assumes that usedMFA evaluates as a boolean to true or false.

forbid ( principal, action, resource )
unless {
  context.authentication.usedMFA
};

Annotations

You can attach arbitrary key-value pairs to Cedar policies in the form of annotations. An annotation has no impact on policy evaluation. Even though annotations are not processed by Cedar, they are stored as part of the policy and are available for use by services and applications that read and process Cedar policies.

You can place annotations only at the very top of the policy before the effect element.

An annotation has the following form:

@annotationname("annotation value")

Annotations don’t need to have an explicit value. When the value is omitted, the annotation implicitly has the value "", so @annotationname is equivalent to @annotationname("").

The following example shows three annotations that could be part of a policy.

@advice("My advice")
@id("My ID")
@shadow_mode
permit (
    ...
);