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 allow
s 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 allow
ances 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 sid
s 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 role
s and role-user-assignemnt
s.
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 allow
ances 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 condition
s 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"
}
}
}
]
condition
s 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:
-
query-parameters: {producers: ...}
mentions that a query parameter of the request calledproducers
must be altered before the request is allowed to go through. ℹ️ If this parameter does not exist, it will be created. -
$britecore_contacts
refers to the contacts module of the previous generation system where relationships such as agent-agency etc. are stored. It is possible to add more targets to fetch information from. -
retrieve_relationships
specifies that we would like to get contacts related to the current user. -
relationships=self,agents_in_agencies
specifies that we’d like to get a list containing information about the current user and all the agents in their agencies. -
attrs=id
specifies that we only need theid
of the related contacts.
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.