Skip to main content
Version: Next

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"
}
FieldDescription
_versionA version identifier to track the policy state. This is not used internally and can have any format.
descriptionA human-readable description of this policy.
validFromThe 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.
rulesThe list of access rules that define who can do what. See Access Rules below.
default_effectThe 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 policyValidityDates value must follow the format yyyyMMdd-HHmmss-SSS and must correspond to the validFrom date in your policy JSON.
  • The number and order of entries in policyDefinitions and policyValidityDates must 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

  1. Rules are matched in order as they appear in the JSON file.
  2. Each rule is matched against the targeted resource(s), the requesting subject(s), the requested action(s), and optionally conditions.
  3. The effect of the first matching rule determines the outcome.
  4. If no rule matches, the default_effect is 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:

ResourceDescription
collectionTargets operations on collections as a whole.
collection-elementTargets 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:

ActionDescription
core:GETRead/retrieve a resource. Corresponds to HTTP GET requests.
core:CREATECreate a new resource. Corresponds to HTTP POST requests.
core:UPDATEModify an existing resource. Corresponds to HTTP PUT/PATCH requests.
core:DELETERemove a resource. Corresponds to HTTP DELETE requests.
core:VALIDATEValidate a resource without modifying it.
*Matches all actions.

Additional actions for fine-grained control over document operations:

ActionDescription
document:createCreation of new documents.
document:pages:addAdding pages to a document.
document:pages:reorderReordering pages within a document.
document:pages:deleteDeletion of pages from a document.
document:pages:rotateRotating pages within a document.
document:pages:gradationChanging the gradation curve of pages.

Additional actions for annotation operations:

ActionDescription
annotations:addCreation of new annotations.
annotations:edit:ownModification of own annotations.
annotations:edit:allModification of all annotations.

Subjects

Subjects identify the principal (user) making the request. The subject is resolved from the Spring Security context.

SubjectDescription
anonymousMatches any unauthenticated user.
authenticatedMatches 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:

ConditionDescription
andLogical AND — all sub-conditions must be true.
orLogical OR — at least one sub-condition must be true.
notLogical NOT — negates its sub-conditions.
equalsChecks equality between a context value and a given value.
existsChecks whether a context variable exists (is non-null).
greaterThanChecks if a context value is greater than a given value.
greaterOrEqualToChecks if a context value is greater than or equal to a given value.
lessThanChecks if a context value is less than a given value.
lessOrEqualToChecks if a context value is less than or equal to a given value.
trueChecks whether a context variable is boolean true.
falseChecks whether a context variable is boolean false.
rangeChecks 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"
}