feat:store permissionAdmin per user

This commit is contained in:
The3D 2021-06-25 11:10:01 +02:00
parent 469fde27c5
commit 6d30477950
4 changed files with 178 additions and 101 deletions

View File

@ -1,21 +1,20 @@
pragma solidity 0.6.12;
interface IPermissionManager {
event RoleSet(address indexed user, uint256 indexed role, bool set);
event RoleSet(address indexed user, uint256 indexed role, address indexed whiteLister, bool set);
event PermissionsAdminSet(address indexed user, bool set);
/**
* @dev Allows owner to add new permission admins
* @param users The addresses of the users to promote to permission admin
* @param admins The addresses to promote to permission admin
**/
function addPermissionAdmins(address[] calldata users) external;
function addPermissionAdmins(address[] calldata admins) external;
/**
* @dev Allows owner to remove permission admins
* @param users The addresses of the users to demote as permission admin
* @param admins The addresses to demote as permission admin
**/
function removePermissionAdmins(address[] calldata users) external;
function removePermissionAdmins(address[] calldata admins) external;
/**
* @dev Allows owner to whitelist a set of addresses for multiple roles
@ -32,39 +31,45 @@ interface IPermissionManager {
function removePermissions(uint256[] calldata roles, address[] calldata users) external;
/**
* @dev Returns the permissions configuration for a specific account
* @param account The address of the user
* @return the set of permissions states for the account
* @dev Returns the permissions configuration for a specific user
* @param user The address of the user
* @return the set of permissions states for the user
**/
function getAccountPermissions(address account) external view returns (uint256[] memory, uint256);
function getUserPermissions(address user) external view returns (uint256[] memory, uint256);
/**
* @dev Used to query if a certain account has a certain role
* @param account The address of the user
* @return True if the account is in the specific role
* @dev Used to query if a certain user has a certain role
* @param user The address of the user
* @return True if the user is in the specific role
**/
function isInRole(address account, uint256 role) external view returns (bool);
function isInRole(address user, uint256 role) external view returns (bool);
/**
* @dev Used to query if a certain account has the permissions admin role
* @param account The address of the user
* @return True if the account is a permissions admin, false otherwise
* @dev Used to query if a certain user has the permissions admin role
* @param user The address of the user
* @return True if the user is a permissions admin, false otherwise
**/
function isPermissionsAdmin(address account) external view returns (bool);
function isPermissionsAdmin(address user) external view returns (bool);
/**
* @dev Used to query if a certain account satisfies certain roles
* @param account The address of the user
/**
* @dev Used to query if a certain user satisfies certain roles
* @param user The address of the user
* @param roles The roles to check
* @return True if the account has all the roles, false otherwise
* @return True if the user has all the roles, false otherwise
**/
function isInAllRoles(address account, uint256[] calldata roles) external view returns (bool);
/**
* @dev Used to query if a certain account is in at least one of the roles specified
* @param account The address of the user
* @return True if the account has all the roles, false otherwise
function isInAllRoles(address user, uint256[] calldata roles) external view returns (bool);
/**
* @dev Used to query if a certain user is in at least one of the roles specified
* @param user The address of the user
* @return True if the user has all the roles, false otherwise
**/
function isInAnyRole(address account, uint256[] calldata roles) external view returns (bool);
function isInAnyRole(address user, uint256[] calldata roles) external view returns (bool);
/**
* @dev Used to query if a certain user is in at least one of the roles specified
* @param user The address of the user
* @return the address of the permissionAdmin of the user
**/
function getUserPermissionAdmin(address user) external view returns (address);
}

View File

