123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.13;
- import "../AccessControl.sol";
- import "../AccessControlDefaultAdminRules.sol";
- import "./IAuthority.sol";
- import "./AccessManaged.sol";
- interface IAccessManager is IAuthority, IAccessControlDefaultAdminRules {
- enum AccessMode {
- Custom,
- Closed,
- Open
- }
- event GroupUpdated(uint8 indexed group, string name);
- event GroupAllowed(address indexed target, bytes4 indexed selector, uint8 indexed group, bool allowed);
- event AccessModeUpdated(address indexed target, AccessMode indexed mode);
- function createGroup(uint8 group, string calldata name) external;
- function updateGroupName(uint8 group, string calldata name) external;
- function hasGroup(uint8 group) external view returns (bool);
- function getUserGroups(address user) external view returns (bytes32 groups);
- function grantGroup(uint8 group, address user) external;
- function revokeGroup(uint8 group, address user) external;
- function renounceGroup(uint8 group, address user) external;
- function getFunctionAllowedGroups(address target, bytes4 selector) external view returns (bytes32 groups);
- function setFunctionAllowedGroup(address target, bytes4[] calldata selectors, uint8 group, bool allowed) external;
- function getContractMode(address target) external view returns (AccessMode);
- function setContractModeCustom(address target) external;
- function setContractModeOpen(address target) external;
- function setContractModeClosed(address target) external;
- function transferContractAuthority(address target, address newAuthority) external;
- }
- /**
- * @dev AccessManager is a central contract to store the permissions of a system.
- *
- * The smart contracts under the control of an AccessManager instance will have a set of "restricted" functions, and the
- * exact details of how access is restricted for each of those functions is configurable by the admins of the instance.
- * These restrictions are expressed in terms of "groups".
- *
- * An AccessManager instance will define a set of groups. Each of them must be created before they can be granted, with
- * a maximum of 255 created groups. Users can be added into any number of these groups. Each of them defines an
- * AccessControl role, and may confer access to some of the restricted functions in the system, as configured by admins
- * through the use of {setFunctionAllowedGroup}.
- *
- * Note that a function in a target contract may become permissioned in this way only when: 1) said contract is
- * {AccessManaged} and is connected to this contract as its manager, and 2) said function is decorated with the
- * `restricted` modifier.
- *
- * There is a special group defined by default named "public" which all accounts automatically have.
- *
- * Contracts can also be configured in two special modes: 1) the "open" mode, where all functions are allowed to the
- * "public" group, and 2) the "closed" mode, where no function is allowed to any group.
- *
- * Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that
- * it will be highly secured (e.g., a multisig or a well-configured DAO). Additionally, {AccessControlDefaultAdminRules}
- * is included to enforce security rules on this account.
- *
- * NOTE: Some of the functions in this contract, such as {getUserGroups}, return a `bytes32` bitmap to succintly
- * represent a set of groups. In a bitmap, bit `n` (counting from the least significant bit) will be 1 if and only if
- * the group with number `n` is in the set. For example, the hex value `0x05` represents the set of the two groups
- * numbered 0 and 2 from its binary equivalence `0b101`
- */
- contract AccessManager is IAccessManager, AccessControlDefaultAdminRules {
- bytes32 _createdGroups;
- // user -> groups
- mapping(address => bytes32) private _userGroups;
- // target -> selector -> groups
- mapping(address => mapping(bytes4 => bytes32)) private _allowedGroups;
- // target -> mode
- mapping(address => AccessMode) private _contractMode;
- uint8 private constant _GROUP_PUBLIC = type(uint8).max;
- /**
- * @dev Initializes an AccessManager with initial default admin and transfer delay.
- */
- constructor(
- uint48 initialDefaultAdminDelay,
- address initialDefaultAdmin
- ) AccessControlDefaultAdminRules(initialDefaultAdminDelay, initialDefaultAdmin) {
- _createGroup(_GROUP_PUBLIC, "public");
- }
- /**
- * @dev Returns true if the caller can invoke on a target the function identified by a function selector.
- * Entrypoint for {AccessManaged} contracts.
- */
- function canCall(address caller, address target, bytes4 selector) public view virtual returns (bool) {
- bytes32 allowedGroups = getFunctionAllowedGroups(target, selector);
- bytes32 callerGroups = getUserGroups(caller);
- return callerGroups & allowedGroups != 0;
- }
- /**
- * @dev Creates a new group with a group number that can be chosen arbitrarily but must be unused, and gives it a
- * human-readable name. The caller must be the default admin.
- *
- * Group numbers are not auto-incremented in order to avoid race conditions, but administrators can safely use
- * sequential numbers.
- *
- * Emits {GroupUpdated}.
- */
- function createGroup(uint8 group, string memory name) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _createGroup(group, name);
- }
- /**
- * @dev Updates an existing group's name. The caller must be the default admin.
- */
- function updateGroupName(uint8 group, string memory name) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- require(group != _GROUP_PUBLIC, "AccessManager: built-in group");
- require(hasGroup(group), "AccessManager: unknown group");
- emit GroupUpdated(group, name);
- }
- /**
- * @dev Returns true if the group has already been created via {createGroup}.
- */
- function hasGroup(uint8 group) public view virtual returns (bool) {
- return _getGroup(_createdGroups, group);
- }
- /**
- * @dev Returns a bitmap of the groups the user has. See note on bitmaps above.
- */
- function getUserGroups(address user) public view virtual returns (bytes32) {
- return _userGroups[user] | _groupMask(_GROUP_PUBLIC);
- }
- /**
- * @dev Grants a user a group.
- *
- * Emits {RoleGranted} with the role id of the group, if wasn't already held by the user.
- */
- function grantGroup(uint8 group, address user) public virtual {
- grantRole(_encodeGroupRole(group), user); // will check msg.sender
- }
- /**
- * @dev Removes a group from a user.
- *
- * Emits {RoleRevoked} with the role id of the group, if previously held by the user.
- */
- function revokeGroup(uint8 group, address user) public virtual {
- revokeRole(_encodeGroupRole(group), user); // will check msg.sender
- }
- /**
- * @dev Allows a user to renounce a group.
- *
- * Emits {RoleRevoked} with the role id of the group, if previously held by the user.
- */
- function renounceGroup(uint8 group, address user) public virtual {
- renounceRole(_encodeGroupRole(group), user); // will check msg.sender
- }
- /**
- * @dev Returns a bitmap of the groups that are allowed to call a function of a target contract. If the target
- * contract is in open or closed mode it will be reflected in the return value.
- */
- function getFunctionAllowedGroups(address target, bytes4 selector) public view virtual returns (bytes32) {
- AccessMode mode = getContractMode(target);
- if (mode == AccessMode.Open) {
- return _groupMask(_GROUP_PUBLIC);
- } else if (mode == AccessMode.Closed) {
- return 0;
- } else {
- return _allowedGroups[target][selector];
- }
- }
- /**
- * @dev Changes whether a group is allowed to call a function of a contract, according to the `allowed` argument.
- * The caller must be the default admin.
- */
- function setFunctionAllowedGroup(
- address target,
- bytes4[] calldata selectors,
- uint8 group,
- bool allowed
- ) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- for (uint256 i = 0; i < selectors.length; i++) {
- bytes4 selector = selectors[i];
- _allowedGroups[target][selector] = _withUpdatedGroup(_allowedGroups[target][selector], group, allowed);
- emit GroupAllowed(target, selector, group, allowed);
- }
- }
- /**
- * @dev Returns the mode of the target contract, which may be custom (`0`), closed (`1`), or open (`2`).
- */
- function getContractMode(address target) public view virtual returns (AccessMode) {
- return _contractMode[target];
- }
- /**
- * @dev Sets the target contract to be in custom restricted mode. All restricted functions in the target contract
- * will follow the group-based restrictions defined by the AccessManager. The caller must be the default admin.
- */
- function setContractModeCustom(address target) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _setContractMode(target, AccessMode.Custom);
- }
- /**
- * @dev Sets the target contract to be in "open" mode. All restricted functions in the target contract will become
- * callable by anyone. The caller must be the default admin.
- */
- function setContractModeOpen(address target) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _setContractMode(target, AccessMode.Open);
- }
- /**
- * @dev Sets the target contract to be in "closed" mode. All restricted functions in the target contract will be
- * closed down and disallowed to all. The caller must be the default admin.
- */
- function setContractModeClosed(address target) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _setContractMode(target, AccessMode.Closed);
- }
- /**
- * @dev Transfers a target contract onto a new authority. The caller must be the default admin.
- */
- function transferContractAuthority(
- address target,
- address newAuthority
- ) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- AccessManaged(target).setAuthority(IAuthority(newAuthority));
- }
- /**
- * @dev Creates a new group.
- *
- * Emits {GroupUpdated}.
- */
- function _createGroup(uint8 group, string memory name) internal virtual {
- require(!hasGroup(group), "AccessManager: existing group");
- _createdGroups = _withUpdatedGroup(_createdGroups, group, true);
- emit GroupUpdated(group, name);
- }
- /**
- * @dev Augmented version of {AccessControl-_grantRole} that keeps track of user group bitmaps.
- */
- function _grantRole(bytes32 role, address user) internal virtual override {
- super._grantRole(role, user);
- (bool isGroup, uint8 group) = _decodeGroupRole(role);
- if (isGroup) {
- require(hasGroup(group), "AccessManager: unknown group");
- _userGroups[user] = _withUpdatedGroup(_userGroups[user], group, true);
- }
- }
- /**
- * @dev Augmented version of {AccessControl-_revokeRole} that keeps track of user group bitmaps.
- */
- function _revokeRole(bytes32 role, address user) internal virtual override {
- super._revokeRole(role, user);
- (bool isGroup, uint8 group) = _decodeGroupRole(role);
- if (isGroup) {
- require(hasGroup(group), "AccessManager: unknown group");
- require(group != _GROUP_PUBLIC, "AccessManager: irrevocable group");
- _userGroups[user] = _withUpdatedGroup(_userGroups[user], group, false);
- }
- }
- /**
- * @dev Sets the restricted mode of a target contract.
- */
- function _setContractMode(address target, AccessMode mode) internal virtual {
- _contractMode[target] = mode;
- emit AccessModeUpdated(target, mode);
- }
- /**
- * @dev Returns the {AccessControl} role id that corresponds to a group.
- *
- * This role id starts with the ASCII characters `group:`, followed by zeroes, and ends with the single byte
- * corresponding to the group number.
- */
- function _encodeGroupRole(uint8 group) internal pure virtual returns (bytes32) {
- return bytes32("group:") | bytes32(uint256(group));
- }
- /**
- * @dev Decodes a role id into a group, if it is a role id of the kind returned by {_encodeGroupRole}.
- */
- function _decodeGroupRole(bytes32 role) internal pure virtual returns (bool isGroup, uint8 group) {
- bytes32 tagMask = ~bytes32(uint256(0xff));
- bytes32 tag = role & tagMask;
- isGroup = tag == bytes32("group:");
- group = uint8(role[31]);
- }
- /**
- * @dev Returns a bit mask where the only non-zero bit is the group number bit.
- */
- function _groupMask(uint8 group) private pure returns (bytes32) {
- return bytes32(1 << group);
- }
- /**
- * @dev Returns the value of the group number bit in a bitmap.
- */
- function _getGroup(bytes32 bitmap, uint8 group) private pure returns (bool) {
- return bitmap & _groupMask(group) > 0;
- }
- /**
- * @dev Returns a new group bitmap where a specific group was updated.
- */
- function _withUpdatedGroup(bytes32 bitmap, uint8 group, bool value) private pure returns (bytes32) {
- bytes32 mask = _groupMask(group);
- if (value) {
- return bitmap | mask;
- } else {
- return bitmap & ~mask;
- }
- }
- }
|