-
Notifications
You must be signed in to change notification settings - Fork 12k
Revamped Access Control #2112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Revamped Access Control #2112
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
b301381
Remove Roles
nventuro 9f6b348
Add AccessControl and tests
nventuro 7947ad3
Removed IAccessControl
nventuro f24302d
Add RoleGranted and RoleRevoked events
nventuro 26052a5
Make roles grantable and revokable regardless of their previous status
nventuro 2a89784
Fix typo
nventuro 6754f63
Add documentation
nventuro 4732b68
Cleanup tests
nventuro c4ddbdd
Add enumeration tests
nventuro 7fb1b43
Add _setRoleAdmin tests
nventuro e5b1cc3
Fix lint error
nventuro 4bc4249
Fix AccessControl link in docs
nventuro bc5c929
WIP on access control guide
nventuro f8ab7d7
Rename getRoleMembersCount
nventuro 6e92c96
Add tests for new role admin
nventuro ac965e7
Make AccessControl GSN compatible
nventuro 0b97267
Update access control guide
nventuro d14c628
Rename admin to adminRole
nventuro 8758632
Rename roleIds to roles
nventuro eea1903
Add 'operator' to RoleGranted and RoleRevoked events.
nventuro 37d4adf
Only emit events if the roles were not previously granted/revoked
nventuro 71f8da2
Uncomment expectEvent.not tests
nventuro d6bad63
Rename operator to sender
nventuro 58fd0be
Add changelog entry
nventuro 9c54ba4
Merge branch 'master' into access-control
nventuro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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`. | ||
* | ||
* `operator` 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 operator); | ||
|
||
/** | ||
* @dev Emitted when `account` is revoked `role`. | ||
* | ||
* `operator` 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 operator); | ||
|
||
/** | ||
* @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)) { | ||
frangio marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 hidden or 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 was deleted.
Oops, something went wrong.
This file contains hidden or 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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.