Skip to content

Commit

Permalink
Release 0.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Aliaksei Hiatsevich committed Jun 28, 2018
2 parents 7a73a4a + 6a9c047 commit 2f6fc0d
Show file tree
Hide file tree
Showing 19 changed files with 7,898 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sol linguist-language=Solidity
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
build/
.tern-port
.DS_Store
node_modules/
.idea/

*.log
*.bac
coverage.json
coverage
7 changes: 7 additions & 0 deletions .solcover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
copyNodeModules: true,
skipFiles: [
'Migrations.sol',
'helpers/StubStorageManager.sol'
]
}
17 changes: 17 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
sudo: required
dist: trusty
language: node_js
node_js:
- '9'
before_install:
- export NODE_OPTIONS="--max_old_space_size=4096"
- export PATH=$PATH:$(pwd)/node_modules/.bin
install:
- npm install
script:
- npm test
before_script:
- ganache-cli > /dev/null &
- sleep 5
after_script:
- travis_wait 30 npm run coverage && cat coverage/lcov.info | coveralls
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# solidity-roles-lib
# solidity-roles-lib [![Build Status](https://travis-ci.org/ChronoBank/solidity-roles-lib.svg?branch=master)](https://travis-ci.org/ChronoBank/solidity-roles-lib) [![Coverage Status](https://coveralls.io/repos/github/ChronoBank/solidity-roles-lib/badge.svg?branch=master)](https://coveralls.io/github/ChronoBank/solidity-roles-lib?branch=master)
14 changes: 14 additions & 0 deletions common/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const errorScope = {
roles: 20000
}

const errorCodes = {
UNAUTHORIZED: 0,
OK: 1,

ROLES_ALREADY_EXISTS: errorScope.roles + 1,
ROLES_INVALID_INVOCATION: errorScope.roles + 2,
ROLES_NOT_FOUND: errorScope.roles + 3
}

module.exports = errorCodes
25 changes: 25 additions & 0 deletions contracts/Migrations.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
pragma solidity ^0.4.23;


// Provided by Truffle.
contract Migrations {
address public owner;
uint public last_completed_migration;

modifier restricted() {
if (msg.sender == owner) _;
}

constructor() public {
owner = msg.sender;
}

function setCompleted(uint completed) restricted public {
last_completed_migration = completed;
}

function upgrade(address new_address) restricted public {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
223 changes: 223 additions & 0 deletions contracts/Roles2Library.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/**
* Copyright 2017–2018, LaborX PTY
* Licensed under the AGPL Version 3 license.
*/

pragma solidity ^0.4.21;

import 'solidity-storage-lib/contracts/StorageAdapter.sol';
import 'solidity-shared-lib/contracts/Owned.sol';


contract Roles2Library is StorageAdapter, Owned {

uint constant OK = 1;

uint constant ROLES_SCOPE = 20000;
uint constant ROLES_ALREADY_EXISTS = ROLES_SCOPE + 1;
uint constant ROLES_INVALID_INVOCATION = ROLES_SCOPE + 2;
uint constant ROLES_NOT_FOUND = ROLES_SCOPE + 3;

event Error(address indexed self, uint errorCode);
event RoleAdded(address indexed self, address indexed user, uint8 indexed role);
event RoleRemoved(address indexed self, address indexed user, uint8 indexed role);
event CapabilityAdded(address indexed self, address indexed code, bytes4 sig, uint8 indexed role);
event CapabilityRemoved(address indexed self, address indexed code, bytes4 sig, uint8 indexed role);
event PublicCapabilityAdded(address indexed self, address indexed code, bytes4 sig);
event PublicCapabilityRemoved(address indexed self, address indexed code, bytes4 sig);

StorageInterface.AddressBoolMapping rootUsers;
StorageInterface.AddressBytes32Mapping userRoles;
StorageInterface.AddressBytes4Bytes32Mapping capabilityRoles;
StorageInterface.AddressBytes4BoolMapping publicCapabilities;

address public eventsHistory;

modifier authorized {
if (msg.sender != contractOwner && !canCall(msg.sender, this, msg.sig)) {
return;
}
_;
}

constructor(Storage _store, bytes32 _crate) StorageAdapter(_store, _crate) public {
rootUsers.init('rootUsers');
userRoles.init('userRoles');
capabilityRoles.init('capabilityRoles');
publicCapabilities.init('publicCapabilities');
}

function setupEventsHistory(address _eventsHistory) onlyContractOwner external returns (uint) {
eventsHistory = _eventsHistory;
}

function getEventsHistory() public view returns (address) {
return eventsHistory != address(0) ? eventsHistory : address(this);
}

function getUserRoles(address _user) public view returns (bytes32) {
return store.get(userRoles, _user);
}

function getCapabilityRoles(address _code, bytes4 _sig) public view returns (bytes32) {
return store.get(capabilityRoles, _code, _sig);
}

function canCall(address _user, address _code, bytes4 _sig) public view returns (bool) {
if (isUserRoot(_user) || isCapabilityPublic(_code, _sig)) {
return true;
}
return bytes32(0) != getUserRoles(_user) & getCapabilityRoles(_code, _sig);
}

function bitNot(bytes32 _input) public pure returns (bytes32) {
return (_input ^ bytes32(uint(-1)));
}

function setRootUser(address _user, bool _enabled) onlyContractOwner external returns (uint) {
store.set(rootUsers, _user, _enabled);
return OK;
}

function addUserRole(address _user, uint8 _role) authorized external returns (uint) {
if (hasUserRole(_user, _role)) {
return _emitErrorCode(ROLES_ALREADY_EXISTS);
}

return _setUserRole(_user, _role, true);
}

function removeUserRole(address _user, uint8 _role) authorized external returns (uint) {
if (!hasUserRole(_user, _role)) {
return _emitErrorCode(ROLES_NOT_FOUND);
}

return _setUserRole(_user, _role, false);
}

function setPublicCapability(address _code, bytes4 _sig, bool _enabled) onlyContractOwner external returns (uint) {
store.set(publicCapabilities, _code, _sig, _enabled);

if (_enabled) {
_emitPublicCapabilityAdded(_code, _sig);
} else {
_emitPublicCapabilityRemoved(_code, _sig);
}
return OK;
}

function addRoleCapability(uint8 _role, address _code, bytes4 _sig) onlyContractOwner public returns (uint) {
return _setRoleCapability(_role, _code, _sig, true);
}

function removeRoleCapability(uint8 _role, address _code, bytes4 _sig) onlyContractOwner public returns (uint) {
if (getCapabilityRoles(_code, _sig) == 0) {
return _emitErrorCode(ROLES_NOT_FOUND);
}

return _setRoleCapability(_role, _code, _sig, false);
}

function isUserRoot(address _user) public view returns (bool) {
return store.get(rootUsers, _user);
}

function isCapabilityPublic(address _code, bytes4 _sig) public view returns (bool) {
return store.get(publicCapabilities, _code, _sig);
}

function hasUserRole(address _user, uint8 _role) public view returns (bool) {
return bytes32(0) != getUserRoles(_user) & _shift(_role);
}

function _setUserRole(address _user, uint8 _role, bool _enabled) internal returns (uint) {
bytes32 lastRoles = getUserRoles(_user);
bytes32 shifted = _shift(_role);

if (_enabled) {
store.set(userRoles, _user, lastRoles | shifted);
_emitRoleAdded(_user, _role);
return OK;
}

store.set(userRoles, _user, lastRoles & bitNot(shifted));
_emitRoleRemoved(_user, _role);
return OK;
}

function _setRoleCapability(uint8 _role, address _code, bytes4 _sig, bool _enabled) internal returns (uint) {
bytes32 lastRoles = getCapabilityRoles(_code, _sig);
bytes32 shifted = _shift(_role);

if (_enabled) {
store.set(capabilityRoles, _code, _sig, lastRoles | shifted);
_emitCapabilityAdded(_code, _sig, _role);
} else {
store.set(capabilityRoles, _code, _sig, lastRoles & bitNot(shifted));
_emitCapabilityRemoved(_code, _sig, _role);
}

return OK;
}

function _shift(uint8 _role) pure internal returns (bytes32) {
return bytes32(uint(uint(2) ** uint(_role)));
}

function _emitErrorCode(uint _errorCode) internal returns (uint) {
Roles2Library(getEventsHistory()).emitError(_errorCode);
return _errorCode;
}

function _emitRoleAdded(address _user, uint8 _role) internal {
Roles2Library(getEventsHistory()).emitRoleAdded(_user, _role);
}

function _emitRoleRemoved(address _user, uint8 _role) internal {
Roles2Library(getEventsHistory()).emitRoleRemoved(_user, _role);
}

function _emitCapabilityAdded(address _code, bytes4 _sig, uint8 _role) internal {
Roles2Library(getEventsHistory()).emitCapabilityAdded(_code, _sig, _role);
}

function _emitCapabilityRemoved(address _code, bytes4 _sig, uint8 _role) internal {
Roles2Library(getEventsHistory()).emitCapabilityRemoved(_code, _sig, _role);
}

function _emitPublicCapabilityAdded(address _code, bytes4 _sig) internal {
Roles2Library(getEventsHistory()).emitPublicCapabilityAdded(_code, _sig);
}

function _emitPublicCapabilityRemoved(address _code, bytes4 _sig) internal {
Roles2Library(getEventsHistory()).emitPublicCapabilityRemoved(_code, _sig);
}

function emitError(uint _errorCode) public {
emit Error(msg.sender, _errorCode);
}

function emitRoleAdded(address _user, uint8 _role) public {
emit RoleAdded(msg.sender, _user, _role);
}

function emitRoleRemoved(address _user, uint8 _role) public {
emit RoleRemoved(msg.sender, _user, _role);
}

function emitCapabilityAdded(address _code, bytes4 _sig, uint8 _role) public {
emit CapabilityAdded(msg.sender, _code, _sig, _role);
}

function emitCapabilityRemoved(address _code, bytes4 _sig, uint8 _role) public {
emit CapabilityRemoved(msg.sender, _code, _sig, _role);
}

function emitPublicCapabilityAdded(address _code, bytes4 _sig) public {
emit PublicCapabilityAdded(msg.sender, _code, _sig);
}

function emitPublicCapabilityRemoved(address _code, bytes4 _sig) public {
emit PublicCapabilityRemoved(msg.sender, _code, _sig);
}
}
51 changes: 51 additions & 0 deletions contracts/Roles2LibraryAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright 2017–2018, LaborX PTY
* Licensed under the AGPL Version 3 license.
*/

pragma solidity ^0.4.18;


contract Roles2LibraryInterface {
function addUserRole(address _user, uint8 _role) public returns (uint);
function canCall(address _src, address _code, bytes4 _sig) public view returns (bool);
}


contract Roles2LibraryAdapter {

uint constant UNAUTHORIZED = 0;
uint constant OK = 1;

event AuthFailedError(address code, address sender, bytes4 sig);

Roles2LibraryInterface roles2Library;

modifier auth {
if (!_isAuthorized(msg.sender, msg.sig)) {
emit AuthFailedError(this, msg.sender, msg.sig);
return;
}
_;
}

constructor(address _roles2Library) public {
roles2Library = Roles2LibraryInterface(_roles2Library);
}

function setRoles2Library(Roles2LibraryInterface _roles2Library) auth external returns (uint) {
roles2Library = _roles2Library;
return OK;
}

function _isAuthorized(address _src, bytes4 _sig) internal view returns (bool) {
if (_src == address(this)) {
return true;
}
if (address(roles2Library) == 0) {
return false;
}

return roles2Library.canCall(_src, this, _sig);
}
}
8 changes: 8 additions & 0 deletions contracts/helpers/StubStorageManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
pragma solidity ^0.4.23;


contract StubStorageManager {
function isAllowed(address _actor, bytes32 _role) public view returns (bool) {
return true;
}
}
5 changes: 5 additions & 0 deletions migrations/1_initial_migration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var Migrations = artifacts.require("./Migrations.sol");

module.exports = function(deployer,network) {
deployer.deploy(Migrations);
};
19 changes: 19 additions & 0 deletions migrations/2_deploy_storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const Storage = artifacts.require('Storage')
const StubStorageManager = artifacts.require('StubStorageManager')
const Roles2Library = artifacts.require('Roles2Library')

module.exports = function(deployer, network) {
if (network === 'development') {
deployer.then(async () => {
await deployer.deploy(Storage)
await deployer.deploy(StubStorageManager)

const storage = await Storage.deployed()
await storage.setManager(StubStorageManager.address)

await deployer.deploy(Roles2Library, Storage.address, "Roles2Library")

console.log("[MIGRATION] Test contracts: #done")
})
}
}
Loading

0 comments on commit 2f6fc0d

Please sign in to comment.