diff --git a/README.md b/README.md index f866b881e0c..887083c2e92 100644 --- a/README.md +++ b/README.md @@ -69,8 +69,7 @@ contract MyContract is Ownable { ## Architecture The following provides visibility into how OpenZeppelin's contracts are organized: -- **access** - Smart contracts that enable functionality that can be used for selective restrictions and basic authorization control functions. Includes address whitelisting and signature-based permissions management. - - **rbac** - A library used to manage addresses assigned to different user roles and an example Role-Based Access Control (RBAC) interface that demonstrates how to handle setters and getters for roles and addresses. +- **access** - Smart contracts that enable functionality that can be used for selective restrictions and basic authorization control functions. - **crowdsale** - A collection of smart contracts used to manage token crowdsales that allow investors to purchase tokens with ETH. Includes a base contract which implements fundamental crowdsale functionality in its simplest form. The base contract can be extended in order to satisfy your crowdsale’s specific requirements. - **distribution** - Includes extensions of the base crowdsale contract which can be used to customize the completion of a crowdsale. - **emission** - Includes extensions of the base crowdsale contract which can be used to mint and manage how tokens are issued to purchasers. diff --git a/contracts/access/rbac/Roles.sol b/contracts/access/Roles.sol similarity index 90% rename from contracts/access/rbac/Roles.sol rename to contracts/access/Roles.sol index e3d9f867bf1..f5523a3e1b1 100644 --- a/contracts/access/rbac/Roles.sol +++ b/contracts/access/Roles.sol @@ -3,9 +3,7 @@ pragma solidity ^0.4.24; /** * @title Roles - * @author Francisco Giordano (@frangio) * @dev Library for managing addresses assigned to a Role. - * See RBAC.sol for example usage. */ library Roles { struct Role { diff --git a/contracts/access/rbac/RBAC.sol b/contracts/access/rbac/RBAC.sol deleted file mode 100644 index 70e2f9bca28..00000000000 --- a/contracts/access/rbac/RBAC.sol +++ /dev/null @@ -1,106 +0,0 @@ -pragma solidity ^0.4.24; - -import "./Roles.sol"; - - -/** - * @title RBAC (Role-Based Access Control) - * @author Matt Condon (@Shrugs) - * @dev Stores and provides setters and getters for roles and addresses. - * Supports unlimited numbers of roles and addresses. - * See //contracts/mocks/RBACMock.sol for an example of usage. - * This RBAC method uses strings to key roles. It may be beneficial - * for you to write your own implementation of this interface using Enums or similar. - */ -contract RBAC { - using Roles for Roles.Role; - - mapping (string => Roles.Role) private roles; - - event RoleAdded(address indexed operator, string role); - event RoleRemoved(address indexed operator, string role); - - /** - * @dev reverts if addr does not have role - * @param _operator address - * @param _role the name of the role - * // reverts - */ - function checkRole(address _operator, string _role) - public - view - { - require(roles[_role].has(_operator)); - } - - /** - * @dev determine if addr has role - * @param _operator address - * @param _role the name of the role - * @return bool - */ - function hasRole(address _operator, string _role) - public - view - returns (bool) - { - return roles[_role].has(_operator); - } - - /** - * @dev add a role to an address - * @param _operator address - * @param _role the name of the role - */ - function _addRole(address _operator, string _role) - internal - { - roles[_role].add(_operator); - emit RoleAdded(_operator, _role); - } - - /** - * @dev remove a role from an address - * @param _operator address - * @param _role the name of the role - */ - function _removeRole(address _operator, string _role) - internal - { - roles[_role].remove(_operator); - emit RoleRemoved(_operator, _role); - } - - /** - * @dev modifier to scope access to a single role (uses msg.sender as addr) - * @param _role the name of the role - * // reverts - */ - modifier onlyRole(string _role) - { - checkRole(msg.sender, _role); - _; - } - - /** - * @dev modifier to scope access to a set of roles (uses msg.sender as addr) - * @param _roles the names of the roles to scope access to - * // reverts - * - * @TODO - when solidity supports dynamic arrays as arguments to modifiers, provide this - * see: https://github.com/ethereum/solidity/issues/2467 - */ - // modifier onlyRoles(string[] _roles) { - // bool hasAnyRole = false; - // for (uint8 i = 0; i < _roles.length; i++) { - // if (hasRole(msg.sender, _roles[i])) { - // hasAnyRole = true; - // break; - // } - // } - - // require(hasAnyRole); - - // _; - // } -} diff --git a/contracts/access/rbac/CapperRole.sol b/contracts/access/roles/CapperRole.sol similarity index 96% rename from contracts/access/rbac/CapperRole.sol rename to contracts/access/roles/CapperRole.sol index d73ebc97a4a..bfa28712c8e 100644 --- a/contracts/access/rbac/CapperRole.sol +++ b/contracts/access/roles/CapperRole.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "./Roles.sol"; +import "../Roles.sol"; contract CapperRole { diff --git a/contracts/access/rbac/MinterRole.sol b/contracts/access/roles/MinterRole.sol similarity index 96% rename from contracts/access/rbac/MinterRole.sol rename to contracts/access/roles/MinterRole.sol index 7fd7d8decaf..4e21b40472e 100644 --- a/contracts/access/rbac/MinterRole.sol +++ b/contracts/access/roles/MinterRole.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "./Roles.sol"; +import "../Roles.sol"; contract MinterRole { diff --git a/contracts/access/rbac/PauserRole.sol b/contracts/access/roles/PauserRole.sol similarity index 96% rename from contracts/access/rbac/PauserRole.sol rename to contracts/access/roles/PauserRole.sol index ec0802d9c68..ed7fb2727a2 100644 --- a/contracts/access/rbac/PauserRole.sol +++ b/contracts/access/roles/PauserRole.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "./Roles.sol"; +import "../Roles.sol"; contract PauserRole { diff --git a/contracts/access/roles/SignerRole.sol b/contracts/access/roles/SignerRole.sol new file mode 100644 index 00000000000..5c01a611eec --- /dev/null +++ b/contracts/access/roles/SignerRole.sol @@ -0,0 +1,35 @@ +pragma solidity ^0.4.24; + +import "../Roles.sol"; + + +contract SignerRole { + using Roles for Roles.Role; + + Roles.Role private signers; + + constructor() public { + signers.add(msg.sender); + } + + modifier onlySigner() { + require(isSigner(msg.sender)); + _; + } + + function isSigner(address _account) public view returns (bool) { + return signers.has(_account); + } + + function addSigner(address _account) public onlySigner { + signers.add(_account); + } + + function renounceSigner() public { + signers.remove(msg.sender); + } + + function _removeSigner(address _account) internal { + signers.remove(_account); + } +} diff --git a/contracts/crowdsale/validation/IndividuallyCappedCrowdsale.sol b/contracts/crowdsale/validation/IndividuallyCappedCrowdsale.sol index e3375d565da..8a65f66bfd2 100644 --- a/contracts/crowdsale/validation/IndividuallyCappedCrowdsale.sol +++ b/contracts/crowdsale/validation/IndividuallyCappedCrowdsale.sol @@ -2,7 +2,7 @@ pragma solidity ^0.4.24; import "../../math/SafeMath.sol"; import "../Crowdsale.sol"; -import "../../access/rbac/CapperRole.sol"; +import "../../access/roles/CapperRole.sol"; /** diff --git a/contracts/drafts/SignatureBouncer.sol b/contracts/drafts/SignatureBouncer.sol index 0b91dcdb3fb..024cfd75c46 100644 --- a/contracts/drafts/SignatureBouncer.sol +++ b/contracts/drafts/SignatureBouncer.sol @@ -1,27 +1,26 @@ pragma solidity ^0.4.24; -import "../ownership/Ownable.sol"; -import "../access/rbac/RBAC.sol"; +import "../access/roles/SignerRole.sol"; import "../cryptography/ECDSA.sol"; /** * @title SignatureBouncer * @author PhABC, Shrugs and aflesher - * @dev Bouncer allows users to submit a signature as a permission to do an action. - * If the signature is from one of the authorized bouncer addresses, the signature - * is valid. The owner of the contract adds/removes bouncers. - * Bouncer addresses can be individual servers signing grants or different + * @dev SignatureBouncer allows users to submit a signature as a permission to do an action. + * If the signature is from one of the authorized signer addresses, the signature + * is valid. + * Signer addresses can be individual servers signing grants or different * users within a decentralized club that have permission to invite other members. * This technique is useful for whitelists and airdrops; instead of putting all * valid addresses on-chain, simply sign a grant of the form - * keccak256(abi.encodePacked(`:contractAddress` + `:granteeAddress`)) using a valid bouncer address. + * keccak256(abi.encodePacked(`:contractAddress` + `:granteeAddress`)) using a valid signer address. * Then restrict access to your crowdsale/whitelist/airdrop using the * `onlyValidSignature` modifier (or implement your own using _isValidSignature). * In addition to `onlyValidSignature`, `onlyValidSignatureAndMethod` and * `onlyValidSignatureAndData` can be used to restrict access to only a given method * or a given method with given parameters respectively. - * See the tests Bouncer.test.js for specific usage examples. + * See the tests in SignatureBouncer.test.js for specific usage examples. * @notice A method that uses the `onlyValidSignatureAndData` modifier must make the _signature * parameter the "last" parameter. You cannot sign a message that has its own * signature in it so the last 128 bytes of msg.data (which represents the @@ -29,11 +28,9 @@ import "../cryptography/ECDSA.sol"; * Also non fixed sized parameters make constructing the data in the signature * much more complex. See https://ethereum.stackexchange.com/a/50616 for more details. */ -contract SignatureBouncer is Ownable, RBAC { +contract SignatureBouncer is SignerRole { using ECDSA for bytes32; - // Name of the bouncer role. - string private constant ROLE_BOUNCER = "bouncer"; // Function selectors are 4 bytes long, as documented in // https://solidity.readthedocs.io/en/v0.4.24/abi-spec.html#function-selector uint256 private constant METHOD_ID_SIZE = 4; @@ -41,7 +38,7 @@ contract SignatureBouncer is Ownable, RBAC { uint256 private constant SIGNATURE_SIZE = 96; /** - * @dev requires that a valid signature of a bouncer was provided + * @dev requires that a valid signature of a signer was provided */ modifier onlyValidSignature(bytes _signature) { @@ -50,7 +47,7 @@ contract SignatureBouncer is Ownable, RBAC { } /** - * @dev requires that a valid signature with a specifed method of a bouncer was provided + * @dev requires that a valid signature with a specifed method of a signer was provided */ modifier onlyValidSignatureAndMethod(bytes _signature) { @@ -59,7 +56,7 @@ contract SignatureBouncer is Ownable, RBAC { } /** - * @dev requires that a valid signature with a specifed method and params of a bouncer was provided + * @dev requires that a valid signature with a specifed method and params of a signer was provided */ modifier onlyValidSignatureAndData(bytes _signature) { @@ -68,36 +65,7 @@ contract SignatureBouncer is Ownable, RBAC { } /** - * @dev Determine if an account has the bouncer role. - * @return true if the account is a bouncer, false otherwise. - */ - function isBouncer(address _account) public view returns(bool) { - return hasRole(_account, ROLE_BOUNCER); - } - - /** - * @dev allows the owner to add additional bouncer addresses - */ - function addBouncer(address _bouncer) - public - onlyOwner - { - require(_bouncer != address(0)); - _addRole(_bouncer, ROLE_BOUNCER); - } - - /** - * @dev allows the owner to remove bouncer addresses - */ - function removeBouncer(address _bouncer) - public - onlyOwner - { - _removeRole(_bouncer, ROLE_BOUNCER); - } - - /** - * @dev is the signature of `this + sender` from a bouncer? + * @dev is the signature of `this + sender` from a signer? * @return bool */ function _isValidSignature(address _address, bytes _signature) @@ -112,7 +80,7 @@ contract SignatureBouncer is Ownable, RBAC { } /** - * @dev is the signature of `this + sender + methodId` from a bouncer? + * @dev is the signature of `this + sender + methodId` from a signer? * @return bool */ function _isValidSignatureAndMethod(address _address, bytes _signature) @@ -131,7 +99,7 @@ contract SignatureBouncer is Ownable, RBAC { } /** - * @dev is the signature of `this + sender + methodId + params(s)` from a bouncer? + * @dev is the signature of `this + sender + methodId + params(s)` from a signer? * @notice the _signature parameter of the method being validated must be the "last" parameter * @return bool */ @@ -153,7 +121,7 @@ contract SignatureBouncer is Ownable, RBAC { /** * @dev internal function to convert a hash to an eth signed message - * and then recover the signature and check it against the bouncer role + * and then recover the signature and check it against the signer role * @return bool */ function _isValidDataHash(bytes32 _hash, bytes _signature) @@ -164,6 +132,6 @@ contract SignatureBouncer is Ownable, RBAC { address signer = _hash .toEthSignedMessageHash() .recover(_signature); - return isBouncer(signer); + return isSigner(signer); } } diff --git a/contracts/examples/RBACWithAdmin.sol b/contracts/examples/RBACWithAdmin.sol deleted file mode 100644 index 83c2dcef7a8..00000000000 --- a/contracts/examples/RBACWithAdmin.sol +++ /dev/null @@ -1,73 +0,0 @@ -pragma solidity ^0.4.24; - -import "../access/rbac/RBAC.sol"; - - -/** - * @title RBACWithAdmin - * @author Matt Condon (@Shrugs) - * @dev It's recommended that you define constants in the contract, - * like ROLE_ADMIN below, to avoid typos. - * @notice RBACWithAdmin is probably too expansive and powerful for your - * application; an admin is actually able to change any address to any role - * which is a very large API surface. It's recommended that you follow a strategy - * of strictly defining the abilities of your roles - * and the API-surface of your contract. - * This is just an example for example's sake. - */ -contract RBACWithAdmin is RBAC { - /** - * A constant role name for indicating admins. - */ - string private constant ROLE_ADMIN = "admin"; - - /** - * @dev modifier to scope access to admins - * // reverts - */ - modifier onlyAdmin() - { - checkRole(msg.sender, ROLE_ADMIN); - _; - } - - /** - * @dev constructor. Sets msg.sender as admin by default - */ - constructor() - public - { - _addRole(msg.sender, ROLE_ADMIN); - } - - /** - * @return true if the account is admin, false otherwise. - */ - function isAdmin(address _account) public view returns(bool) { - return hasRole(_account, ROLE_ADMIN); - } - - /** - * @dev add a role to an account - * @param _account the account that will have the role - * @param _roleName the name of the role - */ - function adminAddRole(address _account, string _roleName) - public - onlyAdmin - { - _addRole(_account, _roleName); - } - - /** - * @dev remove a role from an account - * @param _account the account that will no longer have the role - * @param _roleName the name of the role - */ - function adminRemoveRole(address _account, string _roleName) - public - onlyAdmin - { - _removeRole(_account, _roleName); - } -} diff --git a/contracts/lifecycle/Pausable.sol b/contracts/lifecycle/Pausable.sol index a681aa21b93..28bcac96682 100644 --- a/contracts/lifecycle/Pausable.sol +++ b/contracts/lifecycle/Pausable.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "../access/rbac/PauserRole.sol"; +import "../access/roles/PauserRole.sol"; /** diff --git a/contracts/mocks/CapperRoleMock.sol b/contracts/mocks/CapperRoleMock.sol index 08d2aff7b0a..f4f58a05fdb 100644 --- a/contracts/mocks/CapperRoleMock.sol +++ b/contracts/mocks/CapperRoleMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "../access/rbac/CapperRole.sol"; +import "../access/roles/CapperRole.sol"; contract CapperRoleMock is CapperRole { diff --git a/contracts/mocks/MinterRoleMock.sol b/contracts/mocks/MinterRoleMock.sol index 20c21e11356..232cbc19b20 100644 --- a/contracts/mocks/MinterRoleMock.sol +++ b/contracts/mocks/MinterRoleMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "../access/rbac/MinterRole.sol"; +import "../access/roles/MinterRole.sol"; contract MinterRoleMock is MinterRole { diff --git a/contracts/mocks/PauserRoleMock.sol b/contracts/mocks/PauserRoleMock.sol index ea51335d241..21b0b0c0346 100644 --- a/contracts/mocks/PauserRoleMock.sol +++ b/contracts/mocks/PauserRoleMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "../access/rbac/PauserRole.sol"; +import "../access/roles/PauserRole.sol"; contract PauserRoleMock is PauserRole { diff --git a/contracts/mocks/RBACMock.sol b/contracts/mocks/RBACMock.sol deleted file mode 100644 index f4acc927915..00000000000 --- a/contracts/mocks/RBACMock.sol +++ /dev/null @@ -1,69 +0,0 @@ -pragma solidity ^0.4.24; - -import "../examples/RBACWithAdmin.sol"; - - -contract RBACMock is RBACWithAdmin { - - string internal constant ROLE_ADVISOR = "advisor"; - - modifier onlyAdminOrAdvisor() - { - require( - isAdmin(msg.sender) || - hasRole(msg.sender, ROLE_ADVISOR) - ); - _; - } - - constructor(address[] _advisors) - public - { - _addRole(msg.sender, ROLE_ADVISOR); - - for (uint256 i = 0; i < _advisors.length; i++) { - _addRole(_advisors[i], ROLE_ADVISOR); - } - } - - function onlyAdminsCanDoThis() - external - onlyAdmin - view - { - } - - function onlyAdvisorsCanDoThis() - external - onlyRole(ROLE_ADVISOR) - view - { - } - - function eitherAdminOrAdvisorCanDoThis() - external - onlyAdminOrAdvisor - view - { - } - - function nobodyCanDoThis() - external - onlyRole("unknown") - view - { - } - - // admins can remove advisor's role - function removeAdvisor(address _account) - public - onlyAdmin - { - // revert if the user isn't an advisor - // (perhaps you want to soft-fail here instead?) - checkRole(_account, ROLE_ADVISOR); - - // remove the advisor's role - _removeRole(_account, ROLE_ADVISOR); - } -} diff --git a/contracts/mocks/RolesMock.sol b/contracts/mocks/RolesMock.sol index 22e14312652..6baf873988e 100644 --- a/contracts/mocks/RolesMock.sol +++ b/contracts/mocks/RolesMock.sol @@ -1,6 +1,6 @@ pragma solidity ^0.4.24; -import "../access/rbac/Roles.sol"; +import "../access/Roles.sol"; contract RolesMock { diff --git a/contracts/mocks/SignatureBouncerMock.sol b/contracts/mocks/SignatureBouncerMock.sol index d9fea81f402..d2663d2c527 100644 --- a/contracts/mocks/SignatureBouncerMock.sol +++ b/contracts/mocks/SignatureBouncerMock.sol @@ -1,9 +1,10 @@ pragma solidity ^0.4.24; import "../drafts/SignatureBouncer.sol"; +import "./SignerRoleMock.sol"; -contract SignatureBouncerMock is SignatureBouncer { +contract SignatureBouncerMock is SignatureBouncer, SignerRoleMock { function checkValidSignature(address _address, bytes _signature) public view diff --git a/contracts/mocks/SignerRoleMock.sol b/contracts/mocks/SignerRoleMock.sol new file mode 100644 index 00000000000..0157884e41c --- /dev/null +++ b/contracts/mocks/SignerRoleMock.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.4.24; + +import "../access/roles/SignerRole.sol"; + + +contract SignerRoleMock is SignerRole { + function removeSigner(address _account) public { + _removeSigner(_account); + } + + function onlySignerMock() public view onlySigner { + } + + // Causes a compilation error if super._removeSigner is not internal + function _removeSigner(address _account) internal { + super._removeSigner(_account); + } +} diff --git a/contracts/ownership/Superuser.sol b/contracts/ownership/Superuser.sol deleted file mode 100644 index b1f1fa4b52d..00000000000 --- a/contracts/ownership/Superuser.sol +++ /dev/null @@ -1,62 +0,0 @@ -pragma solidity ^0.4.24; - - -import "./Ownable.sol"; -import "../access/rbac/RBAC.sol"; - - -/** - * @title Superuser - * @dev The Superuser contract defines a single superuser who can transfer the ownership - * of a contract to a new address, even if he is not the owner. - * A superuser can transfer his role to a new address. - */ -contract Superuser is Ownable, RBAC { - string private constant ROLE_SUPERUSER = "superuser"; - - constructor () public { - _addRole(msg.sender, ROLE_SUPERUSER); - } - - /** - * @dev Throws if called by any account that's not a superuser. - */ - modifier onlySuperuser() { - checkRole(msg.sender, ROLE_SUPERUSER); - _; - } - - modifier onlyOwnerOrSuperuser() { - require(msg.sender == owner() || isSuperuser(msg.sender)); - _; - } - - /** - * @dev getter to determine if an account has superuser role - */ - function isSuperuser(address _account) - public - view - returns (bool) - { - return hasRole(_account, ROLE_SUPERUSER); - } - - /** - * @dev Allows the current superuser to transfer his role to a newSuperuser. - * @param _newSuperuser The address to transfer ownership to. - */ - function transferSuperuser(address _newSuperuser) public onlySuperuser { - require(_newSuperuser != address(0)); - _removeRole(msg.sender, ROLE_SUPERUSER); - _addRole(_newSuperuser, ROLE_SUPERUSER); - } - - /** - * @dev Allows the current superuser or owner to transfer control of the contract to a newOwner. - * @param _newOwner The address to transfer ownership to. - */ - function transferOwnership(address _newOwner) public onlyOwnerOrSuperuser { - _transferOwnership(_newOwner); - } -} diff --git a/contracts/token/ERC20/ERC20Mintable.sol b/contracts/token/ERC20/ERC20Mintable.sol index c42319b99dc..6de17feb8c6 100644 --- a/contracts/token/ERC20/ERC20Mintable.sol +++ b/contracts/token/ERC20/ERC20Mintable.sol @@ -1,7 +1,7 @@ pragma solidity ^0.4.24; import "./ERC20.sol"; -import "../../access/rbac/MinterRole.sol"; +import "../../access/roles/MinterRole.sol"; /** diff --git a/test/access/rbac/Roles.test.js b/test/access/Roles.test.js similarity index 100% rename from test/access/rbac/Roles.test.js rename to test/access/Roles.test.js diff --git a/test/access/SignatureBouncer.test.js b/test/access/SignatureBouncer.test.js deleted file mode 100644 index 798ab0ac9fa..00000000000 --- a/test/access/SignatureBouncer.test.js +++ /dev/null @@ -1,246 +0,0 @@ -const { assertRevert } = require('../helpers/assertRevert'); -const { getBouncerSigner } = require('../helpers/sign'); - -const Bouncer = artifacts.require('SignatureBouncerMock'); - -const BigNumber = web3.BigNumber; - -require('chai') - .use(require('chai-bignumber')(BigNumber)) - .should(); - -const UINT_VALUE = 23; -const BYTES_VALUE = web3.toHex('test'); -const INVALID_SIGNATURE = '0xabcd'; - -contract('Bouncer', function ([_, owner, anyone, bouncerAddress, authorizedUser]) { - beforeEach(async function () { - this.bouncer = await Bouncer.new({ from: owner }); - }); - - context('management', function () { - it('has a default owner of self', async function () { - (await this.bouncer.owner()).should.equal(owner); - }); - - it('allows the owner to add a bouncer', async function () { - await this.bouncer.addBouncer(bouncerAddress, { from: owner }); - (await this.bouncer.isBouncer(bouncerAddress)).should.equal(true); - }); - - it('does not allow adding an invalid address', async function () { - await assertRevert( - this.bouncer.addBouncer('0x0', { from: owner }) - ); - }); - - it('allows the owner to remove a bouncer', async function () { - await this.bouncer.addBouncer(bouncerAddress, { from: owner }); - - await this.bouncer.removeBouncer(bouncerAddress, { from: owner }); - (await this.bouncer.isBouncer(bouncerAddress)).should.equal(false); - }); - - it('does not allow anyone to add a bouncer', async function () { - await assertRevert( - this.bouncer.addBouncer(bouncerAddress, { from: anyone }) - ); - }); - - it('does not allow anyone to remove a bouncer', async function () { - await this.bouncer.addBouncer(bouncerAddress, { from: owner }); - - await assertRevert( - this.bouncer.removeBouncer(bouncerAddress, { from: anyone }) - ); - }); - }); - - context('with bouncer address', function () { - beforeEach(async function () { - await this.bouncer.addBouncer(bouncerAddress, { from: owner }); - this.signFor = getBouncerSigner(this.bouncer, bouncerAddress); - }); - - describe('modifiers', function () { - context('plain signature', function () { - it('allows valid signature for sender', async function () { - await this.bouncer.onlyWithValidSignature(this.signFor(authorizedUser), { from: authorizedUser }); - }); - - it('does not allow invalid signature for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignature(INVALID_SIGNATURE, { from: authorizedUser }) - ); - }); - - it('does not allow valid signature for other sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignature(this.signFor(authorizedUser), { from: anyone }) - ); - }); - - it('does not allow valid signature for method for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignature(this.signFor(authorizedUser, 'onlyWithValidSignature'), - { from: authorizedUser }) - ); - }); - }); - - context('method signature', function () { - it('allows valid signature with correct method for sender', async function () { - await this.bouncer.onlyWithValidSignatureAndMethod( - this.signFor(authorizedUser, 'onlyWithValidSignatureAndMethod'), { from: authorizedUser } - ); - }); - - it('does not allow invalid signature with correct method for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndMethod(INVALID_SIGNATURE, { from: authorizedUser }) - ); - }); - - it('does not allow valid signature with correct method for other sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndMethod( - this.signFor(authorizedUser, 'onlyWithValidSignatureAndMethod'), { from: anyone } - ) - ); - }); - - it('does not allow valid method signature with incorrect method for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndMethod(this.signFor(authorizedUser, 'theWrongMethod'), - { from: authorizedUser }) - ); - }); - - it('does not allow valid non-method signature method for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndMethod(this.signFor(authorizedUser), { from: authorizedUser }) - ); - }); - }); - - context('method and data signature', function () { - it('allows valid signature with correct method and data for sender', async function () { - await this.bouncer.onlyWithValidSignatureAndData(UINT_VALUE, - this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), { from: authorizedUser } - ); - }); - - it('does not allow invalid signature with correct method and data for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndData(UINT_VALUE, INVALID_SIGNATURE, { from: authorizedUser }) - ); - }); - - it('does not allow valid signature with correct method and incorrect data for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndData(UINT_VALUE + 10, - this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), - { from: authorizedUser } - ) - ); - }); - - it('does not allow valid signature with correct method and data for other sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndData(UINT_VALUE, - this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), - { from: anyone } - ) - ); - }); - - it('does not allow valid non-method signature for sender', async function () { - await assertRevert( - this.bouncer.onlyWithValidSignatureAndData(UINT_VALUE, - this.signFor(authorizedUser), { from: authorizedUser } - ) - ); - }); - }); - }); - - context('signature validation', function () { - context('plain signature', function () { - it('validates valid signature for valid user', async function () { - (await this.bouncer.checkValidSignature(authorizedUser, this.signFor(authorizedUser))).should.equal(true); - }); - - it('does not validate invalid signature for valid user', async function () { - (await this.bouncer.checkValidSignature(authorizedUser, INVALID_SIGNATURE)).should.equal(false); - }); - - it('does not validate valid signature for anyone', async function () { - (await this.bouncer.checkValidSignature(anyone, this.signFor(authorizedUser))).should.equal(false); - }); - - it('does not validate valid signature for method for valid user', async function () { - (await this.bouncer.checkValidSignature(authorizedUser, this.signFor(authorizedUser, 'checkValidSignature')) - ).should.equal(false); - }); - }); - - context('method signature', function () { - it('validates valid signature with correct method for valid user', async function () { - (await this.bouncer.checkValidSignatureAndMethod(authorizedUser, - this.signFor(authorizedUser, 'checkValidSignatureAndMethod')) - ).should.equal(true); - }); - - it('does not validate invalid signature with correct method for valid user', async function () { - (await this.bouncer.checkValidSignatureAndMethod(authorizedUser, INVALID_SIGNATURE)).should.equal(false); - }); - - it('does not validate valid signature with correct method for anyone', async function () { - (await this.bouncer.checkValidSignatureAndMethod(anyone, - this.signFor(authorizedUser, 'checkValidSignatureAndMethod')) - ).should.equal(false); - }); - - it('does not validate valid non-method signature with correct method for valid user', async function () { - (await this.bouncer.checkValidSignatureAndMethod(authorizedUser, this.signFor(authorizedUser)) - ).should.equal(false); - }); - }); - - context('method and data signature', function () { - it('validates valid signature with correct method and data for valid user', async function () { - (await this.bouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, - this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) - ).should.equal(true); - }); - - it('does not validate invalid signature with correct method and data for valid user', async function () { - (await this.bouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, INVALID_SIGNATURE) - ).should.equal(false); - }); - - it('does not validate valid signature with correct method and incorrect data for valid user', - async function () { - (await this.bouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE + 10, - this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) - ).should.equal(false); - } - ); - - it('does not validate valid signature with correct method and data for anyone', async function () { - (await this.bouncer.checkValidSignatureAndData(anyone, BYTES_VALUE, UINT_VALUE, - this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) - ).should.equal(false); - }); - - it('does not validate valid non-method-data signature with correct method and data for valid user', - async function () { - (await this.bouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, - this.signFor(authorizedUser, 'checkValidSignatureAndData')) - ).should.equal(false); - } - ); - }); - }); - }); -}); diff --git a/test/access/rbac/CapperRole.test.js b/test/access/rbac/CapperRole.test.js index 117b25b03d9..a79944a2778 100644 --- a/test/access/rbac/CapperRole.test.js +++ b/test/access/rbac/CapperRole.test.js @@ -1,4 +1,4 @@ -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); const CapperRoleMock = artifacts.require('CapperRoleMock'); contract('CapperRole', function ([_, capper, otherCapper, ...otherAccounts]) { diff --git a/test/access/rbac/RBAC.test.js b/test/access/rbac/RBAC.test.js deleted file mode 100644 index 32859993d1d..00000000000 --- a/test/access/rbac/RBAC.test.js +++ /dev/null @@ -1,73 +0,0 @@ -const { expectThrow } = require('../../helpers/expectThrow'); -const expectEvent = require('../../helpers/expectEvent'); - -const RBACMock = artifacts.require('RBACMock'); - -require('chai') - .should(); - -const ROLE_ADVISOR = 'advisor'; - -contract('RBAC', function ([_, admin, anyone, advisor, otherAdvisor, futureAdvisor]) { - let mock; - - beforeEach(async function () { - mock = await RBACMock.new([advisor, otherAdvisor], { from: admin }); - }); - - context('in normal conditions', function () { - it('allows admin to call #onlyAdminsCanDoThis', async function () { - await mock.onlyAdminsCanDoThis({ from: admin }); - }); - it('allows admin to call #onlyAdvisorsCanDoThis', async function () { - await mock.onlyAdvisorsCanDoThis({ from: admin }); - }); - it('allows advisors to call #onlyAdvisorsCanDoThis', async function () { - await mock.onlyAdvisorsCanDoThis({ from: advisor }); - }); - it('allows admin to call #eitherAdminOrAdvisorCanDoThis', async function () { - await mock.eitherAdminOrAdvisorCanDoThis({ from: admin }); - }); - it('allows advisors to call #eitherAdminOrAdvisorCanDoThis', async function () { - await mock.eitherAdminOrAdvisorCanDoThis({ from: advisor }); - }); - it('does not allow admins to call #nobodyCanDoThis', async function () { - await expectThrow(mock.nobodyCanDoThis({ from: admin })); - }); - it('does not allow advisors to call #nobodyCanDoThis', async function () { - await expectThrow(mock.nobodyCanDoThis({ from: advisor })); - }); - it('does not allow anyone to call #nobodyCanDoThis', async function () { - await expectThrow(mock.nobodyCanDoThis({ from: anyone })); - }); - it('allows an admin to remove an advisor\'s role', async function () { - await mock.removeAdvisor(advisor, { from: admin }); - }); - it('allows admins to #adminRemoveRole', async function () { - await mock.adminRemoveRole(advisor, ROLE_ADVISOR, { from: admin }); - }); - - it('announces a RoleAdded event on addRole', async function () { - await expectEvent.inTransaction( - mock.adminAddRole(futureAdvisor, ROLE_ADVISOR, { from: admin }), - 'RoleAdded' - ); - }); - - it('announces a RoleRemoved event on removeRole', async function () { - await expectEvent.inTransaction( - mock.adminRemoveRole(futureAdvisor, ROLE_ADVISOR, { from: admin }), - 'RoleRemoved' - ); - }); - }); - - context('in adversarial conditions', function () { - it('does not allow an advisor to remove another advisor', async function () { - await expectThrow(mock.removeAdvisor(otherAdvisor, { from: advisor })); - }); - it('does not allow "anyone" to remove an advisor', async function () { - await expectThrow(mock.removeAdvisor(advisor, { from: anyone })); - }); - }); -}); diff --git a/test/access/roles/CapperRole.test.js b/test/access/roles/CapperRole.test.js new file mode 100644 index 00000000000..a79944a2778 --- /dev/null +++ b/test/access/roles/CapperRole.test.js @@ -0,0 +1,11 @@ +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); +const CapperRoleMock = artifacts.require('CapperRoleMock'); + +contract('CapperRole', function ([_, capper, otherCapper, ...otherAccounts]) { + beforeEach(async function () { + this.contract = await CapperRoleMock.new({ from: capper }); + await this.contract.addCapper(otherCapper, { from: capper }); + }); + + shouldBehaveLikePublicRole(capper, otherCapper, otherAccounts, 'capper'); +}); diff --git a/test/access/rbac/MinterRole.test.js b/test/access/roles/MinterRole.test.js similarity index 81% rename from test/access/rbac/MinterRole.test.js rename to test/access/roles/MinterRole.test.js index ed222d20f60..b2e70b53a45 100644 --- a/test/access/rbac/MinterRole.test.js +++ b/test/access/roles/MinterRole.test.js @@ -1,4 +1,4 @@ -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); const MinterRoleMock = artifacts.require('MinterRoleMock'); contract('MinterRole', function ([_, minter, otherMinter, ...otherAccounts]) { diff --git a/test/access/rbac/PauserRole.test.js b/test/access/roles/PauserRole.test.js similarity index 81% rename from test/access/rbac/PauserRole.test.js rename to test/access/roles/PauserRole.test.js index a0767af22de..927e46c0ba8 100644 --- a/test/access/rbac/PauserRole.test.js +++ b/test/access/roles/PauserRole.test.js @@ -1,4 +1,4 @@ -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); const PauserRoleMock = artifacts.require('PauserRoleMock'); contract('PauserRole', function ([_, pauser, otherPauser, ...otherAccounts]) { diff --git a/test/access/rbac/PublicRole.behavior.js b/test/access/roles/PublicRole.behavior.js similarity index 97% rename from test/access/rbac/PublicRole.behavior.js rename to test/access/roles/PublicRole.behavior.js index 80d78e917b5..3181b705165 100644 --- a/test/access/rbac/PublicRole.behavior.js +++ b/test/access/roles/PublicRole.behavior.js @@ -9,7 +9,7 @@ function shouldBehaveLikePublicRole (authorized, otherAuthorized, [anyone], role rolename = capitalize(rolename); const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; - describe(`role ${rolename}`, function () { + describe('should behave like public role', function () { beforeEach('check preconditions', async function () { (await this.contract[`is${rolename}`](authorized)).should.equal(true); (await this.contract[`is${rolename}`](otherAuthorized)).should.equal(true); diff --git a/test/access/roles/SignerRole.test.js b/test/access/roles/SignerRole.test.js new file mode 100644 index 00000000000..317c100d7ed --- /dev/null +++ b/test/access/roles/SignerRole.test.js @@ -0,0 +1,11 @@ +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); +const SignerRoleMock = artifacts.require('SignerRoleMock'); + +contract('SignerRole', function ([_, signer, otherSigner, ...otherAccounts]) { + beforeEach(async function () { + this.contract = await SignerRoleMock.new({ from: signer }); + await this.contract.addSigner(otherSigner, { from: signer }); + }); + + shouldBehaveLikePublicRole(signer, otherSigner, otherAccounts, 'signer'); +}); diff --git a/test/crowdsale/IndividuallyCappedCrowdsale.test.js b/test/crowdsale/IndividuallyCappedCrowdsale.test.js index 64eddec9748..49a4fe2700d 100644 --- a/test/crowdsale/IndividuallyCappedCrowdsale.test.js +++ b/test/crowdsale/IndividuallyCappedCrowdsale.test.js @@ -10,7 +10,7 @@ require('chai') const IndividuallyCappedCrowdsaleImpl = artifacts.require('IndividuallyCappedCrowdsaleImpl'); const SimpleToken = artifacts.require('SimpleToken'); -const { shouldBehaveLikePublicRole } = require('../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../access/roles/PublicRole.behavior'); contract('IndividuallyCappedCrowdsale', function ( [_, capper, otherCapper, wallet, alice, bob, charlie, anyone, ...otherAccounts]) { diff --git a/test/drafts/SignatureBouncer.test.js b/test/drafts/SignatureBouncer.test.js new file mode 100644 index 00000000000..b320ca98c32 --- /dev/null +++ b/test/drafts/SignatureBouncer.test.js @@ -0,0 +1,212 @@ +const { assertRevert } = require('../helpers/assertRevert'); +const { getSignFor } = require('../helpers/sign'); +const { shouldBehaveLikePublicRole } = require('../access/roles/PublicRole.behavior'); + +const SignatureBouncerMock = artifacts.require('SignatureBouncerMock'); + +const BigNumber = web3.BigNumber; + +require('chai') + .use(require('chai-bignumber')(BigNumber)) + .should(); + +const UINT_VALUE = 23; +const BYTES_VALUE = web3.toHex('test'); +const INVALID_SIGNATURE = '0xabcd'; + +contract('SignatureBouncer', function ([_, signer, otherSigner, anyone, authorizedUser, ...otherAccounts]) { + beforeEach(async function () { + this.sigBouncer = await SignatureBouncerMock.new({ from: signer }); + this.signFor = getSignFor(this.sigBouncer, signer); + }); + + describe('signer role', function () { + beforeEach(async function () { + this.contract = this.sigBouncer; + await this.contract.addSigner(otherSigner, { from: signer }); + }); + + shouldBehaveLikePublicRole(signer, otherSigner, otherAccounts, 'signer'); + }); + + describe('modifiers', function () { + context('plain signature', function () { + it('allows valid signature for sender', async function () { + await this.sigBouncer.onlyWithValidSignature(this.signFor(authorizedUser), { from: authorizedUser }); + }); + + it('does not allow invalid signature for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignature(INVALID_SIGNATURE, { from: authorizedUser }) + ); + }); + + it('does not allow valid signature for other sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignature(this.signFor(authorizedUser), { from: anyone }) + ); + }); + + it('does not allow valid signature for method for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignature(this.signFor(authorizedUser, 'onlyWithValidSignature'), + { from: authorizedUser }) + ); + }); + }); + + context('method signature', function () { + it('allows valid signature with correct method for sender', async function () { + await this.sigBouncer.onlyWithValidSignatureAndMethod( + this.signFor(authorizedUser, 'onlyWithValidSignatureAndMethod'), { from: authorizedUser } + ); + }); + + it('does not allow invalid signature with correct method for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndMethod(INVALID_SIGNATURE, { from: authorizedUser }) + ); + }); + + it('does not allow valid signature with correct method for other sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndMethod( + this.signFor(authorizedUser, 'onlyWithValidSignatureAndMethod'), { from: anyone } + ) + ); + }); + + it('does not allow valid method signature with incorrect method for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndMethod(this.signFor(authorizedUser, 'theWrongMethod'), + { from: authorizedUser }) + ); + }); + + it('does not allow valid non-method signature method for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndMethod(this.signFor(authorizedUser), { from: authorizedUser }) + ); + }); + }); + + context('method and data signature', function () { + it('allows valid signature with correct method and data for sender', async function () { + await this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, + this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), { from: authorizedUser } + ); + }); + + it('does not allow invalid signature with correct method and data for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, INVALID_SIGNATURE, { from: authorizedUser }) + ); + }); + + it('does not allow valid signature with correct method and incorrect data for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE + 10, + this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), + { from: authorizedUser } + ) + ); + }); + + it('does not allow valid signature with correct method and data for other sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, + this.signFor(authorizedUser, 'onlyWithValidSignatureAndData', [UINT_VALUE]), + { from: anyone } + ) + ); + }); + + it('does not allow valid non-method signature for sender', async function () { + await assertRevert( + this.sigBouncer.onlyWithValidSignatureAndData(UINT_VALUE, + this.signFor(authorizedUser), { from: authorizedUser } + ) + ); + }); + }); + }); + + context('signature validation', function () { + context('plain signature', function () { + it('validates valid signature for valid user', async function () { + (await this.sigBouncer.checkValidSignature(authorizedUser, this.signFor(authorizedUser))).should.equal(true); + }); + + it('does not validate invalid signature for valid user', async function () { + (await this.sigBouncer.checkValidSignature(authorizedUser, INVALID_SIGNATURE)).should.equal(false); + }); + + it('does not validate valid signature for anyone', async function () { + (await this.sigBouncer.checkValidSignature(anyone, this.signFor(authorizedUser))).should.equal(false); + }); + + it('does not validate valid signature for method for valid user', async function () { + (await this.sigBouncer.checkValidSignature(authorizedUser, this.signFor(authorizedUser, 'checkValidSignature')) + ).should.equal(false); + }); + }); + + context('method signature', function () { + it('validates valid signature with correct method for valid user', async function () { + (await this.sigBouncer.checkValidSignatureAndMethod(authorizedUser, + this.signFor(authorizedUser, 'checkValidSignatureAndMethod')) + ).should.equal(true); + }); + + it('does not validate invalid signature with correct method for valid user', async function () { + (await this.sigBouncer.checkValidSignatureAndMethod(authorizedUser, INVALID_SIGNATURE)).should.equal(false); + }); + + it('does not validate valid signature with correct method for anyone', async function () { + (await this.sigBouncer.checkValidSignatureAndMethod(anyone, + this.signFor(authorizedUser, 'checkValidSignatureAndMethod')) + ).should.equal(false); + }); + + it('does not validate valid non-method signature with correct method for valid user', async function () { + (await this.sigBouncer.checkValidSignatureAndMethod(authorizedUser, this.signFor(authorizedUser)) + ).should.equal(false); + }); + }); + + context('method and data signature', function () { + it('validates valid signature with correct method and data for valid user', async function () { + (await this.sigBouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, + this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) + ).should.equal(true); + }); + + it('does not validate invalid signature with correct method and data for valid user', async function () { + (await this.sigBouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, INVALID_SIGNATURE) + ).should.equal(false); + }); + + it('does not validate valid signature with correct method and incorrect data for valid user', + async function () { + (await this.sigBouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE + 10, + this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) + ).should.equal(false); + } + ); + + it('does not validate valid signature with correct method and data for anyone', async function () { + (await this.sigBouncer.checkValidSignatureAndData(anyone, BYTES_VALUE, UINT_VALUE, + this.signFor(authorizedUser, 'checkValidSignatureAndData', [authorizedUser, BYTES_VALUE, UINT_VALUE])) + ).should.equal(false); + }); + + it('does not validate valid non-method-data signature with correct method and data for valid user', + async function () { + (await this.sigBouncer.checkValidSignatureAndData(authorizedUser, BYTES_VALUE, UINT_VALUE, + this.signFor(authorizedUser, 'checkValidSignatureAndData')) + ).should.equal(false); + } + ); + }); + }); +}); diff --git a/test/helpers/sign.js b/test/helpers/sign.js index a05238ad4fa..9164a9cd1a2 100644 --- a/test/helpers/sign.js +++ b/test/helpers/sign.js @@ -39,7 +39,7 @@ const transformToFullName = function (json) { * @param methodName string * @param methodArgs any[] */ -const getBouncerSigner = (contract, signer) => (redeemer, methodName, methodArgs = []) => { +const getSignFor = (contract, signer) => (redeemer, methodName, methodArgs = []) => { const parts = [ contract.address, redeemer, @@ -70,5 +70,5 @@ const getBouncerSigner = (contract, signer) => (redeemer, methodName, methodArgs module.exports = { signMessage, toEthSignedMessageHash, - getBouncerSigner, + getSignFor, }; diff --git a/test/lifecycle/Pausable.test.js b/test/lifecycle/Pausable.test.js index 2bbe392963e..2ae4c90d7da 100644 --- a/test/lifecycle/Pausable.test.js +++ b/test/lifecycle/Pausable.test.js @@ -2,7 +2,7 @@ const { assertRevert } = require('../helpers/assertRevert'); const expectEvent = require('../helpers/expectEvent'); const PausableMock = artifacts.require('PausableMock'); -const { shouldBehaveLikePublicRole } = require('../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../access/roles/PublicRole.behavior'); const BigNumber = web3.BigNumber; diff --git a/test/ownership/Superuser.test.js b/test/ownership/Superuser.test.js deleted file mode 100644 index 5e4d2c32456..00000000000 --- a/test/ownership/Superuser.test.js +++ /dev/null @@ -1,69 +0,0 @@ -const { expectThrow } = require('../helpers/expectThrow'); -const expectEvent = require('../helpers/expectEvent'); - -const Superuser = artifacts.require('Superuser'); - -require('chai') - .should(); - -contract('Superuser', function ([_, firstOwner, newSuperuser, newOwner, anyone]) { - const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; - - beforeEach(async function () { - this.superuser = await Superuser.new({ from: firstOwner }); - }); - - context('in normal conditions', function () { - it('should set the owner as the default superuser', async function () { - (await this.superuser.isSuperuser(firstOwner)).should.equal(true); - }); - - it('should change superuser after transferring', async function () { - await this.superuser.transferSuperuser(newSuperuser, { from: firstOwner }); - - (await this.superuser.isSuperuser(firstOwner)).should.equal(false); - - (await this.superuser.isSuperuser(newSuperuser)).should.equal(true); - }); - - it('should prevent changing to a null superuser', async function () { - await expectThrow( - this.superuser.transferSuperuser(ZERO_ADDRESS, { from: firstOwner }) - ); - }); - - it('should change owner after the superuser transfers the ownership', async function () { - await this.superuser.transferSuperuser(newSuperuser, { from: firstOwner }); - - await expectEvent.inTransaction( - this.superuser.transferOwnership(newOwner, { from: newSuperuser }), - 'OwnershipTransferred' - ); - - (await this.superuser.owner()).should.equal(newOwner); - }); - - it('should change owner after the owner transfers the ownership', async function () { - await expectEvent.inTransaction( - this.superuser.transferOwnership(newOwner, { from: firstOwner }), - 'OwnershipTransferred' - ); - - (await this.superuser.owner()).should.equal(newOwner); - }); - }); - - context('in adversarial conditions', function () { - it('should prevent non-superusers from transfering the superuser role', async function () { - await expectThrow( - this.superuser.transferSuperuser(newOwner, { from: anyone }) - ); - }); - - it('should prevent users that are not superuser nor owner from setting a new owner', async function () { - await expectThrow( - this.superuser.transferOwnership(newOwner, { from: anyone }) - ); - }); - }); -}); diff --git a/test/token/ERC20/ERC20Mintable.test.js b/test/token/ERC20/ERC20Mintable.test.js index 460164d3800..9dca0e55890 100644 --- a/test/token/ERC20/ERC20Mintable.test.js +++ b/test/token/ERC20/ERC20Mintable.test.js @@ -1,6 +1,6 @@ const { shouldBehaveLikeERC20Mintable } = require('./ERC20Mintable.behavior'); const ERC20MintableMock = artifacts.require('ERC20MintableMock'); -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); contract('ERC20Mintable', function ([_, minter, otherMinter, ...otherAccounts]) { beforeEach(async function () { diff --git a/test/token/ERC20/ERC20Pausable.test.js b/test/token/ERC20/ERC20Pausable.test.js index 41013c6bded..59eb5ce3877 100644 --- a/test/token/ERC20/ERC20Pausable.test.js +++ b/test/token/ERC20/ERC20Pausable.test.js @@ -1,7 +1,7 @@ const { assertRevert } = require('../../helpers/assertRevert'); const ERC20Pausable = artifacts.require('ERC20PausableMock'); -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); contract('ERC20Pausable', function ([_, pauser, otherPauser, recipient, anotherAccount, ...otherAccounts]) { beforeEach(async function () { diff --git a/test/token/ERC721/ERC721Pausable.test.js b/test/token/ERC721/ERC721Pausable.test.js index 8940400c29e..0cf0299cea9 100644 --- a/test/token/ERC721/ERC721Pausable.test.js +++ b/test/token/ERC721/ERC721Pausable.test.js @@ -1,6 +1,6 @@ const { shouldBehaveLikeERC721PausedToken } = require('./ERC721PausedToken.behavior'); const { shouldBehaveLikeERC721Basic } = require('./ERC721Basic.behavior'); -const { shouldBehaveLikePublicRole } = require('../../access/rbac/PublicRole.behavior'); +const { shouldBehaveLikePublicRole } = require('../../access/roles/PublicRole.behavior'); const BigNumber = web3.BigNumber; const ERC721Pausable = artifacts.require('ERC721PausableMock.sol');