// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; 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 previousMode, 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 succinctly * 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 private _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) { require(hasGroup(group), "AccessManager: unknown group"); 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 { AccessMode previousMode = _contractMode[target]; _contractMode[target] = mode; emit AccessModeUpdated(target, previousMode, 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; } } }