github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/permission/v2/contract/AccountManager.sol (about)

     1  pragma solidity ^0.5.3;
     2  
     3  import "./PermissionsUpgradable.sol";
     4  
     5  /** @title Account manager contract
     6    * @notice This contract holds implementation logic for all account management
     7      functionality. This can be called only by the implementation contract only.
     8      there are few view functions exposed as public and can be called directly.
     9      these are invoked by quorum for populating permissions data in cache
    10    * @dev account status is denoted by a fixed integer value. The values are
    11      as below:
    12          0 - Not in list
    13          1 - Account pending approval
    14          2 - Active
    15          3 - Inactive
    16          4 - Suspended
    17          5 - Blacklisted
    18          6 - Revoked
    19          7 - Recovery Initiated for blacklisted accounts and pending approval
    20              from network admins
    21       Once the account is blacklisted no further activity on the account is
    22       possible.
    23       When adding a new org admin account to an existing org, the existing org
    24       admin account will be in revoked status and can be assigned a new role
    25       later
    26    */
    27  contract AccountManager {
    28      PermissionsUpgradable private permUpgradable;
    29      struct AccountAccessDetails {
    30          address account;
    31          string orgId;
    32          string role;
    33          uint status;
    34          bool orgAdmin;
    35      }
    36  
    37      AccountAccessDetails[] private accountAccessList;
    38      mapping(address => uint) private accountIndex;
    39      uint private numAccounts;
    40  
    41      string private adminRole;
    42      string private orgAdminRole;
    43  
    44      mapping(bytes32 => address) private orgAdminIndex;
    45  
    46      // account permission events
    47      event AccountAccessModified(address _account, string _orgId, string _roleId, bool _orgAdmin, uint _status);
    48      event AccountAccessRevoked(address _account, string _orgId, string _roleId, bool _orgAdmin);
    49      event AccountStatusChanged(address _account, string _orgId, uint _status);
    50  
    51      /** @notice confirms that the caller is the address of implementation
    52          contract
    53        */
    54      modifier onlyImplementation {
    55          require(msg.sender == permUpgradable.getPermImpl(), "invalid caller");
    56          _;
    57      }
    58  
    59      /** @notice checks if the account is exists and belongs to the org id passed
    60        * @param _orgId - org id
    61        * @param _account - account id
    62        */
    63      modifier accountExists(string memory _orgId, address _account) {
    64          require((accountIndex[_account]) != 0, "account does not exists");
    65          require(keccak256(abi.encode(accountAccessList[_getAccountIndex(_account)].orgId)) == keccak256(abi.encode(_orgId)), "account in different org");
    66          _;
    67      }
    68  
    69      /// @notice constructor. sets the permissions upgradable address
    70      constructor (address _permUpgradable) public {
    71          permUpgradable = PermissionsUpgradable(_permUpgradable);
    72      }
    73  
    74  
    75      /** @notice returns the account details for a given account
    76        * @param _account account id
    77        * @return account id
    78        * @return org id of the account
    79        * @return role linked to the account
    80        * @return status of the account
    81        * @return bool indicating if the account is an org admin
    82        */
    83      function getAccountDetails(address _account) external view returns (address,
    84          string memory, string memory, uint, bool){
    85          if (accountIndex[_account] == 0) {
    86              return (_account, "NONE", "", 0, false);
    87          }
    88          uint aIndex = _getAccountIndex(_account);
    89          return (accountAccessList[aIndex].account, accountAccessList[aIndex].orgId,
    90          accountAccessList[aIndex].role, accountAccessList[aIndex].status,
    91          accountAccessList[aIndex].orgAdmin);
    92      }
    93  
    94      /** @notice returns the account details for a given account if account is valid/active
    95        * @param _account account id
    96        * @return org id of the account
    97        * @return role linked to the account
    98        */
    99      function getAccountOrgRole(address _account) external view
   100      returns (string memory, string memory){
   101          if (accountIndex[_account] == 0) {
   102              return ("NONE", "");
   103          }
   104          uint aIndex = _getAccountIndex(_account);
   105          return (accountAccessList[aIndex].orgId, accountAccessList[aIndex].role);
   106      }
   107  
   108      /** @notice returns the account details a given account index
   109        * @param  _aIndex account index
   110        * @return account id
   111        * @return org id of the account
   112        * @return role linked to the account
   113        * @return status of the account
   114        * @return bool indicating if the account is an org admin
   115        */
   116      function getAccountDetailsFromIndex(uint _aIndex) external view returns
   117      (address, string memory, string memory, uint, bool) {
   118          return (accountAccessList[_aIndex].account,
   119          accountAccessList[_aIndex].orgId, accountAccessList[_aIndex].role,
   120          accountAccessList[_aIndex].status, accountAccessList[_aIndex].orgAdmin);
   121      }
   122  
   123      /** @notice returns the total number of accounts
   124        * @return total number accounts
   125        */
   126      function getNumberOfAccounts() external view returns (uint) {
   127          return accountAccessList.length;
   128      }
   129  
   130      /** @notice this is called at the time of network initialization to set
   131          the default values of network admin and org admin roles
   132        */
   133      function setDefaults(string calldata _nwAdminRole, string calldata _oAdminRole)
   134      external onlyImplementation {
   135          adminRole = _nwAdminRole;
   136          orgAdminRole = _oAdminRole;
   137      }
   138  
   139      /** @notice this function is called to assign the org admin or network
   140          admin roles only to the passed account
   141        * @param _account - account id
   142        * @param _orgId - org to which it belongs
   143        * @param _roleId - role id to be assigned
   144        * @param _status - account status to be assigned
   145        */
   146      function assignAdminRole(address _account, string calldata _orgId,
   147          string calldata _roleId, uint _status) external onlyImplementation {
   148          require(((keccak256(abi.encode(_roleId)) == keccak256(abi.encode(orgAdminRole))) ||
   149          (keccak256(abi.encode(_roleId)) == keccak256(abi.encode(adminRole)))),
   150              "can be called to assign admin roles only");
   151  
   152          _setAccountRole(_account, _orgId, _roleId, _status, true);
   153  
   154      }
   155  
   156      /** @notice this function is called to assign the any role to the passed
   157          account.
   158        * @param _account - account id
   159        * @param _orgId - org to which it belongs
   160        * @param _roleId - role id to be assigned
   161        * @param _adminRole - indicates of the role is an admin role
   162        */
   163      function assignAccountRole(address _account, string calldata _orgId,
   164          string calldata _roleId, bool _adminRole) external onlyImplementation {
   165          require(((keccak256(abi.encode(_roleId)) != keccak256(abi.encode(adminRole)))
   166          && (keccak256(abi.encode(abi.encode(_roleId))) != keccak256(abi.encode(orgAdminRole)))),
   167              "cannot be called fro assigning org admin and network admin roles");
   168          _setAccountRole(_account, _orgId, _roleId, 2, _adminRole);
   169      }
   170  
   171      /** @notice this function removes existing admin account. will be called at
   172          the time of adding a new account as org admin account. at org
   173          level there can be one org admin account only
   174        * @param _orgId - org id
   175        * @return bool to indicate if voter update is required or not
   176        * @return _adminRole - indicates of the role is an admin role
   177        */
   178      function removeExistingAdmin(string calldata _orgId) external
   179      onlyImplementation
   180      returns (bool voterUpdate, address account) {
   181          // change the status of existing org admin to revoked
   182          if (orgAdminExists(_orgId)) {
   183              uint id = _getAccountIndex(orgAdminIndex[keccak256(abi.encode(_orgId))]);
   184              accountAccessList[id].status = 6;
   185              accountAccessList[id].orgAdmin = false;
   186              emit AccountAccessModified(accountAccessList[id].account,
   187                  accountAccessList[id].orgId, accountAccessList[id].role,
   188                  accountAccessList[id].orgAdmin, accountAccessList[id].status);
   189              return ((keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole))),
   190              accountAccessList[id].account);
   191          }
   192          return (false, address(0));
   193      }
   194  
   195      /** @notice function to add an account as network admin or org admin.
   196        * @param _orgId - org id
   197        * @param _account - account id
   198        * @return bool to indicate if voter update is required or not
   199        */
   200      function addNewAdmin(string calldata _orgId, address _account) external
   201      onlyImplementation
   202      returns (bool voterUpdate) {
   203          // check of the account role is org admin role and status is pending
   204          // approval. if yes update the status to approved
   205          string memory role = getAccountRole(_account);
   206          uint status = getAccountStatus(_account);
   207          uint id = _getAccountIndex(_account);
   208          if ((keccak256(abi.encode(role)) == keccak256(abi.encode(orgAdminRole))) &&
   209              (status == 1)) {
   210              orgAdminIndex[keccak256(abi.encode(_orgId))] = _account;
   211          }
   212          accountAccessList[id].status = 2;
   213          accountAccessList[id].orgAdmin = true;
   214          emit AccountAccessModified(_account, accountAccessList[id].orgId, accountAccessList[id].role,
   215              accountAccessList[id].orgAdmin, accountAccessList[id].status);
   216          return (keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole)));
   217      }
   218  
   219      /** @notice updates the account status to the passed status value
   220        * @param _orgId - org id
   221        * @param _account - account id
   222        * @param _action - new status of the account
   223        * @dev the following actions are allowed
   224              1 - Suspend the account
   225              2 - Reactivate a suspended account
   226              3 - Blacklist an account
   227              4 - Initiate recovery for black listed account
   228              5 - Complete recovery of black listed account and update status to active
   229        */
   230      function updateAccountStatus(string calldata _orgId, address _account, uint _action) external
   231      onlyImplementation
   232      accountExists(_orgId, _account) {
   233          require((_action > 0 && _action < 6), "invalid status change request");
   234  
   235          // check if the account is org admin. if yes then do not allow any status change
   236          require(checkOrgAdmin(_account, _orgId, "") != true, "status change not possible for org admin accounts");
   237          uint newStatus;
   238          if (_action == 1) {
   239              // for suspending an account current status should be active
   240              require(accountAccessList[_getAccountIndex(_account)].status == 2,
   241                  "account is not in active status. operation cannot be done");
   242              newStatus = 4;
   243          }
   244          else if (_action == 2) {
   245              // for reactivating a suspended account, current status should be suspended
   246              require(accountAccessList[_getAccountIndex(_account)].status == 4,
   247                  "account is not in suspended status. operation cannot be done");
   248              newStatus = 2;
   249          }
   250          else if (_action == 3) {
   251              require(accountAccessList[_getAccountIndex(_account)].status != 5,
   252                  "account is already blacklisted. operation cannot be done");
   253              newStatus = 5;
   254          }
   255          else if (_action == 4) {
   256              require(accountAccessList[_getAccountIndex(_account)].status == 5,
   257                  "account is not blacklisted. operation cannot be done");
   258              newStatus = 7;
   259          }
   260          else if (_action == 5) {
   261              require(accountAccessList[_getAccountIndex(_account)].status == 7, "account recovery not initiated. operation cannot be done");
   262              newStatus = 2;
   263          }
   264  
   265          accountAccessList[_getAccountIndex(_account)].status = newStatus;
   266          emit AccountStatusChanged(_account, _orgId, newStatus);
   267      }
   268  
   269      /** @notice checks if the passed account exists and if exists does it
   270          belong to the passed organization.
   271        * @param _account - account id
   272        * @param _orgId - org id
   273        * @return bool true if the account does not exists or exists and belongs
   274        * @return passed org
   275        */
   276      function validateAccount(address _account, string calldata _orgId) external
   277      view returns (bool){
   278          if (accountIndex[_account] == 0) {
   279              return true;
   280          }
   281          uint256 id = _getAccountIndex(_account);
   282          return (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId)));
   283      }
   284  
   285      /** @notice checks if org admin account exists for the passed org id
   286        * @param _orgId - org id
   287        * @return true if the org admin account exists and is approved
   288        */
   289      function orgAdminExists(string memory _orgId) public view returns (bool) {
   290          if (orgAdminIndex[keccak256(abi.encode(_orgId))] != address(0)) {
   291              address adminAcct = orgAdminIndex[keccak256(abi.encode(_orgId))];
   292              return getAccountStatus(adminAcct) == 2;
   293          }
   294          return false;
   295  
   296      }
   297  
   298      /** @notice returns the role id linked to the passed account
   299        * @param _account account id
   300        * @return role id
   301        */
   302      function getAccountRole(address _account) public view returns (string memory) {
   303          if (accountIndex[_account] == 0) {
   304              return "NONE";
   305          }
   306          uint256 acctIndex = _getAccountIndex(_account);
   307          if (accountAccessList[acctIndex].status != 0) {
   308              return accountAccessList[acctIndex].role;
   309          }
   310          else {
   311              return "NONE";
   312          }
   313      }
   314  
   315      /** @notice returns the account status for a given account
   316        * @param _account account id
   317        * @return account status
   318        */
   319      function getAccountStatus(address _account) public view returns (uint256) {
   320          if (accountIndex[_account] == 0) {
   321              return 0;
   322          }
   323          uint256 aIndex = _getAccountIndex(_account);
   324          return (accountAccessList[aIndex].status);
   325      }
   326  
   327  
   328      /** @notice checks if the account is a org admin for the passed org or
   329          for the ultimate parent organization
   330        * @param _account account id
   331        * @param _orgId org id
   332        * @param _ultParent master org id or
   333        */
   334      function checkOrgAdmin(address _account, string memory _orgId,
   335          string memory _ultParent) public view returns (bool) {
   336          // check if the account role is network admin. If yes return success
   337          if (keccak256(abi.encode(getAccountRole(_account))) == keccak256(abi.encode(adminRole))) {
   338              // check of the orgid is network admin org. then return true
   339              uint256 id = _getAccountIndex(_account);
   340              return ((keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId)))
   341              || (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_ultParent))));
   342          }
   343          return ((orgAdminIndex[keccak256(abi.encode(_orgId))] == _account) || (orgAdminIndex[keccak256(abi.encode(_ultParent))] == _account));
   344      }
   345  
   346      /** @notice returns the index for a given account id
   347        * @param _account account id
   348        * @return account index
   349        */
   350      function _getAccountIndex(address _account) internal view returns (uint256) {
   351          return accountIndex[_account] - 1;
   352      }
   353  
   354      /** @notice sets the account role to the passed role id and sets the status
   355        * @param _account account id
   356        * @param _orgId org id
   357        * @param _status status to be set
   358        * @param _oAdmin bool to indicate if account is org admin
   359        */
   360      function _setAccountRole(address _account, string memory _orgId,
   361          string memory _roleId, uint256 _status, bool _oAdmin) internal onlyImplementation {
   362          // Check if account already exists
   363          uint256 aIndex = _getAccountIndex(_account);
   364          if (accountIndex[_account] != 0) {
   365              accountAccessList[aIndex].role = _roleId;
   366              accountAccessList[aIndex].status = _status;
   367              accountAccessList[aIndex].orgAdmin = _oAdmin;
   368          }
   369          else {
   370              numAccounts ++;
   371              accountIndex[_account] = numAccounts;
   372              accountAccessList.push(AccountAccessDetails(_account, _orgId,
   373                  _roleId, _status, _oAdmin));
   374          }
   375          emit AccountAccessModified(_account, _orgId, _roleId, _oAdmin, _status);
   376      }
   377  }