forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revamped Access Control (OpenZeppelin#2112)
* Remove Roles * Add AccessControl and tests * Removed IAccessControl * Add RoleGranted and RoleRevoked events * Make roles grantable and revokable regardless of their previous status * Fix typo * Add documentation * Cleanup tests * Add enumeration tests * Add _setRoleAdmin tests * Fix lint error * Fix AccessControl link in docs * WIP on access control guide * Rename getRoleMembersCount * Add tests for new role admin * Make AccessControl GSN compatible * Update access control guide * Rename admin to adminRole * Rename roleIds to roles * Add 'operator' to RoleGranted and RoleRevoked events. * Only emit events if the roles were not previously granted/revoked * Uncomment expectEvent.not tests * Rename operator to sender * Add changelog entry
- Loading branch information
Showing
10 changed files
with
502 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
pragma solidity ^0.6.0; | ||
|
||
import "../utils/EnumerableSet.sol"; | ||
import "../GSN/Context.sol"; | ||
|
||
/** | ||
* @dev Contract module that allows children to implement role-based access | ||
* control mechanisms. | ||
* | ||
* Roles are referred to by their `bytes32` identifier. These should be exposed | ||
* in the external API and be unique. The best way to achieve this is by | ||
* using `public constant` hash digests: | ||
* | ||
* ``` | ||
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); | ||
* ``` | ||
* | ||
* Roles can be used to represent a set of permissions. To restrict access to a | ||
* function call, use {hasRole}: | ||
* | ||
* ``` | ||
* function foo() public { | ||
* require(hasRole(MY_ROLE, _msgSender())); | ||
* ... | ||
* } | ||
* ``` | ||
* | ||
* Roles can be granted and revoked programatically by calling the `internal` | ||
* {_grantRole} and {_revokeRole} functions. | ||
* | ||
* This can also be achieved dynamically via the `external` {grantRole} and | ||
* {revokeRole} functions. Each role has an associated admin role, and only | ||
* accounts that have a role's admin role can call {grantRole} and {revokeRoke}. | ||
* | ||
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means | ||
* that only accounts with this role will be able to grant or revoke other | ||
* roles. More complex role relationships can be created by using | ||
* {_setRoleAdmin}. | ||
*/ | ||
abstract contract AccessControl is Context { | ||
using EnumerableSet for EnumerableSet.AddressSet; | ||
|
||
struct RoleData { | ||
EnumerableSet.AddressSet members; | ||
bytes32 adminRole; | ||
} | ||
|
||
mapping (bytes32 => RoleData) private _roles; | ||
|
||
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; | ||
|
||
/** | ||
* @dev Emitted when `account` is granted `role`. | ||
* | ||
* `sender` is the account that originated the contract call: | ||
* - if using `grantRole`, it is the admin role bearer | ||
* - if using `_grantRole`, its meaning is system-dependent | ||
*/ | ||
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); | ||
|
||
/** | ||
* @dev Emitted when `account` is revoked `role`. | ||
* | ||
* `sender` is the account that originated the contract call: | ||
* - if using `revokeRole`, it is the admin role bearer | ||
* - if using `renounceRole`, it is the role bearer (i.e. `account`) | ||
* - if using `_renounceRole`, its meaning is system-dependent | ||
*/ | ||
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); | ||
|
||
/** | ||
* @dev Returns `true` if `account` has been granted `role`. | ||
*/ | ||
function hasRole(bytes32 role, address account) public view returns (bool) { | ||
return _roles[role].members.contains(account); | ||
} | ||
|
||
/** | ||
* @dev Returns the number of accounts that have `role`. Can be used | ||
* together with {getRoleMember} to enumerate all bearers of a role. | ||
*/ | ||
function getRoleMemberCount(bytes32 role) public view returns (uint256) { | ||
return _roles[role].members.length(); | ||
} | ||
|
||
/** | ||
* @dev Returns one of the accounts that have `role`. `index` must be a | ||
* value between 0 and {getRoleMemberCount}, non-inclusive. | ||
* | ||
* Role bearers are not sorted in any particular way, and their ordering may | ||
* change at any point. | ||
* | ||
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure | ||
* you perform all queries on the same block. See the following | ||
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post] | ||
* for more information. | ||
*/ | ||
function getRoleMember(bytes32 role, uint256 index) public view returns (address) { | ||
return _roles[role].members.get(index); | ||
} | ||
|
||
/** | ||
* @dev Returns the admin role that controls `role`. See {grantRole} and | ||
* {revokeRole}. | ||
* | ||
* To change a role's admin, use {_setRoleAdmin}. | ||
*/ | ||
function getRoleAdmin(bytes32 role) external view returns (bytes32) { | ||
return _roles[role].adminRole; | ||
} | ||
|
||
/** | ||
* @dev Grants `role` to `account`. | ||
* | ||
* Calls {_grantRole} internally. | ||
* | ||
* Requirements: | ||
* | ||
* - the caller must have `role`'s admin role. | ||
*/ | ||
function grantRole(bytes32 role, address account) external virtual { | ||
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant"); | ||
|
||
_grantRole(role, account); | ||
} | ||
|
||
/** | ||
* @dev Revokes `role` from `account`. | ||
* | ||
* Calls {_revokeRole} internally. | ||
* | ||
* Requirements: | ||
* | ||
* - the caller must have `role`'s admin role. | ||
*/ | ||
function revokeRole(bytes32 role, address account) external virtual { | ||
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke"); | ||
|
||
_revokeRole(role, account); | ||
} | ||
|
||
/** | ||
* @dev Revokes `role` from the calling account. | ||
* | ||
* Roles are often managed via {grantRole} and {revokeRole}: this function's | ||
* purpose is to provide a mechanism for accounts to lose their privileges | ||
* if they are compromised (such as when a trusted device is misplaced). | ||
* | ||
* Requirements: | ||
* | ||
* - the caller must be `account`. | ||
*/ | ||
function renounceRole(bytes32 role, address account) external virtual { | ||
require(account == _msgSender(), "AccessControl: can only renounce roles for self"); | ||
|
||
_revokeRole(role, account); | ||
} | ||
|
||
/** | ||
* @dev Grants `role` to `account`. | ||
* | ||
* If `account` had not been already granted `role`, emits a {RoleGranted} | ||
* event. | ||
*/ | ||
function _grantRole(bytes32 role, address account) internal virtual { | ||
if (_roles[role].members.add(account)) { | ||
emit RoleGranted(role, account, msg.sender); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Revokes `role` from `account`. | ||
* | ||
* If `account` had been granted `role`, emits a {RoleRevoked} event. | ||
*/ | ||
function _revokeRole(bytes32 role, address account) internal virtual { | ||
if (_roles[role].members.remove(account)) { | ||
emit RoleRevoked(role, account, msg.sender); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Sets `adminRole` as `role`'s admin role. | ||
*/ | ||
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { | ||
_roles[role].adminRole = adminRole; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,4 @@ Contract modules for authorization and access control mechanisms. | |
|
||
{{Ownable}} | ||
|
||
{{Roles}} | ||
{{AccessControl}} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
pragma solidity ^0.6.0; | ||
|
||
import "../access/AccessControl.sol"; | ||
|
||
contract AccessControlMock is AccessControl { | ||
constructor() public { | ||
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender); | ||
} | ||
|
||
function setRoleAdmin(bytes32 roleId, bytes32 adminRoleId) public { | ||
_setRoleAdmin(roleId, adminRoleId); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.