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 }