Skip to main content

Introduction

An authorization policy (or simply, a policy) is expressed in a declarative language called rego.

A policy can include one or more rego files, and one or more JSON data files.

A rego file has a package directive which sets the namespace for the rego file, and defines one or more decisions.

package sample.GET.api.orders

allowed {
true
}
note

In the rego file above, the package name follows the Topaz convention:

policy-root.HTTP method.HTTP route

Decision

A decision is an output from the evaluation of a policy. The policy above exports a decision called allowed, and sets its value to true.

The policy below exports the same decision (allowed), has a default value of false for this decision, and a rule that sets allowed to true only if the logged-in users's department attribute is equal to "Sales":

package sample.GET.api.orders

default allowed = false

allowed {
input.user.attributes.department == "Sales"
}

User context

In the policy above, we were able to use the department attribute to help compute the allowed decision. This is an example of User context that is used in a policy. If you import your identities from your identity provider, any properties they contain about users will be available to your policy.

In addition, you can add properties on your objects in an extensible JSON property bag, and use thse properties in authorization decisions.

Resource context

Policies can also use resource context in their evaluation process. If we added the allowed clause below to our policy, and the caller included a resource context that contains { "id": "<the-user-id>" }, the policy would evaluate allowed to true if the user id and the resource id match.

allowed {
input.user.id == input.resource.id
}

Objects

To resolve the identity of an object that is stored in the directory, you can use the ds.object built-in. Objects are resolved based on their key attribute. For example, you would ideally save users with a useful key such as an email address, and then use that email address to resolve the user object.

user = ds.object(input.user.email)

Check Relation built-in

The ds.check_relation built-in allows you to query the Topaz directory for a relationship between an object an a subject. Once we resolve the object and the subject, we can use the ds.check_relation built-in to check if the object has a relationship with the subject.

allowed {
user = ds.object(input.user.email)
department = ds.object(input.resource.id)
ds.check_relation({
"subject": user.id,
"object": ds.object({
"key": input.resource.id,
"type": "department"
}).id,
"relation": {
"object_type": "department",
"name": "owner"
}
})
}

Check Permission built-in

Similar to the ds.check_relation built-in, the ds.check_permission built-in allows you to query the Topaz directory for a permission that is associated with a relationship between an object and a subject. Once we resolve the object and the subject, we can use the ds.check_permission built-in to check if the object has a permission with the subject.

allowed {
ds.check_permission({
"subject": input.user.id,
"object": ds.object({
"key": input.resource.id,
"type": "department"
}).id,
"permission": {
"name": "can-read"
}
})
}

Example policy

The policy below is a sample policy that will take advantage of the relation and permission built-ins to check if a user has permission to delete a department. It will also combine an ABAC rule with that will check if the user is a member of the "IT" department.

package sample.DELETE.api.departments

default allowed = false

allowed {
input.user.properties.department == "IT"
}

allowed {
ds.check_permission({
"subject": input.user.id,
"object": ds.object({
"key": input.resource.id,
"type": "department"
}).id,
"permission": {
"name": "can-delete"
}
})
}