actor User {}
resource Organization {
roles = ["visitor", "member", "community_admin", "admin"];
permissions = ["read", "update"];
"visitor" if "member";
"member" if "community_admin";
"community_admin" if "admin";
"update" if "admin";
"read" if "visitor";
}
# Account permissions
#
# relation | read | update
# --------------------------|------|--------
# owner | ✓ | ✓
# admin on parent | ✓ | ✓
# community_admin on parent | ✓ | ✓
# member on parent | ✓ | -
# visitor on parent | ✓ | -
resource Account {
permissions = ["read", "update"];
relations = { parent: Organization, owner: User };
"update" if "owner";
"update" if "community_admin" on "parent";
"read" if "update";
"read" if "visitor" on "parent";
}
# Field permissions
#
# relation | read | update
# --------------------------|------|--------
# owner | ✓ | †
# admin on parent | ✓ | ✓
# community_admin on parent | ✓ | *
# member on parent | ✓ | -
# visitor on parent | - | -
#
# †: owner can update only defined fields on their own account
# *: community_admin can update only `Field{"username"}`
resource Field {
permissions = ["read", "update"];
"read" if "update";
}
# Define which fields exist
has_relation(Field{"username"}, "parent", _: Account);
has_relation(Field{"email"}, "parent", _: Account);
# Allow owners to update their own fields
allow_field(user: User, "update", account: Account, field: Field) if
has_relation(account, "owner", user) and
has_relation(field, "parent", account);
# Allow admins to update any field, even those whose relationship with an
# account is not defined
allow_field(user: User, "update", account: Account, _field: Field) if
org matches Organization and
has_role(user, "admin", org) and
has_relation(account, "parent", org);
# Allow community admins to update only usernames
allow_field(user: User, "update", account: Account, field: Field) if
field = Field{"username"} and
org matches Organization and
has_role(user, "community_admin", org) and
has_relation(account, "parent", org) and
has_permission(user, "update", account) and
has_relation(field, "parent", account);
# Allow members to read all fields
allow_field(user: User, "read", account: Account, field: Field) if
org matches Organization and
has_role(user, "member", org) and
has_relation(account, "parent", org) and
has_permission(user, "read", account) and
has_relation(field, "parent", account);
test "admins can update all fields" {
setup {
has_role(User{"bob"}, "admin", Organization{"acme"});
has_relation(Account{"amy"}, "parent", Organization{"acme"});
}
assert allow_field(User{"bob"}, "update", Account{"amy"}, Field{"username"});
assert allow_field(User{"bob"}, "update", Account{"amy"}, Field{"email"});
}
test "community admins can only update usernames" {
setup {
has_role(User{"jim"}, "community_admin", Organization{"acme"});
has_relation(Account{"amy"}, "parent", Organization{"acme"});
}
assert allow_field(User{"jim"}, "update", Account{"amy"}, Field{"username"});
assert_not allow_field(User{"jim"}, "update", Account{"amy"}, Field{"email"});
}
test "members can only read fields" {
setup {
has_role(User{"jim"}, "member", Organization{"acme"});
has_relation(Account{"amy"}, "parent", Organization{"acme"});
}
assert allow_field(User{"jim"}, "read", Account{"amy"}, Field{"username"});
assert_not allow_field(User{"jim"}, "update", Account{"amy"}, Field{"email"});
}