Expand description
Access control module for Soroban contracts
This module provides functionality to manage role-based access control in Soroban contracts.
§Usage
There is a single overarching admin, and the admin has enough privileges to
call any function given in the AccessControl trait.
This admin must be set in the constructor of the contract. Else, none of
the methods exposed by this module will work. You can follow the
nft-access-control example.
§Admin Transfers
Transferring the top-level admin is a critical action, and as such, it is implemented as a two-step process to prevent accidental or malicious takeovers:
- The current admin initiates the transfer by specifying the
new_adminand alive_until_ledger, which defines the expiration time for the offer. - The designated
new_adminmust explicitly accept the transfer to complete it.
Until the transfer is accepted, the original admin retains full control, and
the transfer can be overridden or canceled by initiating a new one or using
a live_until_ledger of 0.
This handshake mechanism ensures that the recipient is aware and willing to assume responsibility, providing a robust safeguard in governance-sensitive deployments.
§Role Hierarchy
Each role can have an “admin role” specified for it. For example, if you
create two roles: minter and minter_admin, you can assign
minter_admin as the admin role for the minter role. This will allow
to accounts with minter_admin role to grant/revoke the minter role
to other accounts.
One can create up to 256 roles simultaneously, and create a chain of command structure if they want to go with this approach.
If you need even more granular control over which roles can do what, you can introduce your own business logic, and annotate it with our macro:
#[has_role(caller, "minter_admin")]
pub fn custom_sensitive_logic(e: &Env, caller: Address) {
...
}§⚠️ Warning: Circular Admin Relationships
When designing your role hierarchy, be careful to avoid creating circular
admin relationships. For example, it’s possible but not recommended to
assign MINT_ADMIN as the admin of MINT_ROLE while also making
MINT_ROLE the admin of MINT_ADMIN. Such circular relationships can lead
to unintended consequences, including:
- Race conditions where each role can revoke the other
- Potential security vulnerabilities in role management
- Confusing governance structures that are difficult to reason about
§Enumeration of Roles
In this access control system, roles don’t exist as standalone entities. Instead, the system stores account-role pairs in storage with additional enumeration logic:
- When a role is granted to an account, the account-role pair is stored and added to enumeration storage (RoleAccountsCount and RoleAccounts).
- When a role is revoked from an account, the account-role pair is removed from storage and from enumeration.
- If all accounts are removed from a role, the helper storage items for that role become empty or 0, but the entries themselves remain.
This means that the question of whether a role can “exist” with 0 accounts
is technically invalid, because roles only exist through their relationships
with accounts. When checking if a role has any accounts via
get_role_member_count, it returns 0 in two cases:
- When accounts were assigned to a role but later all were removed.
- When a role never existed in the first place.
Structs§
- Access
Control Args - AccessControlArgs is a type for building arg lists for functions defined in “AccessControl”.
- Access
Control Client - AccessControlClient is a client for calling the contract defined in “AccessControl”.
- Access
Control Spec - Admin
Renounced - Event emitted when the admin role is renounced.
- Admin
Transfer Completed - Event emitted when an admin transfer is completed.
- Admin
Transfer Initiated - Event emitted when an admin transfer is initiated.
- Role
Admin Changed - Event emitted when a role admin is changed.
- Role
Granted - Event emitted when a role is granted.
- Role
Revoked - Event emitted when a role is revoked.
Enums§
- Access
Control Error - Access
Control Storage Key - Storage keys for the data associated with the access control
Constants§
- MAX_
ROLES - Maximum number of roles that can exist simultaneously.
- ROLE_
EXTEND_ AMOUNT - ROLE_
TTL_ THRESHOLD
Statics§
- __
SPEC_ XDR_ EVENT_ ADMINRENOUNCED - __
SPEC_ XDR_ EVENT_ ADMINTRANSFERCOMPLETED - __
SPEC_ XDR_ EVENT_ ADMINTRANSFERINITIATED - __
SPEC_ XDR_ EVENT_ ROLEADMINCHANGED - __
SPEC_ XDR_ EVENT_ ROLEGRANTED - __
SPEC_ XDR_ EVENT_ ROLEREVOKED - __
SPEC_ XDR_ TYPE_ ACCESSCONTROLERROR
Traits§
Functions§
- accept_
admin_ transfer - Completes the 2-step admin transfer.
- add_
to_ role_ enumeration - Adds an account to role enumeration.
- emit_
admin_ renounced - Emits an event when the admin role is renounced.
- emit_
admin_ transfer_ completed - Emits an event when an admin transfer is completed.
- emit_
admin_ transfer_ initiated - Emits an event when an admin transfer is initiated.
- emit_
role_ admin_ changed - Emits an event when the admin role for a role changes.
- emit_
role_ granted - Emits an event when a role is granted to an account.
- emit_
role_ revoked - Emits an event when a role is revoked from an account.
- enforce_
admin_ auth - Retrieves the admin from storage, enforces authorization, and returns the admin address.
- ensure_
if_ admin_ or_ admin_ role - Ensures that the caller is either the contract admin or has the admin role for the specified role.
- ensure_
role - Ensures that the caller has the specified role.
This function is used to check if an account has a specific role.
The main purpose of this function is to act as a helper for the
#[has_role]macro. - get_
admin - Returns the admin account.
- get_
existing_ roles - Returns a vector containing all existing roles. Defaults to empty vector if no roles exist.
- get_
role_ admin - Returns the admin role for a specific role.
If no admin role is explicitly set, returns
None. - get_
role_ member - Returns the account at the specified index for a given role.
- get_
role_ member_ count - Returns the total number of accounts that have the specified role. If the role does not exist, it returns 0.
- grant_
role - Grants a role to an account. Creates the role if it does not exist. Returns early if the account already has the role.
- grant_
role_ no_ auth - Low-level function to grant a role to an account without performing authorization checks. Creates the role if it does not exist. Returns early if the account already has the role.
- has_
role - Returns
Some(index)if the account has the specified role, whereindexis the position of the account for that role, and can be used to queryget_role_member. ReturnsNoneif the account does not have the specified role. - remove_
from_ role_ enumeration - Removes an account from role enumeration.
- remove_
role_ accounts_ count_ no_ auth - Removes the role accounts count for a specified role without performing authorization checks.
- remove_
role_ admin_ no_ auth - Removes the admin role for a specified role without performing authorization checks.
- renounce_
admin - Allows the current admin to renounce their role, making the contract permanently admin-less. This is useful for decentralization purposes or when the admin role is no longer needed. Once the admin is renounced, it cannot be reinstated.
- renounce_
role - Allows an account to renounce a role assigned to itself. Users can only renounce roles for their own account.
- revoke_
role - Revokes a role from an account.
- revoke_
role_ no_ auth - Low-level function to revoke a role from an account without performing authorization checks.
- set_
admin - Sets the overarching admin role.
- set_
role_ admin - Sets
admin_roleas the admin role forrole. The admin role for a role controls who can grant and revoke that role. - set_
role_ admin_ no_ auth - Low-level function to set the admin role for a specified role without performing authorization checks.
- transfer_
admin_ role - Initiates admin role transfer. Admin privileges for the current admin are not revoked until the recipient accepts the transfer. Overrides the previous pending transfer if there is one.