mirror of https://github.com/poanetwork/quorum.git
363 lines
15 KiB
Solidity
363 lines
15 KiB
Solidity
pragma solidity ^0.5.3;
|
|
|
|
import "./PermissionsUpgradable.sol";
|
|
|
|
/** @title Account manager contract
|
|
* @notice This contract holds implementation logic for all account management
|
|
functionality. This can be called only by the implementation contract only.
|
|
there are few view functions exposed as public and can be called directly.
|
|
these are invoked by quorum for populating permissions data in cache
|
|
* @dev account status is denoted by a fixed integer value. The values are
|
|
as below:
|
|
0 - Not in list
|
|
1 - Account pending approval
|
|
2 - Active
|
|
3 - Inactive
|
|
4 - Suspended
|
|
5 - Blacklisted
|
|
6 - Revoked
|
|
7 - Recovery Initiated for blacklisted accounts and pending approval
|
|
from network admins
|
|
Once the account is blacklisted no further activity on the account is
|
|
possible.
|
|
When adding a new org admin account to an existing org, the existing org
|
|
admin account will be in revoked status and can be assigned a new role
|
|
later
|
|
*/
|
|
contract AccountManager {
|
|
PermissionsUpgradable private permUpgradable;
|
|
struct AccountAccessDetails {
|
|
address account;
|
|
string orgId;
|
|
string role;
|
|
uint status;
|
|
bool orgAdmin;
|
|
}
|
|
|
|
AccountAccessDetails[] private accountAccessList;
|
|
mapping(address => uint) private accountIndex;
|
|
uint private numAccounts;
|
|
|
|
string private adminRole;
|
|
string private orgAdminRole;
|
|
|
|
mapping(bytes32 => address) private orgAdminIndex;
|
|
|
|
// account permission events
|
|
event AccountAccessModified(address _account, string _orgId, string _roleId, bool _orgAdmin, uint _status);
|
|
event AccountAccessRevoked(address _account, string _orgId, string _roleId, bool _orgAdmin);
|
|
event AccountStatusChanged(address _account, string _orgId, uint _status);
|
|
|
|
/** @notice confirms that the caller is the address of implementation
|
|
contract
|
|
*/
|
|
modifier onlyImplementation {
|
|
require(msg.sender == permUpgradable.getPermImpl(), "invalid caller");
|
|
_;
|
|
}
|
|
|
|
/** @notice checks if the account is exists and belongs to the org id passed
|
|
* @param _orgId - org id
|
|
* @param _account - account id
|
|
*/
|
|
modifier accountExists(string memory _orgId, address _account) {
|
|
require((accountIndex[_account]) != 0, "account does not exists");
|
|
require(keccak256(abi.encode(accountAccessList[_getAccountIndex(_account)].orgId)) == keccak256(abi.encode(_orgId)), "account in different org");
|
|
_;
|
|
}
|
|
|
|
/// @notice constructor. sets the permissions upgradable address
|
|
constructor (address _permUpgradable) public {
|
|
permUpgradable = PermissionsUpgradable(_permUpgradable);
|
|
}
|
|
|
|
|
|
/** @notice returns the account details for a given account
|
|
* @param _account account id
|
|
* @return account id
|
|
* @return org id of the account
|
|
* @return role linked to the account
|
|
* @return status of the account
|
|
* @return bool indicating if the account is an org admin
|
|
*/
|
|
function getAccountDetails(address _account) external view returns (address,
|
|
string memory, string memory, uint, bool){
|
|
if (accountIndex[_account] == 0) {
|
|
return (_account, "NONE", "", 0, false);
|
|
}
|
|
uint aIndex = _getAccountIndex(_account);
|
|
return (accountAccessList[aIndex].account, accountAccessList[aIndex].orgId,
|
|
accountAccessList[aIndex].role, accountAccessList[aIndex].status,
|
|
accountAccessList[aIndex].orgAdmin);
|
|
}
|
|
|
|
/** @notice returns the account details a given account index
|
|
* @param _aIndex account index
|
|
* @return account id
|
|
* @return org id of the account
|
|
* @return role linked to the account
|
|
* @return status of the account
|
|
* @return bool indicating if the account is an org admin
|
|
*/
|
|
function getAccountDetailsFromIndex(uint _aIndex) external view returns
|
|
(address, string memory, string memory, uint, bool) {
|
|
return (accountAccessList[_aIndex].account,
|
|
accountAccessList[_aIndex].orgId, accountAccessList[_aIndex].role,
|
|
accountAccessList[_aIndex].status, accountAccessList[_aIndex].orgAdmin);
|
|
}
|
|
|
|
/** @notice returns the total number of accounts
|
|
* @return total number accounts
|
|
*/
|
|
function getNumberOfAccounts() external view returns (uint) {
|
|
return accountAccessList.length;
|
|
}
|
|
|
|
/** @notice this is called at the time of network initialization to set
|
|
the default values of network admin and org admin roles
|
|
*/
|
|
function setDefaults(string calldata _nwAdminRole, string calldata _oAdminRole)
|
|
external onlyImplementation {
|
|
adminRole = _nwAdminRole;
|
|
orgAdminRole = _oAdminRole;
|
|
}
|
|
|
|
/** @notice this function is called to assign the org admin or network
|
|
admin roles only to the passed account
|
|
* @param _account - account id
|
|
* @param _orgId - org to which it belongs
|
|
* @param _roleId - role id to be assigned
|
|
* @param _status - account status to be assigned
|
|
*/
|
|
function assignAdminRole(address _account, string calldata _orgId,
|
|
string calldata _roleId, uint _status) external onlyImplementation {
|
|
require(((keccak256(abi.encode(_roleId)) == keccak256(abi.encode(orgAdminRole))) ||
|
|
(keccak256(abi.encode(_roleId)) == keccak256(abi.encode(adminRole)))),
|
|
"can be called to assign admin roles only");
|
|
|
|
_setAccountRole(_account, _orgId, _roleId, _status, true);
|
|
|
|
}
|
|
|
|
/** @notice this function is called to assign the any role to the passed
|
|
account.
|
|
* @param _account - account id
|
|
* @param _orgId - org to which it belongs
|
|
* @param _roleId - role id to be assigned
|
|
* @param _adminRole - indicates of the role is an admin role
|
|
*/
|
|
function assignAccountRole(address _account, string calldata _orgId,
|
|
string calldata _roleId, bool _adminRole) external onlyImplementation {
|
|
require(((keccak256(abi.encode(_roleId)) != keccak256(abi.encode(adminRole)))
|
|
&& (keccak256(abi.encode(abi.encode(_roleId))) != keccak256(abi.encode(orgAdminRole)))),
|
|
"cannot be called fro assigning org admin and network admin roles");
|
|
_setAccountRole(_account, _orgId, _roleId, 2, _adminRole);
|
|
}
|
|
|
|
/** @notice this function removes existing admin account. will be called at
|
|
the time of adding a new account as org admin account. at org
|
|
level there can be one org admin account only
|
|
* @param _orgId - org id
|
|
* @return bool to indicate if voter update is required or not
|
|
* @return _adminRole - indicates of the role is an admin role
|
|
*/
|
|
function removeExistingAdmin(string calldata _orgId) external
|
|
onlyImplementation
|
|
returns (bool voterUpdate, address account) {
|
|
// change the status of existing org admin to revoked
|
|
if (orgAdminExists(_orgId)) {
|
|
uint id = _getAccountIndex(orgAdminIndex[keccak256(abi.encode(_orgId))]);
|
|
accountAccessList[id].status = 6;
|
|
accountAccessList[id].orgAdmin = false;
|
|
emit AccountAccessModified(accountAccessList[id].account,
|
|
accountAccessList[id].orgId, accountAccessList[id].role,
|
|
accountAccessList[id].orgAdmin, accountAccessList[id].status);
|
|
return ((keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole))),
|
|
accountAccessList[id].account);
|
|
}
|
|
return (false, address(0));
|
|
}
|
|
|
|
/** @notice function to add an account as network admin or org admin.
|
|
* @param _orgId - org id
|
|
* @param _account - account id
|
|
* @return bool to indicate if voter update is required or not
|
|
*/
|
|
function addNewAdmin(string calldata _orgId, address _account) external
|
|
onlyImplementation
|
|
returns (bool voterUpdate) {
|
|
// check of the account role is org admin role and status is pending
|
|
// approval. if yes update the status to approved
|
|
string memory role = getAccountRole(_account);
|
|
uint status = _getAccountStatus(_account);
|
|
uint id = _getAccountIndex(_account);
|
|
if ((keccak256(abi.encode(role)) == keccak256(abi.encode(orgAdminRole))) &&
|
|
(status == 1)) {
|
|
orgAdminIndex[keccak256(abi.encode(_orgId))] = _account;
|
|
}
|
|
accountAccessList[id].status = 2;
|
|
accountAccessList[id].orgAdmin = true;
|
|
emit AccountAccessModified(_account, accountAccessList[id].orgId, accountAccessList[id].role,
|
|
accountAccessList[id].orgAdmin, accountAccessList[id].status);
|
|
return (keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole)));
|
|
}
|
|
|
|
/** @notice updates the account status to the passed status value
|
|
* @param _orgId - org id
|
|
* @param _account - account id
|
|
* @param _action - new status of the account
|
|
* @dev the following actions are allowed
|
|
1 - Suspend the account
|
|
2 - Reactivate a suspended account
|
|
3 - Blacklist an account
|
|
4 - Initiate recovery for black listed account
|
|
5 - Complete recovery of black listed account and update status to active
|
|
*/
|
|
function updateAccountStatus(string calldata _orgId, address _account, uint _action) external
|
|
onlyImplementation
|
|
accountExists(_orgId, _account) {
|
|
require((_action > 0 && _action < 6), "invalid status change request");
|
|
|
|
// check if the account is org admin. if yes then do not allow any status change
|
|
require(checkOrgAdmin(_account, _orgId, "") != true, "status change not possible for org admin accounts");
|
|
uint newStatus;
|
|
if (_action == 1) {
|
|
// for suspending an account current status should be active
|
|
require(accountAccessList[_getAccountIndex(_account)].status == 2,
|
|
"account is not in active status. operation cannot be done");
|
|
newStatus = 4;
|
|
}
|
|
else if (_action == 2) {
|
|
// for reactivating a suspended account, current status should be suspended
|
|
require(accountAccessList[_getAccountIndex(_account)].status == 4,
|
|
"account is not in suspended status. operation cannot be done");
|
|
newStatus = 2;
|
|
}
|
|
else if (_action == 3) {
|
|
require(accountAccessList[_getAccountIndex(_account)].status != 5,
|
|
"account is already blacklisted. operation cannot be done");
|
|
newStatus = 5;
|
|
}
|
|
else if (_action == 4) {
|
|
require(accountAccessList[_getAccountIndex(_account)].status == 5,
|
|
"account is not blacklisted. operation cannot be done");
|
|
newStatus = 7;
|
|
}
|
|
else if (_action == 5) {
|
|
require(accountAccessList[_getAccountIndex(_account)].status == 7, "account recovery not initiated. operation cannot be done");
|
|
newStatus = 2;
|
|
}
|
|
|
|
accountAccessList[_getAccountIndex(_account)].status = newStatus;
|
|
emit AccountStatusChanged(_account, _orgId, newStatus);
|
|
}
|
|
|
|
/** @notice checks if the passed account exists and if exists does it
|
|
belong to the passed organization.
|
|
* @param _account - account id
|
|
* @param _orgId - org id
|
|
* @return bool true if the account does not exists or exists and belongs
|
|
* @return passed org
|
|
*/
|
|
function validateAccount(address _account, string calldata _orgId) external
|
|
view returns (bool){
|
|
if (accountIndex[_account] == 0) {
|
|
return true;
|
|
}
|
|
uint256 id = _getAccountIndex(_account);
|
|
return (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId)));
|
|
}
|
|
|
|
/** @notice checks if org admin account exists for the passed org id
|
|
* @param _orgId - org id
|
|
* @return true if the org admin account exists and is approved
|
|
*/
|
|
function orgAdminExists(string memory _orgId) public view returns (bool) {
|
|
if (orgAdminIndex[keccak256(abi.encode(_orgId))] != address(0)) {
|
|
address adminAcct = orgAdminIndex[keccak256(abi.encode(_orgId))];
|
|
return _getAccountStatus(adminAcct) == 2;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
/** @notice returns the role id linked to the passed account
|
|
* @param _account account id
|
|
* @return role id
|
|
*/
|
|
function getAccountRole(address _account) public view returns (string memory) {
|
|
if (accountIndex[_account] == 0) {
|
|
return "NONE";
|
|
}
|
|
uint256 acctIndex = _getAccountIndex(_account);
|
|
if (accountAccessList[acctIndex].status != 0) {
|
|
return accountAccessList[acctIndex].role;
|
|
}
|
|
else {
|
|
return "NONE";
|
|
}
|
|
}
|
|
|
|
/** @notice checks if the account is a org admin for the passed org or
|
|
for the ultimate parent organization
|
|
* @param _account account id
|
|
* @param _orgId org id
|
|
* @param _ultParent master org id or
|
|
*/
|
|
function checkOrgAdmin(address _account, string memory _orgId,
|
|
string memory _ultParent) public view returns (bool) {
|
|
// check if the account role is network admin. If yes return success
|
|
if (keccak256(abi.encode(getAccountRole(_account))) == keccak256(abi.encode(adminRole))) {
|
|
// check of the orgid is network admin org. then return true
|
|
uint256 id = _getAccountIndex(_account);
|
|
return ((keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId)))
|
|
|| (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_ultParent))));
|
|
}
|
|
return ((orgAdminIndex[keccak256(abi.encode(_orgId))] == _account) || (orgAdminIndex[keccak256(abi.encode(_ultParent))] == _account));
|
|
}
|
|
|
|
/** @notice returns the index for a given account id
|
|
* @param _account account id
|
|
* @return account index
|
|
*/
|
|
function _getAccountIndex(address _account) internal view returns (uint256) {
|
|
return accountIndex[_account] - 1;
|
|
}
|
|
|
|
/** @notice returns the account status for a given account
|
|
* @param _account account id
|
|
* @return account status
|
|
*/
|
|
function _getAccountStatus(address _account) internal view returns (uint256) {
|
|
if (accountIndex[_account] == 0) {
|
|
return 0;
|
|
}
|
|
uint256 aIndex = _getAccountIndex(_account);
|
|
return (accountAccessList[aIndex].status);
|
|
}
|
|
|
|
/** @notice sets the account role to the passed role id and sets the status
|
|
* @param _account account id
|
|
* @param _orgId org id
|
|
* @param _status status to be set
|
|
* @param _oAdmin bool to indicate if account is org admin
|
|
*/
|
|
function _setAccountRole(address _account, string memory _orgId,
|
|
string memory _roleId, uint256 _status, bool _oAdmin) internal onlyImplementation {
|
|
// Check if account already exists
|
|
uint256 aIndex = _getAccountIndex(_account);
|
|
if (accountIndex[_account] != 0) {
|
|
accountAccessList[aIndex].role = _roleId;
|
|
accountAccessList[aIndex].status = _status;
|
|
accountAccessList[aIndex].orgAdmin = _oAdmin;
|
|
}
|
|
else {
|
|
numAccounts ++;
|
|
accountIndex[_account] = numAccounts;
|
|
accountAccessList.push(AccountAccessDetails(_account, _orgId,
|
|
_roleId, _status, _oAdmin));
|
|
}
|
|
emit AccountAccessModified(_account, _orgId, _roleId, _oAdmin, _status);
|
|
}
|
|
}
|