Access Policies
Overview
Access Policies control authorization in Dossier Organizer. Every API request is evaluated against the currently active policy to determine whether it should be allowed or denied.
A policy contains a list of Access Rules that are evaluated in order. The rules are biased towards denying access: even if multiple rules match a request, a single DENY effect results in denied access. If no rule matches, the default effect is applied.
Step-by-Step: Creating and Deploying an Access Policy
Step 1: Create the Policy JSON File
Create a new JSON file (e.g. my-access-policy.json) on your local machine with the following structure:
{
"_version": "2024-01-15",
"description": "Custom Access Policy",
"validFrom": "2024-01-15T00:00:00.00+0000",
"rules": [
],
"default_effect": "DENY"
}
| Field | Description |
|---|---|
_version | A version identifier to track the policy state. This is not used internally and can have any format. |
description | A human-readable description of this policy. |
validFrom | The date from which this policy becomes active. Format: yyyy-MM-dd'T'HH:mm:ss.SSSZ. When multiple policies exist, the one with the most recent validFrom date (that is not in the future) is the active policy. |
rules | The list of access rules that define who can do what. See Access Rules below. |
default_effect | The effect applied when no rule matches a request. Valid values: DENY or ALLOW. Set to DENY for a whitelist approach (only explicitly allowed actions are permitted) or ALLOW for a blacklist approach (only explicitly denied actions are blocked). |
Step 2: Add Access Rules
Add one or more rules to the rules array. Each rule follows this structure:
{
"name": "Allow readers to view collections",
"effect": "ALLOW",
"resources": [
"collection"
],
"actions": [
"core:GET"
],
"subjects": [
"claim:resource_access['neverpile-fusion'].roles.contains('reader')"
],
"conditions": {
}
}
See the Access Rules section below for a detailed explanation of each field.
Step 3: Deploy the Policy via Helm
Add the policy file to your Dossier Organizer deployment using the Helm --set-file parameter. Since policies are list properties, use the brace syntax:
helm upgrade --install \
--values ${VALUES_FILE} \
--set-file organizer.config.policyDefinitions={my-access-policy.json} \
--set organizer.config.policyValidityDates={20240115-000000-000} \
fusion-dossier-organizer levigo/fusion-dossier-organizer
Important:
- The
policyValidityDatesvalue must follow the formatyyyyMMdd-HHmmss-SSSand must correspond to thevalidFromdate in your policy JSON. - The number and order of entries in
policyDefinitionsandpolicyValidityDatesmust match. - To deploy multiple policies (for rolling over at specific times), add them as comma-separated entries in the braces.
If no custom access policy is provided, a default policy is used that allows users full access.
Step 4: Verify
After deployment, test your policy by accessing the API with different users/roles to confirm the rules are applied correctly.
How Rule Matching Works
- Rules are matched in order as they appear in the JSON file.
- Each rule is matched against the targeted resource(s), the requesting subject(s), the requested action(s), and optionally conditions.
- The effect of the first matching rule determines the outcome.
- If no rule matches, the
default_effectis used.
Access Rules
An Access Rule decides whether to allow or deny a request based on the resource being accessed, the action being performed, the subject making the request, and optional conditions.
{
"name": "Example Rule",
"effect": "ALLOW",
"resources": ["collection"],
"actions": ["core:GET"],
"subjects": ["authenticated"],
"conditions": {}
}
Name
A descriptive name for the rule. This is used for documentation and logging purposes.
Effect
Determines the outcome when this rule matches a request. Valid values: ALLOW or DENY.
In most cases, the effect should contrast the default_effect of the policy. For example, in a policy with "default_effect": "DENY", most rules will have "effect": "ALLOW" to explicitly permit specific actions.
Resources
Resources target the rule to a specific context. The following resource values are available:
| Resource | Description |
|---|---|
collection | Targets operations on collections as a whole. |
collection-element | Targets operations on individual elements within a collection. |
* | Matches all resources. |
Actions
Actions describe the intent of the request. They differentiate between different operations on the same resource. For example, you can have different rules for reading vs. deleting a collection.
The following actions are available:
| Action | Description |
|---|---|
core:GET | Read/retrieve a resource. Corresponds to HTTP GET requests. |
core:CREATE | Create a new resource. Corresponds to HTTP POST requests. |
core:UPDATE | Modify an existing resource. Corresponds to HTTP PUT/PATCH requests. |
core:DELETE | Remove a resource. Corresponds to HTTP DELETE requests. |
core:VALIDATE | Validate a resource without modifying it. |
* | Matches all actions. |
Additional actions for fine-grained control over document operations:
| Action | Description |
|---|---|
document:create | Creation of new documents. |
document:pages:add | Adding pages to a document. |
document:pages:reorder | Reordering pages within a document. |
document:pages:delete | Deletion of pages from a document. |
document:pages:rotate | Rotating pages within a document. |
document:pages:gradation | Changing the gradation curve of pages. |
Additional actions for annotation operations:
| Action | Description |
|---|---|
annotations:add | Creation of new annotations. |
annotations:edit:own | Modification of own annotations. |
annotations:edit:all | Modification of all annotations. |
Subjects
Subjects identify the principal (user) making the request. The subject is resolved from the Spring Security context.
| Subject | Description |
|---|---|
anonymous | Matches any unauthenticated user. |
authenticated | Matches all authenticated users. |
* | Matches all users regardless of authentication. |
principal:<NAME> | Matches a specific user by name. |
role:<ROLE> | Matches users with a specific role/authority. |
claim:<SPEL_EXPRESSION> | Evaluates a SpEL expression against the JWT token claims. Only works with JSON Web Token authentication. |
Examples:
"subjects": ["authenticated"]
"subjects": ["role:admin"]
"subjects": ["principal:john.doe"]
"subjects": ["claim:resource_access['neverpile-fusion'].roles.contains('reader')"]
Conditions
Conditions allow fine-grained decisions based on context variables such as collection metadata or element properties.
To create complex conditions, use composite conditions like and and or.
The following condition types are available:
| Condition | Description |
|---|---|
and | Logical AND — all sub-conditions must be true. |
or | Logical OR — at least one sub-condition must be true. |
not | Logical NOT — negates its sub-conditions. |
equals | Checks equality between a context value and a given value. |
exists | Checks whether a context variable exists (is non-null). |
greaterThan | Checks if a context value is greater than a given value. |
greaterOrEqualTo | Checks if a context value is greater than or equal to a given value. |
lessThan | Checks if a context value is less than a given value. |
lessOrEqualTo | Checks if a context value is less than or equal to a given value. |
true | Checks whether a context variable is boolean true. |
false | Checks whether a context variable is boolean false. |
range | Checks if a context value falls between two given values. |
Example: Only allow access to collections of type "generic" where the metadata field test is true:
{
"and": {
"conditions": [
{ "equals": { "collection.metadata.test": true } },
{ "equals": { "collection.type": "generic" } }
]
}
}
For more information about conditions, please refer to the Conditions section.
Ownership Condition
In addition to the collection JSON structure, the special path collection.principal can be used to evaluate ownership of a collection. This path compares the authenticated user (the requesting principal) against the creator of the collection to determine whether the current user owns it.
Possible values:
own: The condition matches only if the authenticated user is the owner of the collection. This is useful for rules that should restrict actions to the user's own collections (e.g., allowing users to edit only collections they created).any: The condition matches regardless of who owns the collection. This effectively bypasses the ownership check, allowing the rule to apply to all collections irrespective of the principal.
Example – Allow users to delete only their own collections:
{
"name": "Allow users to delete own collections",
"effect": "ALLOW",
"resources": ["collection"],
"actions": ["core:DELETE"],
"subjects": ["authenticated"],
"conditions": {
"equals": {
"collection.principal": "own"
}
}
}
Example – Allow admins to read any collection regardless of ownership:
{
"name": "Allow admins to read all collections",
"effect": "ALLOW",
"resources": ["collection"],
"actions": ["core:GET"],
"subjects": ["role:Admin"],
"conditions": {
"equals": {
"collection.principal": "any"
}
}
}
Complete Example
The following example policy demonstrates a realistic setup: it denies access by default and uses rules to grant specific permissions based on user roles.
{
"_version": "2024-01-15",
"description": "Role-based access policy",
"validFrom": "2024-01-15T00:00:00.000+0000",
"rules": [
{
"name": "Allow readers to view all collections",
"effect": "ALLOW",
"resources": ["collection"],
"actions": ["core:GET"],
"subjects": ["claim:resource_access['neverpile-fusion'].roles.contains('reader')"]
},
{
"name": "Allow editors to create and update collections",
"effect": "ALLOW",
"resources": ["*"],
"actions": ["core:GET", "core:CREATE", "core:UPDATE"],
"subjects": ["claim:resource_access['neverpile-fusion'].roles.contains('editor')"]
},
{
"name": "Allow editors to manage documents and pages",
"effect": "ALLOW",
"resources": ["*"],
"actions": ["document:create", "document:pages:add", "document:pages:reorder", "document:pages:delete", "document:pages:rotate", "annotations:add", "annotations:edit:own"],
"subjects": ["claim:resource_access['neverpile-fusion'].roles.contains('editor')"]
},
{
"name": "Allow admins full access",
"effect": "ALLOW",
"resources": ["*"],
"actions": ["*"],
"subjects": ["claim:resource_access['neverpile-fusion'].roles.contains('admin')"]
},
{
"name": "Deny access to confidential collection elements",
"effect": "DENY",
"resources": ["collection-element"],
"actions": ["*"],
"subjects": ["*"],
"conditions": {
"equals": { "collection.metadata.confidential": true }
}
},
{
"name": "Restrict deletion of archived collections",
"effect": "DENY",
"resources": ["collection"],
"actions": ["core:DELETE"],
"subjects": ["*"],
"conditions": {
"equals": { "collection.metadata.archived": true }
}
}
],
"default_effect": "DENY"
}