1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
use super::{
errors::Error,
events::{RoleAdminChanged, RoleGranted, RoleRevoked}
};
use odra::{
contract_env,
types::{event::OdraEvent, Address},
Mapping
};
pub type Role = [u8; 32];
pub const DEFAULT_ADMIN_ROLE: Role = [0u8; 32];
/// This contract module enables the implementation of role-based access control mechanisms for children
/// modules.
///
/// Roles are identified by their 32-bytes identifier, which should be unique and exposed in the external API.
///
/// Roles can be used to represent a set of permissions, and the hasRole function is used to restrict
/// access to a function call.
///
/// Roles can be granted and revoked dynamically using the [`grant_role()`](AccessControl::grant_role) and
/// [`revoke_role()`](AccessControl::revoke_role) functions,
/// respectively. Each role has an associated admin role, and only accounts that have the role's admin role
/// can call grant_role and revoke_role.
///
/// By default, the admin role for all roles is [`DEFAULT_ADMIN_ROLE`], which means that only accounts with
/// this role can grant or revoke other roles.
///
/// More complex role relationships can be established using the [set_admin_role()](AccessControl::set_admin_role) function.
#[odra::module(events = [RoleAdminChanged, RoleGranted, RoleRevoked])]
pub struct AccessControl {
roles: Mapping<Role, Mapping<Address, bool>>,
role_admin: Mapping<Role, Role>
}
#[odra::module]
impl AccessControl {
/// Returns true if account has been granted `role`.
pub fn has_role(&self, role: &Role, address: &Address) -> bool {
self.roles.get_instance(role).get_or_default(address)
}
/// Returns the admin role that controls `role`.
///
/// The admin role may be changed using [set_admin_role()](AccessControl::set_admin_role).
pub fn get_role_admin(&self, role: &Role) -> Role {
let admin_role = self.role_admin.get(role);
if let Some(admin) = admin_role {
admin
} else {
DEFAULT_ADMIN_ROLE
}
}
/// Grants `role` to `address`.
///
/// If the role has been already granted - nothing happens,
/// otherwise [`RoleGranted`] event is emitted.
///
/// The caller must have `role`'s admin role.
pub fn grant_role(&mut self, role: &Role, address: &Address) {
self.check_role(&self.get_role_admin(role), &contract_env::caller());
self.unchecked_grant_role(role, address);
}
/// Grants `role` to `address`.
///
/// If the role has been already revoked - nothing happens,
/// otherwise [`RoleRevoked`] event is emitted.
///
/// The caller must have `role`'s admin role.
pub fn revoke_role(&mut self, role: &Role, address: &Address) {
self.check_role(&self.get_role_admin(role), &contract_env::caller());
self.unchecked_revoke_role(role, address);
}
/// The function is used to remove a role from the account that initiated the call.
///
/// One common way of managing roles is by using [`grant_role()`](Self::grant_role())
/// and [`revoke_role()`](Self::revoke_role()).
/// The purpose of revokeRole is to provide a mechanism for revoking privileges from an account
/// in case it gets compromised.
///
/// If the account had previously been granted the role, the function will trigger a `RoleRevoked` event.
///
/// Note that only `address` is authorized to call this function.
pub fn renounce_role(&mut self, role: &Role, address: &Address) {
if address != &contract_env::caller() {
contract_env::revert(Error::RoleRenounceForAnotherAddress);
}
self.unchecked_revoke_role(role, address);
}
}
impl AccessControl {
/// Ensures `address` has `role`. If not, reverts with [Error::MissingRole].
pub fn check_role(&self, role: &Role, address: &Address) {
if !self.has_role(role, address) {
contract_env::revert(Error::MissingRole);
}
}
/// Sets `admin_role` as `role`'s admin role.
///
/// Emits a `RoleAdminChanged` event.
pub fn set_admin_role(&mut self, role: &Role, admin_role: &Role) {
let previous_admin_role = self.get_role_admin(role);
self.role_admin.set(role, *admin_role);
RoleAdminChanged {
role: *role,
previous_admin_role,
new_admin_role: *admin_role
}
.emit();
}
/// Grants `role` to `address`.
///
/// Internal function without access restriction.
/// This function should be used to setup the initial access control.
///
/// May emit a `RoleGranted` event.
pub fn unchecked_grant_role(&mut self, role: &Role, address: &Address) {
if !self.has_role(role, address) {
self.roles.get_instance(role).set(address, true);
RoleGranted {
role: *role,
address: *address,
sender: contract_env::caller()
}
.emit();
}
}
/// Revokes `role` from `address`.
///
/// Internal function without access restriction.
/// This function should be used to setup the initial access control.
///
/// May emit a `RoleRevoked` event.
pub fn unchecked_revoke_role(&mut self, role: &Role, address: &Address) {
if self.has_role(role, address) {
self.roles.get_instance(role).set(address, false);
RoleRevoked {
role: *role,
address: *address,
sender: contract_env::caller()
}
.emit();
}
}
}