Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
1327b8f
add AccessManager.sol
frangio Mar 1, 2023
b54b922
docs
frangio Mar 1, 2023
5a0fd33
enforce team existence
frangio Mar 1, 2023
ee5de96
remove on-chain enumerability
frangio Mar 15, 2023
6284768
add Authority interface
frangio Mar 15, 2023
bce3641
remove redundancy
frangio Mar 15, 2023
223e6af
simplify and complete features
frangio Mar 16, 2023
eafb571
split in separate files
frangio Mar 16, 2023
24af159
write AccessManager docs
frangio Mar 16, 2023
833f219
add docs for AccessManagerAdapter
frangio Mar 16, 2023
0f27739
add to docs site
frangio Mar 16, 2023
2ac5aa1
docs for IAuthority
frangio Mar 16, 2023
f26c4d3
lint
frangio Mar 16, 2023
63f9393
add initial AccessManaged docs
frangio Mar 16, 2023
6fecd25
typos
frangio Mar 16, 2023
589b5b1
Update contracts/access/manager/AccessManager.sol
frangio Mar 16, 2023
619cc73
fix docs
frangio Mar 16, 2023
1b681a5
rename team "all" -> "public"
frangio Mar 16, 2023
48b31c0
ad hoc selector batching
frangio Mar 16, 2023
b078157
whitespace
frangio Mar 17, 2023
274c04d
add docs on team number choice
frangio Mar 17, 2023
082b05c
add missing function argument
frangio Mar 17, 2023
52f27c8
fix team role decoding
frangio Mar 17, 2023
1285c49
add ungrouped interface
frangio Mar 17, 2023
5c7472a
improve docs
frangio Mar 17, 2023
3cc1e73
remove todo
frangio Mar 17, 2023
ae3482d
add initial tests for teams
frangio Mar 19, 2023
535ea13
rename team -> badge
frangio Mar 19, 2023
68bb5fa
improve testing
frangio Mar 19, 2023
fa58ec5
Update contracts/access/manager/AccessManager.sol
frangio Mar 19, 2023
6d76058
use mapping labels from solidity 0.8.18
frangio Mar 20, 2023
3447438
add AccessManaged tests
frangio Mar 20, 2023
1d10639
fix group encoding
frangio Mar 20, 2023
d875a7b
add test for allowing
frangio Mar 20, 2023
d517d27
lint
frangio Mar 20, 2023
6d33cda
add changeset
frangio Mar 20, 2023
9ed1aef
lint
frangio Mar 20, 2023
6adc0d8
fix mock
frangio Mar 21, 2023
0624794
remove contract groups
frangio Mar 21, 2023
17ec3b6
add transferContractAuthority
frangio Mar 21, 2023
f9210c5
lint
frangio Mar 21, 2023
a54cd10
rename badge -> group
frangio Mar 21, 2023
8af9ca4
change test name
frangio Mar 21, 2023
2a05bcc
add setContractModeCustom and tests
frangio Mar 21, 2023
5f22f2d
remove use of mapping labels
frangio Mar 21, 2023
6a9fbed
Merge branch 'master' into accessmanager
frangio Mar 22, 2023
0580198
tweak comments
frangio Mar 22, 2023
e01e362
add tests for adapter
frangio Mar 22, 2023
cb026bc
lint
frangio Mar 22, 2023
76d35da
add revert reasons and group tests
frangio Mar 22, 2023
4069781
add tests for allowing and disallowing roles
frangio Mar 22, 2023
80ffd2a
lint
frangio Mar 22, 2023
3094d0c
add docs for AccessManaged
frangio Mar 22, 2023
b3a8b1b
Update contracts/access/manager/AccessManager.sol
frangio Mar 22, 2023
8a68322
Update contracts/access/manager/AccessManager.sol
frangio Mar 22, 2023
eeab8cf
Update contracts/access/manager/AccessManager.sol
frangio Mar 22, 2023
ca61e38
add tests for onlyDefaultAdmin
frangio Mar 22, 2023
8824c31
lint
frangio Mar 22, 2023
67e33b6
add internal _setContractMode
frangio Mar 22, 2023
5f270c7
Update .changeset/quiet-trainers-kick.md
frangio Mar 22, 2023
6f7ac96
Update contracts/access/manager/AccessManager.sol
frangio Mar 22, 2023
7ec5311
Update contracts/access/manager/AccessManager.sol
frangio Mar 22, 2023
82402d2
typo
frangio Mar 22, 2023
27807e6
Apply suggestions from code review
frangio Mar 22, 2023
80bc88b
roll back to 0.8.13
frangio Mar 22, 2023
cf4df22
add test for setFunctionAllowedGroup events
frangio Mar 22, 2023
bab4d34
remove mode restriction on setFunctionAllowedGroup
frangio Mar 22, 2023
642e279
simplify use of setFunctionAllowedGroup
frangio Mar 22, 2023
51aff23
lint
frangio Mar 22, 2023
6e3da66
remove unused import
frangio Mar 22, 2023
6b56c8f
remove onlyDefaultAdmin modifier
frangio Mar 22, 2023
b7e3b3f
lint
frangio Mar 22, 2023
8200986
use return value names for _decodeGroupRole
frangio Mar 22, 2023
18e53e2
rename RestrictedMode -> AccessMode
frangio Mar 22, 2023
f94a881
use Context._msgSender
frangio Mar 22, 2023
cd8babd
reorder arguments to grant/revoke/renounceGroup
frangio Mar 22, 2023
597edc0
add IAccessControlDefaultAdminRules
frangio Mar 22, 2023
ee560d0
Apply suggestions from code review
frangio Mar 22, 2023
33f5ace
Fix expected event parameter name in tests
ernestognw Mar 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
add AccessManager.sol
  • Loading branch information
