@lazy-sol/access-control 1.0.6 → 1.1.1
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/CHANGELOG.md +43 -0
- package/README.md +69 -48
- package/artifacts/contracts/OwnableToAccessControlAdapter.sol/OwnableToAccessControlAdapter.json +7 -64
- package/contracts/AccessControl.sol +15 -219
- package/contracts/AccessControlCore.sol +353 -0
- package/contracts/AdapterFactory.sol +1 -1
- package/contracts/OwnableToAccessControlAdapter.sol +7 -5
- package/contracts/mocks/AccessControlMock.sol +7 -1
- package/hardhat.config.js +6 -6
- package/index.js +4 -0
- package/package.json +12 -10
- package/test/include/deployment_routines.js +3 -12
- package/test/include/rbac.behaviour.js +22 -7
- package/test/ownable_to_rbac_adapter.js +1 -1
- package/test/ownable_to_rbac_adapter_rbac.js +3 -3
- package/test/rbac_modifier.js +1 -1
- package/ui.html +506 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
2
|
-
pragma solidity >=0.4
|
2
|
+
pragma solidity >=0.8.4;
|
3
3
|
|
4
4
|
import "../AccessControl.sol";
|
5
5
|
|
@@ -11,4 +11,10 @@ contract AccessControlMock is AccessControl {
|
|
11
11
|
function restricted() public restrictedTo(RESTRICTED_ROLE) {
|
12
12
|
emit Restricted();
|
13
13
|
}
|
14
|
+
function requireSenderInRole(uint256 required) public view {
|
15
|
+
_requireSenderInRole(required);
|
16
|
+
}
|
17
|
+
function requireAccessCondition(bool condition) public pure {
|
18
|
+
_requireAccessCondition(condition);
|
19
|
+
}
|
14
20
|
}
|
package/hardhat.config.js
CHANGED
@@ -453,12 +453,12 @@ function get_endpoint_url(network_name) {
|
|
453
453
|
// create a key: https://www.alchemy.com/
|
454
454
|
if(process.env.ALCHEMY_KEY) {
|
455
455
|
switch(network_name) {
|
456
|
-
case "mainnet": return "https://eth-mainnet.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
457
|
-
case "ropsten": return "https://eth-ropsten.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
458
|
-
case "rinkeby": return "https://eth-rinkeby.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
459
|
-
case "kovan": return "https://eth-kovan.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
460
|
-
case "goerli": return "https://eth-goerli.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
461
|
-
case "sepolia": return "https://eth-sepolia.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
456
|
+
case "mainnet": return "https://eth-mainnet.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
457
|
+
case "ropsten": return "https://eth-ropsten.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
458
|
+
case "rinkeby": return "https://eth-rinkeby.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
459
|
+
case "kovan": return "https://eth-kovan.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
460
|
+
case "goerli": return "https://eth-goerli.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
461
|
+
case "sepolia": return "https://eth-sepolia.g.alchemyapi.io/v2/" + process.env.ALCHEMY_KEY;
|
462
462
|
case "polygon": return "https://polygon-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
|
463
463
|
case "mumbai": return "https://polygon-mumbai.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
|
464
464
|
case "base_mainnet": return "https://base-mainnet.g.alchemy.com/v2/" + process.env.ALCHEMY_KEY;
|
package/index.js
CHANGED
@@ -9,6 +9,9 @@ const {
|
|
9
9
|
not,
|
10
10
|
} = require("./scripts/include/features_roles");
|
11
11
|
|
12
|
+
// RBAC behaviours
|
13
|
+
const {behavesLikeRBAC} = require("./test/include/rbac.behaviour");
|
14
|
+
|
12
15
|
// Re-export the functions
|
13
16
|
module.exports = {
|
14
17
|
ROLE_ACCESS_MANAGER,
|
@@ -16,4 +19,5 @@ module.exports = {
|
|
16
19
|
FULL_PRIVILEGES_MASK,
|
17
20
|
or,
|
18
21
|
not,
|
22
|
+
behavesLikeRBAC,
|
19
23
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lazy-sol/access-control",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.1.1",
|
4
4
|
"description": "Enable the modular plug and play (PnP) architecture for your dapp by incorporating the role-based access control (RBAC) into the smart contracts",
|
5
5
|
"main": "index.js",
|
6
6
|
"scripts": {
|
@@ -21,19 +21,21 @@
|
|
21
21
|
],
|
22
22
|
"author": "Basil Gorin",
|
23
23
|
"license": "MIT",
|
24
|
-
"dependencies": {
|
25
|
-
"@lazy-sol/a-missing-gem": "^1.0.9",
|
26
|
-
"@nomiclabs/hardhat-truffle5": "^2.0.7",
|
27
|
-
"hardhat": "^2.22.6",
|
28
|
-
"hardhat-deploy": "^0.11.45"
|
29
|
-
},
|
30
24
|
"devDependencies": {
|
31
|
-
"@lazy-sol/
|
25
|
+
"@lazy-sol/a-missing-gem": "^1.0.10",
|
26
|
+
"@lazy-sol/zeppelin-test-helpers": "^1.0.5",
|
27
|
+
"@nomiclabs/hardhat-truffle5": "^2.0.7",
|
28
|
+
"hardhat": "^2.22.13",
|
29
|
+
"hardhat-deploy": "^0.11.45",
|
32
30
|
"hardhat-gas-reporter": "^1.0.10",
|
33
|
-
"solidity-coverage": "^0.8.
|
31
|
+
"solidity-coverage": "^0.8.13"
|
34
32
|
},
|
35
33
|
"overrides": {
|
34
|
+
"axios": ">=1.7.5",
|
35
|
+
"micromatch": "^4.0.8",
|
36
|
+
"tar": "^6.2.1",
|
36
37
|
"tough-cookie": "^4.1.3",
|
37
|
-
"yargs-parser": "^5.0.1"
|
38
|
+
"yargs-parser": "^5.0.1",
|
39
|
+
"ws": "^8.0.0"
|
38
40
|
}
|
39
41
|
}
|
@@ -3,23 +3,14 @@
|
|
3
3
|
* (doesn't return any value on successful operation)
|
4
4
|
*
|
5
5
|
* @param a0 smart contract owner
|
6
|
-
* @param H0 initial token holder address
|
7
6
|
* @returns USDT ERC20 instance
|
8
7
|
*/
|
9
|
-
async function deploy_usdt(a0
|
8
|
+
async function deploy_usdt(a0) {
|
10
9
|
// smart contracts required
|
11
10
|
const USDTContract = artifacts.require("TetherToken");
|
12
11
|
|
13
|
-
// deploy the token
|
14
|
-
|
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;
|
12
|
+
// deploy the token and return the reference
|
13
|
+
return await USDTContract.new(0, "Tether USD", "USDT", 6, {from: a0});
|
23
14
|
}
|
24
15
|
|
25
16
|
/**
|
@@ -15,16 +15,17 @@ const {
|
|
15
15
|
MAX_UINT256,
|
16
16
|
} = constants;
|
17
17
|
|
18
|
-
// BN
|
18
|
+
// BN constants and utilities
|
19
19
|
const {
|
20
20
|
random_bn255,
|
21
21
|
random_bn256,
|
22
|
-
} = require("@lazy-sol/a-missing-gem
|
22
|
+
} = require("@lazy-sol/a-missing-gem");
|
23
23
|
|
24
24
|
// RBAC core features and roles
|
25
25
|
const {
|
26
26
|
not,
|
27
|
-
ROLE_ACCESS_MANAGER,
|
27
|
+
ROLE_ACCESS_MANAGER,
|
28
|
+
FULL_PRIVILEGES_MASK,
|
28
29
|
} = require("../../scripts/include/features_roles");
|
29
30
|
|
30
31
|
/**
|
@@ -40,6 +41,18 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
40
41
|
const by = a1;
|
41
42
|
const to = a2;
|
42
43
|
|
44
|
+
describe("requireAccessCondition(condition) pure function", function() {
|
45
|
+
let access_control;
|
46
|
+
beforeEach(async function() {
|
47
|
+
access_control = await deployment_fn.call(this, a0, ZERO_ADDRESS, 0);
|
48
|
+
});
|
49
|
+
it("throws if condition is false", async function() {
|
50
|
+
await expectRevert(access_control.requireAccessCondition(false, {from: a0}), "AccessDenied()");
|
51
|
+
});
|
52
|
+
it("succeeds if condition is true", async function() {
|
53
|
+
await access_control.requireAccessCondition(true, {from: a0});
|
54
|
+
});
|
55
|
+
});
|
43
56
|
describe("deployment and initial state", function() {
|
44
57
|
function deploy_and_check(owner, features) {
|
45
58
|
let access_control;
|
@@ -81,7 +94,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
81
94
|
deploy_and_check(a1, random_bn256());
|
82
95
|
});
|
83
96
|
});
|
84
|
-
describe("when deployed with
|
97
|
+
describe("when deployed with no initial features", function() {
|
85
98
|
let access_control;
|
86
99
|
beforeEach(async function() {
|
87
100
|
access_control = await deployment_fn.call(this, a0);
|
@@ -92,7 +105,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
92
105
|
beforeEach(async function() {
|
93
106
|
await access_control.updateRole(by, ROLE_ACCESS_MANAGER, {from: a0});
|
94
107
|
});
|
95
|
-
describe("when ACCESS_MANAGER has full set of permissions", function() {
|
108
|
+
describe("when ACCESS_MANAGER has the full set of permissions", function() {
|
96
109
|
beforeEach(async function() {
|
97
110
|
await access_control.updateRole(by, MAX_UINT256, {from: a0});
|
98
111
|
});
|
@@ -271,6 +284,7 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
271
284
|
it("operator becomes an ACCESS_MANAGER", async function() {
|
272
285
|
expect(await access_control.isOperatorInRole(to, ROLE_ACCESS_MANAGER), "operator").to.be.true;
|
273
286
|
expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: to}), "sender").to.be.true;
|
287
|
+
await access_control.requireSenderInRole(ROLE_ACCESS_MANAGER, {from: to});
|
274
288
|
});
|
275
289
|
});
|
276
290
|
describe("when ACCESS_MANAGER revokes ACCESS_MANAGER permission from itself", function() {
|
@@ -280,15 +294,16 @@ function behavesLikeRBAC(deployment_fn, a0, a1, a2) {
|
|
280
294
|
it("operator ceases to be an ACCESS_MANAGER", async function() {
|
281
295
|
expect(await access_control.isOperatorInRole(by, ROLE_ACCESS_MANAGER), "operator").to.be.false;
|
282
296
|
expect(await access_control.isSenderInRole(ROLE_ACCESS_MANAGER, {from: by}), "sender").to.be.false;
|
297
|
+
await expectRevert(access_control.requireSenderInRole(ROLE_ACCESS_MANAGER, {from: to}), "AccessDenied()");
|
283
298
|
});
|
284
299
|
});
|
285
300
|
});
|
286
301
|
describe("otherwise (no ACCESS_MANAGER permission)", function() {
|
287
302
|
it("updateFeatures reverts", async function() {
|
288
|
-
await expectRevert(access_control.updateFeatures(1, {from: by}), "
|
303
|
+
await expectRevert(access_control.updateFeatures(1, {from: by}), "AccessDenied()");
|
289
304
|
});
|
290
305
|
it("updateRole reverts", async function() {
|
291
|
-
await expectRevert(access_control.updateRole(to, 1, {from: by}), "
|
306
|
+
await expectRevert(access_control.updateRole(to, 1, {from: by}), "AccessDenied()");
|
292
307
|
});
|
293
308
|
});
|
294
309
|
}
|
@@ -72,7 +72,7 @@ contract("OwnableToAccessControlAdapter tests", function(accounts) {
|
|
72
72
|
from: a1,
|
73
73
|
to: adapter.address,
|
74
74
|
data: target.contract.methods.transferOwnership(a2).encodeABI(),
|
75
|
-
}), "
|
75
|
+
}), "AccessDenied()");
|
76
76
|
});
|
77
77
|
describe("once an account has authorization to execute transferOwnership function", function() {
|
78
78
|
beforeEach(async function() {
|
@@ -43,12 +43,12 @@ contract("OwnableToAccessControlAdapter: RBAC tests", function(accounts) {
|
|
43
43
|
({target, adapter} = await deploy_ownable_to_ac_adapter(a0));
|
44
44
|
});
|
45
45
|
|
46
|
-
it("it is impossible to configure the access role from unauthorized account", async function() {
|
46
|
+
it("it is impossible to configure the access role from an unauthorized account", async function() {
|
47
47
|
const operator = a1;
|
48
48
|
await adapter.updateRole(operator, not(ROLE_ACCESS_ROLES_MANAGER), {from: a0});
|
49
|
-
await expectRevert(adapter.updateAccessRole("1", 1, {from: operator}), "
|
49
|
+
await expectRevert(adapter.updateAccessRole("1", 1, {from: operator}), "AccessDenied()");
|
50
50
|
});
|
51
|
-
it("it is possible to configure the access role from authorized account", async function() {
|
51
|
+
it("it is possible to configure the access role from the authorized account", async function() {
|
52
52
|
const operator = a1;
|
53
53
|
await adapter.updateRole(operator, ROLE_ACCESS_ROLES_MANAGER, {from: a0});
|
54
54
|
adapter.updateAccessRole("1", 1, {from: operator});
|
package/test/rbac_modifier.js
CHANGED
@@ -38,7 +38,7 @@ contract('AccessControl (RBAC) "restrictedTo" Modifier tests', function(accounts
|
|
38
38
|
access_control = await deploy_access_control(a0);
|
39
39
|
});
|
40
40
|
it("function protected with restrictedTo modifier fails when run not by an admin", async function() {
|
41
|
-
await expectRevert(access_control.restricted({from: a1}), "
|
41
|
+
await expectRevert(access_control.restricted({from: a1}), "AccessDenied()");
|
42
42
|
});
|
43
43
|
describe("function protected with restrictedTo modifier succeeds when run by admin", async function() {
|
44
44
|
let receipt;
|