ABAC as additional constraints on RBAC in OPA
Attributes are often used to strengthen another policy model like Role-Based Access Control (RBAC).
Attributes in the RBAC model
The generic notion of permitting an action if a subject has a role that grants them to perform action on a resource is extended:
Thinking of it as an overlay, further constraints are imposed on the access control decision.
In the following, we'll develop the additions over RBAC in Rego to implement this model. We start with a simple dynamic RBAC model from the basic variants of our RBAC in Rego How-To:
Base definitions and data
Input:
{
"subject": "alice",
"action": "create",
"resource": { "type": "documents" }
}
The common model:
package common
import rego.v1
subject := input.subject
action := input.action
resource := input.resource
The data stored in OPA's in-memory store:
{
"subject_mappings": {
"alice": [
"admin"
],
"bob": [
"viewer"
],
"catherine": [
"editor"
]
},
"role_mappings": {
"admin": {
"actions": "*",
"resources": "*"
},
"viewer": {
"actions": [
"view",
"list"
],
"resources": [
"documents"
]
},
"editor": {
"actions": [
"view",
"list",
"create",
"delete"
],
"resources": [
"documents"
]
}
}
}
And the basic RBAC definition:
package rbac
import rego.v1
subject := data.common.subject
action := data.common.action
resource := data.common.resource
role_mappings := data.role_mappings
subject_mappings := data.subject_mappings
subject := data.common.subject
default allow := false
allow if {
some role in subject_mappings[subject]
action_matches(action, role_mappings[role].actions)
resource_matches(resource.type, role_mappings[role].resources)
}
action_matches(_, "*")
action_matches(a, actions) if a in actions
resource_matches(_, "*")
resource_matches(r, resources) if r in resources
Attribute-based Conditions as Mandatory Globally Enforced Policies
Conceptually, this extension of conditions imposed on a common model tuple (subject, action, resource) can be constructed as a form of Mandatory Globally Enforced Policies.
Note the added lines for hooking in mandatory rules.
Evaluating this with the currently-provided input will yield "true":
- "alice" has role "admin"
- role "admin" is permitted for every action on every resource
Recall that our RBAC document management example has the roles "admin", "editor", and "viewer". Only subjects with the "admin" or "editor" role are permitted to delete a document.
As an extra condition, we'll impose that immutable documents can only be deleted by subjects with role "admin". Constructed as a mandatory rule, it would say
Action delete on an immutable document is only granted to subjects with role "admin".
package mandatory.abac
import rego.v1
action := data.common.action
resource := data.common.resource
roles := data.rbac.roles
default allow := true # matches if resource.immutable is undefined or false
allow := "admin" in roles if {
resource.immutable
action == "delete"
}
And use an entrypoint policy for ensuring that our mandatory rules are met:
package authz
import rego.v1
allow := data.rbac.allow
default decision := false
decision if {
allow # allowed by our base RBAC model
every _, result in data.mandatory { # and all our attribute constraints are met
result.allow
}
}
Example Evaluations
Subject "alice" is permitted to "delete" an immutable "document", via the "admin" role:
package tests
import rego.v1
test_admin_can_delete_immutable_documents if {
data.authz.decision with input.action as "delete"
with input.subject as "alice"
with input.resource as {"type": "documents", "immutable": true}
}
Subject "catherine" is permitted to "delete" a "document", via the "editor" role:
test_editor_can_delete_documents if {
data.authz.decision with input.action as "delete"
with input.subject as "catherine"
with input.resource as {"type": "documents"}
}
However, subject "catherine" is not permitted to "delete" an immutable "document", although carrying the "editor" role:
test_editor_cannot_delete_immutable_documents := output if {
output := data.authz.decision with input.action as "delete"
with input.subject as "catherine"
with input.resource as {"type": "documents", "immutable": true}
}
Attribute-based Conditions as Mixed-in Denies
It is also possible to construct extra conditions on attributes as "deny" rules, following Mixing Allows and Denies.
Our example immutable documents condition is defined in a "deny" rule:
package abac
import rego.v1
resource := data.common.resource
roles := data.rbac.roles
deny contains "immutable documents can only be deleted by admins" if {
resource.immutable
not "admin" in roles
}
and used from an entrypoint policy for resolving the "conflict":
package authz
import rego.v1
allow := data.rbac.allow
deny := data.abac.deny
default decision := false
decision if {
allow # there must be at least one rule that allows the operation
count(deny) == 0 # AND there must be no rules that deny the operation
}
context["reason"] := concat(",", deny) if {
count(deny) > 0
}
Example Evaluations
We can now verify our requirements using the conflict resolution model of layered ABAC.
Subject "alice" is permitted to "delete" an immutable "document", via the "admin" role:
package tests
import rego.v1
test_admin_can_delete_immutable_documents if {
data.authz.decision with input.action as "delete"
with input.subject as "alice"
with input.resource as {"type": "documents", "immutable": true}
}
Subject "catherine" is permitted to "delete" a "document", via the "editor" role:
test_editor_can_delete_documents if {
data.authz.decision with input.action as "delete"
with input.subject as "catherine"
with input.resource as {"type": "documents"}
}
However, subject "catherine" is not permitted to "delete" an immutable "document", although carrying the "editor" role:
test_editor_cannot_delete_immutable_documents := {"decision": output.decision, "context": output.context} if {
output := data.authz with input.action as "delete"
with input.subject as "catherine"
with input.resource as {"type": "documents", "immutable": true}
}
Mandatory rules and mixed-in "deny" rules are logically equivalent:
- Mandatory rules: every mandatory rule needs to be true
- Mixed-in "deny" rules: none of these rules may be false
By De Morgan's laws, these are equivalent. Which construction to use in your policies is up to you and should be determined by what's most aligned with your requirements and thought process conceptually.
See Wikipedia: De Morgan's laws for more details on the logic beneath this.