Active and Passive Components: writing rights

Writing rights on Organization architecture

As described here, a smart contract implementing the LazyInitCapableElement contract, provided by the swissKnife library, can define a custom permission system like an ACL (Access Control List).

In particular, in any Organization (in the broadest sense, i.e. the Organization contract and all its components) the writing rights are held by the following subjects:

  • Organization

  • active components of the Organization

Regarding point 1, the Organization has write rights on the Components only if they have the organization address as the host parameter. It is not said that it is always so.

Active Components

An active Component, also known as an "Authorized Component", is a Component that has write permissions for a Organization. This means that active Components themselves determine a Guild's Component architecture, and to perform operations on its behalf.

For example, an active Component with write permissions for an Organization can:

  • Link, replace and un-link Components (including itself)

  • Have the Organization call a specific contract, passing specified data

  • Use or move the Organization's money from the Treasury Manager

  • More generally, call functions that have the authorizedOnly modifier

On a more abstract level, an active Component is one that is set with the key parameter as active:

struct Component {
    bytes32 key;
    address location;
    bool active;
    bool log;
}

Like this, when using the set / batchSet functions to add / update Components:

function set(Component calldata) external returns(address replacedComponentAddress);

function batchSet(Component[] calldata) external returns (address[] memory replacedComponentAddresses);

There can be only one active (or passive) Component attached to each key. So, for example, there can only be one Proposal Manager attached to a key, but another Proposal Manager can be attached to another key.

One-Time Active Components

A one-time active Component is one that, in a single atomic transaction (and in this order):

  1. Is linked to a Organization via an active key

  2. Executes its code and its functionality

  3. Is deactivated (i.e the key is deactivated)

At Step 1, it gains write permissions for the Organization; in that moment, it becomes an authorized component, and belongs to the authorizedOnly modifier as an active Component.

But the moment it is deactivated, it loses write permissions and becomes an inactive one.

Use Case Examples

  • As soon as a Proposal has been voted on and accepted, the Proposal Manager can execute the Proposal code itself as a one-time Component; it must be able to write on the Guild:

function _performAuthorizedCall(address subject, bytes memory inputData) private {
    bytes32 key = _randomKey();
    IOrganization organization = IOrganization(host);
    organization.set(IOrganization.Component(key, subject, true, false)); //here the Proposal code is set as an active Component
    (bool result, bytes memory returnData) = subject.call(inputData); //Proposal code execution
    if(!result) {
        returnData = abi.encode(subject, returnData);
        assembly {
            revert(add(returnData, 0x20), mload(returnData))
        }
    }
    organization.set(IOrganization.Component(key, address(0), false, false)); //after the code execution the Proposal code is set as an inactive Component
}
  • A submittable microservice, at the moment the submit function is called, is executed by the Microservices Manager as a single-use Component; a submittable microservice must be able to write on a Guild:

function submit(string calldata key, bytes calldata data) external override payable returns(bytes memory returnData) {
    IOrganization organization = IOrganization(host);
    organization.storeETH(msg.value);
    (address location, bytes memory payload) = _runCheck(key, data, 1, msg.sender, msg.value);
    bytes32 randomKey = BehaviorUtilities.randomKey(_keyIndex++);
    organization.set(IOrganization.Component(randomKey, location, true, false)); //here the Microservice is set as an active Component
    returnData = location.submit(0, payload); //Microservice code execution
    organization.set(IOrganization.Component(randomKey, address(0), false, false)); //after the code execution the Microservice is set as an inactive Component
}

Why Distinguish Between Active and Passive Components?

Some Components linked to a Guild never need to be active, because they never need to write on other Components, or on the Guild. Rather, they only need to be written on, by active Components.

As they can't be written on, passive Component reduce the amount of possible access points to the Guild's, and thereby bolster its security.

Old Components

An 'old' Component (i.e, one replaced by a new version of itself on a specific key) is passive.

This means that it cannot write data on other Components or on the Guild. However, if still linked to the Guild (i.e still retains internal references to the Organization's address), it can continue to be called by the Organization's presently active Components to perform operations.

How authorizedOnly Is Implemented Referring Only to Active Components

A Guild has this specific permission level based on active and inactive Components built using the swissknife utility methods to create custom permissions in a complex architecture.

We have introduced how a Guild can have a complex permission architecture by using active and passive components.

Since only an active Component attached to the Organization contract has writing rights on others Components and on the Organization itself, the authorizedOnly modifier is customized to represents this behavior.

To learn more about how permissions work and their implementation look here.

In particular, the Organization contract implements this override _subjectIsAuthorizedFor function:

function _subjectIsAuthorizedFor(address subject, address location, bytes4 selector, bytes calldata, uint256) internal override view returns(bool, bool) {
    if(location == address(this) && selector == this.setHost.selector) {
        return (true, false);
    }
    return (true, isActive(subject));
}

The first part of the function says that no one can call the setHost function to change the host of a Guild.

The second part of the function says that the outputs of the function in all the other cases is the result of the isActive function.

(true, isActive(subject))

isActive checks if the subject as an active key and so is an active Component. In this case, the result will be true. If the subject doesn't correspond to an active key, the result will be false and so it has no permission to act on the Guild.

Last updated