frangio committed Mar 1, 2023
commit 1327b8f0d488203b9ee07ba168f5aa70b4fa472c
5 changes: 5 additions & 0 deletions contracts/access/AccessControl.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ abstract contract AccessControl is Context, IAccessControl, ERC165 {
_;
}

modifier onlyDefaultAdmin {
_checkRole(DEFAULT_ADMIN_ROLE);
_;
}

/**
* @dev See {IERC165-supportsInterface}.
*/
Expand Down
205 changes: 205 additions & 0 deletions contracts/access/AccessManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./AccessControl.sol";
import "./AccessControlDefaultAdminRules.sol";

/// AccessManager is a central contract that stores the permissions of a system. It is an AccessControl contract, i.e.
/// it has roles and all the standard functions like `grantRole` and `revokeRole`, but it defines a particular set of
/// roles, with a particular structure.
///
/// Users are grouped in "teams". Teams must be created before users can be assigned into them, up to a maximum of 256
/// teams. A user can be assigned to multiple teams. Each team defines an AccessControl role, identified by a role id of
/// the form [specific format TBD, something like "<team role tag> ... <team number (0-255)>"].
///
/// Contracts in the system are also grouped. These are simply called "contract groups". There can be an arbitrary
/// number of groups. Each group has a group admin role that is allowed to add new contracts in the system into that
/// contract group.
///
/// All contracts in a group share the same permissioning scheme. A permissioning scheme consists of a mapping between
/// functions and allowed teams. Each function can be allowed for multiple teams, meaning that if a user is in at least
/// one of the allowed teams they can call that funcion.
///
/// TODO: Implement AccessMode (from zkSync AllowList) in terms of teams and groups.
/// - Define the built-in "all" team (#255?) of which everyone is a member.
/// - Define a contract group where every function is allowed to the "all" group. (-> AccessMode = Open)
/// - Define a contract group where no function is allowed to any group. (-> AccessMode = Closed)
interface IAccessManager {
function canCall(address caller, address target, bytes4 selector) external view returns (bool allowed);

function createTeam(uint8 team, string calldata name) external;

function updateTeam(uint8 team, string calldata name) external;

function getTeamName(uint8 team) external view returns (string calldata name);

// The result is a bit mask.
function getUserTeams(address user) external view returns (bytes32 teams);

// The result is a bit mask.
function getFunctionAllowedTeams(bytes32 group, bytes4 selector) external view returns (bytes32 teams);

function addFunctionAllowedTeam(bytes32 group, bytes4 selector, uint8 team) external;

function removeFunctionAllowedTeam(bytes32 group, bytes4 selector, uint8 team) external;

function getContractGroup(address target) external view returns (bytes32 group);

function setContractGroup(address target, bytes32 group) external;

function setInitialContractGroup(address target, bytes32 group) external;

function getContractGroupAdmin(bytes32 group) external view returns (bytes32 adminRole);

function setContractGroupAdmin(bytes32 group, bytes32 adminRole) external;

function setRoleAdmin(bytes32 role, bytes32 adminRole) external;

struct EnumeratedTeam {
uint8 id;
string name;
}

/// Decodes a bit mask into array of team ids and names.
/// Meant for user interface purposes.
/// Can be used with the outputs of `getUserTeams` and `getFunctionAllowedTeams`.
function enumerateTeams(bytes32 teams) external view returns (EnumeratedTeam[] memory);
}

