http://www.xiaoyenzi.com

基于角色的以太坊区块链访问控制指南

您决定谁与您的智能合约做什么
从历史上看,隐私几乎是隐含的,因为很难找到并收集信息。但在数字世界中,我们需要有更明确的规则。- 比尔盖茨

介绍

基于角色的访问控制是软件系统的安全需求,旨在由数百个用户访问。虽然这种需求通常在企业软件和操作系统中实现,但对以太坊区块链的处理并不多。
本文旨在展示我们如何在以太坊区块链的Solidity中实现基于角色的访问控制,并教您构建自己的区块链。我们也公开了我们的代码,请随时以任何方式或形式重复使用它。
在将供应链设计为有向非循环图时,我们意识到我们需要动态确定谁可以向图中的每个节点添加信息。从现实世界的角度来看,如果您拥有一家制造工厂,您可能希望装配线上的所有操作员都能够用他们自己的帐户记录他们已经组装了一个零件。
OpenZeppelin,这是我在稳固发展的黄金标准使用,有一个Roles.sol,他们用它来实现自己的角色,如铸币厂和燃烧器合同ERC721.sol合同。遗憾的是,这些实现不允许在运行时创建新角色,如果要使用单独的角色控制对每个单独令牌的访问,则需要这些角色。
本文旨在说明如何为以太坊区块链构建基于角色的访问控制系统。
我结束了根据我们的要求从头开始编写RBAC合同,然后从OpenZeppelin中找到了相同想法的停产版本,它具有几乎相同的方法。为了可重用性,我尽可能地重构我的代码以遵循它们的命名法。
在以下部分中,我将描述:
  1. 我们门禁系统的设计要求;
  2. 作为智能合约的实施;
  3. 我们考虑过的测试案例;
  4. 国家改变方法的燃气使用
  5. 还有一些完善的想法。
让我们深入研究。

概念设计

我对RBAC系统的想法很简单。
  1. 角色将通过数字标识符(如unix中的组)进行标识。
  2. 可以仅使用描述动态创建角色。
  3. 每个角色都会存储用户的地址。
  4. 每个角色都会关联第二个角色,这是唯一允许添加或删除用户的角色。
