@lazy-sol/access-control 1.0.6 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,11 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.4.22; // require with message (0.4.22), pure/view modifiers (0.4.16), hardhat (0.4.11)
2
+ // custom errors (0.8.4)
3
+ // require with message (0.4.22)
4
+ // pure/view modifiers (0.4.16)
5
+ // hardhat (0.4.11)
6
+ pragma solidity >=0.8.4;
7
+
8
+ import "./AccessControlCore.sol";
3
9
 
4
10
  /**
5
11
  * @title Role-based Access Control (RBAC)
@@ -50,189 +56,14 @@ pragma solidity >=0.4.22; // require with message (0.4.22), pure/view modifiers
50
56
  *
51
57
  * @author Basil Gorin
52
58
  */
53
- abstract contract AccessControl {
54
- /**
55
- * @dev Privileged addresses with defined roles/permissions
56
- * @dev In the context of ERC20/ERC721 tokens these can be permissions to
57
- * allow minting or burning tokens, transferring on behalf and so on
58
- *
59
- * @dev Maps user address to the permissions bitmask (role), where each bit
60
- * represents a permission
61
- * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
62
- * represents all possible permissions
63
- * @dev 'This' address mapping represents global features of the smart contract
64
- *
65
- * @dev We keep the mapping private to prevent direct writes to it from the inheriting
66
- * contracts, `getRole()` and `updateRole()` functions should be used instead
67
- */
68
- mapping(address => uint256) private userRoles;
69
-
70
- /**
71
- * @notice Access manager is responsible for assigning the roles to users,
72
- * enabling/disabling global features of the smart contract
73
- * @notice Access manager can add, remove and update user roles,
74
- * remove and update global features
75
- *
76
- * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
77
- * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
78
- */
79
- uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
80
-
81
- /**
82
- * @dev Bitmask representing all the possible permissions (super admin role)
83
- * @dev Has all the bits are enabled (2^256 - 1 value)
84
- */
85
- uint256 internal constant FULL_PRIVILEGES_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
86
-
87
- /**
88
- * @dev Fired in updateRole() and updateFeatures()
89
- *
90
- * @param operator address which was granted/revoked permissions
91
- * @param requested permissions requested
92
- * @param assigned permissions effectively set
93
- */
94
- event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
95
-
96
- /**
97
- * @notice Function modifier making a function defined as public behave as restricted
98
- * (so that only a pre-configured set of accounts can execute it)
99
- *
100
- * @param role the role transaction executor is required to have;
101
- * the function throws an "access denied" exception if this condition is not met
102
- */
103
- modifier restrictedTo(uint256 role) {
104
- // verify the access permission
105
- require(isSenderInRole(role), "access denied");
106
-
107
- // execute the rest of the function
108
- _;
109
- }
110
-
59
+ abstract contract AccessControl is AccessControlCore {
111
60
  /**
112
61
  * @notice Creates an access control instance, setting the contract owner to have full privileges
113
62
  *
114
63
  * @param _owner smart contract owner having full privileges, can be zero
115
64
  * @param _features initial features mask of the contract, can be zero
116
65
  */
117
- constructor(address _owner, uint256 _features) internal { // visibility modifier is required to be compilable with 0.6.x
118
- // grant owner full privileges
119
- __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
120
- // update initial features bitmask
121
- __setRole(address(this), _features, _features);
122
- }
123
-
124
- /**
125
- * @notice Retrieves globally set of features enabled
126
- *
127
- * @dev Effectively reads userRoles role for the contract itself
128
- *
129
- * @return 256-bit bitmask of the features enabled
130
- */
131
- function features() public view returns (uint256) {
132
- // features are stored in 'this' address mapping of `userRoles`
133
- return getRole(address(this));
134
- }
135
-
136
- /**
137
- * @notice Updates set of the globally enabled features (`features`),
138
- * taking into account sender's permissions
139
- *
140
- * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
141
- * @dev Function is left for backward compatibility with older versions
142
- *
143
- * @param _mask bitmask representing a set of features to enable/disable
144
- */
145
- function updateFeatures(uint256 _mask) public {
146
- // delegate call to `updateRole`
147
- updateRole(address(this), _mask);
148
- }
149
-
150
- /**
151
- * @notice Reads the permissions (role) for a given user from the `userRoles` mapping
152
- * (privileged addresses with defined roles/permissions)
153
- * @notice In the context of ERC20/ERC721 tokens these can be permissions to
154
- * allow minting or burning tokens, transferring on behalf and so on
155
- *
156
- * @dev Having a simple getter instead of making the mapping public
157
- * allows enforcing the encapsulation of the mapping and protects from
158
- * writing to it directly in the inheriting smart contracts
159
- *
160
- * @param operator address of a user to read permissions for,
161
- * or self address to read global features of the smart contract
162
- */
163
- function getRole(address operator) public view returns(uint256) {
164
- // read the value from `userRoles` and return
165
- return userRoles[operator];
166
- }
167
-
168
- /**
169
- * @notice Updates set of permissions (role) for a given user,
170
- * taking into account sender's permissions.
171
- *
172
- * @dev Setting role to zero is equivalent to removing an all permissions
173
- * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
174
- * copying senders' permissions (role) to the user
175
- * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
176
- *
177
- * @param operator address of a user to alter permissions for,
178
- * or self address to alter global features of the smart contract
179
- * @param role bitmask representing a set of permissions to
180
- * enable/disable for a user specified
181
- */
182
- function updateRole(address operator, uint256 role) public {
183
- // caller must have a permission to update user roles
184
- require(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");
185
-
186
- // evaluate the role and reassign it
187
- __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
188
- }
189
-
190
- /**
191
- * @notice Determines the permission bitmask an operator can set on the
192
- * target permission set
193
- * @notice Used to calculate the permission bitmask to be set when requested
194
- * in `updateRole` and `updateFeatures` functions
195
- *
196
- * @dev Calculated based on:
197
- * 1) operator's own permission set read from userRoles[operator]
198
- * 2) target permission set - what is already set on the target
199
- * 3) desired permission set - what do we want set target to
200
- *
201
- * @dev Corner cases:
202
- * 1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
203
- * `desired` bitset is returned regardless of the `target` permission set value
204
- * (what operator sets is what they get)
205
- * 2) Operator with no permissions (zero bitset):
206
- * `target` bitset is returned regardless of the `desired` value
207
- * (operator has no authority and cannot modify anything)
208
- *
209
- * @dev Example:
210
- * Consider an operator with the permissions bitmask 00001111
211
- * is about to modify the target permission set 01010101
212
- * Operator wants to set that permission set to 00110011
213
- * Based on their role, an operator has the permissions
214
- * to update only lowest 4 bits on the target, meaning that
215
- * high 4 bits of the target set in this example is left
216
- * unchanged and low 4 bits get changed as desired: 01010011
217
- *
218
- * @param operator address of the contract operator which is about to set the permissions
219
- * @param target input set of permissions to operator is going to modify
220
- * @param desired desired set of permissions operator would like to set
221
- * @return resulting set of permissions given operator will set
222
- */
223
- function _evaluateBy(address operator, uint256 target, uint256 desired) internal view returns (uint256) {
224
- // read operator's permissions
225
- uint256 p = getRole(operator);
226
-
227
- // taking into account operator's permissions,
228
- // 1) enable the permissions desired on the `target`
229
- target |= p & desired;
230
- // 2) disable the permissions desired on the `target`
231
- target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
232
-
233
- // return calculated result
234
- return target;
235
- }
66
+ constructor(address _owner, uint256 _features) AccessControlCore(_owner, _features) {} // visibility modifier is required to be compilable with 0.6.x
236
67
 
237
68
  /**
238
69
  * @notice Checks if requested set of features is enabled globally on the contract
@@ -241,8 +72,8 @@ abstract contract AccessControl {
241
72
  * @return true if all the features requested are enabled, false otherwise
242
73
  */
243
74
  function isFeatureEnabled(uint256 required) public view returns (bool) {
244
- // delegate call to `__hasRole`, passing `features` property
245
- return __hasRole(features(), required);
75
+ // delegate to internal `_isFeatureEnabled`
76
+ return _isFeatureEnabled(required);
246
77
  }
247
78
 
248
79
  /**
@@ -252,8 +83,8 @@ abstract contract AccessControl {
252
83
  * @return true if all the permissions requested are enabled, false otherwise
253
84
  */
254
85
  function isSenderInRole(uint256 required) public view returns (bool) {
255
- // delegate call to `isOperatorInRole`, passing transaction sender
256
- return isOperatorInRole(msg.sender, required);
86
+ // delegate to internal `_isSenderInRole`
87
+ return _isSenderInRole(required);
257
88
  }
258
89
 
259
90
  /**
@@ -264,42 +95,7 @@ abstract contract AccessControl {
264
95
  * @return true if all the permissions requested are enabled, false otherwise
265
96
  */
266
97
  function isOperatorInRole(address operator, uint256 required) public view returns (bool) {
267
- // delegate call to `__hasRole`, passing operator's permissions (role)
268
- return __hasRole(getRole(operator), required);
269
- }
270
-
271
- /**
272
- * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
273
- *
274
- * @dev Unsafe:
275
- * provides direct write access to `userRoles` mapping without any security checks,
276
- * doesn't verify the executor (msg.sender) permissions,
277
- * must be kept private at all times
278
- *
279
- * @param operator address of a user to alter permissions for,
280
- * or self address to alter global features of the smart contract
281
- * @param requestedRole bitmask representing a set of permissions requested
282
- * to be enabled/disabled for a user specified, used only to be logged into event
283
- * @param assignedRole bitmask representing a set of permissions to
284
- * enable/disable for a user specified, used to update the mapping and to be logged into event
285
- */
286
- function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
287
- // assign the role to the operator
288
- userRoles[operator] = assignedRole;
289
-
290
- // fire an event
291
- emit RoleUpdated(operator, requestedRole, assignedRole);
292
- }
293
-
294
- /**
295
- * @dev Checks if role `actual` contains all the permissions required `required`
296
- *
297
- * @param actual existent role
298
- * @param required required role
299
- * @return true if actual has required role (all permissions), false otherwise
300
- */
301
- function __hasRole(uint256 actual, uint256 required) private pure returns (bool) {
302
- // check the bitmask for the role required and return the result
303
- return actual & required == required;
98
+ // delegate to internal `_isOperatorInRole`
99
+ return _isOperatorInRole(operator, required);
304
100
  }
305
101
  }
@@ -0,0 +1,353 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // custom errors (0.8.4)
3
+ // require with message (0.4.22)
4
+ // pure/view modifiers (0.4.16)
5
+ // hardhat (0.4.11)
6
+ pragma solidity >=0.8.4;
7
+
8
+ /**
9
+ * @title Role-based Access Control Core (RBAC-C)
10
+ *
11
+ * @notice Access control smart contract provides an API to check
12
+ * if a specific operation is permitted globally and/or
13
+ * if a particular user has a permission to execute it.
14
+ *
15
+ * @notice This contract is inherited by other contracts requiring the role-based access control (RBAC)
16
+ * protection for the restricted access functions
17
+ *
18
+ * @notice It deals with two main entities: features and roles.
19
+ *
20
+ * @notice Features are designed to be used to enable/disable public functions
21
+ * of the smart contract (used by a wide audience).
22
+ * @notice User roles are designed to control the access to restricted functions
23
+ * of the smart contract (used by a limited set of maintainers).
24
+ *
25
+ * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
26
+ * in the documentation text and may be used interchangeably.
27
+ * @notice Terms "permission", "single permission" implies only one permission bit set.
28
+ *
29
+ * @notice Access manager is a special role which allows to grant/revoke other roles.
30
+ * Access managers can only grant/revoke permissions which they have themselves.
31
+ * As an example, access manager with no other roles set can only grant/revoke its own
32
+ * access manager permission and nothing else.
33
+ *
34
+ * @notice Access manager permission should be treated carefully, as a super admin permission:
35
+ * Access manager with even no other permission can interfere with another account by
36
+ * granting own access manager permission to it and effectively creating more powerful
37
+ * permission set than its own.
38
+ *
39
+ * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
40
+ * to check/know "who is allowed to do this thing".
41
+ * @dev Zeppelin implementation is more flexible:
42
+ * - it allows setting unlimited number of roles, while current is limited to 256 different roles
43
+ * - it allows setting an admin for each role, while current allows having only one global admin
44
+ * @dev Current implementation is more lightweight:
45
+ * - it uses only 1 bit per role, while Zeppelin uses 256 bits
46
+ * - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
47
+ * setting only one role in a single transaction
48
+ *
49
+ * @dev This smart contract is designed to be inherited by other
50
+ * smart contracts which require access control management capabilities.
51
+ *
52
+ * @dev Access manager permission has a bit 255 set.
53
+ * This bit must not be used by inheriting contracts for any other permissions/features.
54
+ *
55
+ * @dev The 'core' version of the RBAC contract hides three rarely used external functions from the public ABI,
56
+ * making them internal and thus reducing the overall compiled implementation size.
57
+ * isFeatureEnabled() public -> _isFeatureEnabled() internal
58
+ * isSenderInRole() public -> _isSenderInRole() internal
59
+ * isOperatorInRole() public -> _isOperatorInRole() internal
60
+ *
61
+ * @custom:since 1.1.0
62
+ *
63
+ * @author Basil Gorin
64
+ */
65
+ abstract contract AccessControlCore {
66
+ /**
67
+ * @dev Privileged addresses with defined roles/permissions
68
+ * @dev In the context of ERC20/ERC721 tokens these can be permissions to
69
+ * allow minting or burning tokens, transferring on behalf and so on
70
+ *
71
+ * @dev Maps user address to the permissions bitmask (role), where each bit
72
+ * represents a permission
73
+ * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
74
+ * represents all possible permissions
75
+ * @dev 'This' address mapping represents global features of the smart contract
76
+ *
77
+ * @dev We keep the mapping private to prevent direct writes to it from the inheriting
78
+ * contracts, `getRole()` and `updateRole()` functions should be used instead
79
+ */
80
+ mapping(address => uint256) private userRoles;
81
+
82
+ /**
83
+ * @notice Access manager is responsible for assigning the roles to users,
84
+ * enabling/disabling global features of the smart contract
85
+ * @notice Access manager can add, remove and update user roles,
86
+ * remove and update global features
87
+ *
88
+ * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
89
+ * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
90
+ */
91
+ uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;
92
+
93
+ /**
94
+ * @dev Bitmask representing all the possible permissions (super admin role)
95
+ * @dev Has all the bits are enabled (2^256 - 1 value)
96
+ */
97
+ uint256 internal constant FULL_PRIVILEGES_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
98
+
99
+ /**
100
+ * @notice Thrown when a function is executed by an account that does not have
101
+ * the required access permission(s) (role)
102
+ *
103
+ * @dev This error is used to enforce role-based access control (RBAC) restrictions
104
+ */
105
+ error AccessDenied();
106
+
107
+ /**
108
+ * @dev Fired in updateRole() and updateFeatures()
109
+ *
110
+ * @param operator address which was granted/revoked permissions
111
+ * @param requested permissions requested
112
+ * @param assigned permissions effectively set
113
+ */
114
+ event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);
115
+
116
+ /**
117
+ * @notice Function modifier making a function defined as public behave as restricted
118
+ * (so that only a pre-configured set of accounts can execute it)
119
+ *
120
+ * @param role the role transaction executor is required to have;
121
+ * the function throws an "access denied" exception if this condition is not met
122
+ */
123
+ modifier restrictedTo(uint256 role) {
124
+ // verify the access permission
125
+ _requireSenderInRole(role);
126
+
127
+ // execute the rest of the function
128
+ _;
129
+ }
130
+
131
+ /**
132
+ * @notice Creates an access control instance, setting the contract owner to have full privileges
133
+ *
134
+ * @param _owner smart contract owner having full privileges, can be zero
135
+ * @param _features initial features mask of the contract, can be zero
136
+ */
137
+ constructor(address _owner, uint256 _features) { // visibility modifier is required to be compilable with 0.6.x
138
+ // grant owner full privileges
139
+ __setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
140
+ // update initial features bitmask
141
+ __setRole(address(this), _features, _features);
142
+ }
143
+
144
+ /**
145
+ * @notice Retrieves globally set of features enabled
146
+ *
147
+ * @dev Effectively reads userRoles role for the contract itself
148
+ *
149
+ * @return 256-bit bitmask of the features enabled
150
+ */
151
+ function features() public view returns (uint256) {
152
+ // features are stored in 'this' address mapping of `userRoles`
153
+ return getRole(address(this));
154
+ }
155
+
156
+ /**
157
+ * @notice Updates set of the globally enabled features (`features`),
158
+ * taking into account sender's permissions
159
+ *
160
+ * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
161
+ * @dev Function is left for backward compatibility with older versions
162
+ *
163
+ * @param _mask bitmask representing a set of features to enable/disable
164
+ */
165
+ function updateFeatures(uint256 _mask) public {
166
+ // delegate call to `updateRole`
167
+ updateRole(address(this), _mask);
168
+ }
169
+
170
+ /**
171
+ * @notice Reads the permissions (role) for a given user from the `userRoles` mapping
172
+ * (privileged addresses with defined roles/permissions)
173
+ * @notice In the context of ERC20/ERC721 tokens these can be permissions to
174
+ * allow minting or burning tokens, transferring on behalf and so on
175
+ *
176
+ * @dev Having a simple getter instead of making the mapping public
177
+ * allows enforcing the encapsulation of the mapping and protects from
178
+ * writing to it directly in the inheriting smart contracts
179
+ *
180
+ * @param operator address of a user to read permissions for,
181
+ * or self address to read global features of the smart contract
182
+ */
183
+ function getRole(address operator) public view returns(uint256) {
184
+ // read the value from `userRoles` and return
185
+ return userRoles[operator];
186
+ }
187
+
188
+ /**
189
+ * @notice Updates set of permissions (role) for a given user,
190
+ * taking into account sender's permissions.
191
+ *
192
+ * @dev Setting role to zero is equivalent to removing an all permissions
193
+ * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
194
+ * copying senders' permissions (role) to the user
195
+ * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
196
+ *
197
+ * @param operator address of a user to alter permissions for,
198
+ * or self address to alter global features of the smart contract
199
+ * @param role bitmask representing a set of permissions to
200
+ * enable/disable for a user specified
201
+ */
202
+ function updateRole(address operator, uint256 role) public {
203
+ // caller must have a permission to update user roles
204
+ _requireSenderInRole(ROLE_ACCESS_MANAGER);
205
+
206
+ // evaluate the role and reassign it
207
+ __setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
208
+ }
209
+
210
+ /**
211
+ * @notice Determines the permission bitmask an operator can set on the
212
+ * target permission set
213
+ * @notice Used to calculate the permission bitmask to be set when requested
214
+ * in `updateRole` and `updateFeatures` functions
215
+ *
216
+ * @dev Calculated based on:
217
+ * 1) operator's own permission set read from userRoles[operator]
218
+ * 2) target permission set - what is already set on the target
219
+ * 3) desired permission set - what do we want set target to
220
+ *
221
+ * @dev Corner cases:
222
+ * 1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
223
+ * `desired` bitset is returned regardless of the `target` permission set value
224
+ * (what operator sets is what they get)
225
+ * 2) Operator with no permissions (zero bitset):
226
+ * `target` bitset is returned regardless of the `desired` value
227
+ * (operator has no authority and cannot modify anything)
228
+ *
229
+ * @dev Example:
230
+ * Consider an operator with the permissions bitmask 00001111
231
+ * is about to modify the target permission set 01010101
232
+ * Operator wants to set that permission set to 00110011
233
+ * Based on their role, an operator has the permissions
234
+ * to update only lowest 4 bits on the target, meaning that
235
+ * high 4 bits of the target set in this example is left
236
+ * unchanged and low 4 bits get changed as desired: 01010011
237
+ *
238
+ * @param operator address of the contract operator which is about to set the permissions
239
+ * @param target input set of permissions to operator is going to modify
240
+ * @param desired desired set of permissions operator would like to set
241
+ * @return resulting set of permissions given operator will set
242
+ */
243
+ function _evaluateBy(address operator, uint256 target, uint256 desired) internal view returns (uint256) {
244
+ // read operator's permissions
245
+ uint256 p = getRole(operator);
246
+
247
+ // taking into account operator's permissions,
248
+ // 1) enable the permissions desired on the `target`
249
+ target |= p & desired;
250
+ // 2) disable the permissions desired on the `target`
251
+ target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));
252
+
253
+ // return calculated result
254
+ return target;
255
+ }
256
+
257
+ /**
258
+ * @notice Ensures that the transaction sender has the required access permission(s) (role)
259
+ *
260
+ * @dev Reverts with an `AccessDenied` error if the sender does not have the required role
261
+ *
262
+ * @param required the set of permissions (role) that the transaction sender is required to have
263
+ */
264
+ function _requireSenderInRole(uint256 required) internal view {
265
+ // check if the transaction has the required permission(s),
266
+ // reverting with the "access denied" error if not
267
+ _requireAccessCondition(_isSenderInRole(required));
268
+ }
269
+
270
+ /**
271
+ * @notice Ensures that a specific condition is met
272
+ *
273
+ * @dev Reverts with an `AccessDenied` error if the condition is not met
274
+ *
275
+ * @param condition the condition that needs to be true for the function to proceed
276
+ */
277
+ function _requireAccessCondition(bool condition) internal pure {
278
+ // check if the condition holds
279
+ if(!condition) {
280
+ // revert with the "access denied" error if not
281
+ revert AccessDenied();
282
+ }
283
+ }
284
+
285
+ /**
286
+ * @notice Checks if requested set of features is enabled globally on the contract
287
+ *
288
+ * @param required set of features to check against
289
+ * @return true if all the features requested are enabled, false otherwise
290
+ */
291
+ function _isFeatureEnabled(uint256 required) internal view returns (bool) {
292
+ // delegate call to `__hasRole`, passing `features` property
293
+ return __hasRole(features(), required);
294
+ }
295
+
296
+ /**
297
+ * @notice Checks if transaction sender `msg.sender` has all the permissions required
298
+ *
299
+ * @param required set of permissions (role) to check against
300
+ * @return true if all the permissions requested are enabled, false otherwise
301
+ */
302
+ function _isSenderInRole(uint256 required) internal view returns (bool) {
303
+ // delegate call to `isOperatorInRole`, passing transaction sender
304
+ return _isOperatorInRole(msg.sender, required);
305
+ }
306
+
307
+ /**
308
+ * @notice Checks if operator has all the permissions (role) required
309
+ *
310
+ * @param operator address of the user to check role for
311
+ * @param required set of permissions (role) to check
312
+ * @return true if all the permissions requested are enabled, false otherwise
313
+ */
314
+ function _isOperatorInRole(address operator, uint256 required) internal view returns (bool) {
315
+ // delegate call to `__hasRole`, passing operator's permissions (role)
316
+ return __hasRole(getRole(operator), required);
317
+ }
318
+
319
+ /**
320
+ * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
321
+ *
322
+ * @dev Unsafe:
323
+ * provides direct write access to `userRoles` mapping without any security checks,
324
+ * doesn't verify the executor (msg.sender) permissions,
325
+ * must be kept private at all times
326
+ *
327
+ * @param operator address of a user to alter permissions for,
328
+ * or self address to alter global features of the smart contract
329
+ * @param requestedRole bitmask representing a set of permissions requested
330
+ * to be enabled/disabled for a user specified, used only to be logged into event
331
+ * @param assignedRole bitmask representing a set of permissions to
332
+ * enable/disable for a user specified, used to update the mapping and to be logged into event
333
+ */
334
+ function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
335
+ // assign the role to the operator
336
+ userRoles[operator] = assignedRole;
337
+
338
+ // fire an event
339
+ emit RoleUpdated(operator, requestedRole, assignedRole);
340
+ }
341
+
342
+ /**
343
+ * @dev Checks if role `actual` contains all the permissions required `required`
344
+ *
345
+ * @param actual existent role
346
+ * @param required required role
347
+ * @return true if actual has required role (all permissions), false otherwise
348
+ */
349
+ function __hasRole(uint256 actual, uint256 required) private pure returns (bool) {
350
+ // check the bitmask for the role required and return the result
351
+ return actual & required == required;
352
+ }
353
+ }
@@ -1,5 +1,5 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.6.2;
2
+ pragma solidity >=0.8.4;
3
3
 
