Skip to main content
User groups let you assign roles to groups; each user inherits the group’s roles, reducing the need to assign roles individually.

Implementation

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.
actor User { }

# Model groups as actors to assign roles to them
actor Group { }

resource Repository {
  roles = ["reader"];
  permissions = ["read"];

  "read" if "reader";
}

# Users inherit roles from groups
# `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 "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.

Nested groups

You can extend this pattern by allowing groups to be members of other groups, forming a hierarchy. Users inherit roles from every group in the hierarchy. Implement this with a recursive rule: a user is a member of a group if they belong to any subgroup of that group.
has_group(user: User, group: Group) if
  g matches Group and
  has_group(user, g) and
  has_group(g, group);