Skip to content
Next Next commit
fix: updates for SignatureBouncer tests and voucher construction
  • Loading branch information
shrugs committed Jul 25, 2018
commit a3df995141f2416f13fd30b865e16d6f54317aa4
4 changes: 2 additions & 2 deletions contracts/access/SignatureBouncer.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pragma solidity ^0.4.24;

import "../ownership/Ownable.sol";
import "../ownership/rbac/RBACOwnable.sol";
import "../ownership/rbac/RBAC.sol";
import "../ECRecovery.sol";

Expand Down Expand Up @@ -29,7 +29,7 @@ import "../ECRecovery.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 RBACOwnable {
using ECRecovery for bytes32;

string public constant ROLE_BOUNCER = "bouncer";
Expand Down
41 changes: 41 additions & 0 deletions contracts/ownership/rbac/RBACOwnable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
pragma solidity ^0.4.24;

import "./RBAC.sol";


/**
* @title RBACOwnable
* @author Matt Condon (@shrugs)
* @dev Ownable logic using RBAC.
* @dev Use RBACOwnable if you could have many different owners and you're ok with
* @dev the security profile of any owner being able to add another owner.
* @dev Only difference from Ownable.sol is that the owners are not stored as public variables.
*/
contract RBACOwnable is RBAC {
string public constant ROLE_OWNER = "owner";

constructor()
public
{
addRole(msg.sender, ROLE_OWNER);
}

modifier onlyOwner() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's Solidity's fault but if a contract has both Ownable and RBACOwnable in its inheritance graph there is no warning about the clashing modifier names... I'm not even sure what will happen.

Having both of those in the inheritance graph is probably a mistake, so in an ideal world the clashing modifiers would raise an error. Given that there is no error, I'm not sure what I prefer. 😟

It's also not clear to me why we're changing Bouncer into RBACOwnable. Perhaps we can undo that so as to sidestep the issue for now...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modifiers will be overwritten just like function implementations afaik, so as long as RBAC is higher in the tree, this one takes precedence. I agree that it's confusing, but I bet it's expected behavior.

Contracts should only inherit from RBACOwnable or Ownable; I can't think of a reason a contract would inherit from both.

In any case, I still think RBACOwnable is a better primitive than ownable, and want to use it as basic access control on new features. thoughts?

Copy link
Contributor

@nventuro nventuro Jul 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about using the current RBACOwnable for new features: I would either change the name of the modifier, or remove Ownable altogether.

While no-one should explicitly use both, I worry about the danger of someone inadvertently doing it indirectly, e.g., by extending a RefundableCrowdsale and then either adding RBAC ownership on top or inheriting from another contract that uses RBAC as the ownership model.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could change it to onlyOwnersβ€”how's that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I think that could work, specially considering that (how many owners there are) is the only difference between Ownable and RBACOwnable.

checkRole(msg.sender, ROLE_OWNER);
_;
}

function addOwner(address _owner)
onlyOwner
public
{
addRole(_owner, ROLE_OWNER);
}

function removeOwner(address _owner)
onlyOwner
public
{
removeRole(_owner, ROLE_OWNER);
}
}
8 changes: 2 additions & 6 deletions test/access/SignatureBouncer.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { assertRevert } = require('../helpers/assertRevert');
const { getBouncerSigner } = require('../helpers/sign');

const Bouncer = artifacts.require('SignatureBouncerMock');
const SignatureBouncer = artifacts.require('SignatureBouncerMock');

require('chai')
.use(require('chai-as-promised'))
Expand All @@ -13,15 +13,11 @@ const INVALID_SIGNATURE = '0xabcd';

contract('Bouncer', ([_, owner, anyone, bouncerAddress, authorizedUser]) => {
beforeEach(async function () {
this.bouncer = await Bouncer.new({ from: owner });
this.bouncer = await SignatureBouncer.new({ from: owner });
this.roleBouncer = await this.bouncer.ROLE_BOUNCER();
});

context('management', () => {
it('has a default owner of self', async function () {
(await this.bouncer.owner()).should.eq(owner);
});

it('allows the owner to add a bouncer', async function () {
await this.bouncer.addBouncer(bouncerAddress, { from: owner });
(await this.bouncer.hasRole(bouncerAddress, this.roleBouncer)).should.eq(true);
Expand Down
34 changes: 34 additions & 0 deletions test/ownership/rbac/RBACOwnable.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import assertRevert from '../../helpers/assertRevert';

const RBACOwnable = artifacts.require('RBACOwnable');

require('chai')
.use(require('chai-as-promised'))
.should();

contract('RBAC', function ([_, owner, newOwner, notOwner]) {
before(async function () {
this.ownable = await RBACOwnable.new({ from: owner });
this.roleOwner = await this.ownable.ROLE_OWNER();
});

it('should not allow notOwner to add or remove owners', async function () {
await assertRevert(
this.ownable.addOwner(newOwner, { from: notOwner })
);

await assertRevert(
this.ownable.removeOwner(owner, { from: notOwner })
);
});

it('should allow existing owner to add and remove a newOwner', async function () {
await this.ownable.addOwner(newOwner, { from: owner });
let hasRole = await this.ownable.hasRole(newOwner, this.roleOwner);
hasRole.should.eq(true);

await this.ownable.removeOwner(newOwner, { from: owner });
hasRole = await this.ownable.hasRole(newOwner, this.roleOwner);
hasRole.should.eq(false);
});
});