The following will demonstrate building a policy using a simple Role Based Access Control in the build.security's system.
First, we'll define a very basic roles and permissions mapping. We'll do so using the internal data source.
- Create a new internal data source (see here for more instructions)
- Use the following json object:
{
"roles": {
"admin": {
"permissions": [
"overview",
],
"sub_roles": ["developer"]
},
"developer": {
"permissions": [
"overview.view"
],
"sub_roles": []
}
},
"users": {
"alice": {
"id": 1,
"role": "admin"
},
"bob": {
"id": 2,
"role": "developer"
}
}
}
Looking closely in the object, we'll identify:
- There are 2 roles:
admin
anddeveloper
. - There are 2 users:
alice
andbob
. Alice
is anadmin
,bob
is adeveloper
.- Every
admin
is also adeveloper
(note thesub_roles
field of theadmin
role) - We're using dot notation permissions, with hierarchy.
admin
has theoverview
permissions (and everything that starts withoverview
while thedeveloper
role has onlyoverview.view
permission.
- Save the internal data source with the name "roles2permissions"
Now that we have a data source that represents the users, the roles and their permissions, we can move forward to the policy creation phase.
- Create a new policy
- Add a new custom rule with the following code snippet:
# Use the data in hand to create a graph of the roles and the attached permissions
roles_graph[entity_name] = descendants {
data.datasources.roles2permissions.roles[entity_name]
descendants := data.datasources.roles2permissions.roles[entity_name].sub_roles
}
permissions_map[entity_name] = permissions {
data.datasources.roles2permissions.roles[entity_name]
reachable := graph.reachable(roles_graph, {entity_name})
permissions := {item | reachable[k]; item := data.datasources.roles2permissions.roles[k].permissions[_]}
}
# Use the name of the user from the request and returns its role as given by the data
role_of_user := role {
role := data.datasources.roles2permissions.users[input.user].role
}
# Returns true of all the required permissions are satisfied by the user's granted permissions
rbac {
granted_permissions := { i | i := permissions_map[role_of_user][_]}
required_permissions := { i | i := input.required_permissions[_]}
some i,j
startswith(required_permissions[i], granted_permissions[j])
}
- Create a new empty rule that only checks for
rbac
to evaluate to true. In the management UI the rule will look like this:
So the generated code of it will look like this:
# RBAC
active[decision] {
rule_type := "allow"
rbac
decision := {
"result": rule_type,
"message": ""
}
}
Policy is ready, next - test the policy.
One way to test the policy is to deploy a PDP and querying it using cURL or straight from your application.
Another option is to use build.security's policy evaluator.
For the following input, where alice
is trying to do overview.edit
, the request will get allowed:
{
"user": "alice",
"required_permissions": [
"overview.edit"
]
}
But for this same request for bob
(who is a developer
) the request will get denied:
{
"user": "bob",
"required_permissions": [
"overview.edit"
]
}