contract AccessManager is IAccessManager, AccessControl /*, AccessControlDefaultAdminRules */ {
mapping (uint8 => string) private _teamName;
mapping (address => bytes32) private _userTeams;
mapping (bytes32 => mapping (bytes4 => bytes32)) private _allowedTeams;
mapping (address => bytes32) private _contractGroup;
mapping (bytes32 => bytes32) private _groupAdmin;

event TeamUpdated(uint8 indexed team, string name);
event ContractGroupAdminUpdated(bytes32 indexed group, bytes32 indexed adminRole);
event TargetGroupUpdated(address indexed target, bytes32 indexed group);

function canCall(address caller, address target, bytes4 selector) public view returns (bool) {
bytes32 group = getContractGroup(target);
bytes32 allowedTeams = getFunctionAllowedTeams(group, selector);
bytes32 callerTeams = getUserTeams(caller);
return callerTeams & allowedTeams != 0;
}

function createTeam(uint8 team, string calldata name) public virtual onlyDefaultAdmin {
require(bytes(_teamName[team]).length == 0);
_updateTeam(team, name);
}

function updateTeam(uint8 team, string calldata name) public virtual onlyDefaultAdmin {
require(bytes(_teamName[team]).length > 0);
_updateTeam(team, name);
}

function _updateTeam(uint8 team, string calldata name) internal virtual {
require(bytes(name).length > 0);
_teamName[team] = name;
emit TeamUpdated(team, name);
}

function getTeamName(uint8 team) public view virtual returns (string memory) {
return _teamName[team];
}

function getUserTeams(address user) public view virtual returns (bytes32) {
return _userTeams[user];
}

function _grantRole(bytes32 role, address user) internal virtual override {
super._grantRole(role, user);
(bool isTeam, uint8 team) = _parseTeamRole(role);
if (isTeam) {
bytes32 mask = bytes32(1 << team);
_userTeams[user] |= mask;
}
}

function _revokeRole(bytes32 role, address user) internal virtual override {
super._revokeRole(role, user);
(bool isTeam, uint8 team) = _parseTeamRole(role);
if (isTeam) {
bytes32 mask = bytes32(1 << team);
_userTeams[user] &= ~mask;
}
}

function _parseTeamRole(bytes32 role) internal virtual returns (bool, uint8) {
bool isTeam = bytes1(role) == hex"01";
uint8 team = uint8(uint256(role & hex"ff"));
return (isTeam, team);
}

function getFunctionAllowedTeams(bytes32 group, bytes4 selector) public view virtual returns (bytes32) {
return _allowedTeams[group][selector];
}

function addFunctionAllowedTeam(bytes32 group, bytes4 selector, uint8 team) public virtual onlyDefaultAdmin {
bytes32 mask = bytes32(1 << uint8(team));
_allowedTeams[group][selector] |= mask;
}

function removeFunctionAllowedTeam(bytes32 group, bytes4 selector, uint8 team) public virtual onlyDefaultAdmin {
bytes32 mask = bytes32(1 << uint8(team));
_allowedTeams[group][selector] &= ~mask;
}

function getContractGroup(address target) public view virtual returns (bytes32) {
return _contractGroup[target];
}

function setContractGroup(address target, bytes32 group) public virtual onlyDefaultAdmin {
_contractGroup[target] = group;
}

function setInitialContractGroup(address target, bytes32 group) public virtual {
_checkRole(getContractGroupAdmin(group));

require(group != 0); // todo: make group optional, default to target-specific group
require(_contractGroup[target] == 0);
_contractGroup[target] = group;

emit TargetGroupUpdated(target, group);
}

function getContractGroupAdmin(bytes32 group) public view virtual returns (bytes32) {
bytes32 admin = _groupAdmin[group];
if (admin != 0) {
return admin;
} else {
return DEFAULT_ADMIN_ROLE;
}
}

function setContractGroupAdmin(bytes32 group, bytes32 adminRole) public virtual onlyDefaultAdmin {
_groupAdmin[group] = adminRole;
emit ContractGroupAdminUpdated(group, adminRole);
}

function setRoleAdmin(bytes32 role, bytes32 adminRole) public virtual onlyDefaultAdmin {
// todo: validate that the roles "exist"?
_setRoleAdmin(role, adminRole);
}

function enumerateTeams(bytes32 roles) public view virtual returns (EnumeratedTeam[] memory) {
EnumeratedTeam[] memory roleList = new EnumeratedTeam[](256);

uint nextPos = 0;

for (uint256 id = 0; id < 256; id += 1) {
bytes32 mask = bytes32(1 << id);
if (roles & mask != 0) {
roleList[nextPos].id = uint8(id);
roleList[nextPos].name = getTeamName(uint8(id));
nextPos += 1;
}
}

// todo: shrink array to length = nextPos and reset free pointer, if safe to do so

return roleList;
}
}