Skip to the content.

Access Control Permissions

Introduction

There are two types of Access Control Permissions in BriteAccess: system permissions and custom Permissions. System permissions cannot be edited by users directly but will have to be changed in code by the owner of the system permission. Custom permissions are created in each client site and can be altered by users that are appropriately authorized to do so.

Each Access Control Permission (ACP) has a name that is at least 6 characters long and begins with a capital letter. Only alphanumeric characters are allowed in an ACP’s name. ACPs can also have a description mentioning their purpose.

An ACP can have one or more statements that are used to determine which actions a user is denied or allowed to perform. The section on ACP statements below goes into details of how to craft these statements and what each field does.

ACPs can be assigned to roles and cannot be directly assigned to a user. When a user selects a role for their current session, they receive permissions from all the ACPs that are assigned to the chosen role only.

ACP Statements

The basic structure of an ACP statement is as follows:

[
  {
    "sid": 1,
    "effect": "(allow|deny)",
    "resource": "org.domain.resource",
    "actions": ["action"],
    "records": ["*"],
    "condition": {},
    "transform": {}
  }
]

The top level data structure in an ACP is a list of dicts. Each dict is a permission statement and there can be multiple statements in an ACP. There are no hard restrictions imposed on how each statement is related to other statements within the same permission. This opens up possibilities of having a statement that allows access to a resource and then a statement which will deny access to another resource all within a single permission. For the sake of clarity, it is recommended to group permission statements around allowances and designing the permissions in such a way that they are all related to a single resource, or group of resources.

ℹ️ Note: The examples below may have incomplete ACPs to focus on key aspects relevant to the section while ignoring the others. A real ACP requires all the fields to be present.

Statement id

[
    {
        "sid": 1
    },
    {
        "sid": 2
    }
]

Each statement in an ACP has an integer statement id (sid) which should be unique within the given ACP. The example ACP at the beginning of this section has two statements with sids of 1, and 2.

Resource

[
    {
        "sid": 1,
        "resource": "bc.briteaccess.role"
    },
    {
        "sid": 2,
        "resource": "bc.briteaccess.role-user-assignment"
    }
]

Values in the resource field are structured as organization.domain.resource. bc is the only organization supported at the moment. domain corresponds to the service that owns the resources being acted upon - it takes on values such as briteaccess and britequote. The resource field specifies the resource under consideration in the current statement. An ACP trying to grant a user permissions on a quote, would have its resource field set to bc.britequote.quote.

As mentioned above, it is possible to design ACPs with statements targeting different resources. However, it is strongly recommended to design ACPs around common groups of resources.

The example ACP has now been extended such that it acts on roles and role-user-assignemnts.

Action

[
    {
        "sid": 1,
        "resource": "bc.briteaccess.role",
        "actions": ["RetrieveList"]
    },
    {
        "sid": 2,
        "resource": "bc.briteaccess.role-user-assignment",
        "actions": ["RetrieveList", "RetrieveRecord", "Create"]
    }
]

The actions property is a list of strings and each of these represent various actions that can be performed on the resource. Some common actions that can be performed are Create, RetrieveList, RetrieveRecord, Update, and Delete. While these are commonly used actions, it is perfectly acceptable to name other actions as required by the service. For example, an action like Verify or Enable may be required if the common actions are insufficient for your use case.

⚠️ Care should be taken to ensure that there is uniformity in the naming scheme of these actions and to avoid redundancy. For instance, an action called CreateRole that acts on bc.briteaccess.role would be unnecessary as just Create would be sufficient. Having CreateRole instead of Create could also lead to confusion for permission authors since they would be expecting just Create unless they look at specific documentation for action names.

Effect

[
    {
        "sid": 1,
        "resource": "bc.briteaccess.role",
        "actions": ["RetrieveList"],
        "effect": "deny"
    },
    {
        "sid": 2,
        "resource": "bc.briteaccess.role",
        "actions": ["RetrieveList"],
        "effect": "allow"
    },
    {
        "sid": 3,
        "resource": "bc.briteaccess.role",
        "actions": ["RetrieveList"],
        "effect": "allow"
    },
    {
        "sid": 4,
        "resource": "bc.briteaccess.role-user-assignment",
        "actions": [
            "RetrieveList",
            "RetrieveRecord",
            "Create",
            "Update",
            "Delete"
        ],
        "effect": "allow"
    }
]

