Organization Hierarchies

Many companies need their authorization policy to correspond to their org chart.

Human resources systems, recruiting and applicant tracking systems, and customer relationship management systems all require this pattern.

To implement this, we want our authorization model to express authorization in terms of the relationship between two users.

Implement the logic

A common model for organization hierarchies is some concept of a manager or supervisor having access to the data of their direct reports.

We'll extend our typical GitCloud example to say that managers should be able to see the repositories of their direct reports, as well as those reports' reports.

We can use relations in Oso Cloud to implement this directly. Specifically, a user has the "viewer" role on a repository if they are the manager of the repository's creator.

We also define the "manager" role recursively, so everyone in a user's direct chain of command is considered a "manager".


actor User {
relations = { direct_manager: User };
roles = ["manager"];
"manager" if "direct_manager";
# This forms the recursive hierarchy; we could remove this line and simplify
# the policy a bit if we only wanted a single-level of hierarchical
# visibility.
"manager" if "manager" on "direct_manager";
}
resource Repository {
roles = ["viewer"];
permissions = ["read"];
relations = { creator: User };
"viewer" if "creator";
"viewer" if "manager" on "creator";
"read" if "viewer";
}

Test it out

In our test case, Alice created a repository, and has a number of layers of management above her.

Based on those relationships, all of the managers above Alice inherit the "viewer" role on the repository Alice created.

However, Fergie is not in Alice's chain of command so does not inherit any permissions on the repository.


test "manager can have viewer role on employees repos" {
setup {
has_relation(Repository{"acme"}, "creator", User{"alice"});
has_relation(User{"alice"}, "direct_manager", User{"bhav"});
has_relation(User{"bhav"}, "direct_manager", User{"crystal"});
# fergie not in alice's direct hierarchy
has_relation(User{"fergie"}, "direct_manager", User{"crystal"});
}
assert allow(User{"alice"}, "read", Repository{"acme"});
assert allow(User{"bhav"}, "read", Repository{"acme"});
assert allow(User{"crystal"}, "read", Repository{"acme"});
# fergie not in alice's direct hierarchy, so cannot read
assert_not allow(User{"fergie"}, "read", Repository{"acme"});
}