存在必有BUG

2022
FYS重构版
首页 » openZeppelin » openzeppelin 权限控制 AccessControl

openzeppelin 权限控制 AccessControl

AccessControl 是一个openzeppelin的权限控制虚拟合约
本文源码解析,文章的所有内容均不构成任何投资比特币或其他数字货币的意见和建议,也不赞成个人炒作任何数字货币!

简介

AccessControl 支持多级控制。AccessControl 的核心是角色,每个角色都是一个bytes32的数字。

  • 每个角色都可以添加地址为自己的会员,可以根据会员的地址判断此会员是否属于某角色,角色下面的会员可以遍历,会员是地址可以为合约或者钱包账户。
  • 会员可以自己退出某个角色,不用经过管理员。
  • 角色的管理员是adminRole下面的会员
  • 角色的管理员可以将给角色添加会员,或者移除某会员
  • 一般用_setupRole方法初始化根角色(超级管理员),为了合约的安全性最好只在构造方法中调用一次
  • AccessControl 合约是一颗很明显的树,不过只支持从叶子向根进行寻址,无法进行遍历。只允许父级控制不允许管理员越级控制。其结构图如下

在上图的角色中 E,F,G 的管理员角色是B ;H的管理员角色是C ;I,J的管理员角色是D;B,C,D的管理员角色是A
则A可以控制B,C,D的会员增减,B可以控制E,F,G的会员增减,但是A无法控制E,F,G的会员。

代码分析

  • 数据分析

    using EnumerableSet for EnumerableSet.AddressSet;
    using Address for address;
    
    struct RoleData {
        EnumerableSet.AddressSet members;
        bytes32 adminRole;
    }
    
    mapping (bytes32 => RoleData) private _roles;
    
    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;

    AccessControl 使用了EnumerableSetAddress库。
    RoleData 结构体包含两部分 一个地址类型的Set和一个byte32
    _roles是个以byte32为键,RoleData 为值的mapping
    DEFAULT_ADMIN_ROLE 是个默认的管理员角色,使用时可以被重写。

  • function hasRole(bytes32 role, address account) public view returns (bool)
    判断这个角色中是否有这个会员
    function hasRole(bytes32 role, address account) public view returns (bool) {
        return _roles[role].members.contains(account);
    }
  • function getRoleMemberCount(bytes32 role) public view returns (uint256)
    获取某个角色下面会员的数量

    function getRoleMemberCount(bytes32 role) public view returns (uint256) {
        return _roles[role].members.length();
    }
  • function getRoleMember(bytes32 role, uint256 index) public view returns (address)
    根据某角色会员的索引获取会员的地址

    function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
        return _roles[role].members.at(index);
    }
  • function getRoleAdmin(bytes32 role) public view returns (bytes32)
    获取某个角色的管理角色

    function getRoleAdmin(bytes32 role) public view returns (bytes32) {
        return _roles[role].adminRole;
    }
    • function grantRole(bytes32 role, address account) public virtual
      往某个角色中添加会员,只有执行人是此角色的管理员时才能添加成功

      function grantRole(bytes32 role, address account) public virtual {
      require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
      
      _grantRole(role, account);
      }
  • function revokeRole(bytes32 role, address account) public virtual
    从某个角色中移除某个账户,只有执行人是此角色的管理员时才能移除成功

    function revokeRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
    
        _revokeRole(role, account);
    }
  • function renounceRole(bytes32 role, address account) public virtual
    从某个角色中移除某个账户,只有移除执行人账户时才能成功

    function renounceRole(bytes32 role, address account) public virtual {
        require(account == _msgSender(), "AccessControl: can only renounce roles for self");
    
        _revokeRole(role, account);
    }
  • function _setupRole(bytes32 role, address account) internal virtual
    内部方法,往某个角色中添加某个账户。
    为了避免bug这个方法最好只在构造函数中使用。一般来说这个方法用来初始化根角色。

    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }
  • function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual
    内部方法,设置某个角色的管理员角色

    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        emit RoleAdminChanged(role, _roles[role].adminRole, adminRole);
        _roles[role].adminRole = adminRole;
    }
  • function _grantRole(bytes32 role, address account) private
    内部方法,往某个角色中添加某个账户

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }
  • function _revokeRole(bytes32 role, address account) private
    内部方法,从某个角色中移除某个账户

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }

完整代码

pragma solidity ^0.6.0;

import "../utils/EnumerableSet.sol";
import "../utils/Address.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 dynamically via the {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 {revokeRole}.
 *
 * 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;
    using Address for address;

    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, an admin role
     * bearer except when using {_setupRole}.
     */
    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`)
     */
    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.at(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) public view returns (bytes32) {
        return _roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) public virtual {
        require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");

        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) public 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).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `account`.
     */
    function renounceRole(bytes32 role, address account) public 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. Note that unlike {grantRole}, this function doesn't perform any
     * checks on the calling account.
     *
     * [WARNING]
     * ====
     * This function should only be called from the constructor when setting
     * up the initial roles for the system.
     *
     * Using this function in any other way is effectively circumventing the admin
     * system imposed by {AccessControl}.
     * ====
     */
    function _setupRole(bytes32 role, address account) internal virtual {
        _grantRole(role, account);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        _roles[role].adminRole = adminRole;
    }

    function _grantRole(bytes32 role, address account) private {
        if (_roles[role].members.add(account)) {
            emit RoleGranted(role, account, _msgSender());
        }
    }

    function _revokeRole(bytes32 role, address account) private {
        if (_roles[role].members.remove(account)) {
            emit RoleRevoked(role, account, _msgSender());
        }
    }
}

文章如无特别注明均为原创! 作者: 于凯歌, 转载或复制请以 超链接形式 并注明出处 kg
原文地址《 openzeppelin 权限控制 AccessControl》发布于2021年4月13日

分享到:
打赏

评论

游客

看不清楚?点图切换