-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathRoles.t.sol
More file actions
413 lines (326 loc) · 16.5 KB
/
Roles.t.sol
File metadata and controls
413 lines (326 loc) · 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.17;
import {FirmTest} from "../../bases/test/lib/FirmTest.sol";
import {SafeStub} from "../../bases/test/mocks/SafeStub.sol";
import {SafeAware} from "../../bases/SafeAware.sol";
import "../Roles.sol";
contract RolesTest is FirmTest {
SafeStub safe;
Roles roles;
address SOMEONE = account("someone");
address SOMEONE_ELSE = account("someone else");
address SAFE_OWNER = account("safe owner");
function setUp() public {
safe = new SafeStub();
safe.setOwner(SAFE_OWNER, true);
roles = Roles(createProxy(new Roles(), abi.encodeCall(Roles.initialize, (ISafe(payable(safe)), address(0)))));
}
function testInitialRoot() public {
assertTrue(roles.hasRole(address(safe), ROOT_ROLE_ID));
assertTrue(roles.isRoleAdmin(address(safe), ROOT_ROLE_ID));
assertFalse(roles.hasRole(address(this), ROOT_ROLE_ID));
assertFalse(roles.isRoleAdmin(address(this), ROOT_ROLE_ID));
assertTrue(roles.hasRole(address(safe), ROLE_MANAGER_ROLE_ID));
assertTrue(roles.isRoleAdmin(address(safe), ROLE_MANAGER_ROLE_ID));
assertFalse(roles.hasRole(address(this), ROLE_MANAGER_ROLE_ID));
assertFalse(roles.isRoleAdmin(address(this), ROLE_MANAGER_ROLE_ID));
assertTrue(roles.roleExists(ROLE_MANAGER_ROLE_ID));
assertFalse(roles.roleExists(ROLE_MANAGER_ROLE_ID + 1));
assertTrue(roles.roleExists(SAFE_OWNER_ROLE_ID));
}
function testCannotReinit() public {
vm.expectRevert(abi.encodeWithSelector(SafeAware.AlreadyInitialized.selector));
roles.initialize(ISafe(payable(address(2))), address(0));
}
function testAdminCanCreateRoles() public {
vm.prank(address(safe));
uint8 roleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
assertEq(roleId, ROLE_MANAGER_ROLE_ID + 1);
assertTrue(roles.isRoleAdmin(address(safe), roleId));
assertTrue(roles.hasRole(address(safe), roleId));
}
function testCannotCreateRolesWithoutRolesManagerRole() public {
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNoRole.selector, ROLE_MANAGER_ROLE_ID));
roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
}
function testCannotCreateRoleWithNoAdmins() public {
vm.prank(address(safe));
vm.expectRevert(abi.encodeWithSelector(Roles.InvalidRoleAdmins.selector));
roles.createRole(NO_ROLE_ADMINS, "");
}
function testCannotCreateRoleWithNonExistentAdmin() public {
vm.startPrank(address(safe));
uint256 roleCount = roles.roleCount();
// Can't create a role that would be admined by a role that doesn't exist
vm.expectRevert(abi.encodeWithSelector(Roles.InvalidRoleAdmins.selector));
roles.createRole(bytes32(1 << (roleCount + 1)), "");
// Can't create a role that would be admined by itself (the next role to exist)
roles.createRole(bytes32(1 << roleCount), "");
vm.stopPrank();
}
function testSomeoneWithPermissionCanCreateRolesUntilRevoked() public {
vm.prank(address(safe));
roles.setRole(SOMEONE, ROLE_MANAGER_ROLE_ID, true);
vm.prank(SOMEONE);
uint8 roleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
assertEq(roleId, ROLE_MANAGER_ROLE_ID + 1);
vm.prank(address(safe));
roles.setRole(SOMEONE, ROLE_MANAGER_ROLE_ID, false);
vm.prank(SOMEONE);
testCannotCreateRolesWithoutRolesManagerRole();
}
function testCanOnlyHave255RegularRoles() public {
vm.startPrank(address(safe));
// We set all initial existing roles as admins of the new role to test that
// all existing roles can be admins of the new role
bytes32 roleAdmins = ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << ROLE_MANAGER_ROLE_ID) | bytes32(1 << SAFE_OWNER_ROLE_ID);
for (uint256 i = 0; i < 253; i++) {
uint8 roleId = roles.createRole(roleAdmins, "");
// We add the new role as an admin to all roles to be created
roleAdmins = roleAdmins | bytes32(1 << roleId);
}
assertEq(roles.roleCount(), 255);
vm.expectRevert(abi.encodeWithSelector(Roles.RoleLimitReached.selector));
roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
vm.stopPrank();
}
function testCanToggleAllRolesAsAdminsWhenAllExist() public {
testCanOnlyHave255RegularRoles();
vm.prank(address(safe));
roles.setRoleAdmins(254, ~bytes32(0));
}
function testAdminCanGrantAndRevokeRoles() public {
vm.startPrank(address(safe));
uint8 roleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
roles.setRole(SOMEONE, roleId, true);
assertTrue(roles.hasRole(SOMEONE, roleId));
assertTrue(roles.isRoleAdmin(address(safe), roleId));
roles.setRole(SOMEONE, roleId, false);
assertFalse(roles.hasRole(SOMEONE, roleId));
}
function testNonAdminCannotGrantRole() public {
vm.startPrank(address(safe));
uint8 roleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
roles.setRole(SOMEONE, roleId, true);
vm.stopPrank();
vm.prank(SOMEONE);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, roleId));
roles.setRole(SOMEONE_ELSE, roleId, true);
}
function testCanSetAndRevokeMultipleRoles() public {
vm.startPrank(address(safe));
uint8 roleOne = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, ""); // Admin for role one is ROOT_ROLE_ID
uint8 roleTwo = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << uint256(roleOne)), ""); // Admin for role 2 is ROOT_ROLE_ID and roleOne
uint8[] memory rolesSomeone = new uint8[](2);
rolesSomeone[0] = roleOne;
rolesSomeone[1] = roleTwo;
roles.setRoles(SOMEONE, rolesSomeone, new uint8[](0));
assertTrue(roles.hasRole(SOMEONE, roleOne));
assertTrue(roles.hasRole(SOMEONE, roleTwo));
assertFalse(roles.isRoleAdmin(SOMEONE, roleOne));
assertTrue(roles.isRoleAdmin(SOMEONE, roleTwo));
vm.stopPrank();
vm.prank(SOMEONE);
roles.setRole(SOMEONE_ELSE, roleTwo, true);
assertTrue(roles.hasRole(SOMEONE_ELSE, roleTwo));
assertFalse(roles.isRoleAdmin(SOMEONE_ELSE, roleTwo));
vm.prank(address(safe));
roles.setRoles(SOMEONE, new uint8[](0), rolesSomeone);
assertFalse(roles.hasRole(SOMEONE, roleOne));
assertFalse(roles.hasRole(SOMEONE, roleTwo));
}
function testCanChangeRoleAdmin() public {
vm.startPrank(address(safe));
uint8 newRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
roles.setRole(SOMEONE, newRoleId, true);
vm.stopPrank();
vm.prank(SOMEONE);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, newRoleId));
roles.setRole(SOMEONE_ELSE, newRoleId, true);
vm.prank(address(safe));
bytes32 newRoleAdmin = ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << uint256(newRoleId));
roles.setRoleAdmins(newRoleId, newRoleAdmin); // those with newRoleId are admins
assertEq(roles.getRoleAdmins(newRoleId), newRoleAdmin);
vm.prank(SOMEONE);
roles.setRole(SOMEONE_ELSE, newRoleId, true); // action that was previously reverting, now succeeds
assertTrue(roles.hasRole(SOMEONE_ELSE, newRoleId));
}
function testCannotChangeRoleAdminWithoutRolesManagerRole() public {
vm.startPrank(address(safe));
uint8 newRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
roles.setRole(SOMEONE, newRoleId, true);
vm.stopPrank();
vm.prank(SOMEONE);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNoRole.selector, ROLE_MANAGER_ROLE_ID));
roles.setRoleAdmins(newRoleId, ONLY_ROOT_ROLE_AS_ADMIN);
}
function testCannotChangeRoleNameWithoutRolesManagerRole() public {
vm.startPrank(address(safe));
uint8 newRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
roles.setRole(SOMEONE, newRoleId, true);
vm.stopPrank();
vm.prank(SOMEONE);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNoRole.selector, ROLE_MANAGER_ROLE_ID));
roles.setRoleName(newRoleId, "Some name");
}
function testCannotHaveNonExistentRoleAdmin() public {
vm.startPrank(address(safe));
uint8 newRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
vm.expectRevert(abi.encodeWithSelector(Roles.InvalidRoleAdmins.selector));
roles.setRoleAdmins(newRoleId, bytes32(1 << newRoleId + 1)); // fails to set it to a non-existent role (next one)
roles.setRoleAdmins(newRoleId, bytes32(1 << newRoleId)); // succeeds to set it to itself (last one)
vm.stopPrank();
}
function testCannotChangeRoleAdminToNoAdmin() public {
vm.startPrank(address(safe));
uint8 newRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
vm.expectRevert(abi.encodeWithSelector(Roles.InvalidRoleAdmins.selector));
roles.setRoleAdmins(newRoleId, NO_ROLE_ADMINS);
vm.stopPrank();
}
function testAdminCanChangeAdminForAdminRole() public {
bytes32 newRoleAdmin = ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << uint256(ROLE_MANAGER_ROLE_ID));
vm.prank(address(safe));
roles.setRoleAdmins(ROOT_ROLE_ID, newRoleAdmin);
assertEq(roles.getRoleAdmins(ROOT_ROLE_ID), newRoleAdmin);
}
function testNonAdminCantChangeAdminForAdminRole() public {
vm.prank(address(safe));
roles.setRole(SOMEONE, ROLE_MANAGER_ROLE_ID, true);
// As SOMEONE is granted ROLE_MANAGER_ROLE_ID, it can change the admin for all roles, including ROLE_MANAGER_ROLE_ID
vm.startPrank(SOMEONE);
bytes32 newRoleAdmin = ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << uint256(ROLE_MANAGER_ROLE_ID));
roles.setRoleAdmins(ROLE_MANAGER_ROLE_ID, newRoleAdmin);
assertEq(roles.getRoleAdmins(ROLE_MANAGER_ROLE_ID), newRoleAdmin);
// However, when attempting to change the admin role, it will fail
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, ROOT_ROLE_ID));
roles.setRoleAdmins(ROOT_ROLE_ID, newRoleAdmin);
}
function testRoleAdminHasRole() public {
vm.startPrank(address(safe));
uint8 roleOneId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
uint8 roleTwoId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN | bytes32(1 << uint256(roleOneId)), "");
assertFalse(roles.hasRole(SOMEONE, roleTwoId));
roles.setRole(SOMEONE, roleOneId, true);
assertTrue(roles.hasRole(SOMEONE, roleTwoId));
vm.stopPrank();
}
function testCannotSetAdminRolesOnUnexistentRole() public {
uint8 unexistentRoleId = 100;
vm.startPrank(address(safe));
vm.expectRevert(abi.encodeWithSelector(Roles.UnexistentRole.selector, unexistentRoleId));
roles.setRoleAdmins(unexistentRoleId, ONLY_ROOT_ROLE_AS_ADMIN);
}
function testCannotSetRoleNameOnUnexistentRole() public {
uint8 unexistentRoleId = 100;
vm.startPrank(address(safe));
vm.expectRevert(abi.encodeWithSelector(Roles.UnexistentRole.selector, unexistentRoleId));
roles.setRoleName(unexistentRoleId, "new name");
}
function testSafeOwnerHasRole() public {
assertTrue(roles.hasRole(SAFE_OWNER, SAFE_OWNER_ROLE_ID));
assertFalse(roles.hasRole(SOMEONE, SAFE_OWNER_ROLE_ID));
// safe is not a safe owner but has root role
assertTrue(roles.hasRole(address(safe), SAFE_OWNER_ROLE_ID));
}
function testSafeOwnerRoleCannotBeGrantedNorRevoked() public {
vm.startPrank(address(safe));
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, SAFE_OWNER_ROLE_ID));
roles.setRole(SOMEONE, SAFE_OWNER_ROLE_ID, true);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, SAFE_OWNER_ROLE_ID));
roles.setRole(SAFE_OWNER, SAFE_OWNER_ROLE_ID, false);
uint8[] memory roleArray = new uint8[](1);
roleArray[0] = SAFE_OWNER_ROLE_ID;
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, SAFE_OWNER_ROLE_ID));
roles.setRoles(SOMEONE, roleArray, new uint8[](0));
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, SAFE_OWNER_ROLE_ID));
roles.setRoles(SAFE_OWNER, new uint8[](0), roleArray);
vm.stopPrank();
}
function testCannotSetAdminRolesOnSafeOwnerRole() public {
vm.prank(address(safe));
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, SAFE_OWNER_ROLE_ID));
roles.setRoleAdmins(SAFE_OWNER_ROLE_ID, ONLY_ROOT_ROLE_AS_ADMIN);
}
function testSafeOwnerRoleCanBeRoleAdmin() public {
vm.prank(address(safe));
uint8 newRole = roles.createRole(bytes32(uint256(1 << SAFE_OWNER_ROLE_ID)), "");
vm.prank(SAFE_OWNER);
roles.setRole(SOMEONE, newRole, true);
assertTrue(roles.hasRole(SOMEONE, newRole));
// Remove as safe owner and try granting the role to someone else
safe.setOwner(SAFE_OWNER, false);
vm.prank(SAFE_OWNER);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, newRole));
roles.setRole(SOMEONE_ELSE, newRole, true);
}
event RoleNameChanged(uint8 indexed roleId, string name, address indexed actor);
function testSafeOwnerRoleNameCanBeChanged() public {
vm.prank(address(safe));
vm.expectEmit(true, true, true, false);
emit RoleNameChanged(SAFE_OWNER_ROLE_ID, "new name", address(safe));
roles.setRoleName(SAFE_OWNER_ROLE_ID, "new name");
}
}
contract RolesRootRoleTest is FirmTest {
SafeStub safe;
Roles roles;
address ROOT = account("Root");
address ROLE_MANAGER = account("Role Manager");
address SOMEONE = account("Someone");
uint8 someRoleId;
function setUp() public {
safe = new SafeStub();
roles = Roles(createProxy(new Roles(), abi.encodeCall(Roles.initialize, (ISafe(payable(safe)), address(0)))));
vm.startPrank(address(safe));
someRoleId = roles.createRole(ONLY_ROOT_ROLE_AS_ADMIN, "");
// Separate the roles
roles.setRole(ROOT, ROOT_ROLE_ID, true);
roles.setRole(ROLE_MANAGER, ROLE_MANAGER_ROLE_ID, true);
roles.setRole(SOMEONE, someRoleId, true);
vm.stopPrank();
}
function testRootHasAllRoles() public {
assertTrue(roles.hasRole(ROOT, ROOT_ROLE_ID));
assertTrue(roles.hasRole(ROOT, ROLE_MANAGER_ROLE_ID));
assertTrue(roles.hasRole(ROOT, someRoleId));
}
function testRootCanRemoveSafeAsRoot() public {
vm.prank(ROOT);
roles.setRole(address(safe), ROOT_ROLE_ID, false);
assertFalse(roles.hasRole(address(safe), ROOT_ROLE_ID));
assertFalse(roles.hasRole(address(safe), ROLE_MANAGER_ROLE_ID));
assertFalse(roles.hasRole(address(safe), someRoleId));
}
function testRootCanSetNoAdminsFromRootRole() public {
// As admin, root can grant the root role
vm.prank(ROOT);
roles.setRole(SOMEONE, ROOT_ROLE_ID, true);
assertTrue(roles.hasRole(SOMEONE, ROOT_ROLE_ID));
// As root holder, root can set the admins of the root role (which allows setting it to no admins)
vm.prank(ROOT);
roles.setRoleAdmins(ROOT_ROLE_ID, bytes32(0));
// Root can no longer revoke the root role and it is fixed
vm.prank(ROOT);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, ROOT_ROLE_ID));
roles.setRole(SOMEONE, ROOT_ROLE_ID, false);
}
function testRoleManagerCannotChangeRootRoleAdmins() public {
vm.prank(ROLE_MANAGER);
vm.expectRevert(abi.encodeWithSelector(Roles.UnauthorizedNotAdmin.selector, ROOT_ROLE_ID));
roles.setRoleAdmins(ROOT_ROLE_ID, bytes32(0));
}
function testCannotSetNoAdminsForRootManagerRole() public {
vm.prank(ROOT);
vm.expectRevert(abi.encodeWithSelector(Roles.InvalidRoleAdmins.selector));
roles.setRoleAdmins(ROLE_MANAGER_ROLE_ID, bytes32(0));
}
function testSomeRoleCanAdminRootRole() public {
vm.prank(ROOT);
roles.setRoleAdmins(ROOT_ROLE_ID, bytes32(uint256(1 << someRoleId)));
assertTrue(roles.hasRole(SOMEONE, someRoleId)); // transitively gets the root role as admin
vm.prank(SOMEONE);
roles.setRole(SOMEONE, ROOT_ROLE_ID, true);
assertTrue(roles.hasRole(SOMEONE, ROOT_ROLE_ID));
}
}