@lazy-sol/access-control 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +21 -0
- package/.solcover.js +39 -0
- package/CONTRIBUTING.md +200 -0
- package/CRIBBED_CODE.txt +26 -0
- package/LICENSE.txt +22 -0
- package/README.md +162 -0
- package/artifacts/contracts/OwnableToAccessControlAdapter.sol/OwnableToAccessControlAdapter.json +342 -0
- package/contracts/AccessControl.sol +305 -0
- package/contracts/AdapterFactory.sol +56 -0
- package/contracts/OwnableToAccessControlAdapter.sol +231 -0
- package/contracts/mocks/AccessControlMock.sol +14 -0
- package/contracts/mocks/TetherToken.sol +443 -0
- package/deploy/deploy-AdapterFactory.js +59 -0
- package/deployments/goerli/.chainId +1 -0
- package/deployments/goerli/AdapterFactory.json +104 -0
- package/deployments/goerli/solcInputs/9f8f20c7b4fd0796d45c56d37e790191.json +42 -0
- package/docs/commit_policy.md +201 -0
- package/docs/pull_request_template.md +40 -0
- package/docs/style_guides.md +240 -0
- package/hardhat.config.js +359 -0
- package/package.json +39 -0
- package/test/adapter_factory.js +54 -0
- package/test/include/deployment_routines.js +150 -0
- package/test/include/features_roles.js +42 -0
- package/test/include/rbac.behaviour.js +315 -0
- package/test/ownable_to_rbac_adapter.js +100 -0
- package/test/ownable_to_rbac_adapter_rbac.js +57 -0
- package/test/rbac_core.js +24 -0
- package/test/rbac_modifier.js +53 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
/**
|
2
|
+
* Deploys USDT token, used to test non ERC20 compliant transfer function
|
3
|
+
* (doesn't return any value on successful operation)
|
4
|
+
*
|
5
|
+
* @param a0 smart contract owner
|
6
|
+
* @param H0 initial token holder address
|
7
|
+
* @returns USDT ERC20 instance
|
8
|
+
*/
|
9
|
+
async function deploy_usdt(a0, H0 = a0) {
|
10
|
+
// smart contracts required
|
11
|
+
const USDTContract = artifacts.require("TetherToken");
|
12
|
+
|
13
|
+
// deploy the token
|
14
|
+
const token = await USDTContract.new(0, "Tether USD", "USDT", 6, {from: a0});
|
15
|
+
|
16
|
+
// move the initial supply if required
|
17
|
+
if(H0 !== a0) {
|
18
|
+
await token.transfer(H0, S0, {from: a0});
|
19
|
+
}
|
20
|
+
|
21
|
+
// return the reference
|
22
|
+
return token;
|
23
|
+
}
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Deploys AccessControl contract
|
27
|
+
*
|
28
|
+
* @param a0 smart contract deployer
|
29
|
+
* @param owner smart contract owner, super admin, optional
|
30
|
+
* @param features initial smart contract features, optional
|
31
|
+
* @returns AccessControl instance
|
32
|
+
*/
|
33
|
+
async function deploy_access_control(a0, owner = a0, features = 0) {
|
34
|
+
// deploy AccessControlMock
|
35
|
+
const AccessControlMock = artifacts.require("AccessControlMock");
|
36
|
+
|
37
|
+
// deploy and return the instance
|
38
|
+
return await AccessControlMock.new(owner, features, {from: a0});
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Deploys OwnableToAccessControlAdapter
|
43
|
+
* Deploys OZ Ownable contract (TetherToken) if target is not specified
|
44
|
+
* Transfers the ownership on the ownable to the adapter
|
45
|
+
*
|
46
|
+
* @param a0 smart contract deployer, owner, super admin
|
47
|
+
* @param target target OZ Ownable contract address or instance, optional
|
48
|
+
* @returns OwnableToAccessControlAdapter instance
|
49
|
+
*/
|
50
|
+
async function deploy_ownable_to_ac_adapter(a0, target) {
|
51
|
+
// deploy the target if required
|
52
|
+
if(!target) {
|
53
|
+
target = await deploy_usdt(a0);
|
54
|
+
}
|
55
|
+
// wrap the target into the Ownable if required
|
56
|
+
else if(!target.address) {
|
57
|
+
const Ownable = artifacts.require("contracts/AdapterFactory.sol:Ownable");
|
58
|
+
target = await Ownable.at(target);
|
59
|
+
}
|
60
|
+
|
61
|
+
// deploy adapter
|
62
|
+
const adapter = await deploy_no_deps_ownable_to_ac_adapter(a0, target);
|
63
|
+
|
64
|
+
// transfer ownership to the adapter
|
65
|
+
await target.transferOwnership(adapter.address, {from: a0});
|
66
|
+
|
67
|
+
// return both instances
|
68
|
+
return {target, adapter};
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Deploys OwnableToAccessControlAdapter
|
73
|
+
*
|
74
|
+
* @param a0 smart contract deployer, owner, super admin
|
75
|
+
* @param target target OZ Ownable contract address or instance, required
|
76
|
+
* @returns OwnableToAccessControlAdapter instance
|
77
|
+
*/
|
78
|
+
async function deploy_no_deps_ownable_to_ac_adapter(a0, target) {
|
79
|
+
// artifacts in use
|
80
|
+
const OwnableToAccessControlAdapter = artifacts.require("OwnableToAccessControlAdapter");
|
81
|
+
// deploy and return the deployd instance
|
82
|
+
return await OwnableToAccessControlAdapter.new(target.address || target, a0, {from: a0});
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Deploys the AdapterFactory
|
87
|
+
*
|
88
|
+
* @param a0 deployer address, optional
|
89
|
+
* @returns AdapterFactory instance
|
90
|
+
*/
|
91
|
+
async function deploy_adapter_factory(a0) {
|
92
|
+
// artifacts in use
|
93
|
+
const AdapterFactory = artifacts.require("AdapterFactory");
|
94
|
+
// deploy and return the deployd instance
|
95
|
+
return await AdapterFactory.new(a0? {from: a0}: undefined);
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Deploys OwnableToAccessControlAdapter via the AdapterFactory
|
100
|
+
* Deploys the AdapterFactory and target Ownable if required
|
101
|
+
*
|
102
|
+
* @param a0 deployer address, target owner, required
|
103
|
+
* @param factory AdapterFactory instance or address, optional
|
104
|
+
* @param target Ownable instance or address, optional
|
105
|
+
* @returns OwnableToAccessControlAdapter instance
|
106
|
+
*/
|
107
|
+
async function factory_deploy_ownable_to_ac_adapter(a0, factory, target) {
|
108
|
+
if(!factory) {
|
109
|
+
factory = await deploy_adapter_factory(a0);
|
110
|
+
}
|
111
|
+
else if(!factory.address) {
|
112
|
+
const AdapterFactory = artifacts.require("AdapterFactory");
|
113
|
+
factory = await AdapterFactory.at(factory);
|
114
|
+
}
|
115
|
+
|
116
|
+
if(!target) {
|
117
|
+
target = await deploy_usdt(a0);
|
118
|
+
}
|
119
|
+
else if(!target.address) {
|
120
|
+
const Ownable = artifacts.require("contracts/AdapterFactory.sol:Ownable");
|
121
|
+
target = await Ownable.at(target);
|
122
|
+
}
|
123
|
+
|
124
|
+
// deploy the adapter via the AdapterFactory
|
125
|
+
const receipt = await factory.deployNewOwnableToAccessControlAdapter(target.address, {from: a0});
|
126
|
+
const {
|
127
|
+
adapterAddress,
|
128
|
+
ownableTargetAddress,
|
129
|
+
} = receipt.logs.find(log => log.event === "NewOwnableToAccessControlAdapterDeployed").args;
|
130
|
+
|
131
|
+
// connect to the adapter
|
132
|
+
const OwnableToAccessControlAdapter = artifacts.require("OwnableToAccessControlAdapter");
|
133
|
+
const adapter = await OwnableToAccessControlAdapter.at(adapterAddress);
|
134
|
+
|
135
|
+
// transfer ownership to the adapter
|
136
|
+
await target.transferOwnership(adapter.address, {from: a0});
|
137
|
+
|
138
|
+
// return the results
|
139
|
+
return {factory, target, adapter};
|
140
|
+
}
|
141
|
+
|
142
|
+
// export public deployment API
|
143
|
+
module.exports = {
|
144
|
+
deploy_usdt,
|
145
|
+
deploy_access_control,
|
146
|
+
deploy_no_deps_ownable_to_ac_adapter,
|
147
|
+
deploy_ownable_to_ac_adapter,
|
148
|
+
deploy_adapter_factory,
|
149
|
+
factory_deploy_ownable_to_ac_adapter,
|
150
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
// copy and export all the features and roles constants from different contracts
|
2
|
+
|
3
|
+
// Auxiliary BN stuff
|
4
|
+
const BN = web3.utils.BN;
|
5
|
+
const TWO = new BN(2);
|
6
|
+
|
7
|
+
// Access manager is responsible for assigning the roles to users,
|
8
|
+
// enabling/disabling global features of the smart contract
|
9
|
+
const ROLE_ACCESS_MANAGER = TWO.pow(new BN(255));
|
10
|
+
|
11
|
+
// Upgrade manager is responsible for smart contract upgrades
|
12
|
+
const ROLE_UPGRADE_MANAGER = TWO.pow(new BN(254));
|
13
|
+
|
14
|
+
// Access Roles manager is responsible for assigning the access roles to functions
|
15
|
+
const ROLE_ACCESS_ROLES_MANAGER = TWO.pow(new BN(253));
|
16
|
+
|
17
|
+
// Bitmask representing all the possible permissions (super admin role)
|
18
|
+
const FULL_PRIVILEGES_MASK = TWO.pow(new BN(256)).subn(1);
|
19
|
+
|
20
|
+
// combine the role (permission set) provided
|
21
|
+
function or(...roles) {
|
22
|
+
let roles_sum = new BN(0);
|
23
|
+
for(let role of roles) {
|
24
|
+
roles_sum = roles_sum.or(new BN(role));
|
25
|
+
}
|
26
|
+
return roles_sum;
|
27
|
+
}
|
28
|
+
|
29
|
+
// negates the role (permission set) provided
|
30
|
+
function not(...roles) {
|
31
|
+
return FULL_PRIVILEGES_MASK.xor(or(...roles));
|
32
|
+
}
|
33
|
+
|
34
|
+
// export public module API
|
35
|
+
module.exports = {
|
36
|
+
ROLE_ACCESS_MANAGER,
|
37
|
+
ROLE_UPGRADE_MANAGER,
|
38
|
+
ROLE_ACCESS_ROLES_MANAGER,
|
39
|
+
FULL_PRIVILEGES_MASK,
|
40
|
+
or,
|
41
|
+
not,
|
42
|
+
};
|
@@ -0,0 +1,315 @@
|
|
1
|
+
// Zeppelin test helpers
|
2
|
+
const {
|
3
|
+
BN,
|
4
|
+
constants,
|
5
|
+
expectEvent,
|
6
|
+
expectRevert,
|
7
|
+
} = require("@openzeppelin/test-helpers");
|
8
|
+
const {
|
9
|
+
assert,
|
10
|
+
expect,
|
11
|
+
} = require("chai");
|
12
|
+
const {
|
13
|
+
ZERO_ADDRESS,
|
14
|
+
ZERO_BYTES32,
|
15
|
+
MAX_UINT256,
|
16
|
+
} = constants;
|
17
|
+
|
18
|
+
// BN utils
|
19
|
+
const {
|
20
|
+
random_bn255,
|
21
|
+
random_bn256,
|
22
|
+
} = require("@lazy-sol/a-missing-jem/bn_utils");
|
23
|
+
|
24
|
+
// RBAC core features and roles
|
25
|
+
const {
|
26
|
+
not,
|
27
|
+
ROLE_ACCESS_MANAGER, FULL_PRIVILEGES_MASK,
|
28
|
+
} = require("./features_roles");
|
29
|
+
|
30
|
+
/**
|
31
|
+
* RBAC core behaviour
|
32
|
+
*
|
33
|
+
* @param deployment_fn RBAC contract deployment function
|
34
|
+
* @param a0 deployer/admin account
|
35
|
+
* @param a1 participant 1
|
36
|
+
* @param a2 participant 2
|
37
|
+
*/
|
38
|
+
function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
39
|
+
// define the "players"
|
40
|
+
const by = a1;
|
41
|
+
const to = a2;
|
42
|
+
|
43
|
+
describe("deployment and initial state", function() {
|
44
|
+
function deploy_and_check(owner, features) {
|
45
|
+
let access_control;
|
46
|
+
beforeEach(async function() {
|
47
|
+
access_control = await deployment_fn.call(this, a0, owner, features);
|
48
|
+
});
|
49
|
+
it('"RoleUpdated(owner)" event is emitted correctly', async function() {
|
50
|
+
await expectEvent.inConstruction(access_control, "RoleUpdated", {
|
51
|
+
operator: owner,
|
52
|
+
requested: FULL_PRIVILEGES_MASK,
|
53
|
+
assigned: FULL_PRIVILEGES_MASK,
|
54
|
+
});
|
55
|
+
});
|
56
|
+
it('"RoleUpdated(this)" event is emitted correctly', async function() {
|
57
|
+
await expectEvent.inConstruction(access_control, "RoleUpdated", {
|
58
|
+
operator: access_control.address,
|
59
|
+
requested: features,
|
60
|
+
assigned: features,
|
61
|
+
});
|
62
|
+
});
|
63
|
+
it("owners' role is set correctly", async function() {
|
64
|
+
expect(await access_control.getRole(owner)).to.be.bignumber.that.equals(FULL_PRIVILEGES_MASK);
|
65
|
+
});
|
66
|
+
it("features are set correctly", async function() {
|
67
|
+
expect(await access_control.features()).to.be.bignumber.that.equals(features);
|
68
|
+
});
|
69
|
+
}
|
70
|
+
|
71
|
+
describe("owner = 0, features = 0", function() {
|
72
|
+
deploy_and_check(ZERO_ADDRESS, new BN(0));
|
73
|
+
});
|
74
|
+
describe("owner = 0, features ≠ 0", function() {
|
75
|
+
deploy_and_check(ZERO_ADDRESS, random_bn256());
|
76
|
+
});
|
77
|
+
describe("owner ≠ 0, features = 0", function() {
|
78
|
+
deploy_and_check(a1, new BN(0));
|
79
|
+
});
|
80
|
+
describe("owner ≠ 0, features ≠ 0", function() {
|
81
|
+
deploy_and_check(a1, random_bn256());
|
82
|
+
});
|
83
|
+
});
|
84
|
+
describe("when deployed with not initial features", function() {
|
85
|
+
let access_control;
|
86
|
+
beforeEach(async function() {
|
87
|
+
access_control = await deployment_fn.call(this, a0);
|
88
|
+
});
|
89
|
+
|
90
|
+
function test_suite(write_fn, read_fn, check_fn, to_fn) {
|
91
|
+
describe("when performed by ACCESS_MANAGER", function() {
|
92
|
+
beforeEach(async function() {
|
93
|
+
await access_control.updateRole(by, ROLE_ACCESS_MANAGER, {from: a0});
|
94
|
+
});
|
95
|
+
describe("when ACCESS_MANAGER has full set of permissions", function() {
|
96
|
+
beforeEach(async function() {
|
97
|
+
await access_control.updateRole(by, MAX_UINT256, {from: a0});
|
98
|
+
});
|
99
|
+
describe("what you set", function() {
|
100
|
+
let receipt, set;
|
101
|
+
beforeEach(async function() {
|
102
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
103
|
+
set = random_bn255();
|
104
|
+
receipt = await write_fn(by, to, set);
|
105
|
+
});
|
106
|
+
describe("is what you get", function() {
|
107
|
+
it('"userRoles" value', async function() {
|
108
|
+
expect(await read_fn(to)).to.be.bignumber.that.equals(set);
|
109
|
+
});
|
110
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
111
|
+
expect(await check_fn(to, set)).to.be.true;
|
112
|
+
});
|
113
|
+
it('"RoleUpdated" event', async function() {
|
114
|
+
expectEvent(receipt, "RoleUpdated", {
|
115
|
+
operator: to_fn(to),
|
116
|
+
requested: set,
|
117
|
+
assigned: set,
|
118
|
+
});
|
119
|
+
});
|
120
|
+
});
|
121
|
+
});
|
122
|
+
describe("what you remove", function() {
|
123
|
+
let receipt, remove;
|
124
|
+
beforeEach(async function() {
|
125
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
126
|
+
remove = random_bn255();
|
127
|
+
receipt = await write_fn(by, to, not(remove));
|
128
|
+
});
|
129
|
+
describe("is what gets removed", function() {
|
130
|
+
it('"userRoles" value', async function() {
|
131
|
+
expect(await read_fn(to)).to.be.bignumber.that.equals(not(remove));
|
132
|
+
});
|
133
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
134
|
+
expect(await check_fn(to, not(remove))).to.be.true;
|
135
|
+
});
|
136
|
+
it('"RoleUpdated" event', async function() {
|
137
|
+
expectEvent(receipt, "RoleUpdated", {
|
138
|
+
operator: to_fn(to),
|
139
|
+
requested: not(remove),
|
140
|
+
assigned: not(remove),
|
141
|
+
});
|
142
|
+
});
|
143
|
+
});
|
144
|
+
});
|
145
|
+
});
|
146
|
+
describe("when ACCESS_MANAGER doesn't have any permissions", function() {
|
147
|
+
describe("what you get, independently of what you set", function() {
|
148
|
+
let receipt, set;
|
149
|
+
beforeEach(async function() {
|
150
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
151
|
+
set = random_bn255();
|
152
|
+
receipt = await write_fn(by, to, set);
|
153
|
+
});
|
154
|
+
describe("is always zero", function() {
|
155
|
+
it('"userRoles" value', async function() {
|
156
|
+
expect(await read_fn(to)).to.be.bignumber.that.is.zero;
|
157
|
+
});
|
158
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
159
|
+
expect(await check_fn(to, set)).to.be.false;
|
160
|
+
});
|
161
|
+
it('"RoleUpdated" event', async function() {
|
162
|
+
expectEvent(receipt, "RoleUpdated", {
|
163
|
+
operator: to_fn(to),
|
164
|
+
requested: set,
|
165
|
+
assigned: "0",
|
166
|
+
});
|
167
|
+
});
|
168
|
+
});
|
169
|
+
});
|
170
|
+
describe("what you get, independently of what you remove", function() {
|
171
|
+
let receipt, remove;
|
172
|
+
beforeEach(async function() {
|
173
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
174
|
+
remove = random_bn255();
|
175
|
+
await write_fn(a0, to, MAX_UINT256);
|
176
|
+
receipt = await write_fn(by, to, not(remove));
|
177
|
+
});
|
178
|
+
describe("is always what you had", function() {
|
179
|
+
it('"userRoles" value', async function() {
|
180
|
+
expect(await read_fn(to)).to.be.bignumber.that.equals(MAX_UINT256);
|
181
|
+
});
|
182
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
183
|
+
expect(await check_fn(to, MAX_UINT256)).to.be.true;
|
184
|
+
});
|
185
|
+
it('"RoleUpdated" event', async function() {
|
186
|
+
expectEvent(receipt, "RoleUpdated", {
|
187
|
+
operator: to_fn(to),
|
188
|
+
requested: not(remove),
|
189
|
+
assigned: MAX_UINT256,
|
190
|
+
});
|
191
|
+
});
|
192
|
+
});
|
193
|
+
});
|
194
|
+
});
|
195
|
+
describe("when ACCESS_MANAGER has some permissions", function() {
|
196
|
+
let role;
|
197
|
+
beforeEach(async function() {
|
198
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
199
|
+
role = random_bn255();
|
200
|
+
await access_control.updateRole(by, ROLE_ACCESS_MANAGER.or(role), {from: a0});
|
201
|
+
});
|
202
|
+
describe("what you get", function() {
|
203
|
+
let receipt, set;
|
204
|
+
beforeEach(async function() {
|
205
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
206
|
+
set = random_bn255();
|
207
|
+
receipt = await write_fn(by, to, set);
|
208
|
+
});
|
209
|
+
describe("is an intersection of what you set and what you have", function() {
|
210
|
+
it('"userRoles" value', async function() {
|
211
|
+
expect(await read_fn(to)).to.be.bignumber.that.equals(role.and(set));
|
212
|
+
});
|
213
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
214
|
+
expect(await check_fn(to, role.and(set))).to.be.true;
|
215
|
+
});
|
216
|
+
it('"RoleUpdated" event', async function() {
|
217
|
+
expectEvent(receipt, "RoleUpdated", {
|
218
|
+
operator: to_fn(to),
|
219
|
+
requested: set,
|
220
|
+
assigned: role.and(set),
|
221
|
+
});
|
222
|
+
});
|
223
|
+
});
|
224
|
+
});
|
225
|
+
describe("what you remove", function() {
|
226
|
+
let receipt, remove;
|
227
|
+
beforeEach(async function() {
|
228
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
229
|
+
remove = random_bn255();
|
230
|
+
await write_fn(a0, to, MAX_UINT256);
|
231
|
+
receipt = await write_fn(by, to, not(remove));
|
232
|
+
});
|
233
|
+
describe("is an intersection of what you tried to remove and what you have", function() {
|
234
|
+
it('"userRoles" value', async function() {
|
235
|
+
expect(await read_fn(to)).to.be.bignumber.that.equals(not(role.and(remove)));
|
236
|
+
});
|
237
|
+
it("role check (isOperatorInRole/isFeatureEnabled)", async function() {
|
238
|
+
expect(await check_fn(to, not(role.and(remove)))).to.be.true;
|
239
|
+
});
|
240
|
+
it('"RoleUpdated" event', async function() {
|
241
|
+
expectEvent(receipt, "RoleUpdated", {
|
242
|
+
operator: to_fn(to),
|
243
|
+
requested: not(remove),
|
244
|
+
assigned: not(role.and(remove)),
|
245
|
+
});
|
246
|
+
});
|
247
|
+
});
|
248
|
+
});
|
249
|
+
});
|
250
|
+
describe("ACCESS_MANAGER updates itself", function() {
|
251
|
+
beforeEach(async function() {
|
252
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
253
|
+
const role = random_bn255();
|
254
|
+
await access_control.updateRole(by, ROLE_ACCESS_MANAGER.or(role), {from: a0});
|
255
|
+
});
|
256
|
+
it("and degrades to zero with the 99.99% probability in 14 runs", async function() {
|
257
|
+
// randomly remove 255 bits of permissions
|
258
|
+
for(let i = 0; i < 14; i++) {
|
259
|
+
// do not touch the highest permission bit (ACCESS_MANAGER permission)
|
260
|
+
const role = random_bn255();
|
261
|
+
await access_control.updateRole(by, not(role), {from: by});
|
262
|
+
}
|
263
|
+
// this may fail with the probability 2^(-14) < 0.01%
|
264
|
+
expect(await access_control.getRole(by)).to.be.bignumber.that.equals(ROLE_ACCESS_MANAGER);
|
265
|
+
})
|
266
|
+
});
|
267
|
+
describe("when ACCESS_MANAGER grants ACCESS_MANAGER permission", function() {
|
268
|
+
beforeEach(async function() {
|
269
|
+
await access_control.updateRole(to, ROLE_ACCESS_MANAGER, {from: by});
|
270
|
+
});
|
271
|
+
it("operator becomes an ACCESS_MANAGER", async function() {
|
272
|
+
expect(await access_control.isOperatorInRole(to, ROLE_ACCESS_MANAGER), "operator").to.be.true;
|
273
|
+
expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: to}), "sender").to.be.true;
|
274
|
+
});
|
275
|
+
});
|
276
|
+
describe("when ACCESS_MANAGER revokes ACCESS_MANAGER permission from itself", function() {
|
277
|
+
beforeEach(async function() {
|
278
|
+
await access_control.updateRole(by, 0, {from: by});
|
279
|
+
});
|
280
|
+
it("operator ceases to be an ACCESS_MANAGER", async function() {
|
281
|
+
expect(await access_control.isOperatorInRole(by, ROLE_ACCESS_MANAGER), "operator").to.be.false;
|
282
|
+
expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: by}), "sender").to.be.false;
|
283
|
+
});
|
284
|
+
});
|
285
|
+
});
|
286
|
+
describe("otherwise (no ACCESS_MANAGER permission)", function() {
|
287
|
+
it("updateFeatures reverts", async function() {
|
288
|
+
await expectRevert(access_control.updateFeatures(1, {from: by}), "access denied");
|
289
|
+
});
|
290
|
+
it("updateRole reverts", async function() {
|
291
|
+
await expectRevert(access_control.updateRole(to, 1, {from: by}), "access denied");
|
292
|
+
});
|
293
|
+
});
|
294
|
+
}
|
295
|
+
|
296
|
+
// run two test suites to test get/set role and get/set features
|
297
|
+
test_suite(
|
298
|
+
async(by, to, set) => await access_control.updateRole(to, set, {from: by}),
|
299
|
+
async(op) => await access_control.getRole(op),
|
300
|
+
async(op, role) => await access_control.isOperatorInRole(op, role),
|
301
|
+
(to) => to
|
302
|
+
);
|
303
|
+
test_suite(
|
304
|
+
async(by, to, set) => await access_control.updateFeatures(set, {from: by}),
|
305
|
+
async(op) => await access_control.features(),
|
306
|
+
async(op, role) => await access_control.isFeatureEnabled(role),
|
307
|
+
(to) => access_control.address
|
308
|
+
);
|
309
|
+
});
|
310
|
+
}
|
311
|
+
|
312
|
+
// export the RBAC core behaviour
|
313
|
+
module.exports = {
|
314
|
+
behavesLikeRBAC,
|
315
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
// OwnableToAccessControlAdapter tests
|
2
|
+
|
3
|
+
// Zeppelin test helpers
|
4
|
+
const {
|
5
|
+
BN,
|
6
|
+
constants,
|
7
|
+
expectEvent,
|
8
|
+
expectRevert,
|
9
|
+
} = require("@openzeppelin/test-helpers");
|
10
|
+
const {
|
11
|
+
assert,
|
12
|
+
expect,
|
13
|
+
} = require("chai");
|
14
|
+
const {
|
15
|
+
ZERO_ADDRESS,
|
16
|
+
ZERO_BYTES32,
|
17
|
+
MAX_UINT256,
|
18
|
+
} = constants;
|
19
|
+
|
20
|
+
// deployment routines in use
|
21
|
+
const {
|
22
|
+
deploy_ownable_to_ac_adapter,
|
23
|
+
deploy_no_deps_ownable_to_ac_adapter,
|
24
|
+
} = require("./include/deployment_routines");
|
25
|
+
|
26
|
+
// run OwnableToAccessControlAdapter tests
|
27
|
+
contract("OwnableToAccessControlAdapter tests", function(accounts) {
|
28
|
+
// extract accounts to be used:
|
29
|
+
// A0 – special default zero account accounts[0] used by Truffle, reserved
|
30
|
+
// a0 – deployment account having all the permissions, reserved
|
31
|
+
// H0 – initial token holder account
|
32
|
+
// a1, a2,... – working accounts to perform tests on
|
33
|
+
const [A0, a0, H0, a1, a2, a3] = accounts;
|
34
|
+
|
35
|
+
it("Adapter won't deploy targeting to a zero address", async function() {
|
36
|
+
await expectRevert(deploy_no_deps_ownable_to_ac_adapter(a0, ZERO_ADDRESS), "zero address");
|
37
|
+
});
|
38
|
+
describe("after the Adapter is deployed and target Ownable ownership transferred to the Adapter", function() {
|
39
|
+
let target, adapter;
|
40
|
+
beforeEach(async function() {
|
41
|
+
({target, adapter} = await deploy_ownable_to_ac_adapter(a0));
|
42
|
+
});
|
43
|
+
|
44
|
+
it("it is impossible to send ether to the non-payable target contract via the adapter", async function() {
|
45
|
+
await expectRevert(web3.eth.sendTransaction({
|
46
|
+
from: a0,
|
47
|
+
to: adapter.address,
|
48
|
+
value: 1_000_000_000, // 1 gwei
|
49
|
+
}), "execution failed");
|
50
|
+
});
|
51
|
+
it("it is impossible to execute transferOwnership function before its access role is configured", async function() {
|
52
|
+
await expectRevert(web3.eth.sendTransaction({
|
53
|
+
from: a0,
|
54
|
+
to: adapter.address,
|
55
|
+
data: target.contract.methods.transferOwnership(a2).encodeABI(),
|
56
|
+
}), "access role not set");
|
57
|
+
});
|
58
|
+
describe("once transferOwnership function access control is configured", function() {
|
59
|
+
const ROLE_OWNERSHIP_MANAGER = 0x00010000;
|
60
|
+
let receipt;
|
61
|
+
beforeEach(async function() {
|
62
|
+
receipt = await adapter.updateAccessRole("transferOwnership(address)", ROLE_OWNERSHIP_MANAGER, {from: a0});
|
63
|
+
});
|
64
|
+
it('"AccessRoleUpdated" event is emitted', async function() {
|
65
|
+
expectEvent(receipt, "AccessRoleUpdated", {
|
66
|
+
selector: web3.eth.abi.encodeFunctionSignature("transferOwnership(address)"),
|
67
|
+
role: "" + ROLE_OWNERSHIP_MANAGER,
|
68
|
+
});
|
69
|
+
});
|
70
|
+
it("it is impossible to execute transferOwnership function from unauthorized account", async function() {
|
71
|
+
await expectRevert(web3.eth.sendTransaction({
|
72
|
+
from: a1,
|
73
|
+
to: adapter.address,
|
74
|
+
data: target.contract.methods.transferOwnership(a2).encodeABI(),
|
75
|
+
}), "access denied");
|
76
|
+
});
|
77
|
+
describe("once an account has authorization to execute transferOwnership function", function() {
|
78
|
+
beforeEach(async function() {
|
79
|
+
await adapter.updateRole(a1, ROLE_OWNERSHIP_MANAGER, {from: a0});
|
80
|
+
});
|
81
|
+
it("execution of the transferOwnership function succeeds", async function() {
|
82
|
+
await web3.eth.sendTransaction({
|
83
|
+
from: a1,
|
84
|
+
to: adapter.address,
|
85
|
+
data: target.contract.methods.transferOwnership(a2).encodeABI(),
|
86
|
+
});
|
87
|
+
expect(await target.owner(), "wrong owner after ownership transfer").to.equal(a2);
|
88
|
+
});
|
89
|
+
it("execution of the transferOwnership non-payable function fails if ether is supplied", async function() {
|
90
|
+
await expectRevert(web3.eth.sendTransaction({
|
91
|
+
from: a1,
|
92
|
+
to: adapter.address,
|
93
|
+
data: target.contract.methods.transferOwnership(a2).encodeABI(),
|
94
|
+
value: 1_000_000_000, // 1 gwei
|
95
|
+
}), "execution failed");
|
96
|
+
});
|
97
|
+
});
|
98
|
+
});
|
99
|
+
});
|
100
|
+
});
|
@@ -0,0 +1,57 @@
|
|
1
|
+
// OwnableToAccessControlAdapter: RBAC tests
|
2
|
+
|
3
|
+
// Zeppelin test helpers
|
4
|
+
const {
|
5
|
+
BN,
|
6
|
+
constants,
|
7
|
+
expectEvent,
|
8
|
+
expectRevert,
|
9
|
+
} = require("@openzeppelin/test-helpers");
|
10
|
+
const {
|
11
|
+
assert,
|
12
|
+
expect,
|
13
|
+
} = require("chai");
|
14
|
+
const {
|
15
|
+
ZERO_ADDRESS,
|
16
|
+
ZERO_BYTES32,
|
17
|
+
MAX_UINT256,
|
18
|
+
} = constants;
|
19
|
+
|
20
|
+
// RBAC core features and roles
|
21
|
+
const {
|
22
|
+
not,
|
23
|
+
ROLE_ACCESS_ROLES_MANAGER,
|
24
|
+
} = require("./include/features_roles");
|
25
|
+
|
26
|
+
// deployment routines in use
|
27
|
+
const {
|
28
|
+
deploy_ownable_to_ac_adapter,
|
29
|
+
} = require("./include/deployment_routines");
|
30
|
+
|
31
|
+
// run OwnableToAccessControlAdapter: RBAC tests
|
32
|
+
contract("OwnableToAccessControlAdapter: RBAC tests", function(accounts) {
|
33
|
+
// extract accounts to be used:
|
34
|
+
// A0 – special default zero account accounts[0] used by Truffle, reserved
|
35
|
+
// a0 – deployment account having all the permissions, reserved
|
36
|
+
// H0 – initial token holder account
|
37
|
+
// a1, a2,... – working accounts to perform tests on
|
38
|
+
const [A0, a0, H0, a1, a2, a3] = accounts;
|
39
|
+
|
40
|
+
describe("after the Adapter is deployed and target Ownable ownership transferred to the Adapter", function() {
|
41
|
+
let target, adapter;
|
42
|
+
beforeEach(async function() {
|
43
|
+
({target, adapter} = await deploy_ownable_to_ac_adapter(a0));
|
44
|
+
});
|
45
|
+
|
46
|
+
it("it is impossible to configure the access role from unauthorized account", async function() {
|
47
|
+
const operator = a1;
|
48
|
+
await adapter.updateRole(operator, not(ROLE_ACCESS_ROLES_MANAGER), {from: a0});
|
49
|
+
await expectRevert(adapter.updateAccessRole("1", 1, {from: operator}), "access denied");
|
50
|
+
});
|
51
|
+
it("it is possible to configure the access role from authorized account", async function() {
|
52
|
+
const operator = a1;
|
53
|
+
await adapter.updateRole(operator, ROLE_ACCESS_ROLES_MANAGER, {from: a0});
|
54
|
+
adapter.updateAccessRole("1", 1, {from: operator});
|
55
|
+
});
|
56
|
+
});
|
57
|
+
});
|
@@ -0,0 +1,24 @@
|
|
1
|
+
// AccessControl (RBAC) Core Tests
|
2
|
+
|
3
|
+
// import the core RBAC behaviour to use
|
4
|
+
const {
|
5
|
+
behavesLikeRBAC,
|
6
|
+
} = require("./include/rbac.behaviour");
|
7
|
+
|
8
|
+
// deployment routines in use
|
9
|
+
const {
|
10
|
+
deploy_access_control,
|
11
|
+
} = require("./include/deployment_routines");
|
12
|
+
|
13
|
+
// run AccessControl (RBAC) tests
|
14
|
+
contract("AccessControl (RBAC) Core tests", function(accounts) {
|
15
|
+
// extract accounts to be used:
|
16
|
+
// A0 – special default zero account accounts[0] used by Truffle, reserved
|
17
|
+
// a0 – deployment account having all the permissions, reserved
|
18
|
+
// H0 – initial token holder account
|
19
|
+
// a1, a2,... – working accounts to perform tests on
|
20
|
+
const [A0, a0, H0, a1, a2, a3] = accounts;
|
21
|
+
|
22
|
+
// run the core RBACs behaviour test
|
23
|
+
behavesLikeRBAC(deploy_access_control, a0, a1, a2);
|
24
|
+
});
|