Roles
Last updated
Last updated
Roles is a module which enables optional role-based access control in other core Firm modules. Roles handles the creation and management of roles as well as granting and revoking these roles to different accounts interacting with the organization.
Except for some special roles (more on this under Special roles), Roles doesn’t care about the semantic meaning of roles nor it handles what permissions or rights are derived from having a certain role.
Roles can be thought of as the directory for who has what role and handling the logic for performing administrative functions over roles. Other components of the system can have certain permissions in them assigned to a particular role, and will use Roles to check whether a particular user has a role or not when performing access control for an action.
As such, the Roles module is not a Safe module and cannot perform any actions in the Safe directly. It is instead a module that other Firm modules will use as a centralized directory for keeping track and managing user roles.
Roles, which was heavily inspired by Solmate’s and OpenZeppelin’s implementations, has been built to optimize for setups in which many users might have many roles which might be changing.
As an initial implementation constraint and optimization, Roles supports a total of 256 roles (253 user-defined roles, as there are 3 special roles out of the box). It is optimized for bulk edits of a particular user’s roles with minimal gas impact (constant storage gas costs).
All roles (expect for special roles ‘Root’ and ‘Safe owner’) have a series of other roles that can act as admins to the role. If a user holds a role A which is a role admin of another role B, they will be able to grant or revoke that role B. Also, even if they don’t explicitly hold the role, a user that has a role which is an admin to another role, Roles will consider the user to have the role.
Example setup:
There are a few special roles in the system that have some special properties:
Assigned to just the Safe initially, holding the Root role will make the user appear as having all roles and being able to admin all roles even if they haven’t been explicitly granted to them.
Some notes that only apply to the Root role as special security measures:
The Root role has the special peculiarity that only other accounts holding the Root role can change the admins of the root role.
Another role could be set as admin of the Root role which would be very dangerous.
The Root role is the only one that can be set without any admin roles, effectively freezing who has the Root role forever, since not even Root role holders could override.
Aside from the Root role exceptions specified above, all other roles’ admins can only be changed by accounts with the Role manager role. This role is also the one required to create a new role.
The Safe owner role is a dynamic role. It cannot be granted or revoked, but instead, an account will appear as having or not having this role depending on whether the account is an owner of the organization’s Safe.
It is possible that more dynamic roles could be introduced, potentially allowing user-defined dynamic roles that can perform an arbitrary call to determine role membership.
An account with the Role manager role can create a new role using Roles:createRole(bytes32 roleAdmins, string name)
. Roles are assigned incremental IDs up until ID 254 which is the last possible user-defined role.
Role admins: is a bitmap of which roles are admins to the new role (if the value of bit N of the bitmap is a 1
, role N is considered an admin). Holders of those roles will be able to grant and revoke the role. Some considerations:
All new roles must have at least one role which admins it.
A role can be one of its own role admins or sole admin (accounts with the role will be able to grant or revoke it)
Upon role creation, it isn’t checked whether any of the admin roles exist. Therefore it is possible that a role which doesn’t exist yet (and will be created later on) will be the admin for the role. It is not recommended to do so as it could be confusing.
Accounts that hold a role which admins a particular role can grant it or revoke it using Roles:setRole(address *user*, uint8 *roleId*, bool *isGrant*)
.
It is also possible to grant and revoke multiple roles for a particular user in just one call by using Roles:setRoles(address *user*, uint8[] memory *grantingRoles*, uint8[] memory *revokingRoles*)
. In order for the call to be successful, the actor must have an admin role for all the roles being granted or revoked.
Root role exception: modifying parameters for the Root role requires holding a role that admins the Root role
Accounts with the Role manager role can perform the following parameter changes in a role:
Roles:setRoleAdmins(uint8 *roleId*, bytes32 *roleAdmins)*
: changes which roles are admins to the role
Roles:setRoleName(uint8 *roleId*, string memory *name*)
: changes the name of the role (has no on-chain side-effects)
As explained above, Roles only takes care of keeping track of who has what role, but it has no notion of what any particular role does (with the exception of performing admin actions on Roles itself).
External contracts in the system can use the RolesAuth base contract to use roles instead of individual accounts for permission to perform specific actions.
When a contract uses RolesAuth, if an address with a specific flag format is used, checking whether the sender is authorized to perform an action will result in checking whether the sender has a role.
The format of this role flag is 0x00...[byte with roleId][01]
(e.g. 0x0000000000000000000000000000000000000301
is a role flag for roleId = 3)
Roles:hasRole(address user, uint8 roleId)
will return true if at least one of the following is true:
User explicitly was granted that role.
User has been granted a role which is an admin to the role (this is only checked with one level of depth).
User has the root role.
The role being checked is the Safe owner role (roleId=255
) and the user is an owner of the Safe.