Skip to content

Commit 88db2d6

Browse files
authored
refactor: move module access controller (#56)
1 parent 8095a10 commit 88db2d6

15 files changed

+260
-11
lines changed

solidity/contracts/Oracle.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {IRequestModule} from '../interfaces/modules/request/IRequestModule.sol';
1010
import {IResolutionModule} from '../interfaces/modules/resolution/IResolutionModule.sol';
1111
import {IResponseModule} from '../interfaces/modules/response/IResponseModule.sol';
1212
import {ValidatorLib} from '../libraries/ValidatorLib.sol';
13-
import {OracleAccessController} from './OracleAccessController.sol';
13+
import {OracleAccessController} from './access/OracleAccessController.sol';
1414
import {OracleTypehash} from './utils/OracleTypehash.sol';
1515

1616
contract Oracle is IOracle, OracleAccessController {

solidity/contracts/CommonAccessController.sol renamed to solidity/contracts/access/CommonAccessController.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.19;
33

4-
import {IAccessController} from '../interfaces/IAccessController.sol';
5-
import {IAccessModule} from '../interfaces/modules/access/IAccessModule.sol';
4+
import {IAccessController} from '../../interfaces/access/IAccessController.sol';
5+
import {IAccessModule} from '../../interfaces/modules/access/IAccessModule.sol';
66

77
abstract contract CommonAccessController is IAccessController {
88
/**
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.19;
3+
4+
import {IOracle} from '../../interfaces/IOracle.sol';
5+
import {IModuleAccessController} from '../../interfaces/access/IModuleAccessController.sol';
6+
import {Module} from '../Module.sol';
7+
import {CommonAccessController} from './CommonAccessController.sol';
8+
9+
abstract contract ModuleAccessController is IModuleAccessController, CommonAccessController, Module {
10+
constructor(IOracle _oracle) Module(_oracle) {}
11+
12+
/**
13+
* @notice Returns an access control object using the contract address as user and the given data
14+
* @dev should only be used by modules as the self-access-control object
15+
* @param _data Arbitrary data
16+
* @return _accessControl The self access control for this contract.
17+
*/
18+
function _defaultAccessControl(bytes memory _data) internal view returns (AccessControl memory _accessControl) {
19+
_accessControl = AccessControl({user: address(this), data: _data});
20+
}
21+
22+
/**
23+
* @notice Returns an access control object using the contract address as user, and empty data
24+
*
25+
* @dev should only be used by modules as the self-access-control object
26+
* @return _accessControl The self access control for this contract.
27+
*/
28+
function _defaultAccessControl() internal view returns (AccessControl memory _accessControl) {
29+
_accessControl = _defaultAccessControl(bytes(''));
30+
}
31+
32+
modifier hasAccess(
33+
address _accessModule,
34+
bytes32 _typehash,
35+
bytes memory _params,
36+
AccessControl memory _accessControl
37+
) {
38+
if (_accessControl.user != msg.sender) {
39+
if (_accessModule == address(0)) {
40+
revert AccessController_NoAccess();
41+
} else {
42+
if (!ORACLE.isAccessModuleApproved(_accessControl.user, _accessModule)) {
43+
revert ModuleAccessController_AccessModuleNotApproved();
44+
}
45+
_hasAccess(_accessModule, _typehash, _params, _accessControl);
46+
}
47+
}
48+
_;
49+
}
50+
}

solidity/contracts/OracleAccessController.sol renamed to solidity/contracts/access/OracleAccessController.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.19;
33

4-
import {IOracleAccessController} from '../interfaces/IOracleAccessController.sol';
4+
import {IOracleAccessController} from '../../interfaces/access/IOracleAccessController.sol';
55
import {CommonAccessController} from './CommonAccessController.sol';
66

77
abstract contract OracleAccessController is IOracleAccessController, CommonAccessController {

solidity/interfaces/IOracle.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.19;
33

4-
import {IAccessController, IOracleAccessController} from './IOracleAccessController.sol';
4+
import {IAccessController, IOracleAccessController} from './access/IOracleAccessController.sol';
55

66
/**
77
* @title Oracle
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.19;
3+
4+
import {IAccessController} from './IAccessController.sol';
5+
6+
/**
7+
* @title Module Access Controller Interface
8+
* @notice Interface for the module access controller
9+
*/
10+
interface IModuleAccessController is IAccessController {
11+
/**
12+
* @notice Thrown when user has not approved the access module
13+
*/
14+
error ModuleAccessController_AccessModuleNotApproved();
15+
}

solidity/interfaces/modules/access/IAccessModule.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
pragma solidity ^0.8.19;
33

4-
import {IAccessController} from '../../IAccessController.sol';
54
import {IModule} from '../../IModule.sol';
5+
import {IAccessController} from '../../access/IAccessController.sol';
66

77
/**
88
* @title AccessModule

solidity/test/integration/IntegrationBase.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {IResolutionModule} from '../../interfaces/modules/resolution/IResolution
1414
import {IResponseModule} from '../../interfaces/modules/response/IResponseModule.sol';
1515

1616
import {IOracle, Oracle} from '../../contracts/Oracle.sol';
17-
import {IAccessController, IOracleAccessController} from '../../interfaces/IOracleAccessController.sol';
17+
import {IAccessController, IOracleAccessController} from '../../interfaces/access/IOracleAccessController.sol';
1818

1919
import {IMockAccounting, MockAccounting} from '../mocks/contracts/MockAccounting.sol';
2020
import {MockCallback} from '../mocks/contracts/MockCallback.sol';

solidity/test/unit/CommonAccessController.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.19;
33

44
import 'forge-std/Test.sol';
55

6-
import {CommonAccessController, IAccessController} from '../../contracts/CommonAccessController.sol';
6+
import {CommonAccessController, IAccessController} from '../../contracts/access/CommonAccessController.sol';
77
import {IAccessController, IAccessModule} from '../../interfaces/modules/access/IAccessModule.sol';
88
import {Helpers} from '../utils/Helpers.sol';
99

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.19;
3+
4+
import {IModuleAccessController, ModuleAccessController} from '../../contracts/access/ModuleAccessController.sol';
5+
import {IOracle} from '../../interfaces/IOracle.sol';
6+
7+
import {IOracleAccessController} from '../../interfaces/access/IOracleAccessController.sol';
8+
import {IAccessController, IAccessModule} from '../../interfaces/modules/access/IAccessModule.sol';
9+
import {Helpers} from '../utils/Helpers.sol';
10+
import 'forge-std/Test.sol';
11+
12+
contract MockModuleAccessController is ModuleAccessController {
13+
constructor(IOracle _oracle) ModuleAccessController(_oracle) {}
14+
15+
function hasAccessForTest(
16+
address _accessModule,
17+
bytes32 _typehash,
18+
bytes memory _params,
19+
AccessControl memory _accessControl
20+
) public hasAccess(_accessModule, _typehash, _params, _accessControl) {}
21+
22+
function moduleName() external pure returns (string memory _moduleName) {
23+
_moduleName = 'MockModuleAccessController';
24+
}
25+
}
26+
27+
/**
28+
* @title Module Abstract Unit tests
29+
*/
30+
contract BaseTest is Test, Helpers {
31+
// A mock access controller
32+
MockModuleAccessController public moduleAccessController;
33+
// The oracle
34+
IOracle public oracle;
35+
// Mock caller
36+
address public caller;
37+
38+
// Events
39+
event AccessModuleSet(address indexed user, address indexed accessModule, bool approved);
40+
41+
/**
42+
* @notice Deploy the access controller
43+
*/
44+
function setUp() public {
45+
oracle = IOracle(makeAddr('Oracle'));
46+
moduleAccessController = new MockModuleAccessController(oracle);
47+
caller = makeAddr('Caller');
48+
}
49+
50+
function _setAccessModuleForTest(address _user, address _accessModule, bool _approved) public {
51+
vm.mockCall(
52+
address(oracle),
53+
abi.encodeWithSelector(IOracleAccessController.isAccessModuleApproved.selector, _user, _accessModule),
54+
abi.encode(_approved)
55+
);
56+
}
57+
}
58+
59+
contract ModuleAccessController_Unit_HasAccess is BaseTest {
60+
modifier happyPath(
61+
address _accessModule,
62+
bytes32 _typehash,
63+
bytes memory _params,
64+
IAccessController.AccessControl memory _accessControl
65+
) {
66+
vm.assume(_accessControl.user != caller);
67+
vm.assume(_accessModule != address(0) && _accessModule != caller);
68+
vm.assume(_params.length < 32);
69+
70+
_setAccessModuleForTest(_accessControl.user, _accessModule, true);
71+
72+
vm.startPrank(caller);
73+
_;
74+
}
75+
76+
function test_revertIfNoAccessModuleAndSenderIsNotUser(
77+
address _accessModule,
78+
bytes32 _typehash,
79+
bytes memory _params,
80+
IAccessController.AccessControl memory _accessControl
81+
) public happyPath(_accessModule, _typehash, _params, _accessControl) {
82+
// Expect the revert
83+
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessController_NoAccess.selector));
84+
85+
// Call the hasAccess function
86+
moduleAccessController.hasAccessForTest(address(0), _typehash, _params, _accessControl);
87+
}
88+
89+
function test_revertIfAccessModuleNotApproved(
90+
address _accessModule,
91+
bytes32 _typehash,
92+
bytes memory _params,
93+
IAccessController.AccessControl memory _accessControl
94+
) public happyPath(_accessModule, _typehash, _params, _accessControl) {
95+
// Ensure the access module is not approved
96+
_setAccessModuleForTest(_accessControl.user, _accessModule, false);
97+
98+
// Expect the revert
99+
vm.expectRevert(
100+
abi.encodeWithSelector(IModuleAccessController.ModuleAccessController_AccessModuleNotApproved.selector)
101+
);
102+
103+
// Call the hasAccess function
104+
moduleAccessController.hasAccessForTest(_accessModule, _typehash, _params, _accessControl);
105+
}
106+
107+
function test_revertIfNoHasAccess(
108+
address _accessModule,
109+
bytes32 _typehash,
110+
bytes memory _params,
111+
IAccessController.AccessControl memory _accessControl
112+
) public happyPath(_accessModule, _typehash, _params, _accessControl) {
113+
IAccessModule.AccessControlParameters memory _expectedParams = IAccessModule.AccessControlParameters({
114+
sender: caller,
115+
accessControl: _accessControl,
116+
typehash: _typehash,
117+
typehashParams: _params
118+
});
119+
120+
// Expect the hasAccess function to not be called
121+
_mockAndExpect(
122+
_accessModule,
123+
abi.encodeWithSelector(IAccessModule.hasAccess.selector, abi.encode(_expectedParams)),
124+
abi.encode(false)
125+
);
126+
127+
// Expect the revert
128+
vm.expectRevert(abi.encodeWithSelector(IAccessController.AccessController_NoAccess.selector));
129+
130+
// Call the hasAccess function
131+
moduleAccessController.hasAccessForTest(_accessModule, _typehash, _params, _accessControl);
132+
}
133+
134+
function test_hasAccessSenderIsNotUser(
135+
address _accessModule,
136+
bytes32 _typehash,
137+
bytes memory _params,
138+
IAccessController.AccessControl memory _accessControl
139+
) public happyPath(_accessModule, _typehash, _params, _accessControl) {
140+
IAccessModule.AccessControlParameters memory _expectedParams = IAccessModule.AccessControlParameters({
141+
sender: caller,
142+
accessControl: _accessControl,
143+
typehash: _typehash,
144+
typehashParams: _params
145+
});
146+
147+
// Expect the hasAccess function to be called
148+
_mockAndExpect(
149+
_accessModule,
150+
abi.encodeWithSelector(IAccessModule.hasAccess.selector, abi.encode(_expectedParams)),
151+
abi.encode(true)
152+
);
153+
154+
// Call the hasAccess function
155+
moduleAccessController.hasAccessForTest(_accessModule, _typehash, _params, _accessControl);
156+
}
157+
158+
function test_hasAccessSenderIsUser(
159+
address _accessModule,
160+
bytes32 _typehash,
161+
bytes memory _params,
162+
IAccessController.AccessControl memory _accessControl
163+
) public happyPath(_accessModule, _typehash, _params, _accessControl) {
164+
_setAccessModuleForTest(caller, _accessModule, true);
165+
_accessControl.user = caller;
166+
// We expect the hasAccess function to not be called
167+
vm.expectCall(
168+
_accessModule,
169+
abi.encodeWithSelector(
170+
IAccessModule.hasAccess.selector,
171+
abi.encode(
172+
IAccessModule.AccessControlParameters({
173+
sender: caller,
174+
accessControl: _accessControl,
175+
typehash: _typehash,
176+
typehashParams: _params
177+
})
178+
)
179+
),
180+
0
181+
);
182+
moduleAccessController.hasAccessForTest(_accessModule, _typehash, _params, _accessControl);
183+
}
184+
}

solidity/test/unit/Oracle.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {IOracle} from '../../interfaces/IOracle.sol';
88

99
import {IDisputeModule} from '../../interfaces/modules/dispute/IDisputeModule.sol';
1010

11-
import {IAccessController, IOracleAccessController} from '../../interfaces/IOracleAccessController.sol';
11+
import {IAccessController, IOracleAccessController} from '../../interfaces/access/IOracleAccessController.sol';
1212
import {IAccessModule} from '../../interfaces/modules/access/IAccessModule.sol';
1313
import {IFinalityModule} from '../../interfaces/modules/finality/IFinalityModule.sol';
1414
import {IRequestModule} from '../../interfaces/modules/request/IRequestModule.sol';

solidity/test/unit/OracleAccessController.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ pragma solidity ^0.8.19;
33

44
import 'forge-std/Test.sol';
55

6-
import {IOracleAccessController, OracleAccessController} from '../../contracts/OracleAccessController.sol';
6+
import {IOracleAccessController, OracleAccessController} from '../../contracts/access/OracleAccessController.sol';
77
import {IAccessController, IAccessModule} from '../../interfaces/modules/access/IAccessModule.sol';
88
import {Helpers} from '../utils/Helpers.sol';
99

solidity/test/utils/Helpers.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
pragma solidity ^0.8.19;
33

44
import {IOracle} from '../../contracts/Oracle.sol';
5-
import {IAccessController} from '../../interfaces/IAccessController.sol';
5+
import {IAccessController} from '../../interfaces/access/IAccessController.sol';
66
import {Test} from 'forge-std/Test.sol';
77

88
contract Helpers is Test {

0 commit comments

Comments
 (0)