Access Control

Access Control is role-based authorization for Daml. It is the Daml analogue of OpenZeppelin's AccessControl and AccessControlDefaultAdminRules. An admin party (the DEFAULT_ADMIN_ROLE holder in Solidity) grants named roles to parties, and gated choices check that the caller holds the required role.

The package has no dependency on other OpenZeppelin packages.

Experimental. Version 0.x, unaudited, and subject to a redesign before release. Source: experiments/access/access-control-v1 in OpenZeppelin/canton-contracts.

This page documents the current experimental package. A replacement is in review in PR #48 and will be merged soon. The new ScopedAuthorizationGrantV1 component issues revocable, time-bounded grants. Before a protected choice runs, the application checks the caller, the issuing authority, and the resource scope of the grant. A treasury example in the PR builds role-based access control on these grants. This page will change when the new version is merged.

import OpenZeppelin.AccessControlV1

The Daml Model

Roles are Text. Solidity uses bytes32 role IDs such as keccak256("MINTER_ROLE"). A Daml template field cannot be a type variable, so a generic role primitive stores role : Text. For compile-time checks, define your own role type and convert it with a function such as roleId : MyRole -> Text.

A grant is a contract. Daml-LF 2.1 has no contract keys, so there is no global role table. A RoleGrant is a contract that admin signs and that names one account. A gated choice fetches the grant that the caller presents and checks three things: admin issued it, it names the caller, and it carries the required role. A party cannot forge a grant, because only admin can sign one. A party that presents a grant for another account fails the check.

Templates

RoleGrant

A role that admin grants to account.

FieldTypeDescription
adminPartyThe authority that issued the grant.
accountPartyThe party that holds the role.
roleTextThe role ID, for example "MINTER_ROLE".

Signatory admin, observer account.

  • RoleGrant_Renounce (controller account, consuming, returns ()): the holder gives up the role (the renounceRole analogue).

admin can also archive a grant directly, because it is the signatory.

RoleAdmin

The contract that issues and revokes grants. It has one field, admin : Party. Signatory admin. All choices are nonconsuming, so the contract stays active.

ChoiceControllerArgumentsResult
RoleAdmin_GrantRoleadminaccount, roleContractId RoleGrant
RoleAdmin_RevokeRoleadmingrantCid()
RoleAdmin_GrantRoleAscallercaller, adminRole, adminGrantCid, account, roleContractId RoleGrant
RoleAdmin_RevokeRoleAscallercaller, adminRole, adminGrantCid, grantCid()
RoleAdmin_BeginDefaultAdminTransferadminnewAdmin, role, effectiveTimeContractId DefaultAdminTransferOffer

There are two ways to manage roles:

  • Directly. admin grants or revokes any role.
  • Through a delegate (the getRoleAdmin analogue). The caller presents its own grant for adminRole, then grants or revokes role. The choice runs with the authority of the RoleAdmin contract, so admin still signs the new grant. The delegate needs RoleAdmin disclosed to it, usually by an off-ledger service of the application.

The library has no role hierarchy. The caller supplies adminRole, and the choice checks only that the caller holds a grant for it. A party that can see RoleAdmin and holds any grant from the same admin can name that role as adminRole, then grant or revoke any role. The application must check that adminRole is the correct admin role for role before it accepts a delegated grant or revoke, and it must bind grants to the resource that they protect.

DEFAULT_ADMIN_ROLE has no special handling. Restrictions on who can delegate the root role are application policy.

DefaultAdminTransferOffer

A pending, timelocked transfer of a role to newAdmin (the AccessControlDefaultAdminRules analogue). Use the DEFAULT_ADMIN_ROLE ID as role to hand over the default admin role.

FieldTypeDescription
adminPartyThe current admin.
newAdminPartyThe party that receives the role.
roleTextThe role to transfer.
effectiveTimeTimeThe earliest ledger time for acceptance.

Signatory admin, observer newAdmin.

  • DefaultAdminTransferOffer_Accept (controller newAdmin, returns ContractId RoleGrant): accept when ledger time is at or after effectiveTime. Creates a RoleGrant of role for newAdmin.
  • DefaultAdminTransferOffer_Cancel (controller admin, returns ()): cancel the transfer at any time before acceptance (the cancelDefaultAdminTransfer analogue).

Acceptance does not revoke the role from the current admin. To make the transfer exclusive, revoke the old grant after acceptance.

Helper Functions

  • requireRole : Party -> Text -> Party -> RoleGrant -> Update (): the onlyRole modifier analogue. Call it at the start of a gated choice with the caller, the required role, the expected admin, and the fetched grant.
  • hasRole : Party -> Text -> Party -> RoleGrant -> Bool: the same check as a pure predicate.
  • requireTimelockElapsed : Time -> Update (): fails if ledger time is before the given time. DefaultAdminTransferOffer uses it, and you can use it in your own timelocks.

Gating a Choice

A privileged choice fetches the grant that the caller presents and checks it first. In this example, the enclosing template has an admin : Party field:

nonconsuming choice Mint : ContractId Token
  with
    caller : Party
    grantCid : ContractId RoleGrant
    amount : Int
  controller caller
  do
    grant <- fetch grantCid
    requireRole caller "MINTER_ROLE" admin grant
    create Token with owner = caller; amount

The fetch succeeds because caller is an observer of its own grant.

Errors

MessageCause
AccessControl: grant admin is not the expected authorityA different admin issued the presented grant, or the grant to revoke.
AccessControl: grant does not name the caller (impersonation)The grant names another party.
AccessControl: grant does not carry the required roleThe grant is for another role.
AccessControl: default-admin handoff nominee is the current adminnewAdmin is the current admin.
AccessControl: default-admin transfer timelock has not elapsedAcceptance before effectiveTime.