Multitenant Roles

Almost every application will have some form of role-based access control (RBAC). RBAC is so ubiquitous that Oso Cloud provides concise syntax for modeling it.

The simplest RBAC model to begin with is organization-level roles, in which users are assigned roles on the organization that they belong to.

Implement the logic

The basic logic of RBAC is: "a user has a permission if they are granted a role and the role grants that permission".

For organization-level roles, we'll assign users a role on the organization they belong to.

The User and Organization types would correspond to classes in our application. We've defined a pair of roles, arranged them in a hierarchy where "admin" inherits all of the permissions granted to "member", and granted each declared permission to one of the two roles.

actor User { }
resource Organization {
roles = ["admin", "member"];
permissions = [
"read", "add_member", "repository.create",
"", "repository.delete"
# role hierarchy:
# admins inherit all member permissions
"member" if "admin";
# org-level permissions
"read" if "member";
"add_member" if "admin";
# permission to create a repository
# in the organization
"repository.create" if "admin";
# permissions on child resources
"" if "member";
"repository.delete" if "admin";

Test it works

We can test the logic by assigning the "member" role to Alice on the Acme organization.

Given Alice has the "member" role, she has permission to do things that members can do -- like read the organization, and read repositories. But she cannot delete repositories in the Acme organization, nor can she read other organizations.

test "org members can read organizations, and read repositories for organizations" {
setup {
has_role(User{"alice"}, "member", Organization{"acme"});
assert allow(User{"alice"}, "read", Organization{"acme"});
assert allow(User{"alice"}, "", Organization{"acme"});
assert_not allow(User{"alice"}, "repository.delete", Organization{"acme"});
assert_not allow(User{"alice"}, "read", Organization{"foobar"});