The effect field can take on two values - allow or deny. The default behaviour of BriteAccess is to deny actions i.e. if none of the ACPs of the user have a matching ACP for the action they are trying to perform, then BriteAccess will deny the request implicitly. As such, we can design our ACP statements to only allow the user to perform certain actions while not mentioning the resources and actions that the user does not need access to.

It is important to note that even a single deny statement will override any number of allowances the user may have for the same resource-action, and the action will be denied.

In the example above, the first statement has a deny effect preventing the user from listing roles. The second and third statements on the other hand have an allow effect for the same action. But when the user tries to list roles, the action will be denied.

Record

[
    {
        "resource": "bc.briteaccess.role",
        "effect": "allow",
        "actions": ["Delete"],
        "records": ["AllowAllActions"]
    },
    {
        "resource": "bc.briteaccess.role-user-assignment",
        "effect": "allow",
        "actions": ["Delete"],
        "records": ["*"]
    }
]

The records field allows for targeted permissions where a user is granted permissions to act on selected records of a resource. This field is limited in its ability to grant targeted permissions compared to conditions which can handle various scenarios robustly as described below.

In the example above, the first statement grants the user permission to delete a single role named AllowAllActions. On the other hand, the second statement has a wildcard "*" in the records list so the user has permission to delete all role user assignments.

Condition

[
    {
        "sid": 1,
        "resource": "bc.briteauth.user",
        "effect": "allow",
        "actions": ["Delete"],
        "condition": {}
    },
    {
        "sid": 2,
        "resource": "bc.briteauth.group",
        "effect": "allow",
        "actions": ["Delete"],
        "condition": {
            "ForAnyValue:StringEquals": {
                "$subject.groups": "Administrator"
            }
        }
    },
    {
        "sid": 3,
        "resource": "bc.briteaccess.role",
        "effect": "allow",
        "actions": ["Delete"],
        "condition": {
            "StringEquals": {
                "$record.id": "1234"
            }
        }
    },
    {
        "sid": 4,
        "resource": "bc.britequote.quote",
        "effect": "allow",
        "actions": ["Bind"],
        "condition": {
            "StringEquals": {
                "$context.straight_through_processing_status": "pass"
            }
        }
    }
]

conditions are a more advanced variant of the records field.

An ACP with no condition is always taken into consideration while evaluating a user’s access attempt. A user with the ACP exemplified above will be allowed to delete BriteAuth users without any restrictions as the conditions dict is empty in the first statement.

In the second statement, the condition is set to "ForAnyValue:StringEquals": {"$subject. groups": "Administrator"}. The key $subject.groups is expanded into a list of all the groups that the user belongs to. An action to delete a BriteAuth group is only allowed if the user belongs to the Administrator group. $subject.name and $subject.role are other string attributes of the user that can be used to craft conditions based on the user’s configuration.

The third statement has a StringEquals condition which simply compares two string values. Much like in the example under the documentation of records, the user is only allowed to delete the role if the role’s id i.e. $record.id is “1234”. $record may have different keys available for comparison depending on the resource under consideration.

The fourth statement introduces $context used in comparison. $context is a dict that contains information which is not present in the request sent by the user but is instead provided by the target of the API call. From the example ACP statement, we can infer that information about the STP status of a quote is not available in the request sent from the client trying to bind a quote. The BriteQuote service would have to enrich the request with this information so that BriteAccess can allow the user to bind a quote if STP status is “pass”. Additional coding is required to pass $context info along to the enforcement commander. See more about how to set that up in our context documentation.

Transform

[
    {
        "sid": 1,
        "resource": "bc.briteclaims.claim",
        "effect": "allow",
        "actions": ["RetrieveList"],
        "transform": {
            "query-parameters": {
                "producers": "$britecore_contacts.retrieve_relationships?relationships=self,agents_in_agencies&attrs=id"
            }
        }
    }
]

The transform field allows an ACP to manipulate the request made by the user. In the above example, when a user tries to list claims, the request is allowed. However, we do not want an agent to view all the claims in the system but only those claims for which they themselves, or another agent in their agency is the producer. So, we add some query parameters to the request so that the result from claims can be filtered such that the user only sees a subset of claims in the system.

The transform field can be read as follows:

Thus, a request that would have originally looked like /claims would eventually be seen by the Claims API as /claims?producers=agent-id-1&producers=agent-id-2. It is not within the scope of BriteAccess to ensure that the claims endpoint is only returning claims of these agents.