Ontoserver security model
Ontoserver's security model has two layers – API-level policies and Resource-level policies.
The policies themselves consist of the required permissions for an API request or a Resource, and the API and Resource-level authorisations granted to an entity.
Furthermore, Ontoserver can be operated at different enforcement levels via the configuration properties:
| Property | Options |
|---|---|
ontoserver.security.enabled |
false, true, fine |
ontoserver.security.readOnly.fhir |
false, true |
ontoserver.security.readOnly.api |
false, true |
ontoserver.security.readOnly.synd |
false, true |
API-level policies
Ontoserver can be configured to enforce role-based access control (RBAC) permissions with ontoserver.security.enabled equal to either true or fine.
Ontoserver provides three endpoint API families: /api (Admin), /fhir (FHIR), and /synd (Syndication).
Within each of these familes there are two role types: READ and WRITE, corresponding to whether the operation can change the state of the server (WRITE) or not (READ).
Additionally, there is one operation-specific permission for [base]/CodeSystem/$x-upload-external.
These authorisations are managed by the scope field of the JWT Token that is provided by the Authorization Server.
Ontoserver understands the following scopes:
system/*.readsystem/*.writesystem/CodeSystem.x-upload-externalonto/api.readonto/api.writeonto/synd.readonto/synd.write
Equivalently the authorities field can be used with the following corresponding values:
FHIR_READFHIR_WRITEFHIR_CS_X_UEAPI_READAPI_WRITESYND_READSYND_WRITE
If the Ontoserver is operating in “anonymous read” mode (i.e., ontoserver.security.readOnly.fhir=true), then all entities, whether authenticated or not, are deemed to have system/*.read (equivalently, FHIR_READ) authorisation.
Resource-level policies
Ontoserver can be configured to enforce Resource-level access control with ontoserver.security.enabled equal to fine.
Resource instance level authorisations are also managed by the scope field of the JWT Token that is provided by the Authorization server (or mapped-equivalent values in the authorities field).
These authorisations are layered over the API (/fhir) authorisations and further restrict what an entity may do.
This means an entity's API-level authorisations cannot be increased via this mechanism, but only reduced.
Resource-level authorisations are managed through FHIR's security labels model.
Security labels are attached to a Resource's meta element.
Ontoserver uses codes from the CodeSystem http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions to manage access to resources.
This CodeSystem is open-ended and has codes of the form: [category].read and [category].write, where [category] names an authorisation category, and is a non-empty sequence of the characters conforming to the regular expression [_a-zA-Z0-9].
Ontoserver understands the following scopes (where [category] is a placeholder described below):
grouping/*.readgrouping/*.writegrouping/[category].readgrouping/[category].write
or equivalent authorities values:
PERM_READPERM_WRITEPERM_[category]_READPERM_[category]_WRITE
If a resource has any security labels from this CodeSystem then the following rules are applied:
-
For READ access to any given Resource with a security label from the above CodeSystem and of the form
[category].read, Ontoserver will check that the entity's JWT Token contains a matchingscopevaluegrouping/[category].read(or containsgrouping/*.read). -
For READ access to any given Resource with a security label from the above CodeSystem and of the form
*.read, the entity must have API (/fhir) READ authorisation, but no additional checks are made. -
For WRITE access to any given Resource with a security label from the above CodeSystem and of the form
[category].write, Ontoserver will check that the entity's JWT Token contains a matchingscopevaluegrouping/[category].write(or containsgrouping/*.write). -
For WRITE access to any given Resource with a security label from the above CodeSystem and of the form
*.write, the entity must have API (/fhir) WRITE authorisation, but no additional checks are made.
Notes
A user only requires write permission to a resource to add security labels, and does not need read or write permission for the labels they are adding. So a user may add foo.read without having read access to the foo category themselves. This enables those with write access to resources to grant permissions to categories they may not themselves have rights to.
This does mean that users are able to modify security labels on a resource such that they no longer have read and/or write access themselves. This may be intentional, however if this occurs accidentally the grouping/*.write scope (PERM_WRITE authority) will allow a user to write to any resource regardless of its other security labels. Typically this scope is given to highly trusted users or administrators and allows them to correct security labels in this scenario.
When creating a resource only the system/*.write scope (FHIR_WRITE authority) is required - no security labels are in play on the server as the resource does not yet exist on the server. Similar to a resource update scenario, a user creating a resource can specify security labels in the resource meta on creation and need not have permission for these labels themselves in order to add these labels in resource creation.
If a resource is created with no security labels on a server with resource-level access control enabled, any user with the system/*.read (FHIR_READ authority) and/or system/*.write scope (FHIR_WRITE authority) will be able to read and/or modify the resource. I.e. no security labels on a resource implies no resource-level authority is necessary and only API-level authority is required for that resource until a security label is added.
Write permissions do not imply read permissions - i.e. adding the label foo.write does not imply foo.read which must additionally be explicitly added if the foo category requires both read and write permission.
Authorization process
When an entity requests authorisation, it can ask for grouping/*.read and grouping/*.write.
The authorisation server will either return a set of scopes specific to appropriate permissions categories of the entity, or the all categories scopes grouping/*.read and grouping/*.write, if appropriately authorised.
Note that these all categories scopes grouping/*.read (grouping/*.write) convey READ (WRITE) access to all resources, regardless of their security label(s).
Note, a scope conveying WRITE permissions does not also convey READ permissions.
Multi-Ontoserver environments
When multiple Ontoserver instances share the same authorisation server, it may be necessary to control which permissions apply to which instance. For example, a user may need write access on an authoring server but only read access on a production server. Ontoserver supports this through audience-prefixed permissions.
Configuration
Set ontoserver.security.audience on each Ontoserver instance to a unique value, typically its base FHIR URL:
| Instance | Configuration |
|---|---|
| Authoring | ontoserver.security.audience=https://author.example.com/fhir |
| Production | ontoserver.security.audience=https://tx.example.com/fhir |
How audience-prefixed permissions work
To grant a permission to a specific Ontoserver instance, prepend the instance's ontoserver.security.audience value directly to the permission value in the JWT token. No additional separator character is inserted; the resulting authority is exactly the audience value followed immediately by the permission value.
Each Ontoserver instance recognises permissions that are either:
- Unprefixed — applies to all Ontoserver instances that accept the token
- Prefixed with this instance's audience — applies only to this instance
Permissions prefixed with a different instance's audience are ignored.
For example, if ontoserver.security.audience=https://author.example.com/fhir, then the authority value https://author.example.com/fhirFHIR_WRITE is recognised as FHIR_WRITE by that instance only.
Which permissions support audience prefixing?
The following authorities claim values support audience prefixing across all endpoints:
| Authority | Equivalent scope | Endpoint |
|---|---|---|
FHIR_READ |
system/*.read |
/fhir |
FHIR_WRITE |
system/*.write |
/fhir |
FHIR_CS_X_UE |
system/CodeSystem.x-upload-external |
/fhir |
API_READ |
onto/api.read |
/api |
API_WRITE |
onto/api.write |
/api |
SYND_READ |
onto/synd.read |
/synd |
SYND_WRITE |
onto/synd.write |
/synd |
The scope claim values for /api and /synd endpoints (onto/api.read, onto/api.write, onto/synd.read, onto/synd.write) also support audience prefixing.
Note: The SMART-style
scopevaluessystem/*.read,system/*.write, andsystem/CodeSystem.x-upload-externaldo not support audience prefixing. When audience disambiguation is needed for FHIR endpoint permissions, use the equivalentauthoritiesvalues (FHIR_READ,FHIR_WRITE,FHIR_CS_X_UE).Resource-level permissions (
grouping/scopes andPERM_authorities) do not support audience prefixing.
Example
Given an authoring server with audience https://author.example.com/fhir and a production server with audience https://tx.example.com/fhir, a token with:
{
"authorities": [
"https://author.example.com/fhirFHIR_WRITE",
"https://author.example.com/fhirFHIR_READ",
"https://tx.example.com/fhirFHIR_READ",
"SYND_READ"
]
}
Would grant:
| Permission | Authoring server | Production server |
|---|---|---|
FHIR_WRITE |
yes | no |
FHIR_READ |
yes | yes |
SYND_READ |
yes | yes |
FHIR_WRITEis only granted to the authoring server because it is prefixed with the authoring server's audience.FHIR_READis granted to both because each has its own audience-prefixed grant.SYND_READis granted to both because it is unprefixed — unprefixed permissions apply everywhere.
Alternative: single-audience tokens
If the authorization server can issue tokens for a single audience at a time (based on the requested audience), then audience-prefixing permissions is not necessary. The authorization server issues each token containing only the permissions relevant to the requested Ontoserver instance, and standard unprefixed permission values are used.
This approach is simpler but requires the client to obtain a separate token for each Ontoserver instance.
Use Cases / Requirements
User
-
A user
Uhas a maximum level of permission that cannot be increased through granting of category permissions. e.g., They only have READ access and this cannot be changed.User
Uwould have permissionsystem/*.readbut NOTsystem/*.write. If the user also has permissionsgrouping/X.writethen this will not allow them WRITE access to any resources because they lack the basesystem/*.writepermission. -
User
Ubelongs to communities X and Y.This would be managed in the Authorization server. Ontoserver knows nothing about group membership and it does not need to be included in the JWT Token.
-
Members of X can write some resources but only read others; need to be able to specify read/write permissions for a community separately.
This is supported by the separate
.readand.writeforms of the security labels. -
You must have write permissions on an existing resource in order to set / update its permissions.
For any WRITE action there are either one or two resource instances involved, the latest (active) instance in the the server at the time of the action, and the desired instance in the server following successful execution of the action.
Permissions are checked against the existing instance only, not the new instance.
Actions
-
I want to make the foo Resource available for reading to community X
* foo.meta.security.label = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|X.read -
I want to make the foo Resource available for reading to all (both authenticated and non-authenticated)
* foo.meta.security.label = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|*.read -
I want to make the foo Resource available for reading only to authenticated entities (regardless of community membership)
This can be achieved by running Ontoserver NOT in “anonymous read” mode (i.e.,
ontoserver.security.readOnly.fhir=false) and not specifying any security labels on the resource.If security labels must be used on the resource (for example to control write access) then a
default.read(any term can be used,defaultis simply an example) security label can be added to the resource and configured into the authorisation server to be added by default to all authenticated users. This second approach with work withontoserver.security.readOnly.fhirset totrueorfalse. -
I want to make the foo Resource available in the syndication feed to community Y
FHIR Resources in the syndication feed use the normal FHIR read instance URL and are thus subject to normal authorisation constraints.
* foo.meta.security.label = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|Y.read /synd/setSyndicationStatus?resource=[Resource]/foo&status=true -
I want to make the foo Resource available for reading to community X and for writing to community Y
Note that community Y is also given READ permission in the following as would normally be expected
* foo.meta.security.label[0] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|X.read * foo.meta.security.label[1] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|Y.write * foo.meta.security.label[2] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|Y.read -
I want to make the foo Resource available for reading to all and for writing to community Y only
* foo.meta.security.label[0] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|*.read * foo.meta.security.label[1] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|Y.write * foo.meta.security.label[2] = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|Y.read -
I want to create a Resource only visible to community X
* foo.meta.security.label = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|X.read -
My CodeSystem resource requires agreement to licence X (for read access) here and in all downstream Terminology Solutions
Create and apply category permissions for licence X products.
* foo.meta.security.label = http://ontoserver.csiro.au/CodeSystem/ontoserver-permissions|X.read

Ontoserver