Skip to content

Instantly share code, notes, and snippets.

@roderik
Created January 4, 2019 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roderik/5ebc445431eb4bb58d2770ca1de10ee1 to your computer and use it in GitHub Desktop.
Save roderik/5ebc445431eb4bb58d2770ca1de10ee1 to your computer and use it in GitHub Desktop.
pragma solidity ^0.4.25;
// File: @settlemint/solidity-mint/contracts/authentication/interfaces/IRoleRegistry.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
/**
* @title RoleRegistry
* @dev The RoleRegistry contract defines the methods and data structures to
* record if addresses have certain roles or not.
*/
contract IRoleRegistry {
event Designated(address _address);
event Discharged(address _address);
event RoleRegistryCreated(address _address);
/**
* @dev Checks if an address has a certain role
* @param _address The address to check for the role.
* @return A boolean that is True if the address has the role.
*/
function hasRole(address _address) public view returns (bool hasTheRole);
/**
* @dev Gives the role to an address
* @param _address The address to designate the role to.
*/
function designate(address _address) public;
/**
* @dev Removes the role from an address
* @param _address The address to discharge fromn the role.
*/
function discharge(address _address) public;
}
// File: @settlemint/solidity-mint/contracts/authentication/GateKeeper.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
contract GateKeeper {
bytes32 constant public CREATE_PERMISSIONS_ROLE = bytes32("CREATE_PERMISSIONS_ROLE");
bytes32 constant public ADD_ROLEREGISTRY_ROLE = bytes32("ADD_ROLEREGISTRY_ROLE");
event SetPermission(address indexed entity, address indexed contractAddress, bytes32 indexed role, bool allowed);
event ChangePermissionManager(address indexed contractAddress, bytes32 indexed role, address indexed manager);
// whether a certain entity has a permission
mapping (address => mapping (address => mapping (bytes32 => bool))) permissions;
// who is the manager of a permission
mapping (address => mapping (bytes32 => address)) permissionManager;
// a mapping of roles to the address of their correspending role registry
mapping(bytes32 => address) roleToRoleRegistry;
// a list of all RoleRegistries
IRoleRegistry[] roleRegistries;
modifier onlyPermissionManager(address _contract, bytes32 role) {
require(msg.sender == getPermissionManager(_contract, role), "Sender is not the permission manager");
_;
}
modifier auth(bytes32 _role) {
require(hasPermission(msg.sender, address(this), _role), "Sender does not have the correct permissions");
_;
}
constructor() public {
_createPermission(
msg.sender,
address(this),
CREATE_PERMISSIONS_ROLE,
msg.sender
);
_createPermission(
msg.sender,
address(this),
ADD_ROLEREGISTRY_ROLE,
msg.sender
);
}
function addRoleRegistry(address roleRegisty) external auth(ADD_ROLEREGISTRY_ROLE) {
roleRegistries.push(IRoleRegistry(roleRegisty));
}
/**
* @dev Creates a permission that wasn't previously set. Access is limited by the ACL.
* if a created permission is removed it is possible to reset it with createPermission.
* @notice Create a new permission granting `_entity` the ability to perform actions of role `_role` on `_contract` (setting `_manager` as parent)
* @param _entity Address of the whitelisted entity that will be able to perform the role, this can be a user or a roleregistry
* @param _contract Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
* @param _role Identifier for the group of actions in app given access to perform
* @param _manager Address of the entity that will be able to grant and revoke the permission further.
*/
function createPermission(
address _entity,
address _contract,
bytes32 _role,
address _manager
)
public
auth(CREATE_PERMISSIONS_ROLE)
{
_createPermission(
_entity,
_contract,
_role,
_manager
);
}
/**
* @dev Grants a permission if allowed. This requires `msg.sender` to be the permission manager
* @notice Grants `_entity` the ability to perform actions of role `_role` on `_contract`
* @param _entity Address of the whitelisted entity that will be able to perform the role
* @param _contract Address of the app in which the role will be allowed (requires app to depend on kernel for ACL)
* @param _role Identifier for the group of actions in app given access to perform
*/
function grantPermission(address _entity, address _contract, bytes32 _role)
public
onlyPermissionManager(_contract, _role)
{
_setPermission(
_entity,
_contract,
_role,
true
);
}
/**
* @dev Revokes permission if allowed. This requires `msg.sender` to be the parent of the permission
* @notice Revokes `_entity` the ability to perform actions of role `_role` on `_contract`
* @param _entity Address of the whitelisted entity that will be revoked access
* @param _contract Address of the app in which the role is revoked
* @param _role Identifier for the group of actions in app given access to perform
*/
function revokePermission(address _entity, address _contract, bytes32 _role)
public
onlyPermissionManager(_contract, _role)
{
_setPermission(
_entity,
_contract,
_role,
false
);
}
/**
* @notice Sets `_newManager` as the manager of the permission `_role` in `_contract`
* @param _newManager Address for the new manager
* @param _contract Address of the app in which the permission management is being transferred
* @param _role Identifier for the group of actions in app given access to perform
*/
function setPermissionManager(address _newManager, address _contract, bytes32 _role)
public
onlyPermissionManager(_contract, _role)
{
_setPermissionManager(_newManager, _contract, _role);
}
/**
* @dev Get manager address for permission
* @param _contract Address of the app
* @param _role Identifier for a group of actions in app
* @return address of the manager for the permission
*/
function getPermissionManager(address _contract, bytes32 _role) public view returns (address) {
return permissionManager[_contract][_role];
}
/**
* @dev Function called by apps to check ACL on kernel or to check permission statu
* @param _entity Sender of the original call
* @param _contract Address of the app
* @param _role Identifier for a group of actions in app
* @return boolean indicating whether the ACL allows the role or not
*/
function hasPermission(address _entity, address _contract, bytes32 _role) public view returns (bool) {
// the address passed in has the permissions themselves
bool personalPermission = permissions[_entity][_contract][_role];
if (personalPermission) {
return personalPermission;
}
// or we will check if any of the role registries have the permission
for (uint256 counter = 0; counter < roleRegistries.length; counter++) {
address registry = address(roleRegistries[counter]);
bool registryPermission = permissions[registry][_contract][_role];
if (registryPermission) {
if (roleRegistries[counter].hasRole(_entity)) {
return true;
}
}
}
// if, not, deny!
return false;
}
/**
* @dev Function called to retrieve the role registry for a given role
* @param _role Identifier for the role mapped to a role registry
* @return address of the role registry that corresponds to the role
*/
function getRoleRegistryAddress(bytes32 _role) public view returns (address) {
return roleToRoleRegistry[_role];
}
/**
* @dev Function called to set the role registry for a given role
* @param _role Identifier for the role mapped to a role registry
* @param _address address of the role registry to put into the store
*/
function setRoleRegistryAddress(bytes32 _role, address _address) public auth(ADD_ROLEREGISTRY_ROLE) {
roleToRoleRegistry[_role] = _address;
}
/**
* @dev Internal createPermission for access inside the gatekeeper (on instantiation)
*/
function _createPermission(
address _entity,
address _contract,
bytes32 _role,
address _manager
)
internal
{
require(permissionManager[_contract][_role] == 0, "only allow permission creation when it has no manager (hasn't been created before)");
_setPermission(
_entity,
_contract,
_role,
true
);
_setPermissionManager(_manager, _contract, _role);
}
/**
* @dev Internal function called to actually save the permission
*/
function _setPermission(
address _entity,
address _contract,
bytes32 _role,
bool _allowed
)
internal
{
permissions[_entity][_contract][_role] = _allowed;
emit SetPermission(
_entity,
_contract,
_role,
_allowed
);
}
/**
* @dev Internal function that sets management
*/
function _setPermissionManager(address _newManager, address _contract, bytes32 _role) internal {
require(_newManager > 0, "_newManager should be a real address");
permissionManager[_contract][_role] = _newManager;
emit ChangePermissionManager(_contract, _role, _newManager);
}
}
// File: @settlemint/solidity-mint/contracts/utility/conversions/Converter.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
contract Converter {
function addressToString(address x) internal pure returns (string) {
bytes memory b = new bytes(20);
for (uint i = 0; i < 20; i++) {
b[i] = byte(uint8(uint(x) / (2**(8*(19 - i)))));
}
return string(b);
}
function bytes32ToString(bytes32 x) internal pure returns (string) {
bytes memory bytesString = new bytes(32);
uint charCount = 0;
for (uint j = 0; j < 32; j++) {
byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
if (char != 0) {
bytesString[charCount] = char;
charCount++;
}
}
bytes memory bytesStringTrimmed = new bytes(charCount);
for (j = 0; j < charCount; j++) {
bytesStringTrimmed[j] = bytesString[j];
}
return string(bytesStringTrimmed);
}
}
// File: @settlemint/solidity-mint/contracts/authentication/Secured.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
contract Secured is Converter {
GateKeeper public gateKeeper;
modifier auth(bytes32 _role) {
require(
canPerform(msg.sender, _role),
"Sender do not have the correct role");
_;
}
constructor(address _gateKeeper) public {
gateKeeper = GateKeeper(_gateKeeper);
}
function canPerform(address _sender, bytes32 _role) internal view returns (bool) {
return address(gateKeeper) == 0 || gateKeeper.hasPermission(_sender, address(this), _role);
}
}
// File: @settlemint/solidity-mint/contracts/utility/syncing/Syncable.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
/**
* The listable item should also implement
* @dev function getInfo() constant public returns (bytes32 _productID, bytes32 _issuerProductID, bytes32 _productPayoffCode, address _currency, uint256 _totalSupply) {
*/
contract Syncable {
function getIndexLength() public view returns (uint length);
// Waiting for the time we can return structs from functions!
//function getByIndex(uint index) constant public returns (address key, bool hasRole){
//function getByKey(address _key) constant public returns (address key, bool hasRole){
}
// File: @settlemint/solidity-mint/contracts/authentication/RoleRegistry.sol
/**
* Copyright (C) SettleMint NV - All Rights Reserved
*
* Use of this file is strictly prohibited without an active license agreement.
* Distribution of this file, via any medium, is strictly prohibited.
*
* For license inquiries, contact hello@settlemint.com
*/
pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
/**
* @title RoleRegistry
* @dev The RoleRegistry contract defines the methods and data structures to
* record if addresses have certain roles or not.
*/
contract RoleRegistry is IRoleRegistry, Syncable, Secured {
bytes32 constant public DESIGNATE_ROLE = bytes32("DESIGNATE_ROLE");
struct RoleHolderContainer {
bool roleDesignated;
uint256 creationDate;
}
mapping(address => RoleHolderContainer) private roleHolders;
address[] private roleHoldersIndex;
event Designated(address _address);
event Discharged(address _address);
event RoleRegistryCreated(address _address);
constructor(address _gateKeeper) Secured(_gateKeeper) public {
emit RoleRegistryCreated(this);
}
/**
* @notice Returns a list of all the holders of this role.
*/
function getRoleHolders() public view returns(address[] allRoleHolders) {
return roleHoldersIndex;
}
/**
* @dev Checks if an address has a certain role
* @param _address The address to check for the role.
* @return A boolean that is True if the address has the role.
*/
function hasRole(address _address) public view returns (bool hasTheRole) {
hasTheRole = roleHolders[_address].roleDesignated;
}
/**
* @dev Gives the role to an address
* @param _address The address to designate the role to.
*/
function designate(address _address) public auth(DESIGNATE_ROLE) {
if (roleHolders[_address].creationDate == 0) {
roleHoldersIndex.push(_address);
roleHolders[_address].creationDate = now;
}
roleHolders[_address].roleDesignated = true;
emit Designated(_address);
}
/**
* @dev Removes the role from an address
* @param _address The address to discharge fromn the role.
*/
function discharge(address _address) public auth(DESIGNATE_ROLE) {
require(roleHolders[_address].creationDate > 0, "This address was never designated to this role");
uint i = 0;
while (roleHoldersIndex[i] != _address) {
i++;
}
for (uint j = i; j<roleHoldersIndex.length-1; j++) {
roleHoldersIndex[j] = roleHoldersIndex[j+1];
}
roleHoldersIndex.length--;
roleHolders[_address].roleDesignated = false;
emit Discharged(_address);
}
/**
* @dev Returns the length of the index array
* @return the amount of items in the index array
*/
function getIndexLength() public view returns (uint length) {
length = roleHoldersIndex.length;
}
/**
* @dev Returns the information for the key on a certain index
* @param _index The index of the key in the key array
* @return the information for the key on a certain index
*/
function getByIndex(uint _index) public view returns (address key, bool hasTheRole) {
key = roleHoldersIndex[_index];
hasTheRole = roleHolders[roleHoldersIndex[_index]].roleDesignated;
}
/**
* @dev Returns the information for the key
* @param _key The key to get the info for
* @return the information for the key
*/
function getByKey(address _key) public view returns (address key, bool hasTheRole) {
key = _key;
hasTheRole = roleHolders[_key].roleDesignated;
}
}
// File: contracts/Administrator/AdministratorRegistry.sol
/**
* @title AdministratorRegistry
* @dev Contains all the administrators of this system.
*/
contract AdministratorRegistry is RoleRegistry {
bytes32 constant public ROLE = "ADMIN_ROLE";
constructor(address _gateKeeper) public RoleRegistry(_gateKeeper) {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment