github.com/kisexp/xdchain@v0.0.0-20211206025815-490d6b732aa7/permission/v1/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 a given account index 95 * @param _aIndex account index 96 * @return account id 97 * @return org id of the account 98 * @return role linked to the account 99 * @return status of the account 100 * @return bool indicating if the account is an org admin 101 */ 102 function getAccountDetailsFromIndex(uint _aIndex) external view returns 103 (address, string memory, string memory, uint, bool) { 104 return (accountAccessList[_aIndex].account, 105 accountAccessList[_aIndex].orgId, accountAccessList[_aIndex].role, 106 accountAccessList[_aIndex].status, accountAccessList[_aIndex].orgAdmin); 107 } 108 109 /** @notice returns the total number of accounts 110 * @return total number accounts 111 */ 112 function getNumberOfAccounts() external view returns (uint) { 113 return accountAccessList.length; 114 } 115 116 /** @notice this is called at the time of network initialization to set 117 the default values of network admin and org admin roles 118 */ 119 function setDefaults(string calldata _nwAdminRole, string calldata _oAdminRole) 120 external onlyImplementation { 121 adminRole = _nwAdminRole; 122 orgAdminRole = _oAdminRole; 123 } 124 125 /** @notice this function is called to assign the org admin or network 126 admin roles only to the passed account 127 * @param _account - account id 128 * @param _orgId - org to which it belongs 129 * @param _roleId - role id to be assigned 130 * @param _status - account status to be assigned 131 */ 132 function assignAdminRole(address _account, string calldata _orgId, 133 string calldata _roleId, uint _status) external onlyImplementation { 134 require(((keccak256(abi.encode(_roleId)) == keccak256(abi.encode(orgAdminRole))) || 135 (keccak256(abi.encode(_roleId)) == keccak256(abi.encode(adminRole)))), 136 "can be called to assign admin roles only"); 137 138 _setAccountRole(_account, _orgId, _roleId, _status, true); 139 140 } 141 142 /** @notice this function is called to assign the any role to the passed 143 account. 144 * @param _account - account id 145 * @param _orgId - org to which it belongs 146 * @param _roleId - role id to be assigned 147 * @param _adminRole - indicates of the role is an admin role 148 */ 149 function assignAccountRole(address _account, string calldata _orgId, 150 string calldata _roleId, bool _adminRole) external onlyImplementation { 151 require(((keccak256(abi.encode(_roleId)) != keccak256(abi.encode(adminRole))) 152 && (keccak256(abi.encode(abi.encode(_roleId))) != keccak256(abi.encode(orgAdminRole)))), 153 "cannot be called fro assigning org admin and network admin roles"); 154 _setAccountRole(_account, _orgId, _roleId, 2, _adminRole); 155 } 156 157 /** @notice this function removes existing admin account. will be called at 158 the time of adding a new account as org admin account. at org 159 level there can be one org admin account only 160 * @param _orgId - org id 161 * @return bool to indicate if voter update is required or not 162 * @return _adminRole - indicates of the role is an admin role 163 */ 164 function removeExistingAdmin(string calldata _orgId) external 165 onlyImplementation 166 returns (bool voterUpdate, address account) { 167 // change the status of existing org admin to revoked 168 if (orgAdminExists(_orgId)) { 169 uint id = _getAccountIndex(orgAdminIndex[keccak256(abi.encode(_orgId))]); 170 accountAccessList[id].status = 6; 171 accountAccessList[id].orgAdmin = false; 172 emit AccountAccessModified(accountAccessList[id].account, 173 accountAccessList[id].orgId, accountAccessList[id].role, 174 accountAccessList[id].orgAdmin, accountAccessList[id].status); 175 return ((keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole))), 176 accountAccessList[id].account); 177 } 178 return (false, address(0)); 179 } 180 181 /** @notice function to add an account as network admin or org admin. 182 * @param _orgId - org id 183 * @param _account - account id 184 * @return bool to indicate if voter update is required or not 185 */ 186 function addNewAdmin(string calldata _orgId, address _account) external 187 onlyImplementation 188 returns (bool voterUpdate) { 189 // check of the account role is org admin role and status is pending 190 // approval. if yes update the status to approved 191 string memory role = getAccountRole(_account); 192 uint status = _getAccountStatus(_account); 193 uint id = _getAccountIndex(_account); 194 if ((keccak256(abi.encode(role)) == keccak256(abi.encode(orgAdminRole))) && 195 (status == 1)) { 196 orgAdminIndex[keccak256(abi.encode(_orgId))] = _account; 197 } 198 accountAccessList[id].status = 2; 199 accountAccessList[id].orgAdmin = true; 200 emit AccountAccessModified(_account, accountAccessList[id].orgId, accountAccessList[id].role, 201 accountAccessList[id].orgAdmin, accountAccessList[id].status); 202 return (keccak256(abi.encode(accountAccessList[id].role)) == keccak256(abi.encode(adminRole))); 203 } 204 205 /** @notice updates the account status to the passed status value 206 * @param _orgId - org id 207 * @param _account - account id 208 * @param _action - new status of the account 209 * @dev the following actions are allowed 210 1 - Suspend the account 211 2 - Reactivate a suspended account 212 3 - Blacklist an account 213 4 - Initiate recovery for black listed account 214 5 - Complete recovery of black listed account and update status to active 215 */ 216 function updateAccountStatus(string calldata _orgId, address _account, uint _action) external 217 onlyImplementation 218 accountExists(_orgId, _account) { 219 require((_action > 0 && _action < 6), "invalid status change request"); 220 221 // check if the account is org admin. if yes then do not allow any status change 222 require(checkOrgAdmin(_account, _orgId, "") != true, "status change not possible for org admin accounts"); 223 uint newStatus; 224 if (_action == 1) { 225 // for suspending an account current status should be active 226 require(accountAccessList[_getAccountIndex(_account)].status == 2, 227 "account is not in active status. operation cannot be done"); 228 newStatus = 4; 229 } 230 else if (_action == 2) { 231 // for reactivating a suspended account, current status should be suspended 232 require(accountAccessList[_getAccountIndex(_account)].status == 4, 233 "account is not in suspended status. operation cannot be done"); 234 newStatus = 2; 235 } 236 else if (_action == 3) { 237 require(accountAccessList[_getAccountIndex(_account)].status != 5, 238 "account is already blacklisted. operation cannot be done"); 239 newStatus = 5; 240 } 241 else if (_action == 4) { 242 require(accountAccessList[_getAccountIndex(_account)].status == 5, 243 "account is not blacklisted. operation cannot be done"); 244 newStatus = 7; 245 } 246 else if (_action == 5) { 247 require(accountAccessList[_getAccountIndex(_account)].status == 7, "account recovery not initiated. operation cannot be done"); 248 newStatus = 2; 249 } 250 251 accountAccessList[_getAccountIndex(_account)].status = newStatus; 252 emit AccountStatusChanged(_account, _orgId, newStatus); 253 } 254 255 /** @notice checks if the passed account exists and if exists does it 256 belong to the passed organization. 257 * @param _account - account id 258 * @param _orgId - org id 259 * @return bool true if the account does not exists or exists and belongs 260 * @return passed org 261 */ 262 function validateAccount(address _account, string calldata _orgId) external 263 view returns (bool){ 264 if (accountIndex[_account] == 0) { 265 return true; 266 } 267 uint256 id = _getAccountIndex(_account); 268 return (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId))); 269 } 270 271 /** @notice checks if org admin account exists for the passed org id 272 * @param _orgId - org id 273 * @return true if the org admin account exists and is approved 274 */ 275 function orgAdminExists(string memory _orgId) public view returns (bool) { 276 if (orgAdminIndex[keccak256(abi.encode(_orgId))] != address(0)) { 277 address adminAcct = orgAdminIndex[keccak256(abi.encode(_orgId))]; 278 return _getAccountStatus(adminAcct) == 2; 279 } 280 return false; 281 282 } 283 284 /** @notice returns the role id linked to the passed account 285 * @param _account account id 286 * @return role id 287 */ 288 function getAccountRole(address _account) public view returns (string memory) { 289 if (accountIndex[_account] == 0) { 290 return "NONE"; 291 } 292 uint256 acctIndex = _getAccountIndex(_account); 293 if (accountAccessList[acctIndex].status != 0) { 294 return accountAccessList[acctIndex].role; 295 } 296 else { 297 return "NONE"; 298 } 299 } 300 301 /** @notice checks if the account is a org admin for the passed org or 302 for the ultimate parent organization 303 * @param _account account id 304 * @param _orgId org id 305 * @param _ultParent master org id or 306 */ 307 function checkOrgAdmin(address _account, string memory _orgId, 308 string memory _ultParent) public view returns (bool) { 309 // check if the account role is network admin. If yes return success 310 if (keccak256(abi.encode(getAccountRole(_account))) == keccak256(abi.encode(adminRole))) { 311 // check of the orgid is network admin org. then return true 312 uint256 id = _getAccountIndex(_account); 313 return ((keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_orgId))) 314 || (keccak256(abi.encode(accountAccessList[id].orgId)) == keccak256(abi.encode(_ultParent)))); 315 } 316 return ((orgAdminIndex[keccak256(abi.encode(_orgId))] == _account) || (orgAdminIndex[keccak256(abi.encode(_ultParent))] == _account)); 317 } 318 319 /** @notice returns the index for a given account id 320 * @param _account account id 321 * @return account index 322 */ 323 function _getAccountIndex(address _account) internal view returns (uint256) { 324 return accountIndex[_account] - 1; 325 } 326 327 /** @notice returns the account status for a given account 328 * @param _account account id 329 * @return account status 330 */ 331 function _getAccountStatus(address _account) internal view returns (uint256) { 332 if (accountIndex[_account] == 0) { 333 return 0; 334 } 335 uint256 aIndex = _getAccountIndex(_account); 336 return (accountAccessList[aIndex].status); 337 } 338 339 /** @notice sets the account role to the passed role id and sets the status 340 * @param _account account id 341 * @param _orgId org id 342 * @param _status status to be set 343 * @param _oAdmin bool to indicate if account is org admin 344 */ 345 function _setAccountRole(address _account, string memory _orgId, 346 string memory _roleId, uint256 _status, bool _oAdmin) internal onlyImplementation { 347 // Check if account already exists 348 uint256 aIndex = _getAccountIndex(_account); 349 if (accountIndex[_account] != 0) { 350 accountAccessList[aIndex].role = _roleId; 351 accountAccessList[aIndex].status = _status; 352 accountAccessList[aIndex].orgAdmin = _oAdmin; 353 } 354 else { 355 numAccounts ++; 356 accountIndex[_account] = numAccounts; 357 accountAccessList.push(AccountAccessDetails(_account, _orgId, 358 _roleId, _status, _oAdmin)); 359 } 360 emit AccountAccessModified(_account, _orgId, _roleId, _oAdmin, _status); 361 } 362 }