0

I have a PermissionsManager contract that uses OpenZeppelin's Role-Based Access Control (RBAC) template as a base. Currently function modifiers in my app call PermissionsManager directly to check for access rights. I'd like to have this done through a proxy instead, so that if PermissionsManager gets upgraded/redeployed, I don't have to change its address in every contract that uses RBAC, but only in a single place - its proxy.

In other words, I'd like to have PermissionsManagerProxy contract to delegate all calls to PermissionsManager. When I deploy PermissionsManager, I'm automatically an Admin, but the problem is that PermissionsManagerProxy doesn't see this, so I'm not an Admin if I ask the proxy. How can the deployer of the Proxy become an Admin?

In short, I have this flow and it works:

ADMIN_USER --calls--> testOnlyAdmin() of PermissionsTestProxy --delegates_to--> PermissionsTest --is--> Permissions --calls--> PermissionsManager --uses--> Roles library

I'd like to have this instead to allow for upgradability (new items in bold):

ADMIN_USER --calls--> testOnlyAdmin() of PermissionsTestProxy --delegates_to--> PermissionsTest --is--> Permissions --calls--> PermissionsManagerProxy --delegates_to--> PermissionsManager --uses--> Roles library

However, the call above fails, because ADMIN_USER doesn't have the Admin role according to PermissionsManagerProxy.

Simplified example code below.

pragma solidity ^0.4.25;

RBAC Library:

library Roles {
  struct Role {
    mapping (address => bool) bearer;
  }

  function add(Role storage role, address account) 
  internal
  {
    require(account != address(0));
    role.bearer[account] = true;
  }

  function remove(Role storage role, address account) 
  internal 
  {
    require(account != address(0));
    role.bearer[account] = false;
  }

  function has(Role storage role, address account)
    internal
    view
    returns (bool)
  {
    require(account != address(0));
    return role.bearer[account];
  }
}

PermissionsManager:

contract PermissionsManager {
  using Roles for Roles.Role;

  event LogAdminRoleAdded(address indexed account);
  event LogAdminRoleRemoved(address indexed account);

  Roles.Role internal adminRole;

  constructor() public {
    adminRole.add(msg.sender);
  }

  modifier onlyAdmin() {
    require(isAdmin(msg.sender));
    _;
  }

  function isAdmin(address account) 
  public
  view 
  returns (bool) 
  {
    return adminRole.has(account);
  }

  function addAdmin(address account) 
  public 
  onlyAdmin 
  {
    adminRole.add(account);
    emit LogAdminRoleAdded(account);
  }
}

Permissions contract accessing PermissionsManager's roles:

contract IPermissionsManager {  
    function isAdmin(address) public view returns (bool);
}

contract Permissions {    
    IPermissionsManager private permissionsManager;

    constructor(address _permissionsManagerAddress) public {
        permissionsManager = IPermissionsManager(_permissionsManagerAddress);
    }

    function isAdmin(address _who) 
    public
    view
    returns(bool) 
    {
        return permissionsManager.isAdmin(_who);
    }


    modifier onlyAdmin() {
    require(permissionsManager.isAdmin(msg.sender));
    _;
  }
}

Example of end-user contract:

contract PermissionsTest is Permissions {    
    event LogSuccess();

    constructor(address _permissionsManager)
    Permissions(_permissionsManager)
    public
    {    }

    function testAnyone()
    public  
    {
        emit LogSuccess();
    }

    function testOnlyAdmin()
    public
    onlyAdmin
    {
        emit LogSuccess();
    }
}

Proxy template:

contract Proxy {    
    address public _currentImplementation;

    constructor(address _initialImplementation) 
    public
    {
        _currentImplementation = _initialImplementation;
    }   

    function () 
    payable 
    public 
    {
        bool callSuccess = _currentImplementation.delegatecall(msg.data);
        if (callSuccess) {
            assembly {
                returndatacopy(0x0, 0x0, returndatasize)
                return(0x0, returndatasize)
            }
        } else {
            revert();
        }       
    }
}

Proxy of end-user's contract:

contract PermissionsTestProxy is Proxy {        
    constructor(address _initialImplementation)
    Proxy(_initialImplementation)
    public
    {   }
}

PermissionsManagerProxy:

contract PermissionsManagerProxy is Proxy { 
    constructor(address _initialImplementation)
    Proxy(_initialImplementation)
    public
    {   }
}

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Browse other questions tagged or ask your own question.