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
}
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"
}
})
}