User Groups

If your customers have lots of users, it'll be easier for them to manage access for entire groups of users rather than assigning roles individually.

With Oso Cloud, you model this with a rule that allows members of a group to inherit any role assigned to the group.

Implement the logic

The core idea of user groups is that a user inherits all roles from the groups that user belongs to. Assigning a user to a group is sufficient to grant them all roles assigned to that group.

We'll add a rule that expresses exactly that:

# `user` has `role` on `resource` if
has_role(user: User, role: String, resource: Resource) if
# there exists a `group`
group matches Group and
# `user` is in the group
has_group(user, group) and
# the `group` has `role` on `resource`
has_role(group, role, resource);

Test it out

Here's a full example and test:

actor User { }
# A group is a kind of actor
actor Group { }
resource Repository {
roles = ["reader"];
permissions = ["read"];
"read" if "reader";
# users inherit roles from groups
has_role(user: User, role: String, resource: Resource) if
group matches Group and
has_group(user, group) and
has_role(group, role, resource);
test "group members can read repositories" {
setup {
has_role(Group{"anvil-readers"}, "reader", Repository{"anvil"});
has_group(User{"alice"}, Group{"anvil-readers"});
has_group(User{"bob"}, Group{"anvil-readers"});
has_group(User{"charlie"}, Group{"anvil-readers"});
assert allow(User{"alice"}, "read", Repository{"anvil"});
assert allow(User{"bob"}, "read", Repository{"anvil"});
assert allow(User{"charlie"}, "read", Repository{"anvil"});

We've assigned the reader role to the anvil-readers group, and then added Alice, Bob, and Charlie to that group. Now they can all read the anvil repository.

Extension: Nested Groups

We can extend this logic and let groups be members of other groups. This will allow us to create a hierarchy of groups, and users will inherit roles from all groups in the hierarchy.

We can do this be adding a recursive rule that says a user is a member of a group if they are a member of any of the groups that group is a member of:

has_group(user: User, group: Group) if
g matches Group and
has_group(user, g) and
has_group(g, group);