Policy-Based Access Control
The policy-based access control system determines accessibility of objects (tables, records, views, roles) to a principal role based on lookup of the rule scope, and filtering based on the filter expression syntax defined in Filtering Expressions.
A policy for a given tenant consists of a set of rules. Rules consist of three components: the rule scope, which determines to which subjects and objects the rule applies; a set of capabilities for which the rule is relevant; and a filter expression, which is applied to queries and mutations during execution. Not all of these components are always required: a rule must always have a set of capabilities, and at least a set of targets in its scopes, but rules may provide scopes with no filter, or scopes with no roles or role classes specified. When and how this is viable is discussed below.
ALL policy rules grant access: it is not possible to use a rule to deny access. The default action for the security system is to deny access: thus, denial of access is handled by not having a rule which grants it. This is for two reasons: first, "default deny" is just good security sense, and second, having rules which only grant access simplifies parsing and evaluation. The rule is only required to be a set of scopes, capabilities, and filter.
Rule scopes may consist of the following:
Role IDs. This is a list of role identifiers. To be in scope, the principal must have one of the listed IDs.
Role classes. A list of role class identifiers. Being a member of one of the listed role classes means the principal is in this rule's scope.
Target names. This is a list of table or view names, or exported function names, optionally schema-qualified if that is supported by the database backend. The rule is in scope if the target of a given query or mutation is one of the listed objects.
Rule capability sets list which capabilities are allowed to be used on the object, and thus, which operations may be performed under this rule.
Finally, rule filters are filters that are applied to queries and mutations, to modify which rows are returned or acted upon. These are specified using the expressions grammar defined in Filtering Expressions. Filters will be applied when a rule matches, and are checked for validity against the target (columns specified must exist, for instance).
Rule Structure
Rule objects are represented as structures (dictionaries, objects, what have you) with the following elements, here specified in a JSON-like syntax, with data types:
{
ruleid: integer (auto),
name: string,
tenantid: integer,
capabilities: [string,...]
scopes: {roles: [int,...],
classes: [int,...],
targets: [string,...],
},
filter: string,
creatorid: int (auto),
createtime: tstamp (auto)
}
A few explanations: "auto" means that the value of the field is automatically assigned by the database or by Devii. The 'tstamp' type means a SQL datetime type, which will correspond to "timestamp without time zone" for Oracle and PostgreSQL, or "datetime" for MySQL and MSSQL.
Required Elements
Not all elements of a rule are required. For instance, it is not necessarily required to specify all possible scope types, nor is it necessary to add a filter to every rule. Every rule MUST always have the following: name, tenantid, capabilities, and targets scope. A rule MAY also have any of the following: a scope which specifies a set of roles or role classes; or a filter.
In addition, for some capabilities, certain scopes are not valid, and filters are not valid either. The requirements and options for each capability in Devii Core are described here.
select, insert, update, delete: Must specify a target scope. Other scopes are valid, as well as filters.
create_role, create_class, update_role, update_class, delete_role, delete_class, view_role, view_class: Must specify role or role class scope. Target scopes must be "roles" for roles, "role_classes" for classes. Filters are valid, and may filter on role or role class attributes.
set_policy, login, admin: These capabilities may not be part of a rule specification.
Filtering Special Note
Rule filters work mostly like normal table filters, as specified for the GraphQL query filter syntax. However, there is a special variable name that rules may use when referring to the principal performing an operation: $_PRINCIPAL. As this is not a valid SQL identifier, there is no chance of a column having the same name. $_PRINCIPAL will be substituted with the attribute requested from the principal, as follows:
- $_PRINCIPAL.roleid => integer ID of the principal.
- $_PRINCIPAL.classes => list of class IDs.
- $_PRINCIPAL.parentid => parent ID.
- $_PRINCIPAL.children => list of child IDs.
- $_PRINCIPAL.tenantid => tenantid of the principal.
Sample Rules
Here is a set of sample rule structures (the relevant bits), and the English language meaning of each of them.
"The accounting department (role class 12) can read all records in the sales_transactions table, the expense_transactions table, the views for daily, weekly, and monthly sales."
Rule:
{
capabilities: ['select'],
scopes: {
classes: [12],
targets: ['sales_transactions', 'expense_transactions', 'daily_sales',
'weekly_sales', 'monthly_sales']
}
}
"Brian, role ID 1337, who does QA on field boundaries, can view and edit any record in the boundaries table flagged as unfinished (a boolean attribute)."
Rule:
{
capabilities: ['select', 'update'],
scopes: {
roles: [1337],
targets: ['boundaries']
}
filter: 'unfinished = true'
}
"Phil, role ID 4242, is an agriculturist (class 42). All agriculturists can fully access boundaries assigned to them as the agriculturist."
Rule:
{
capabilities: ['select', 'insert', 'update', 'delete'],
scopes: {
classes: [42],
targets: ['boundaries']
}
filter: 'agriculturist = $_PRINCIPAL.roleid'
"All roles can fully access records on the posts table that they created, or that their child roles created."
Rule:
{
capabilities: ['select', 'insert', 'update', 'delete'],
scopes: {
targets: ['posts']
}
filter: 'creatorid = $_PRINCIPAL.id OR creatorid IN $_PRINCIPAL.children'
}
Rules Tables Layout: "policy_rules"
ruleid: Integer. ID of the rule. Primary key.
name: Text. Name of the rule.
filter: Text. Filter expression, using Devii expressions syntax.
global: Boolean. If true, this rule has no role or class scopes, and access to the targets is granted to all roles.
creatorid: Integer. ID of the role that created this rule.
createtime: Timestamp. Date and time rule was created.
policy_rule_capabilities: Subtable of capabilities. Names the operations that can be performed on the targets using this rule. Two columns: ruleid and capability.
policy_rule_roles: Subtable of role IDs. Used for scoping by role. Two columns: ruleid and roleid.
policy_rule_role_classes: Subtable of role class IDs. Scoping by role class. Two columns: ruleid and classid.
policy_rule_targets: Subtable of table, view, and function names. Scoping by target table , view, or function. Two columns: ruleid and targetname.