From a009d6e15a3c92904ad806574fdf0c223ef6d5a1 Mon Sep 17 00:00:00 2001 From: kingster-will <83567446+kingster-will@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:32:50 -0800 Subject: [PATCH] AccessController support gobal permission (#13) --- contracts/AccessController.sol | 16 +++++++++ contracts/interfaces/IAccessController.sol | 8 +++++ test/foundry/AccessController.t.sol | 40 +++++++++++++++++++++ test/foundry/mocks/MockAccessController.sol | 3 ++ 4 files changed, 67 insertions(+) diff --git a/contracts/AccessController.sol b/contracts/AccessController.sol index 5333db98..d0a017bd 100644 --- a/contracts/AccessController.sol +++ b/contracts/AccessController.sol @@ -39,6 +39,19 @@ contract AccessController is IAccessController { MODULE_REGISTRY = moduleRegistry_; } + /// @notice Sets the permission for all IPAccounts + function setGlobalPermission(address signer_, address to_, bytes4 func_, uint8 permission_) external { + // TODO: access controller can only be called by protocol admin + if (signer_ == address(0)) { + revert Errors.AccessController__SignerIsZeroAddress(); + } + // permission must be one of ABSTAIN, ALLOW, DENY + if (permission_ > 2) { + revert Errors.AccessController__PermissionIsNotValid(); + } + permissions[address(0)][signer_][to_][func_] = permission_; + } + /// @notice Sets the permission for a specific function call /// @dev By default, all policies are set to ABSTAIN, which means that the permission is not set /// Owner of ipAccount by default has permission sets the permission @@ -130,6 +143,9 @@ contract AccessController is IAccessController { } // If module level permission is ABSTAIN, check transaction signer level permission if (permissions[ipAccount_][signer_][to_][bytes4(0)] == AccessPermission.ABSTAIN) { + if (permissions[address(0)][signer_][to_][func_] == AccessPermission.ALLOW) { + return true; + } // Return true if the ipAccount allow the signer can call all functions of all modules // Otherwise, return false return permissions[ipAccount_][signer_][address(0)][bytes4(0)] == AccessPermission.ALLOW; diff --git a/contracts/interfaces/IAccessController.sol b/contracts/interfaces/IAccessController.sol index 1d37a4d0..87179bc2 100644 --- a/contracts/interfaces/IAccessController.sol +++ b/contracts/interfaces/IAccessController.sol @@ -14,6 +14,14 @@ interface IAccessController { /// @param permission_ The permission level function setPermission(address ipAccount_, address signer_, address to_, bytes4 func_, uint8 permission_) external; + /// @notice Sets the permission for all IPAccounts + /// @dev Only the protocol admin can set the global permission + /// @param signer_ The account that signs the transaction + /// @param to_ The recipient(modules) of the transaction + /// @param func_ The function selector + /// @param permission_ The permission level + function setGlobalPermission(address signer_, address to_, bytes4 func_, uint8 permission_) external; + /// @notice Gets the permission for a specific function call /// @param ipAccount_ The account that owns the IP /// @param signer_ The account that signs the transaction diff --git a/test/foundry/AccessController.t.sol b/test/foundry/AccessController.t.sol index 1063bce7..19ea3242 100644 --- a/test/foundry/AccessController.t.sol +++ b/test/foundry/AccessController.t.sol @@ -590,6 +590,7 @@ contract AccessControllerTest is Test { ); } + function test_AccessController_functionWildcardOverrideToAddressWildcard_allowOverrideDeny() public { moduleRegistry.registerModule("MockModule", address(mockModule)); address signer = vm.addr(2); @@ -863,4 +864,43 @@ contract AccessControllerTest is Test { vm.expectRevert("Invalid signer"); mockOrchestratorModule.workflowFailure(payable(address(ipAccount))); } + + function test_AccessController_OrchestratorModuleWithGlobalPermission() public { + MockOrchestratorModule mockOrchestratorModule = new MockOrchestratorModule( + address(ipAccountRegistry), + address(moduleRegistry) + ); + moduleRegistry.registerModule("MockOrchestratorModule", address(mockOrchestratorModule)); + + MockModule module1WithPermission = new MockModule( + address(ipAccountRegistry), + address(moduleRegistry), + "Module1WithPermission" + ); + moduleRegistry.registerModule("Module1WithPermission", address(module1WithPermission)); + + MockModule module2WithPermission = new MockModule( + address(ipAccountRegistry), + address(moduleRegistry), + "Module2WithPermission" + ); + moduleRegistry.registerModule("Module2WithPermission", address(module2WithPermission)); + + accessController.setGlobalPermission( + address(mockOrchestratorModule), + address(module1WithPermission), + mockModule.executeSuccessfully.selector, + AccessPermission.ALLOW + ); + + accessController.setGlobalPermission( + address(mockOrchestratorModule), + address(module2WithPermission), + mockModule.executeNoReturn.selector, + AccessPermission.ALLOW + ); + + vm.prank(owner); + mockOrchestratorModule.workflowPass(payable(address(ipAccount))); + } } diff --git a/test/foundry/mocks/MockAccessController.sol b/test/foundry/mocks/MockAccessController.sol index a5a1b8cb..b22c5783 100644 --- a/test/foundry/mocks/MockAccessController.sol +++ b/test/foundry/mocks/MockAccessController.sol @@ -10,7 +10,10 @@ contract MockAccessController is IAccessController { function setAllowed(bool _isAllowed) external { isAllowed = _isAllowed; } + function setGlobalPermission(address signer_, address to_, bytes4 func_, uint8 permission_) external{ + } + function setPermission(address, address, address, bytes4, uint8) external pure { }