From a0c03ee61cdb77327f2435ba2ab1c539b1d34774 Mon Sep 17 00:00:00 2001 From: Patricio Mosse Date: Sun, 3 Jun 2018 20:14:30 -0300 Subject: [PATCH] Adding new Superuser contract with test (#952) * Adding new Superuser contract + tests --- contracts/ownership/Superuser.sol | 62 +++++++++++++++++++++++++++++++ test/ownership/Superuser.test.js | 62 +++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 contracts/ownership/Superuser.sol create mode 100644 test/ownership/Superuser.test.js diff --git a/contracts/ownership/Superuser.sol b/contracts/ownership/Superuser.sol new file mode 100644 index 000000000..196984f68 --- /dev/null +++ b/contracts/ownership/Superuser.sol @@ -0,0 +1,62 @@ +pragma solidity ^0.4.23; + + +import "./Ownable.sol"; +import "./rbac/RBAC.sol"; + + +/** + * @title Superuser + * @dev The Superuser contract defines a single superuser who can transfer the ownership + * @dev of a contract to a new address, even if he is not the owner. + * @dev A superuser can transfer his role to a new address. + */ +contract Superuser is Ownable, RBAC { + string public 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); + _; + } + + /** + * @dev getter to determine if address has superuser role + */ + function isSuperuser(address _addr) + public + view + returns (bool) + { + return hasRole(_addr, 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) + onlySuperuser + public + { + require(_newSuperuser != address(0)); + removeRole(msg.sender, ROLE_SUPERUSER); + addRole(_newSuperuser, ROLE_SUPERUSER); + } + + /** + * @dev Allows the current superuser to transfer control of the contract to a newOwner. + * @param _newOwner The address to transfer ownership to. + */ + function transferOwnership(address _newOwner) public onlySuperuser { + require(_newOwner != address(0)); + owner = _newOwner; + emit OwnershipTransferred(owner, _newOwner); + } +} diff --git a/test/ownership/Superuser.test.js b/test/ownership/Superuser.test.js new file mode 100644 index 000000000..4421a2cb3 --- /dev/null +++ b/test/ownership/Superuser.test.js @@ -0,0 +1,62 @@ +import expectThrow from '../helpers/expectThrow'; +import expectEvent from '../helpers/expectEvent'; + +const Superuser = artifacts.require('Superuser'); + +require('chai') + .use(require('chai-as-promised')) + .should(); + +contract('Superuser', function (accounts) { + const [ + firstOwner, + newSuperuser, + newOwner, + anyone, + ] = accounts; + + before(async function () { + this.superuser = await Superuser.new(); + }); + + context('in normal conditions', () => { + it('should set the owner as the default superuser', async function () { + const ownerIsSuperuser = await this.superuser.isSuperuser(firstOwner); + ownerIsSuperuser.should.be.equal(true); + }); + + it('should change superuser after transferring', async function () { + await this.superuser.transferSuperuser(newSuperuser, { from: firstOwner }); + + const ownerIsSuperuser = await this.superuser.isSuperuser(firstOwner); + ownerIsSuperuser.should.be.equal(false); + + const address1IsSuperuser = await this.superuser.isSuperuser(newSuperuser); + address1IsSuperuser.should.be.equal(true); + }); + + it('should change owner after transferring', async function () { + await expectEvent.inTransaction( + this.superuser.transferOwnership(newOwner, { from: newSuperuser }), + 'OwnershipTransferred' + ); + + const currentOwner = await this.superuser.owner(); + currentOwner.should.be.equal(newOwner); + }); + }); + + context('in adversarial conditions', () => { + it('should prevent non-superusers from transfering the superuser role', async function () { + await expectThrow( + this.superuser.transferSuperuser(newOwner, { from: anyone }) + ); + }); + + it('should prevent non-superusers from setting a new owner', async function () { + await expectThrow( + this.superuser.transferOwnership(newOwner, { from: anyone }) + ); + }); + }); +});