@ -10,7 +10,12 @@ import {Ownable} from '../../dependencies/openzeppelin/contracts/Ownable.sol';
* @author Aave
**/
contract PermissionManager is IPermissionManager, Ownable {
mapping(address => uint256) _permissions;
struct UserData {
uint256 permissions;
address permissionAdmin;
}
mapping(address => UserData) _users;
mapping(address => uint256) _permissionsAdmins;
uint256 public constant MAX_NUM_OF_ROLES = 256;
@ -21,27 +26,24 @@ contract PermissionManager is IPermissionManager, Ownable {
}
///@inheritdoc IPermissionManager
function addPermissionAdmins(address[] calldata admins) external override onlyOwner {
for (uint256 i = 0; i < admins.length; i++) {
_permissionsAdmins[admins[i]] = 1;
function addPermissionAdmins(address[] calldata users) external override onlyOwner {
for (uint256 i = 0; i < users.length; i++) {
_permissionsAdmins[users[i]] = 1;
emit PermissionsAdminSet(users[i], true);
emit PermissionsAdminSet(admins[i], true);
}
}
///@inheritdoc IPermissionManager
function removePermissionAdmins(address[] calldata admins) external override onlyOwner {
for (uint256 i = 0; i < admins.length; i++) {
_permissionsAdmins[admins[i]] = 0;
function removePermissionAdmins(address[] calldata users) external override onlyOwner {
for (uint256 i = 0; i < users.length; i++) {
_permissionsAdmins[users[i]] = 0;
emit PermissionsAdminSet(users[i], false);
emit PermissionsAdminSet(admins[i], false);
}
}
///@inheritdoc IPermissionManager
function addPermissions(uint256[] calldata roles, address[] calldata users)
external
override
@ -51,18 +53,30 @@ contract PermissionManager is IPermissionManager, Ownable {
for (uint256 i = 0; i < users.length; i++) {
uint256 role = roles[i];
address user = users[i];
require(role < MAX_NUM_OF_ROLES, 'INVALID_ROLE');
uint256 permissions = _permissions[users[i]];
_permissions[users[i]] = permissions | (1 << role);
uint256 permissions = _users[user].permissions;
address permissionAdmin = _users[user].permissionAdmin;
emit RoleSet(users[i], roles[i], true);
require(
(permissions != 0 && permissionAdmin == msg.sender) ||
_users[user].permissionAdmin == address(0),
'INVALID_PERMISSIONADMIN'
);
if (permissions == 0) {
_users[user].permissionAdmin = msg.sender;
}
_users[user].permissions = permissions | (1 << role);
emit RoleSet(user, role, msg.sender, true);
}
}
///@inheritdoc IPermissionManager
function removePermissions(uint256[] calldata roles, address[] calldata users)
external
override
@ -72,18 +86,32 @@ contract PermissionManager is IPermissionManager, Ownable {
for (uint256 i = 0; i < users.length; i++) {
uint256 role = roles[i];
address user = users[i];
require(role < MAX_NUM_OF_ROLES, 'INVALID_ROLE');
uint256 permissions = _permissions[users[i]];
_permissions[users[i]] = permissions & ~(1 << role);
emit RoleSet(users[i], roles[i], false);
uint256 permissions = _users[user].permissions;
address permissionAdmin = _users[user].permissionAdmin;
require(
(permissions != 0 && permissionAdmin == msg.sender) ||
_users[user].permissionAdmin == address(0),
'INVALID_PERMISSIONADMIN'
);
_users[user].permissions = permissions & ~(1 << role);
if (_users[user].permissions == 0) {
//all permission have been removed
_users[user].permissionAdmin = address(0);
}
emit RoleSet(user, role, msg.sender, false);
}
}
///@inheritdoc IPermissionManager
function getAccountPermissions(address account)
function getUserPermissions(address user)
external
view
override
@ -91,10 +119,10 @@ contract PermissionManager is IPermissionManager, Ownable {
{
uint256[] memory roles = new uint256[](256);
uint256 rolesCount = 0;
uint256 accountPermissions = _permissions[account];
uint256 userPermissions = _users[user].permissions;
for (uint256 i = 0; i < 256; i++) {
if ((accountPermissions >> i) & 1 > 0) {
if ((userPermissions >> i) & 1 > 0) {
roles[rolesCount] = i;
rolesCount++;
}
@ -104,26 +132,34 @@ contract PermissionManager is IPermissionManager, Ownable {
}
///@inheritdoc IPermissionManager
function isInRole(address account, uint256 role) external view override returns (bool) {
return (_permissions[account] >> role) & 1 > 0;
function isInRole(address user, uint256 role) external view override returns (bool) {
return (_users[user].permissions >> role) & 1 > 0;
}
///@inheritdoc IPermissionManager
function isInAllRoles(address account, uint256[] calldata roles) external view override returns (bool) {
for(uint256 i=0; i<roles.length; i++){
if((_permissions[account] >> roles[i]) & 1 == 0){
function isInAllRoles(address user, uint256[] calldata roles)
external
view
override
returns (bool)
{
for (uint256 i = 0; i < roles.length; i++) {
if ((_users[user].permissions >> roles[i]) & 1 == 0) {
return false;
}
}
}
return true;
}
///@inheritdoc IPermissionManager
function isInAnyRole(address account, uint256[] calldata roles) external view override returns (bool) {
for(uint256 i=0; i<roles.length; i++){
if((_permissions[account] >> roles[i]) & 1 > 0){
function isInAnyRole(address user, uint256[] calldata roles)
external
view
override
returns (bool)
{
for (uint256 i = 0; i < roles.length; i++) {
if ((_users[user].permissions >> roles[i]) & 1 > 0) {
return true;
}
}
@ -131,7 +167,12 @@ contract PermissionManager is IPermissionManager, Ownable {
}
///@inheritdoc IPermissionManager
function isPermissionsAdmin(address account) public view override returns (bool) {
return _permissionsAdmins[account] > 0;
function isPermissionsAdmin(address admin) public view override returns (bool) {
return _permissionsAdmins[admin] > 0;
}
///@inheritdoc IPermissionManager
function getUserPermissionAdmin(address user) external view override returns (address) {
return _users[user].permissionAdmin;
}
}

2
package-lock.json generated
View File

@ -14793,7 +14793,7 @@
}
},
"ethereumjs-abi": {
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1a27c59c15ab1e95ee8e5c4ed6ad814c49cc439e",
"version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#ee3994657fa7a427238e6ba92a84d0b529bbcde0",
"from": "git+https://github.com/ethereumjs/ethereumjs-abi.git",
"dev": true,
"requires": {

View File

@ -6,7 +6,9 @@ import { PermissionManager } from '../../types';
makeSuite('Permission manager', (testEnv: TestEnv) => {
let permissionManager: PermissionManager;
const DEPOSITOR = 0, BORROWER = 1, LIQUIDATOR = 2;
const DEPOSITOR = 0,
BORROWER = 1,
LIQUIDATOR = 2;
before('deploying a new Permission manager', async () => {
permissionManager = await deployContract<PermissionManager>('PermissionManager', []);
@ -25,7 +27,9 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Registers a new depositor', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).addPermissions([DEPOSITOR], [users[0].address]);
await permissionManager
.connect(users[0].signer)
.addPermissions([DEPOSITOR], [users[0].address]);
const isDepositor = await permissionManager.isInRole(users[0].address, DEPOSITOR);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
@ -53,7 +57,9 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Registers a new liquidator', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).addPermissions([LIQUIDATOR], [users[0].address]);
await permissionManager
.connect(users[0].signer)
.addPermissions([LIQUIDATOR], [users[0].address]);
const isDepositor = await permissionManager.isInRole(users[0].address, DEPOSITOR);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
@ -64,14 +70,33 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
expect(isLiquidator).to.be.equal(true);
});
it('Checks the permission admin of the user', async () => {
const { users } = testEnv;
const permissionAdmin = await permissionManager.getUserPermissionAdmin(users[0].address);
expect(permissionAdmin).to.be.eq(users[0].address);
});
it('Adds user 1 as permission admin; checks user 1 cannot change user 0 permission', async () => {
const { users } = testEnv;
await permissionManager.addPermissionAdmins([users[1].address]);
await expect(
permissionManager.connect(users[1].signer).addPermissions([LIQUIDATOR], [users[0].address])
).to.be.revertedWith('INVALID_PERMISSIONADMIN');
await permissionManager.removePermissionAdmins([users[1].address]);
});
it('Checks getPermissions', async () => {
const { users } = testEnv;
const {
0: permissions,
} = await permissionManager.getAccountPermissions(users[0].address);
const { 0: permissions } = await permissionManager.getUserPermissions(users[0].address);
const mappedPermissions = permissions.map(item => item.toString());
const mappedPermissions = permissions.map((item) => item.toString());
expect(mappedPermissions.indexOf(BORROWER.toString())).to.be.gte(0);
expect(mappedPermissions.indexOf(DEPOSITOR.toString())).to.be.gte(0);
@ -81,25 +106,33 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks isInAllRoles (positive test)', async () => {
const { users } = testEnv;
const result = await permissionManager.isInAllRoles(users[0].address, [DEPOSITOR, BORROWER, LIQUIDATOR])
const result = await permissionManager.isInAllRoles(users[0].address, [
DEPOSITOR,
BORROWER,
LIQUIDATOR,
]);
expect(result).to.be.equal(true, "Invalid result for isInAllRoles");
expect(result).to.be.equal(true, 'Invalid result for isInAllRoles');
});
it('Checks isInAnyRole (positive test)', async () => {
const { users } = testEnv;
const result = await permissionManager.isInAnyRole(users[0].address, [DEPOSITOR, BORROWER, LIQUIDATOR])
const result = await permissionManager.isInAnyRole(users[0].address, [
DEPOSITOR,
BORROWER,
LIQUIDATOR,
]);
expect(result).to.be.equal(true, "Invalid result for isInAnyRole");
expect(result).to.be.equal(true, 'Invalid result for isInAnyRole');
});
it('Removes the depositor', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).removePermissions([DEPOSITOR], [users[0].address]);
await permissionManager
.connect(users[0].signer)
.removePermissions([DEPOSITOR], [users[0].address]);
const isDepositor = await permissionManager.isInRole(users[0].address, DEPOSITOR);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
@ -113,16 +146,21 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks isInAllRoles (negative test)', async () => {
const { users } = testEnv;
const result = await permissionManager.isInAllRoles(users[0].address, [DEPOSITOR, BORROWER, LIQUIDATOR])
const result = await permissionManager.isInAllRoles(users[0].address, [
DEPOSITOR,
BORROWER,
LIQUIDATOR,
]);
expect(result).to.be.equal(false, "Invalid result for isInAllRoles");
expect(result).to.be.equal(false, 'Invalid result for isInAllRoles');
});
it('Removes the borrower', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).removePermissions([BORROWER], [users[0].address]);
await permissionManager
.connect(users[0].signer)
.removePermissions([BORROWER], [users[0].address]);
const isDepositor = await permissionManager.isInRole(users[0].address, DEPOSITOR);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
@ -136,15 +174,17 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks isInAnyRole (negative test)', async () => {
const { users } = testEnv;
const result = await permissionManager.isInAnyRole(users[0].address, [DEPOSITOR, BORROWER])
const result = await permissionManager.isInAnyRole(users[0].address, [DEPOSITOR, BORROWER]);
expect(result).to.be.equal(false, "Invalid result for isInAnyRole");
expect(result).to.be.equal(false, 'Invalid result for isInAnyRole');
});
it('Removes the liquidator', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).removePermissions([LIQUIDATOR], [users[0].address]);
await permissionManager
.connect(users[0].signer)
.removePermissions([LIQUIDATOR], [users[0].address]);
const isDepositor = await permissionManager.isInRole(users[0].address, DEPOSITOR);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
@ -158,10 +198,7 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks getPermissions', async () => {
const { users } = testEnv;
const {
1: permissionsCount
} = await permissionManager.getAccountPermissions(users[0].address);
const { 1: permissionsCount } = await permissionManager.getUserPermissions(users[0].address);
expect(permissionsCount).to.be.equal(0);
});
@ -169,8 +206,7 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks that only the permissions manager can set permissions', async () => {
const { users } = testEnv;
await expect(permissionManager.connect(users[1].signer).addPermissions([], [])).to.be
.reverted;
await expect(permissionManager.connect(users[1].signer).addPermissions([], [])).to.be.reverted;
await expect(permissionManager.connect(users[1].signer).removePermissions([], [])).to.be
.reverted;
});
@ -178,17 +214,13 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
it('Checks that only the owner can add permissions admins', async () => {
const { users } = testEnv;
await expect(permissionManager.connect(users[1].signer).addPermissionAdmins([])).to.be
.reverted;
await expect(permissionManager.connect(users[1].signer).addPermissionAdmins([])).to.be
.reverted;
await expect(permissionManager.connect(users[1].signer).addPermissionAdmins([])).to.be.reverted;
await expect(permissionManager.connect(users[1].signer).addPermissionAdmins([])).to.be.reverted;
});
it('Add borrower role to user 0. Removes permission admin to user 0, check permission admin is removed and other permissions are not affected', async () => {
const { users } = testEnv;
await permissionManager.connect(users[0].signer).addPermissions([BORROWER], [users[0].address]);
await permissionManager.removePermissionAdmins([users[0].address]);
@ -196,7 +228,6 @@ makeSuite('Permission manager', (testEnv: TestEnv) => {
const isPermissionAdmin = await permissionManager.isPermissionsAdmin(users[0].address);
const isBorrower = await permissionManager.isInRole(users[0].address, BORROWER);
expect(isPermissionAdmin).to.be.equal(false);
expect(isBorrower).to.be.equal(true);
});