4
4
  import "./OwnableToAccessControlAdapter.sol";
5
5
 
@@ -1,5 +1,7 @@
1
1
  // SPDX-License-Identifier: MIT
2
- pragma solidity >=0.6.2; // breaking changes in .call() (0.5.0), allow .call{}() (0.6.2)
2
+ // breaking changes in .call() (0.5.0)
3
+ // allow .call{}() (0.6.2)
4
+ pragma solidity >=0.8.4;
3
5
 
4
6
  import "./AccessControl.sol";
5
7
 
@@ -63,7 +65,7 @@ import "./AccessControl.sol";
63
65
  *
64
66
  * @author Basil Gorin
65
67
  */
66
- contract OwnableToAccessControlAdapter is AccessControl {
68
+ contract OwnableToAccessControlAdapter is AccessControlCore {
67
69
  /**
68
70
  * @dev Target OZ Ownable contract AccessControl Adapter executes the transactions on
69
71
  *
@@ -111,7 +113,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
111
113
  * @param _target target OZ Ownable contract address
112
114
  * @param _owner smart contract owner having full privileges
113
115
  */
114
- constructor(address _target, address _owner) public AccessControl(_owner, 0) {
116
+ constructor(address _target, address _owner) AccessControlCore(_owner, 0) { // visibility modifier is required to be compilable with 0.6.x
115
117
  // verify the inputs
116
118
  require(_target != address(0), "zero address");
117
119
 
@@ -148,7 +150,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
148
150
  */
149
151
  function updateAccessRole(bytes4 selector, uint256 role) public {
150
152
  // verify the access permission
151
- require(isSenderInRole(ROLE_ACCESS_ROLES_MANAGER), "access denied");
153
+ _requireSenderInRole(ROLE_ACCESS_ROLES_MANAGER);
152
154
 
153
155
  // update the function access role
154
156
  accessRoles[selector] = role;
@@ -190,7 +192,7 @@ contract OwnableToAccessControlAdapter is AccessControl {
190
192
  require(roleRequired != 0, "access role not set");
191
193
 
192
194
  // verify the access permission
193
- require(isSenderInRole(roleRequired), "access denied");
195
+ _requireSenderInRole(roleRequired);
194
196
  }
195
197
 
196
198
  // execute the call on the target