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