权限管理
Version 1.0.0
English / 中文
当前,智能合约的函数可以被任何人调用,这显然不符合现实要求。基于角色的权限管理的基本思想是,每个角色可以调用部分函数,每个实体可以被赋予多种角色(实体是由其ONT ID来标识)。
如果智能合约需要增加权限管理功能,那就必须记录合约中分配的角色,以及角色可调用的函数,哪些实体具有该角色等等信息。这个工作比较繁琐,可交由一个系统合约来管理。
具体请参考权限合约
-
权限合约管理
-
用法示例
-
接口列表
权限合约管理
Auth合约负责管理应用合约的函数调用权限,功能有合约管理员可以转让合约管理权限,合约管理员为角色分配函数,合约管理员绑定角色到实体身份,有合约函数调用权的实体将合约调用权代理给其他人,合约管理员收回合约调用权,实体验证合约调用token的有效性。
Auth合约实现了一套基于角色的权限管理方案,每个角色对应一些可调用的函数,管理员通过将角色分配给ONT ID,使之可以调用该角色下的函数。与此同时,角色是可以传递的,即A可以将某个角色代理给B,并指定代理时间,这样的话B就可以一段时间内也可以调用对应的函数。
用法示例
Ontology智能合约不支持在部署的时候执行初始化,所以合约管理员需要硬编码在合约代码中,即将合约管理员ONT ID在合约中定义成一个常量,具体请看下面的合约样例。Ontology智能合约可以调用verifyToken函数进行权限验证,同时为了方便开发者验证某个OntID是否有某个角色,Java-SDK也提供了verifyToken接口,可以实时查询角色的分配情况。
使用流程:
1. 将智能合约部署到链上。
2. 调用智能合约中的init方法,在合约代码中通过调用initContractAdmin方法,将合约中预先定义的管理员ONT ID,设置为该合约的管理员(注意:需先将管理员ONT ID注册到链上)。
3. 合约管理员设计需要用到的角色,并将角色和智能合约中的函数进行绑定,该步骤可以调用Java-SDK中的assignFuncsToRole接口进行设置。
4. 合约管理员将角色分配给ONT ID,拥有该角色的ONT ID将有权限调用该角色对应的函数,该步骤可以调用Java-SDK中的assignOntIDsToRole接口进行设置。
5. 拥有某个角色的ONT ID在调用该角色对应的函数之前,可以通过Java-SDK中的verifyToken接口验证该ontid是否有调用相应函数的权利。
结合下面的示例讲解使用流程:
A. 将智能合约部署到链上。 B. 调用该合约中init方法。
AbiInfo abiInfo = JSON.parseObject(abi,AbiInfo.class);
String name = "init";
AbiFunction function = abiInfo.getFunction(name);
function.setParamsValue();
String txhash = (String) sdk.neovm().sendTransaction(Helper.reverse(codeAddress),account,account,sdk.DEFAULT_GAS_LIMIT,0,function,false);
C. 合约管理员设计角色role1和role2,并将角色role1和函数foo1绑定,将角色role2和函数foo2、foo3绑定。
String txhash = sdk.nativevm().auth().assignFuncsToRole(adminIdentity.ontid, password, adminIdentity.controls.get(0).getSalt(),
1, Helper.reverse(codeAddress), "role1", new String[]{"foo1"}, account, sdk.DEFAULT_GAS_LIMIT, 0);
String txhash = sdk.nativevm().auth().assignFuncsToRole(adminIdentity.ontid, password, adminIdentity.controls.get(0).getSalt(), 1,
Helper.reverse(codeAddress), "role2", new String[]{"foo2","foo3"}, account, sdk.DEFAULT_GAS_LIMIT, 0);
D. 合约管理员将角色”role1”分配给ontId1,将角色”role2”分配给ontId2,则ontId1拥有调用函数foo1的权限,ontId2拥有调用函数foo2和函数foo3的权限。
String txhash = sdk.nativevm().auth().assignOntIdsToRole(adminIdentity.ontid, password, adminIdentity.controls.get(0).getSalt(), 1,
Helper.reverse(codeAddress), "role1", new String[]{identity1.ontid}, account, sdk.DEFAULT_GAS_LIMIT, 0);
String txhash = sdk.nativevm().auth().assignOntIdsToRole(adminIdentity.ontid, password, adminIdentity.controls.get(0).getSalt(), 1,
Helper.reverse(codeAddress), "role2", new String[]{identity2.ontid}, account, sdk.DEFAULT_GAS_LIMIT, 0);
E. 由于ontId1的角色是合约管理员分配的,其权限level默认是2,即ontId1可以将权限代理给其他ontidX,代理的Java-SDK接口是delegate,具体接口信息请参考下面的接口信息。 在代理权限的时候需要指定被代理人权限的级别以及代理时间,如果代理人的level是2,则被代理人的level只能是1。
sdk.nativevm().auth().delegate(identity1.ontid,password,identity1.controls.get(0).getSalt(),1,Helper.reverse(codeAddress),
identityX.ontid,"role1",60*5,1,account,sdk.DEFAULT_GAS_LIMIT,0);
F. 验证某个ontId是否有调用某个函数的权限,可以通过verifyToken接口查询。
String result = sdk.nativevm().auth().verifyToken(identityX.ontid, password, identityX.controls.get(0).getSalt(), 1, Helper.reverse(codeAddress), "foo1");
返回值: "01"表示有权限,"00"表示没有权限。
G. 如果被代理人的权限时间没有结束,代理人可以提前收回代理给别人的权限。
sdk.nativevm().auth().withdraw(identity1.ontid,password,identity1.controls.get(0).getSalt(),1,Helper.reverse(codeAddress),identityX.ontid,"role1",account,sdk.DEFAULT_GAS_LIMIT,0);
H. 合约管理员可以将自己的管理权限转移给其他的ontId
String txhash = sdk.nativevm().auth().sendTransfer(adminIdentity.ontid,password,adminIdentity.controls.get(0).getSalt(),1,Helper.reverse(codeAddress),adminIdentity.ontid,
account,sdk.DEFAULT_GAS_LIMIT,0);
Ontology合约示例:
using Neo.SmartContract.Framework;
using Neo.SmartContract.Framework.Services.Neo;
using Neo.SmartContract.Framework.Services.System;
using System;
using System.ComponentModel;
using System.Numerics;
namespace Example
{
public class AppContract : SmartContract
{
public struct initContractAdminParam
{
public byte[] adminOntID;
}
public struct verifyTokenParam
{
public byte[] contractAddr; //合约地址
public byte[] calllerOntID; //调用者ontId
public string funcName; //调用的函数名
public int keyNo; //使用调用者ontId的第几把公钥
}
//the admin ONT ID of this contract must be hardcoded.
public static readonly byte[] adminOntID = "did:ont:AazEvfQPcQ2GEFFPLF1ZLwQ7K5jDn81hve".AsByteArray();
public static Object Main(string operation,object[] args)
{
if (operation == "init") return init();
if (operation == "foo")
{
//we need to check if the caller is authorized to invoke foo
if (!verifyToken(operation, args)) return "no auth";
return foo();
}
if (operation == "foo2")
{
//we need to check if the caller is authorized to invoke foo
if (!verifyToken(operation, args)) return "no auth";
return foo2();
}
if (operation == "foo3")
{
//we need to check if the caller is authorized to invoke foo
if (!verifyToken(operation, args)) return "no auth";
return foo3();
}
return "over";
}
public static string foo()
{
return "A";
}
public static string foo2()
{
return "B";
}
public static string foo3()
{
return "C";
}
//this method is a must-defined method if you want to use native auth contract.
public static bool init()
{
byte[] authContractAddr = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06 };
byte[] ret = Native.Invoke(0, authContractAddr, "initContractAdmin", adminOntID);
return ret[0] == 1;
}
internal static bool verifyToken(string operation, object[] args)
{
verifyTokenParam param = new verifyTokenParam{};
param.contractAddr = ExecutionEngine.ExecutingScriptHash;
param.funcName = operation;
param.calllerOntID = (byte[])args[0];
param.keyNo = (int)args[1];
byte[] authContractAddr = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x06 };
byte[] ret = Native.Invoke(0, authContractAddr, "verifyToken", param);
return ret[0] == 1;
}
}
}
权限合约接口
Java-SDK已经封装好权限合约的调用接口,可以通过Java-SDK进行权限管理。
1. 合约管理员转让合约管理权限
String sendTransfer(String adminOntId, String password, byte[] salt, String contractAddr, String newAdminOntID, long keyNo, Account payerAcct, long gaslimit, long gasprice)
|说明||描述| |:–|:–|:–| |功能说明|合约管理员转让合约管理权限|此函数必须由合约管理员调用,即将会以adminOntID名下编号为keyNo的公钥来验证交易签名是否合法。| |参数说明|字段|描述| ||adminOntId|合约管理员ontid| ||password|合约管理员密码| ||salt |私钥解密参数| ||contractAddr|合约地址| ||newAdminOntID|新的管理员| ||keyNo|合约管理员的公钥编号| ||payerAcct|付费账户| ||gaslimit|gas价格| ||gasprice|gas价格| |返回值说明|交易hash||
2. 为角色分配函数
String assignFuncsToRole(String adminOntID,String password,byte[] salt,String contractAddr,String role,String[] funcName,long keyNo,Account payerAcct,long gaslimit,long gasprice)
|说明||描述| |:–|:–|:–| |功能说明|为角色分配函数|必须由合约管理者调用,将所有函数自动绑定到role,若已经绑定,自动跳过,最后返回true。| |参数说明|字段|描述| ||adminOntId|合约管理员ontid| ||password|合约管理员密码| ||salt|私钥解密参数| ||contractAddr|合约地址| ||role|角色| ||funcName|函数名数组| ||keyNo|合约管理员的公钥编号| ||payerAcct|付费账户| ||gaslimit|gas价格| ||gasprice|gas价格| |返回值说明|交易hash||
3. 绑定角色到实体身份
String assignOntIDsToRole(String adminOntId,String password,byte[] salt, String contractAddr,String role,String[] ontIDs,long keyNo,Account payerAcct,long gaslimit,long gasprice)
|说明||描述| |:–|:–|:–| |功能说明|绑定角色到实体身份|必须由合约管理者调用,ontIDs数组中的ONT ID被分配role角色,最后返回true。 在当前实现中,权限token的级别level默认等于2。| |参数说明|字段|描述| ||adminOntId|合约管理员ontid| ||password|合约管理员密码| ||salt|私钥解密参数| ||contractAddr|合约地址| ||role|角色| ||ontIDs|ontid数组| ||keyNo|合约管理员的公钥编号| ||payerAcct|付费账户| ||gaslimit|gas价格| ||gasprice|gas价格| |返回值说明|交易hash||
4. 将合约调用权代理给其他人
String delegate(String ontid,String password,byte[] salt,String contractAddr,String toOntId,String role,long period,long level,long keyNo,Account payerAcct,long gaslimit,long gasprice)
角色拥有者可以将角色代理给其他人,from是转让者的ONT ID,to是代理人的ONT ID,role表示要代理的角色,period参数指定委托任期时间(以second为单位)。
代理人可以再次将其角色代理给更多的人,level参数指定委托层次深度。例如,
level = 1: 此时代理人就无法将其角色再次代理出去;当前实现只支持此情况。
说明 | 描述 | |
---|---|---|
功能说明 | 将合约调用权代理给其他人 | |
参数说明 | 字段 | 描述 |
ontid | 拥有合约中某个函数调用权的ontid | |
password | ontid密码 | |
salt | 私钥解密参数 | |
contractAddr | 合约地址 | |
toOntId | 接收合约调用权的ontid | |
role | 角色 | |
period | 以秒为单位的时间 | |
keyNo | ontid的公钥编号 | |
payerAcct | 付费账户 | |
gaslimit | gas价格 | |
gasprice | gas价格 | |
返回值说明 | 交易hash |
5. 收回合约调用权
String withdraw(String initiatorOntid,String password,byte[] salt,String contractAddr,String delegate, String role,long keyNo,Account payerAcct,long gaslimit,long gasprice)
角色拥有者可以提前将角色代理提前撤回,initiatorOntid是发起者,delegate是角色代理人,initiator将代理给delegate的角色提前撤回。
说明 | 描述 | |
---|---|---|
功能说明 | 收回合约调用权(配合delegate使用) | |
参数说明 | 字段 | 描述 |
initiatorOntid | 将合约调用权转让给其他人的ontid | |
password | ontid密码 | |
salt | 私钥解密参数 | |
contractAddr | 合约地址 | |
delegate | 代理人ontid | |
role | 角色 | |
keyNo | ontid的公钥编号 | |
payerAcct | 付费账户 | |
gaslimit | gas价格 | |
gasprice | gas价格 | |
返回值说明 | 交易hash |
6. 验证合约调用token的有效性
String verifyToken(String ontid,String password,byte[] salt,String contractAddr,String funcName,long keyNo)
|说明||描述| |:–|:–|:–| |功能说明|验证合约调用token的有效性|| |参数说明|字段|描述| ||ontid|验证的ontid| ||password|ontid密码| ||salt|私钥解密参数| ||contractAddr|合约地址| ||funcName|函数名| ||keyNo|ontid的公钥编号| |返回值说明|交易hash||