如果您正在使用OpenZeppelin中的Roles.sol和RBAC.sol合同,则需要注意Roles.sol仅实现在角色内生效的操作,而在角色外部发生的操作在RBAC.sol或访问中实现/roles/*Role.sol收缩,包括在创建角色时存储角色的数据结构。
在我的实现中,我根据我们的用例做了一些裁决:
  1. 我在Role结构中包含一个描述字符串,结构本身存储在一个数组中。数组中每个Role结构的位置用作标识符。有一种使用映射来存储角色的诱惑,但我觉得这里没有必要。
  2. 每个角色在实例化时接收我们指定为其管理角色的另一个角色的标识符,并且在实例化之后不能修改该角色。此管理员角色是唯一可以为此角色添加和删除承载者的角色。
  3. 出于安全性和一致性的原因,您可以从角色中删除承载,但没有方法可以从系统中完全删除角色。
在我的实现中,我根据我们的用例采取了一些实施决策。
*本文的早期版本使用数组来存储每个角色中的承载。根据Nico Vergauwen的建议,这被改为映射  。即使审计更加繁琐,代码也更有效,更清晰。

履行

pragma solidity ^0.5.0;
/**
* @title RBAC
* @author Alberto Cuesta Canada
* @notice Implements runtime configurable Role Based Access Control.
*/
contract RBAC {
  event RoleCreated(uint256 role);
  event BearerAdded(address account, uint256 role);
  event BearerRemoved(address account, uint256 role);
  uint256 constant NO_ROLE = 0;
  /**
   * @notice A role, which will be used to group users.
   * @dev The role id is its position in the roles array.
   * @param description A description for the role.
   * @param admin The only role that can add or remove bearers from
   * this role. To have the role bearers to be also the role admins 
   * you should pass roles.length as the admin role.
   * @param bearers Addresses belonging to this role.
   */
  struct Role {
    string description;
    uint256 admin;
    mapping (address => bool) bearers;
  }
  /**
   * @notice All roles ever created.
   */
  Role[] public roles;
  /**
   * @notice The contract constructor, empty as of now.
   */
  constructor() public {
    addRootRole("NO_ROLE");
  }
  /**
   * @notice Create a new role that has itself as an admin. 
   * msg.sender is added as a bearer.
   * @param _roleDescription The description of the role created.
   * @return The role id.
   */
  function addRootRole(string memory _roleDescription)
    public
    returns(uint256)
  {
    uint256 role = addRole(_roleDescription, roles.length);
    roles[role].bearers[msg.sender] = true;
    emit BearerAdded(msg.sender, role);
  }
  /**
   * @notice Create a new role.
   * @param _roleDescription The description of the role created.
   * @param _admin The role that is allowed to add and remove
   * bearers from the role being created.
   * @return The role id.
   */
  function addRole(string memory _roleDescription, uint256 _admin)
    public
    returns(uint256)
  {
    require(_admin <= roles.length, "Admin role doesn't exist.");
    uint256 role = roles.push(
      Role({
        description: _roleDescription,
        admin: _admin
      })
    ) - 1;
    emit RoleCreated(role);
    return role;
  }
  /**
   * @notice Retrieve the number of roles in the contract.
   * @dev The zero position in the roles array is reserved for
   * NO_ROLE and doesn't count towards this total.
   */
  function totalRoles()
    public
    view
    returns(uint256)
  {
    return roles.length - 1;
  }
  /**
   * @notice Verify whether an account is a bearer of a role
   * @param _account The account to verify.
   * @param _role The role to look into.
   * @return Whether the account is a bearer of the role.
   */
  function hasRole(address _account, uint256 _role)
    public
    view
    returns(bool)
  {
    return _role < roles.length && roles[_role].bearers[_account];
  }
  /**
   * @notice A method to add a bearer to a role
   * @param _account The account to add as a bearer.
   * @param _role The role to add the bearer to.
   */
  function addBearer(address _account, uint256 _role)
    public
  {
    require(
      _role < roles.length,
      "Role doesn't exist."
    );
    require(
      hasRole(msg.sender, roles[_role].admin),
      "User can't add bearers."
    );
    require(
      !hasRole(_account, _role),
      "Account is bearer of role."
    );
    roles[_role].bearers[_account] = true;
    emit BearerAdded(_account, _role);
  }
  /**
   * @notice A method to remove a bearer from a role
   * @param _account The account to remove as a bearer.
   * @param _role The role to remove the bearer from.
   */
  function removeBearer(address _account, uint256 _role)
    public
  {
    require(
      _role < roles.length,
      "Role doesn't exist."
    );
    require(
      hasRole(msg.sender, roles[_role].admin),
      "User can't remove bearers."
    );
    require(
      hasRole(_account, _role),
      "Account is not bearer of role."
    );
    delete roles[_role].bearers[_account];
    emit BearerRemoved(_account, _role);
  }
}

测试

我想描述在公开合同时通过的测试,既可以显示边缘情况,也可以对代码的可靠性提供一些信心。
Contract: RBAC
RBACaddRootRole creates a role.
✓ hasRole returns false for non existing roles.
✓ hasRole returns false for non existing bearerships.
addRootRole adds msg.sender as bearer.
addRole doesn’t add msg.sender with admin role.
✓ addBearer reverts on non existing roles.
✓ addBearer reverts on non authorized users.
✓ addBearer reverts if the bearer belongs to the role.
✓ addBearer adds a bearer to a role.
✓ removeBearer reverts on non existing roles.
✓ removeBearer reverts on non authorized users.
✓ removeBearer reverts if the bearer doesn't belong to the role.
✓ removeBearer removes a bearer from a role.
为了回应之前的反馈,我现在还包括使用eth-gas-reporter的气体使用报告。

结论

本文介绍了基于智能合约角色的访问控制系统的实现,该系统具有以下属性:
  1. 允许在运行时创建新角色。
  2. 包括角色管理员的概念,允许添加和删除角色的成员。
  3. 允许轻松确定所有现有角色及其承载。
基于角色的访问控制实现起来并不一定复杂,但正如本文所示,需要考虑许多权衡和设计决策,这些决策与您的用户及其允许的操作密切相关去表演。虽然如果您决定重复使用 RBAC系统的这种实现,我会很高兴,但我鼓励您寻找并考虑其他选择。
 
对于此RBAC合同的实际应用,请继续关注星际矿场供应链  系列中的下一篇文章。

作者:
AlbertoCuestaCañada

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。