@@ -6,8 +6,15 @@ import com.jd.blockchain.utils.Bytes; | |||
public interface LedgerSecurityManager { | |||
String DEFAULT_ROLE = "_DEFAULT"; | |||
String DEFAULT_ROLE = "DEFAULT"; | |||
SecurityPolicy getSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes); | |||
/** | |||
* 创建一项与指定的终端用户和节点参与方相关的安全策略; | |||
* | |||
* @param endpoints 终端用户的地址列表; | |||
* @param nodes 节点参与方的地址列表; | |||
* @return 一项安全策略; | |||
*/ | |||
SecurityPolicy createSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes); | |||
} |
@@ -29,8 +29,9 @@ public class LedgerSecurityManagerImpl implements LedgerSecurityManager { | |||
private UserRoleSettings userRolesSettings; | |||
//用户的权限配置 | |||
private Map<Bytes, UserRolesPrivileges> userPrivilegesCache = new ConcurrentHashMap<>(); | |||
private Map<Bytes, UserRoles> userRolesCache = new ConcurrentHashMap<>(); | |||
private Map<String, RolePrivileges> rolesPrivilegeCache = new ConcurrentHashMap<>(); | |||
@@ -40,8 +41,7 @@ public class LedgerSecurityManagerImpl implements LedgerSecurityManager { | |||
} | |||
@Override | |||
public SecurityPolicy getSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes) { | |||
public SecurityPolicy createSecurityPolicy(Set<Bytes> endpoints, Set<Bytes> nodes) { | |||
Map<Bytes, UserRolesPrivileges> endpointPrivilegeMap = new HashMap<>(); | |||
Map<Bytes, UserRolesPrivileges> nodePrivilegeMap = new HashMap<>(); | |||
@@ -90,7 +90,7 @@ public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
TransactionRequestExtension reqExt = new TransactionRequestExtensionImpl(request); | |||
// 初始化交易的用户安全策略; | |||
SecurityPolicy securityPolicy = securityManager.getSecurityPolicy(reqExt.getEndpointAddresses(), | |||
SecurityPolicy securityPolicy = securityManager.createSecurityPolicy(reqExt.getEndpointAddresses(), | |||
reqExt.getNodeAddresses()); | |||
SecurityContext.setContextUsersPolicy(securityPolicy); | |||
@@ -200,7 +200,7 @@ public class ContractInvokingTest { | |||
when(securityPolicy.isEnableToNodes(any(LedgerPermission.class), any())).thenReturn(true); | |||
when(securityPolicy.isEnableToNodes(any(TransactionPermission.class), any())).thenReturn(true); | |||
when(securityManager.getSecurityPolicy(any(), any())).thenReturn(securityPolicy); | |||
when(securityManager.createSecurityPolicy(any(), any())).thenReturn(securityPolicy); | |||
return securityManager; | |||
} | |||
@@ -1,5 +1,6 @@ | |||
package test.com.jd.blockchain.ledger.core; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertFalse; | |||
import static org.junit.Assert.assertTrue; | |||
@@ -18,10 +19,8 @@ import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.LedgerPermission; | |||
import com.jd.blockchain.ledger.Privileges; | |||
import com.jd.blockchain.ledger.RolePrivilegeSettings; | |||
import com.jd.blockchain.ledger.RolesPolicy; | |||
import com.jd.blockchain.ledger.TransactionPermission; | |||
import com.jd.blockchain.ledger.UserRoleSettings; | |||
import com.jd.blockchain.ledger.core.CryptoConfig; | |||
import com.jd.blockchain.ledger.core.LedgerSecurityManager; | |||
import com.jd.blockchain.ledger.core.LedgerSecurityManagerImpl; | |||
@@ -56,30 +55,18 @@ public class LedgerSecurityManagerTest { | |||
CRYPTO_SETTINGS = cryptoConfig; | |||
} | |||
private RolePrivilegeSettings initRoles(MemoryKVStorage testStorage, String[] roles, Privileges[] privilege) { | |||
private RolePrivilegeDataset createRolePrivilegeDataset(MemoryKVStorage testStorage) { | |||
String prefix = "role-privilege/"; | |||
RolePrivilegeDataset rolePrivilegeDataset = new RolePrivilegeDataset(CRYPTO_SETTINGS, prefix, testStorage, | |||
testStorage); | |||
for (int i = 0; i < roles.length; i++) { | |||
rolePrivilegeDataset.addRolePrivilege(roles[i], privilege[i]); | |||
} | |||
rolePrivilegeDataset.commit(); | |||
return rolePrivilegeDataset; | |||
} | |||
private UserRoleSettings initUserRoless(MemoryKVStorage testStorage, Bytes[] userAddresses, RolesPolicy[] policies, | |||
String[][] roles) { | |||
private UserRoleDataset createUserRoleDataset(MemoryKVStorage testStorage) { | |||
String prefix = "user-roles/"; | |||
UserRoleDataset userRolesDataset = new UserRoleDataset(CRYPTO_SETTINGS, prefix, testStorage, testStorage); | |||
for (int i = 0; i < userAddresses.length; i++) { | |||
userRolesDataset.addUserRoles(userAddresses[i], policies[i], roles[i]); | |||
} | |||
userRolesDataset.commit(); | |||
return userRolesDataset; | |||
} | |||
@@ -87,55 +74,102 @@ public class LedgerSecurityManagerTest { | |||
public void testGetSecurityPolicy() { | |||
MemoryKVStorage testStorage = new MemoryKVStorage(); | |||
// 定义不同角色用户的 keypair; | |||
final BlockchainKeypair kpManager = BlockchainKeyGenerator.getInstance().generate(); | |||
final BlockchainKeypair kpEmployee = BlockchainKeyGenerator.getInstance().generate(); | |||
final BlockchainKeypair kpDevoice = BlockchainKeyGenerator.getInstance().generate(); | |||
final Map<Bytes, BlockchainKeypair> endpoints = new HashMap<>(); | |||
endpoints.put(kpManager.getAddress(), kpManager); | |||
endpoints.put(kpEmployee.getAddress(), kpEmployee); | |||
final Map<Bytes, BlockchainKeypair> nodes = new HashMap<>(); | |||
nodes.put(kpDevoice.getAddress(), kpDevoice); | |||
final BlockchainKeypair kpPlatform = BlockchainKeyGenerator.getInstance().generate(); | |||
// 定义角色和权限; | |||
final String ROLE_ADMIN = "ID_ADMIN"; | |||
final String ROLE_OPERATOR = "OPERATOR"; | |||
final String ROLE_DATA_COLLECTOR = "DATA_COLLECTOR"; | |||
final String ROLE_PLATFORM = "PLATFORM"; | |||
// 定义管理员角色的权限:【账本权限只允许:注册用户、注册数据账户】【交易权限只允许:调用账本直接操作】 | |||
final Privileges PRIVILEGES_ADMIN = Privileges.configure() | |||
.enable(LedgerPermission.REGISTER_USER, LedgerPermission.REGISTER_DATA_ACCOUNT) | |||
.enable(TransactionPermission.DIRECT_OPERATION, TransactionPermission.CONTRACT_OPERATION); | |||
.enable(TransactionPermission.DIRECT_OPERATION); | |||
final Privileges PRIVILEGES_OPERATOR = Privileges.configure() | |||
.enable(LedgerPermission.WRITE_DATA_ACCOUNT, LedgerPermission.APPROVE_TX) | |||
// 定义操作员角色的权限:【账本权限只允许:写入数据账户】【交易权限只允许:调用合约】 | |||
final Privileges PRIVILEGES_OPERATOR = Privileges.configure().enable(LedgerPermission.WRITE_DATA_ACCOUNT) | |||
.enable(TransactionPermission.CONTRACT_OPERATION); | |||
// 定义数据收集器角色的权限:【账本权限只允许:写入数据账户】【交易权限只允许:调用账本直接操作】 | |||
final Privileges PRIVILEGES_DATA_COLLECTOR = Privileges.configure().enable(LedgerPermission.WRITE_DATA_ACCOUNT) | |||
.enable(TransactionPermission.CONTRACT_OPERATION); | |||
RolePrivilegeSettings rolePrivilegeSettings = initRoles(testStorage, | |||
new String[] { ROLE_ADMIN, ROLE_OPERATOR, ROLE_DATA_COLLECTOR }, | |||
new Privileges[] { PRIVILEGES_ADMIN, PRIVILEGES_OPERATOR, PRIVILEGES_DATA_COLLECTOR }); | |||
.enable(TransactionPermission.DIRECT_OPERATION); | |||
// 定义平台角色的权限:【账本权限只允许:签署合约】 (只允许作为节点签署交易,不允许作为终端发起交易指令) | |||
final Privileges PRIVILEGES_PLATFORM = Privileges.configure().enable(LedgerPermission.APPROVE_TX); | |||
RolePrivilegeDataset rolePrivilegeDataset = createRolePrivilegeDataset(testStorage); | |||
long v = rolePrivilegeDataset.addRolePrivilege(ROLE_ADMIN, PRIVILEGES_ADMIN); | |||
assertTrue(v > -1); | |||
v = rolePrivilegeDataset.addRolePrivilege(ROLE_OPERATOR, PRIVILEGES_OPERATOR); | |||
assertTrue(v > -1); | |||
v = rolePrivilegeDataset.addRolePrivilege(ROLE_DATA_COLLECTOR, PRIVILEGES_DATA_COLLECTOR); | |||
assertTrue(v > -1); | |||
v = rolePrivilegeDataset.addRolePrivilege(ROLE_PLATFORM, PRIVILEGES_PLATFORM); | |||
assertTrue(v > -1); | |||
rolePrivilegeDataset.commit(); | |||
// 为用户分配角色; | |||
String[] managerRoles = new String[] { ROLE_ADMIN, ROLE_OPERATOR }; | |||
String[] employeeRoles = new String[] { ROLE_OPERATOR }; | |||
String[] devoiceRoles = new String[] { ROLE_DATA_COLLECTOR }; | |||
UserRoleSettings userRolesSettings = initUserRoless(testStorage, | |||
new Bytes[] { kpManager.getAddress(), kpEmployee.getAddress(), kpDevoice.getAddress() }, | |||
new RolesPolicy[] { RolesPolicy.UNION, RolesPolicy.UNION, RolesPolicy.UNION }, | |||
new String[][] { managerRoles, employeeRoles, devoiceRoles }); | |||
LedgerSecurityManager securityManager = new LedgerSecurityManagerImpl(rolePrivilegeSettings, userRolesSettings); | |||
SecurityPolicy policy = securityManager.getSecurityPolicy(endpoints.keySet(), nodes.keySet()); | |||
assertTrue(policy.isEnableToEndpoints(LedgerPermission.REGISTER_USER, MultiIdsPolicy.AT_LEAST_ONE)); | |||
assertTrue(policy.isEnableToEndpoints(LedgerPermission.REGISTER_DATA_ACCOUNT, MultiIdsPolicy.AT_LEAST_ONE)); | |||
assertTrue(policy.isEnableToEndpoints(LedgerPermission.WRITE_DATA_ACCOUNT, MultiIdsPolicy.AT_LEAST_ONE)); | |||
assertTrue(policy.isEnableToEndpoints(LedgerPermission.APPROVE_TX, MultiIdsPolicy.AT_LEAST_ONE)); | |||
assertFalse(policy.isEnableToEndpoints(LedgerPermission.REGISTER_USER, MultiIdsPolicy.ALL)); | |||
assertFalse(policy.isEnableToEndpoints(LedgerPermission.AUTHORIZE_ROLES, MultiIdsPolicy.AT_LEAST_ONE)); | |||
String[] platformRoles = new String[] { ROLE_PLATFORM }; | |||
UserRoleDataset userRolesDataset = createUserRoleDataset(testStorage); | |||
userRolesDataset.addUserRoles(kpManager.getAddress(), RolesPolicy.UNION, managerRoles); | |||
userRolesDataset.addUserRoles(kpEmployee.getAddress(), RolesPolicy.UNION, employeeRoles); | |||
userRolesDataset.addUserRoles(kpDevoice.getAddress(), RolesPolicy.UNION, devoiceRoles); | |||
userRolesDataset.addUserRoles(kpPlatform.getAddress(), RolesPolicy.UNION, platformRoles); | |||
userRolesDataset.commit(); | |||
// 创建安全管理器; | |||
LedgerSecurityManager securityManager = new LedgerSecurityManagerImpl(rolePrivilegeDataset, userRolesDataset); | |||
// 定义终端用户列表;终端用户一起共同具有 ADMIN、OPERATOR 角色; | |||
final Map<Bytes, BlockchainKeypair> endpoints = new HashMap<>(); | |||
endpoints.put(kpManager.getAddress(), kpManager); | |||
endpoints.put(kpEmployee.getAddress(), kpEmployee); | |||
// 定义节点参与方列表; | |||
final Map<Bytes, BlockchainKeypair> nodes = new HashMap<>(); | |||
nodes.put(kpPlatform.getAddress(), kpPlatform); | |||
// 创建一项与指定的终端用户和节点参与方相关的安全策略; | |||
SecurityPolicy policy = securityManager.createSecurityPolicy(endpoints.keySet(), nodes.keySet()); | |||
// 校验安全策略的正确性; | |||
LedgerPermission[] ledgerPermissions = LedgerPermission.values(); | |||
for (LedgerPermission p : ledgerPermissions) { | |||
// 终端节点有 ADMIN 和 OPERATOR 两种角色的合并权限; | |||
if (p == LedgerPermission.REGISTER_USER || p == LedgerPermission.REGISTER_DATA_ACCOUNT | |||
|| p == LedgerPermission.WRITE_DATA_ACCOUNT) { | |||
assertTrue(policy.isEnableToEndpoints(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} else { | |||
assertFalse(policy.isEnableToEndpoints(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} | |||
if (p == LedgerPermission.APPROVE_TX) { | |||
// 共识参与方只有 PLATFORM 角色的权限:核准交易; | |||
assertTrue(policy.isEnableToNodes(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} else { | |||
assertFalse(policy.isEnableToNodes(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} | |||
} | |||
TransactionPermission[] transactionPermissions = TransactionPermission.values(); | |||
for (TransactionPermission p : transactionPermissions) { | |||
// 终端节点有 ADMIN 和 OPERATOR 两种角色的合并权限; | |||
if (p == TransactionPermission.DIRECT_OPERATION || p == TransactionPermission.CONTRACT_OPERATION) { | |||
assertTrue(policy.isEnableToEndpoints(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} else { | |||
assertFalse(policy.isEnableToEndpoints(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} | |||
assertFalse(policy.isEnableToNodes(p, MultiIdsPolicy.AT_LEAST_ONE)); | |||
} | |||
} | |||
} |
@@ -125,7 +125,7 @@ public class TransactionBatchProcessorTest { | |||
when(securityPolicy.isEnableToNodes(any(LedgerPermission.class), any())).thenReturn(true); | |||
when(securityPolicy.isEnableToNodes(any(TransactionPermission.class), any())).thenReturn(true); | |||
when(securityManager.getSecurityPolicy(any(), any())).thenReturn(securityPolicy); | |||
when(securityManager.createSecurityPolicy(any(), any())).thenReturn(securityPolicy); | |||
return securityManager; | |||
} | |||
@@ -45,7 +45,9 @@ public enum LedgerPermission { | |||
/** | |||
* 参与方核准交易;<br> | |||
* | |||
* 如果不具备此项权限,则无法作为网关节点接入并签署由终端提交的交易; | |||
* 如果不具备此项权限,则无法作为节点签署由终端提交的交易; | |||
* <p> | |||
* 只对交易请求的节点签名列表{@link TransactionRequest#getNodeSignatures()}的用户产生影响; | |||
*/ | |||
APPROVE_TX((byte) 0x06), | |||
@@ -11,17 +11,37 @@ import com.jd.blockchain.utils.io.BytesSerializable; | |||
* | |||
*/ | |||
public class PrivilegeBitset<E extends Enum<?>> implements Privilege<E>, BytesSerializable { | |||
// 加入前缀位,可避免序列化时输出空的字节数组; | |||
private static final boolean[] PREFIX = { false, false, false, true, false, false, false, true }; | |||
private static final int OFFSET = PREFIX.length; | |||
private static final int MAX_SIZE = 256 - PREFIX.length; | |||
private BitSet permissionBits; | |||
private CodeIndexer<E> codeIndexer; | |||
public PrivilegeBitset(CodeIndexer<E> codeIndexer) { | |||
this(new BitSet(), codeIndexer); | |||
this.permissionBits = new BitSet(); | |||
this.codeIndexer = codeIndexer; | |||
// 设置前缀; | |||
for (int i = 0; i < PREFIX.length; i++) { | |||
permissionBits.set(i, PREFIX[i]); | |||
} | |||
} | |||
public PrivilegeBitset(byte[] codeBytes, CodeIndexer<E> codeIndexer) { | |||
this(BitSet.valueOf(codeBytes), codeIndexer); | |||
if (codeBytes.length > MAX_SIZE) { | |||
throw new IllegalArgumentException( | |||
"The size of code bytes specified to PrivilegeBitset exceed the max size[" + MAX_SIZE + "]!"); | |||
} | |||
this.permissionBits = BitSet.valueOf(codeBytes); | |||
this.codeIndexer = codeIndexer; | |||
// 校验前缀; | |||
for (int i = 0; i < PREFIX.length; i++) { | |||
if (permissionBits.get(i) != PREFIX[i]) { | |||
throw new IllegalArgumentException("The code bytes is not match the privilege prefix code!"); | |||
} | |||
} | |||
} | |||
private PrivilegeBitset(BitSet bits, CodeIndexer<E> codeIndexer) { | |||
@@ -30,28 +50,28 @@ public class PrivilegeBitset<E extends Enum<?>> implements Privilege<E>, BytesSe | |||
} | |||
public boolean isEnable(E permission) { | |||
return permissionBits.get(codeIndexer.getCodeIndex(permission)); | |||
return permissionBits.get(index(permission)); | |||
} | |||
public void enable(E permission) { | |||
permissionBits.set(codeIndexer.getCodeIndex(permission)); | |||
permissionBits.set(index(permission)); | |||
} | |||
public void disable(E permission) { | |||
permissionBits.clear(codeIndexer.getCodeIndex(permission)); | |||
permissionBits.clear(index(permission)); | |||
} | |||
@SuppressWarnings("unchecked") | |||
public void enable(E... permissions) { | |||
for (E p : permissions) { | |||
permissionBits.set(codeIndexer.getCodeIndex(p)); | |||
permissionBits.set(index(p)); | |||
} | |||
} | |||
@SuppressWarnings("unchecked") | |||
public void disable(E... permissions) { | |||
for (E p : permissions) { | |||
permissionBits.clear(codeIndexer.getCodeIndex(p)); | |||
permissionBits.clear(index(p)); | |||
} | |||
} | |||
@@ -72,6 +92,7 @@ public class PrivilegeBitset<E extends Enum<?>> implements Privilege<E>, BytesSe | |||
/** | |||
* 把指定的权限合并到当前的权限中; <br> | |||
* | |||
* @param privileges | |||
* @param offset | |||
* @param count | |||
@@ -115,6 +136,10 @@ public class PrivilegeBitset<E extends Enum<?>> implements Privilege<E>, BytesSe | |||
return new PrivilegeBitset<E>((BitSet) permissionBits.clone(), codeIndexer); | |||
} | |||
private int index(E permission) { | |||
return OFFSET + codeIndexer.getCodeIndex(permission); | |||
} | |||
static interface CodeIndexer<E extends Enum<?>> { | |||
int getCodeIndex(E permission); | |||
} | |||
@@ -0,0 +1,92 @@ | |||
package test.com.jd.blockchain.ledger; | |||
import static org.junit.Assert.*; | |||
import org.junit.Test; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.ledger.LedgerPermission; | |||
import com.jd.blockchain.ledger.PrivilegeSet; | |||
import com.jd.blockchain.ledger.Privileges; | |||
import com.jd.blockchain.ledger.TransactionPermission; | |||
public class PrivilegesTest { | |||
@Test | |||
public void test() { | |||
// 正常情形; | |||
{ | |||
Privileges privileges = Privileges.configure() | |||
.enable(LedgerPermission.REGISTER_USER, LedgerPermission.APPROVE_TX) | |||
.enable(TransactionPermission.DIRECT_OPERATION); | |||
byte[] bytes = BinaryProtocol.encode(privileges, PrivilegeSet.class); | |||
PrivilegeSet decodePrivileges = BinaryProtocol.decode(bytes); | |||
assertNotNull(decodePrivileges.getLedgerPrivilege()); | |||
assertNotNull(decodePrivileges.getTransactionPrivilege()); | |||
for (LedgerPermission p : LedgerPermission.values()) { | |||
if (p == LedgerPermission.REGISTER_USER || p == LedgerPermission.APPROVE_TX) { | |||
assertTrue(decodePrivileges.getLedgerPrivilege().isEnable(p)); | |||
} else { | |||
assertFalse(decodePrivileges.getLedgerPrivilege().isEnable(p)); | |||
} | |||
} | |||
for (TransactionPermission p : TransactionPermission.values()) { | |||
if (p == TransactionPermission.DIRECT_OPERATION) { | |||
assertTrue(decodePrivileges.getTransactionPrivilege().isEnable(p)); | |||
} else { | |||
assertFalse(decodePrivileges.getTransactionPrivilege().isEnable(p)); | |||
} | |||
} | |||
} | |||
// 只定义账本权限的情形; | |||
{ | |||
Privileges privileges = Privileges.configure().enable(LedgerPermission.REGISTER_USER, | |||
LedgerPermission.APPROVE_TX); | |||
byte[] bytes = BinaryProtocol.encode(privileges, PrivilegeSet.class); | |||
PrivilegeSet decodePrivileges = BinaryProtocol.decode(bytes); | |||
assertNotNull(decodePrivileges.getLedgerPrivilege()); | |||
assertNotNull(decodePrivileges.getTransactionPrivilege()); | |||
for (LedgerPermission p : LedgerPermission.values()) { | |||
if (p == LedgerPermission.REGISTER_USER || p == LedgerPermission.APPROVE_TX) { | |||
assertTrue(decodePrivileges.getLedgerPrivilege().isEnable(p)); | |||
} else { | |||
assertFalse(decodePrivileges.getLedgerPrivilege().isEnable(p)); | |||
} | |||
} | |||
for (TransactionPermission p : TransactionPermission.values()) { | |||
assertFalse(decodePrivileges.getTransactionPrivilege().isEnable(p)); | |||
} | |||
} | |||
// 只定义交易权限的情形; | |||
{ | |||
Privileges privileges = Privileges.configure().enable(TransactionPermission.CONTRACT_OPERATION); | |||
byte[] bytes = BinaryProtocol.encode(privileges, PrivilegeSet.class); | |||
PrivilegeSet decodePrivileges = BinaryProtocol.decode(bytes); | |||
assertNotNull(decodePrivileges.getLedgerPrivilege()); | |||
assertNotNull(decodePrivileges.getTransactionPrivilege()); | |||
for (LedgerPermission p : LedgerPermission.values()) { | |||
assertFalse(decodePrivileges.getLedgerPrivilege().isEnable(p)); | |||
} | |||
for (TransactionPermission p : TransactionPermission.values()) { | |||
if (p == TransactionPermission.CONTRACT_OPERATION) { | |||
assertTrue(decodePrivileges.getTransactionPrivilege().isEnable(p)); | |||
} else { | |||
assertFalse(decodePrivileges.getTransactionPrivilege().isEnable(p)); | |||
} | |||
} | |||
} | |||
} | |||
} |