Evaluation engines
Evaluation engines are used to implement checks for AccessPolicy rules
Last updated
Was this helpful?
Evaluation engines are used to implement checks for AccessPolicy rules
Last updated
Was this helpful?
Aidbox provides several ways to specify rules for AccessPolicy
resources — so-called evaluation engines. They come with their syntax and offer varying degrees of flexibility and convenience for writing those rules.
There are five engines. They are used to specify rules for a generic request object.
Allow
Matcho
JSON Schema
SQL
Complex
Two additional engines provide a convenient way to write rules specifically for RPC endpoints.
Allow-RPC
Matcho-RPC
It is recommended to use Matcho engine. In 90% of cases, it is enough. Sometimes, the complex access policy can be only written by SQL or Complex engines.
All access policies are listed in Access Control -> Access Policy Aidbox UI page.
To debug the created access policy, use .
The allow
engine always evaluates true
regardless of a request object. Use it if you want to provide unrestricted access to everything.
In this example, the request is allowed only if:
Uri is related to Encounter. E.g. /fhir/Encounter
or /Encounter
,
HTTP request is either GET
or POST
, not PUT
,
practitioner
parameter provided as a query string in case of the GET
request or body params in case of the POST
request must be equal to practitioner_id
of the user,
User data, which is fetched from the "user"
table must contain inpatient
department and practitioner.
Strings, numbers, and booleans are compared by value.
If the pattern is a dictionary, search for its inclusion into a test subject. This check is nested and recursive.
Pattern: {x: 1}
matches: {x: 1}
, {x: 1, y: 2, ...}
doesn't match: {z: 1}
Pattern {a: {b: 5}
matches: {a: {b: 5, c: 6, ...}, d: 7}
doesn't match: {a: {c: 5}}
, {b: {a: 5}}
If a pattern is an array, search for its elements in the order given.
Pattern [1, 2]
matches [1, 2]
, [1, 2, 3, …]
doesn't match: [2, 1]
If a string starts with #
, it is treated as a regular expression.
Pattern {a: "#\\d+"}
matches: {a: "2345"}
doesn't match: {a: "abc"}
?
)present? — matches non-null
values
Pattern {a: "present?"}
matches: {a: 5}
,{a: {b: 6}}
doesn't match: {b: 5}
nil? — matches null
values;
not-blank? — matches a non-empty string.
Pattern: {params: {user_id: ".user.id"}}
matches: {user: {id: 1}, params: {user_id: 1}}
where user.id == param.user_id
.
In this example, $matcho will evaluate true only if 'a' is equal to my-value from the context.
$enum — value must be equal to one of the items in the enumeration. Strings, numbers, and booleans are supported.
Pattern: {request-method: {$enum: ["get", "post"]}}
matches: {request-method: "post"},
{request-method: "get"}
doesn't match: {request-method: "put"}
$one-of — value must match any of the patterns. Should be used in cases, when $enum can not be used.
Pattern {a: {$one-of: [{b: "present?"}, {c: "present?"}]}}
matches {a: {c: 5}}
doesn't match: {a: {d: 5}
, {a: {b: null}
Note that the $one-of
key cannot be utilized concurrently with other keys.
Correct usage example. Here, the resource/type
key is correctly nested within each option specified by the $one-of
key.
Incorrect usage example.
In this case, the $one-of
key is improperly combined with the resource/type
key within the same params
block:
{reference: "Patient/pid"}
=> {id: "pid", resourceType: "Patient"}
Example: {resource: {patient: {$reference: {id: '.user.data.patient_id'}}}
$contains — collection must contain at least one match.
Pattern: {type: {$contains: {system: "loinc"}}
matches {type: [{system: "snomed"}, {system: "loinc"}]}
$every — each item in a collection must satisfy a pattern.
Pattern: {col: {"$every": {foo: "bar"}}
matches: {col: [{foo: "bar"}, {foo: "bar", baz: "quux"}]}
$not — negates a pattern.
Pattern: {message: {$not: {status: private}}
matches: {message: {status: public}}
doesn't match: {message: {status: private}}
.
Be careful when using $not, as it is possible to create policies that are too permissive.
Consider the following policy which uses $not
key.
While the original intent was to prevent Guest users from deleting Patient resources, this AccessPolicy allows them to perform DELETE /Patient/ for all other users, including unauthorized ones. In this case, it is better to explicitly list allowed roles with $enum.
$present-all — checks that every element in $present-all is present in the original array, with no sort check. It can be combined with $length.
$length — checks the length of the array.
Requires to specify type: rpc
in the AccessPolicy
resource.
allow-rpc
engine allows access to every RPC endpoint listed under the rpc
field of the AccessPolicy
resource.
Requires to specify type: rpc
for AccessPolicy
resource.
The sql
engine executes an SQL statement and uses its return value as an evaluation result. Thus the statement should return a single row with just one column:
SQL statement can refer to request fields with a double curly braces interpolation. The string inside the braces will be used as a path to value in the request object.
The SQL engine is sometimes the only way to perform complex checks when the AccesPolicy needs to check data stored in the database but not in the request context.
Suppose that we are checking a request that comes with a User
resource referencing a Practitioner
through User.data.practitioner_id
element. The following policy allows requests only to /fhir/Patient/<patient_id>
URLs and only to those patients who have a Patient.generalPractitioner
element referencing the same practitioner as a User
of our current request. In other words, User
as a Practitioner
is only allowed to see his own patients — those who reference him as their generalPractitioner
.
You can parameterize your SQL queries with request object using {{path}}
syntax. For example, to get a user role provided {user: {data: {role: "admin"}}}
you write {{user.data.role}}
. Parameter expressions are escaped by default to protect from SQL injection.
For dynamic queries — to parameterize table name, for example — you have to use {{!path}}
syntax. The expression SELECT true from {{!params.resource/type}} limit 1
when a request contains {params: {"resource/type": "Patient"}}
will be transformed into SELECT true from "patient".
By default, identifier names are double-quoted and lower-cased.
In this example, User
as a Practitioner
is only allowed to see the conditions of his patients — those who reference him as their generalPractitioner
.
The following policy requires request.params.resource/type
to be present and have a value of "Organization"
:
The complex
engine allows you to combine several rules with and
and or
operators. You can use any engine rule to define a rule and even complex
engine itself but it is forbidden to have both and
and or
keys on the same level. Rules are defined as an array of objects that must include an engine with a set of corresponding keys.
Aidbox applies inner policies one after the other top-bottom.
Aidbox applies policies till one of two events happens:
One policy rejects the access. No further policies are applied. The access is rejected
There are no more policies to evaluate. It means the access is granted
Aidbox applies policies till at least one grants access. If it has happened no further policies are applied.
Policy in the example above represents the following logical expression:
check-1 AND (check-2 OR check-3)
Let's split the SQL policy example from above into two separate rules and combine them under and
rule:
matcho
engine leverages pattern matching — custom DSL developed by Health Samurai. It has compact and declarative syntax with limited expressivity. It is well-suited for writing all sorts of rules and thus is one of the easiest options to specify AccessPolicy
checks.
To test matcho DSL without creating AccessPolicy and sending a request, you can use the endpoint.
If a string starts with .
, it is interpreted as a path in
$reference — parse Reference
or string into . It is useful to map FHIR reference into resourceType and id like this:
Need help with the matcho
engine? Contact us on the !
matcho-rpc
allows access to all endpoints that satisfy specified rules.
To control access, use tenant/org
in macho-rpc. tenant/org
contains the current organization id in the multitenancy API.
json-schema
engine allows you to use to validate the request object. It is specified under schema
a field of AccessPolicy
resource. The currently supported JSON Schema version is draft-07.