簡(jiǎn)介
基于角色的訪問控制是軟件系統(tǒng)的安全需求,旨在為數(shù)百個(gè)用戶提供訪問。雖然這種需求通常在企業(yè)軟件和操作系統(tǒng)中實(shí)現(xiàn),但對(duì)以太坊區(qū)塊鏈的處理并不多。
當(dāng)將供應(yīng)鏈設(shè)計(jì)為有向無(wú)環(huán)圖時(shí),我們意識(shí)到需要?jiǎng)討B(tài)地確定誰(shuí)可以向圖中的每個(gè)節(jié)點(diǎn)添加信息。從現(xiàn)實(shí)世界的角度來(lái)看,如果您擁有一家制造工廠,您可能希望裝配線上的所有操作員都能夠用他們自己的帳戶記錄他們已經(jīng)組裝了一個(gè)零件。
OpenZeppelin是我在Solidity開發(fā)中使用的金標(biāo)準(zhǔn),它有一個(gè)roles.sol合同,用于在erc721.sol合同中實(shí)現(xiàn)諸如minter和burner等角色。不幸的是,這些實(shí)現(xiàn)不允許在運(yùn)行時(shí)創(chuàng)建新角色,如果您想使用單獨(dú)的角色控制對(duì)每個(gè)單獨(dú)令牌的訪問,則需要?jiǎng)?chuàng)建新角色。
本文旨在展示如何為以太坊區(qū)塊鏈構(gòu)建基于角色的訪問控制系統(tǒng)。根據(jù)我們的要求從頭開始編寫RBAC合同,然后從OpenZeppelin中找到了相同想法的版本,它具有幾乎相同的方法。為了可重用性,我盡可能地重構(gòu)我的代碼以遵循它們的命名法。
在以下各節(jié)中,我將介紹:
1. 我們進(jìn)入訪問系統(tǒng)的設(shè)計(jì)要求;
2. 智能合約的實(shí)施;
3. 測(cè)試案例;
4. 狀態(tài)變換法的gas利用;
5. 還有一些完善的想法。
概念設(shè)計(jì)
我對(duì)RBAC系統(tǒng)的想法很簡(jiǎn)單。
1. 角色將由數(shù)字標(biāo)識(shí)符標(biāo)識(shí),如Unix中的組。
2. 角色可以動(dòng)態(tài)創(chuàng)建。
3. 每個(gè)角色存儲(chǔ)用戶的地址。
4. 每個(gè)角色都會(huì)有一個(gè)關(guān)聯(lián)的第二個(gè)角色,這是唯一允許添加或刪除用戶的角色。
如果您是使用OpenZeppelin中的Roles.sol和RBAC.sol合同,則需要注意Roles.sol僅實(shí)現(xiàn)在角色內(nèi)生效的操作,而在角色外部發(fā)生的操作在RBAC.sol或訪問中實(shí)現(xiàn)/roles/*Role.sol收縮,包括在創(chuàng)建角色時(shí)存儲(chǔ)角色的數(shù)據(jù)結(jié)構(gòu)。
在我的實(shí)現(xiàn)中,我根據(jù)我們的用例做了一些決策:
· 角色結(jié)構(gòu)中包含一個(gè)描述字符串,結(jié)構(gòu)本身存儲(chǔ)在一個(gè)數(shù)組中。數(shù)組中每個(gè)角色結(jié)構(gòu)的位置用作標(biāo)識(shí)符。有一種使用映射來(lái)存儲(chǔ)角色,但我發(fā)現(xiàn)這里沒有必要。
· 每個(gè)角色在實(shí)例化時(shí)接收我們指定為其管理角色的另一個(gè)角色的標(biāo)識(shí)符,并且在實(shí)例化之后不能修改該角色。此管理員角色是唯一可以為此角色添加和刪除承載者的角色。
出于安全性和一致性的原因,您可以從角色中刪除承載,但沒有方法可以從系統(tǒng)中完全刪除角色。
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);
}
}
測(cè)試
我喜歡公開測(cè)試智能合約,既展示了操作案例,又能對(duì)代碼的可靠性提供了一些信心。
Contract: RBAC
RBAC
? addRootRole 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.
為了回應(yīng)之前的反饋,我現(xiàn)在還使用eth-gas-reporter的gas使用報(bào)告。
結(jié)論
本文描述了一個(gè)基于智能合約角色的訪問控制系統(tǒng)的實(shí)現(xiàn),它具有以下屬性:
1. 允許在系統(tǒng)運(yùn)行時(shí)創(chuàng)建新角色。
2. 包括角色管理員的概念,允許添加和刪除角色的成員。
3. 允許輕松確定所有現(xiàn)有角色及其承載。
4. 基于角色的訪問控制實(shí)現(xiàn)起來(lái)并不一定復(fù)雜,但正如本文所示,需要考慮許多權(quán)衡和設(shè)計(jì)決策,這些決策與您的用戶及其允許的操作密切相關(guān) 去設(shè)計(jì)。如果您決定復(fù)用RBAC系統(tǒng)的這種實(shí)現(xiàn),我會(huì)很高興,但我也鼓勵(lì)您尋找并考慮其他選擇。
評(píng)論
查看更多