mirror of https://github.com/poanetwork/quorum.git
265 lines
11 KiB
Solidity
265 lines
11 KiB
Solidity
pragma solidity ^0.5.3;
|
|
import "./PermissionsUpgradable.sol";
|
|
|
|
contract AccountManager {
|
|
PermissionsUpgradable private permUpgradable;
|
|
// enum AccountStatus {0-NotInList, 1-PendingApproval, 2-Active, 3-Inactive, 4-Suspended, 5-Blacklisted, 6-Revoked}
|
|
struct AccountAccessDetails {
|
|
address acctId;
|
|
string orgId;
|
|
string role;
|
|
uint status;
|
|
bool orgAdmin;
|
|
}
|
|
|
|
AccountAccessDetails[] private acctAccessList;
|
|
mapping(address => uint) private accountIndex;
|
|
uint private numberOfAccts;
|
|
|
|
string private adminRole;
|
|
string private orgAdminRole;
|
|
|
|
mapping(bytes32 => address) private orgAdminIndex;
|
|
|
|
// account permission events
|
|
event AccountAccessModified(address _address, string _orgId, string _roleId, bool _orgAdmin, uint _status);
|
|
event AccountAccessRevoked(address _address, string _orgId, string _roleId, bool _orgAdmin);
|
|
event AccountStatusChanged(address _address, string _orgId, uint _status);
|
|
|
|
modifier onlyImpl
|
|
{
|
|
require(msg.sender == permUpgradable.getPermImpl());
|
|
_;
|
|
}
|
|
|
|
modifier accountExists(string memory _orgId, address _account)
|
|
{
|
|
require((accountIndex[_account]) != 0, "account does not exists");
|
|
// if account exists it should belong to the same orgAdminIndex
|
|
require(keccak256(abi.encodePacked(acctAccessList[getAcctIndex(_account)].orgId)) == keccak256(abi.encodePacked(_orgId)), "account in different org");
|
|
_;
|
|
}
|
|
|
|
constructor (address _permUpgradable) public {
|
|
permUpgradable = PermissionsUpgradable(_permUpgradable);
|
|
}
|
|
|
|
// Get account details given index
|
|
|
|
function orgAdminExists(string memory _orgId) public view returns (bool)
|
|
{
|
|
if (orgAdminIndex[keccak256(abi.encodePacked(_orgId))] != address(0)) {
|
|
address adminAcct = orgAdminIndex[keccak256(abi.encodePacked(_orgId))];
|
|
return getAccountStatus(adminAcct) == 2;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
function getAccountStatus(address _acct) internal view returns (uint)
|
|
{
|
|
if (accountIndex[_acct] == 0) {
|
|
return 0;
|
|
}
|
|
uint aIndex = getAcctIndex(_acct);
|
|
return (acctAccessList[aIndex].status);
|
|
}
|
|
|
|
function getAccountDetails(address _acct) external view returns (address, string memory, string memory, uint, bool)
|
|
{
|
|
if (accountIndex[_acct] == 0) {
|
|
return (_acct, "NONE", "", 0, false);
|
|
}
|
|
uint aIndex = getAcctIndex(_acct);
|
|
return (acctAccessList[aIndex].acctId, acctAccessList[aIndex].orgId, acctAccessList[aIndex].role, acctAccessList[aIndex].status, acctAccessList[aIndex].orgAdmin);
|
|
}
|
|
|
|
function getAccountDetailsFromIndex(uint aIndex) external view returns (address, string memory, string memory, uint, bool)
|
|
{
|
|
return (acctAccessList[aIndex].acctId, acctAccessList[aIndex].orgId, acctAccessList[aIndex].role, acctAccessList[aIndex].status, acctAccessList[aIndex].orgAdmin);
|
|
}
|
|
|
|
// Get number of accounts
|
|
function getNumberOfAccounts() external view returns (uint)
|
|
{
|
|
return acctAccessList.length;
|
|
}
|
|
|
|
function setDefaults(string calldata _nwAdminRole, string calldata _oAdminRole) external
|
|
onlyImpl
|
|
{
|
|
adminRole = _nwAdminRole;
|
|
orgAdminRole = _oAdminRole;
|
|
}
|
|
|
|
function setAccountRole(address _address, string memory _orgId, string memory _roleId, uint _status, bool _oAdmin) internal
|
|
onlyImpl
|
|
{
|
|
// Check if account already exists
|
|
uint aIndex = getAcctIndex(_address);
|
|
if (accountIndex[_address] != 0) {
|
|
acctAccessList[aIndex].role = _roleId;
|
|
acctAccessList[aIndex].status = _status;
|
|
acctAccessList[aIndex].orgAdmin = _oAdmin;
|
|
}
|
|
else {
|
|
numberOfAccts ++;
|
|
accountIndex[_address] = numberOfAccts;
|
|
acctAccessList.push(AccountAccessDetails(_address, _orgId, _roleId, _status, _oAdmin));
|
|
}
|
|
// if (_oAdmin) {
|
|
// orgAdminIndex[keccak256(abi.encodePacked(_orgId))] = _address;
|
|
// }
|
|
emit AccountAccessModified(_address, _orgId, _roleId, _oAdmin, _status);
|
|
}
|
|
|
|
// function changeOrgAdmin(address _address, string calldata _orgId, string calldata _roleId) external
|
|
// onlyImpl
|
|
// {
|
|
// // this function can ony be called from network admin to assign the org admin role to a new account
|
|
// setAccountRole(_address, _orgId, _roleId, 1, false);
|
|
// }
|
|
//
|
|
// // TODO: can we merge and remove this
|
|
// function addNWAdminAccount(address _address, string calldata _orgId) external
|
|
// onlyImpl
|
|
// {
|
|
// setAccountRole(_address, _orgId, adminRole, 2, true);
|
|
// }
|
|
|
|
function assignAdminRole(address _address, string calldata _orgId, string calldata _roleId, uint _status) external
|
|
onlyImpl
|
|
{
|
|
require(((keccak256(abi.encodePacked(_roleId)) == keccak256(abi.encodePacked(orgAdminRole))) ||
|
|
(keccak256(abi.encodePacked(_roleId)) == keccak256(abi.encodePacked(adminRole)))), "can be called to assign admin roles only");
|
|
|
|
setAccountRole(_address, _orgId, _roleId, _status, true);
|
|
|
|
}
|
|
|
|
function assignAccountRole(address _address, string calldata _orgId, string calldata _roleId, bool _adminRole) external
|
|
onlyImpl
|
|
{
|
|
require(((keccak256(abi.encodePacked(_roleId)) != keccak256(abi.encodePacked(adminRole))) && (keccak256(abi.encodePacked(abi.encodePacked(_roleId))) != keccak256(abi.encodePacked(orgAdminRole)))), "cannot be called fro assigning org admin and network admin roles");
|
|
setAccountRole(_address, _orgId, _roleId, 2, _adminRole);
|
|
}
|
|
|
|
function removeExistingAdmin(string calldata _orgId) external
|
|
onlyImpl
|
|
returns (bool voterUpdate, address acct)
|
|
{
|
|
// change the status of existing org admin to revoked
|
|
if (orgAdminExists(_orgId)) {
|
|
uint id = getAcctIndex(orgAdminIndex[keccak256(abi.encodePacked(_orgId))]);
|
|
acctAccessList[id].status = 6;
|
|
acctAccessList[id].orgAdmin = false;
|
|
emit AccountAccessModified(acctAccessList[id].acctId, acctAccessList[id].orgId, acctAccessList[id].role, acctAccessList[id].orgAdmin, acctAccessList[id].status);
|
|
return ((keccak256(abi.encodePacked(acctAccessList[id].role)) == keccak256(abi.encodePacked(adminRole))), acctAccessList[id].acctId);
|
|
}
|
|
return (false, address(0));
|
|
}
|
|
|
|
|
|
function addNewAdmin(string calldata _orgId, address _address) external
|
|
onlyImpl
|
|
returns (bool voterUpdate)
|
|
{
|
|
// check of the account role is ORGADMIN and status is pending approval
|
|
// if yes update the status to approved
|
|
string memory role = getAccountRole(_address);
|
|
uint status = getAccountStatus(_address);
|
|
uint id = getAcctIndex(_address);
|
|
if ((keccak256(abi.encodePacked(role)) == keccak256(abi.encodePacked(orgAdminRole))) &&
|
|
(status == 1)) {
|
|
orgAdminIndex[keccak256(abi.encodePacked(_orgId))] = _address;
|
|
}
|
|
acctAccessList[id].status = 2;
|
|
acctAccessList[id].orgAdmin = true;
|
|
emit AccountAccessModified(_address, acctAccessList[id].orgId, acctAccessList[id].role, acctAccessList[id].orgAdmin, acctAccessList[id].status);
|
|
return (keccak256(abi.encodePacked(acctAccessList[id].role)) == keccak256(abi.encodePacked(adminRole)));
|
|
}
|
|
|
|
function revokeAccountRole(address _address) external
|
|
onlyImpl
|
|
{
|
|
// Check if account already exists
|
|
uint aIndex = getAcctIndex(_address);
|
|
if (accountIndex[_address] != 0) {
|
|
acctAccessList[aIndex].status = 3;
|
|
emit AccountAccessRevoked(_address, acctAccessList[aIndex].orgId, acctAccessList[aIndex].role, acctAccessList[aIndex].orgAdmin);
|
|
}
|
|
}
|
|
|
|
function updateAccountStatus(string calldata _orgId, address _account, uint _status) external
|
|
onlyImpl
|
|
accountExists(_orgId, _account)
|
|
{
|
|
// changing node status to integer (0-NotInList, 1-PendingApproval, 2-Active, 3-Suspended, 4-Blacklisted, 5-Revoked)
|
|
// operations that can be done 1-Suspend account, 2-Unsuspend Account, 3-Blacklist account
|
|
require((_status == 1 || _status == 2 || _status == 3), "invalid operation");
|
|
// check if the account is org admin. if yes then do not allow any status change
|
|
require(checkOrgAdmin(_account, _orgId, "") != true, "cannot perform the operation on org admin account");
|
|
uint newStat;
|
|
if (_status == 1) {
|
|
// account current status should be active
|
|
require(acctAccessList[getAcctIndex(_account)].status == 2, "account should be active");
|
|
newStat = 4;
|
|
}
|
|
else if (_status == 2) {
|
|
// account current status should be suspended
|
|
require(acctAccessList[getAcctIndex(_account)].status == 4, "account should be suspended");
|
|
newStat = 2;
|
|
}
|
|
else if (_status == 3) {
|
|
require(acctAccessList[getAcctIndex(_account)].status != 5, "account already blacklisted");
|
|
newStat = 5;
|
|
}
|
|
acctAccessList[getAcctIndex(_account)].status = newStat;
|
|
emit AccountStatusChanged(_account, _orgId, newStat);
|
|
}
|
|
|
|
function getAccountRole(address _acct) public view returns (string memory)
|
|
{
|
|
if (accountIndex[_acct] == 0) {
|
|
return "NONE";
|
|
}
|
|
uint acctIndex = getAcctIndex(_acct);
|
|
if (acctAccessList[acctIndex].status != 0) {
|
|
return acctAccessList[acctIndex].role;
|
|
}
|
|
else {
|
|
return "NONE";
|
|
}
|
|
}
|
|
|
|
function checkOrgAdmin(address _acct, 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.encodePacked(getAccountRole(_acct))) == keccak256(abi.encodePacked(adminRole))) {
|
|
// check of the orgid is network admin org. then return true
|
|
uint id = getAcctIndex(_acct);
|
|
return ((keccak256(abi.encodePacked(acctAccessList[id].orgId)) == keccak256(abi.encodePacked(_orgId)))
|
|
|| (keccak256(abi.encodePacked(acctAccessList[id].orgId)) == keccak256(abi.encodePacked(_ultParent))));
|
|
}
|
|
return ((orgAdminIndex[keccak256(abi.encodePacked(_orgId))] == _acct) || (orgAdminIndex[keccak256(abi.encodePacked(_ultParent))] == _acct));
|
|
}
|
|
|
|
// this function checks if account access can be modified. Account access can be modified for a new account
|
|
// or if the call is from the orgadmin of the same org.
|
|
function validateAccount(address _acct, string calldata _orgId) external view returns (bool)
|
|
{
|
|
if (accountIndex[_acct] == 0) {
|
|
return true;
|
|
}
|
|
// check if the acount is part of this org else return false
|
|
uint id = getAcctIndex(_acct);
|
|
return (keccak256(abi.encodePacked(acctAccessList[id].orgId)) == keccak256(abi.encodePacked(_orgId)));
|
|
}
|
|
// Returns the account index based on account id
|
|
function getAcctIndex(address _acct) internal view returns (uint)
|
|
{
|
|
return accountIndex[_acct] - 1;
|
|
}
|
|
|
|
}
|