Skip to content
1 change: 1 addition & 0 deletions docs/src/process-development/api-v2/dsf/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ icon: creative
- [Read Access Tag](read-access-tag.md)
- [Requester and Recipient](requester-and-recipient.md)
- [Spring Framework Integration](spring-framework-integration.md)
- [Understanding the Process Authorization Extension](./understanding-the-process-authorization-extension.md)
- [Versions, Placeholders and URLs](versions-placeholders-urls.md)
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,4 @@ The `recipeint` element uses one of the following Codings:


## Related Topics
[ActivityDefinition](../fhir/activitydefinition.md)
[ActivityDefinition](../fhir/activitydefinition.md), [Understanding the Process Authorization Extension](./understanding-the-process-authorization-extension.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Understanding the Process Authorization Extension
icon: creative
---

The authorization rules live inside a `<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization">` block in the `ActivityDefinition`. Before you make changes, read through the annotated structure below so you know exactly what each line does and where to make your edits later.

```xml
<!-- The outermost container for one complete authorization rule block.
One block covers exactly one message name, one Task profile, and the
requester/recipient rules for it.
Add a second block if you have a second message type. -->
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization">

<!-- Which BPMN Message name does this rule apply to? -->
<extension url="message-name">
<valueString value="startDicProcess" />
</extension>

<!-- Which StructureDefinition (Task profile) must an incoming Task conform to? -->
<extension url="task-profile">
<valueCanonical value="http://example.org/fhir/StructureDefinition/task-start-dic-process|#{version}" />
</extension>

<!-- ─── REQUESTER: Who is allowed to SEND this message? ────────────────
valueCoding selects a rule from the DSF authorization CodeSystem. -->
<extension url="requester">
<valueCoding>
<!-- The CodeSystem (dictionary) that defines the available rules.
Always this fixed DSF URL. -->
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />

<!-- Which rule from that dictionary?
LOCAL_ALL = any local client (certificate or OIDC user)
LOCAL_ROLE = any local client with a specific OIDC practitioner role
LOCAL_ORGANIZATION = one specific local organization (needs nested extension)
REMOTE_ORGANIZATION = one specific remote organization (needs nested extension) -->
<code value="LOCAL_ALL" />
</valueCoding>
</extension>

<!-- ─── RECIPIENT: Who is allowed to RECEIVE / execute this process? ───
Same structure as requester above. -->
<extension url="recipient">
<valueCoding>
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
<code value="LOCAL_ALL" />
</valueCoding>
</extension>

</extension>
```

When you need to restrict access to a **specific organization** or a **specific OIDC role**, add a nested `<extension>` inside `<valueCoding>` to narrow the rule further:

```xml
<!-- Example: only a specific remote organization may send -->
<extension url="requester">
<valueCoding>
<!-- Narrow down: which specific organization? -->
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-organization">
<valueIdentifier>
<!-- The DSF system for organization identifiers -->
<system value="http://dsf.dev/sid/organization-identifier" />
<!-- The concrete organization identifier -->
<value value="cos.dsf.test" />
</valueIdentifier>
</extension>
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
<!-- Must match the nested extension: REMOTE_ORGANIZATION for a remote org,
LOCAL_ORGANIZATION for a local org -->
<code value="REMOTE_ORGANIZATION" />
</valueCoding>
</extension>

<!-- Example: only local clients with the OIDC practitioner role DSF_ADMIN -->
<extension url="requester">
<valueCoding>
<!-- Narrow down: which OIDC practitioner role? -->
<extension url="http://dsf.dev/fhir/StructureDefinition/extension-process-authorization-practitioner-role">
<valueCoding>
<system value="http://dsf.dev/fhir/CodeSystem/practitioner-role" />
<code value="DSF_ADMIN" />
</valueCoding>
</extension>
<system value="http://dsf.dev/fhir/CodeSystem/process-authorization" />
<code value="LOCAL_ROLE" />
</valueCoding>
</extension>
```

> **Reading tip:** Think of the outer `<code>` as the *category* of the rule (e.g. "a local client with a specific role") and the inner `<extension>` as the *precise value* within that category (e.g. "the role `DSF_ADMIN`"). You always need both together.

## Related Topics
[ActivityDefinition](../fhir/activitydefinition.md), [Creating ActivityDefinitions](../guides/creating-activity-definitions.md), [Requester and Recipient Examples](./requester-and-recipient.md)
Original file line number Diff line number Diff line change
Expand Up @@ -757,4 +757,4 @@ All other elements can technically be omitted. Still, the following elements are
</details>

## Related Topics
[ActivityDefinition](../fhir/activitydefinition.md), [Creating CodeSystems for DSF Processes](creating-codesystems-for-dsf-processes.md), [Creating ValueSets for DSF Processes](creating-valuesets-for-dsf-processes.md), [Task](../fhir/task.md)
[ActivityDefinition](../fhir/activitydefinition.md), [Creating CodeSystems for DSF Processes](creating-codesystems-for-dsf-processes.md), [Creating ValueSets for DSF Processes](creating-valuesets-for-dsf-processes.md), [Requester and Recipient Examples](../dsf/requester-and-recipient.md), [Task](../fhir/task.md), [Understanding the Process Authorization Extension](../dsf/understanding-the-process-authorization-extension.md)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Below is a template for a [Questionnaire](https://www.hl7.org/fhir/R4/questionna
```xml
<Questionnaire xmlns="http://hl7.org/fhir">
<meta>
<profile value="http://dsf.dev/fhir/StructureDefinition/questionnaire"/>
<profile value="http://dsf.dev/fhir/StructureDefinition/questionnaire|2.0.0"/>
<tag>
<system value="http://dsf.dev/fhir/CodeSystem/read-access-tag"/>
<code value="ALL"/>
Expand Down
141 changes: 138 additions & 3 deletions docs/src/process-development/linter-tool/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,28 @@ The linter performs comprehensive validation on BPMN 2.0 process definitions usi
- The process engine only deploys and executes processes marked as executable
- Non-executable processes are typically used for documentation or as templates

##### Process Version Tag Validation

- **Requirement**:
- Process must define `camunda:versionTag`
- Expected placeholder value in DSF process plugins: `#{version}`
- Error: `BPMN_PROCESS_VERSION_TAG_MISSING_OR_EMPTY` (missing, empty/blank, or literal `"null"`)
- Warning: `BPMN_PROCESS_VERSION_TAG_NO_PLACEHOLDER` (present but without `#{version}`)
- Success: `SUCCESS` when `camunda:versionTag` contains `#{version}`

- **Valid Example**:
- ✅ `<process id="example_process" isExecutable="true" camunda:versionTag="#{version}">`

- **Error Examples**:
- ❌ `<process id="example_process" isExecutable="true">` (missing `camunda:versionTag`)
- ❌ `<process id="example_process" isExecutable="true" camunda:versionTag="">` (empty value)
- ❌ `<process id="example_process" isExecutable="true" camunda:versionTag="null">` (literal `null`)

- **Warning Examples**:
- ⚠️ `<process id="example_process" isExecutable="true" camunda:versionTag="1.0.0">`
- ⚠️ `<process id="example_process" isExecutable="true" camunda:versionTag="some-fixed-version">`


#### Task Validation

##### Service Tasks
Expand Down Expand Up @@ -638,9 +660,51 @@ Task resources are validated against the DSF Task base profile (`http://dsf.dev/
- Error: `FHIR_TASK_INPUT_SLICE_COUNT_BELOW_SLICE_MIN`
- Error: `FHIR_TASK_INPUT_SLICE_COUNT_EXCEEDS_SLICE_MAX`

- **Terminology Validation**:
- Code/system combinations validated against DSF CodeSystems
- Error: `FHIR_TASK_UNKNOWN_CODE`
- **Terminology Validation**:
- Generic coding validation (outside `Task.input.type.coding`) checks code/system pairs against known DSF CodeSystems
- Error: `FHIR_TASK_UNKNOWN_CODE`

- **Task.input.type.coding Terminology Validation (binding-driven)**:
- Validation for `Task.input.type.coding` is performed in three strict, ordered checks:
1. **Known CodeSystem check**:
- `Task.input.type.coding.system` must be a known CodeSystem URI
- Error: `FHIR_TASK_INPUT_CODING_SYSTEM_UNKNOWN`
- If this check fails, subsequent checks for that input are skipped
2. **Binding context check**:
- The system must be allowed by the slice-specific binding context from the referenced `StructureDefinition`
- Resolution order:
- `fixedUri` on `Task.input:sliceName.type.coding.system` (exact match required)
- `binding.valueSet` on `Task.input:sliceName.type` (fallback: `...type.coding`) and membership in `ValueSet.compose.include.system`
- Error: `FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
- If this check fails, code validation for that input is skipped
3. **Code-in-system check**:
- `Task.input.type.coding.code` must exist in the specified CodeSystem
- Error: `FHIR_TASK_INPUT_CODING_CODE_UNKNOWN_FOR_SYSTEM`

- **Binding Resolution Notes**:
- ValueSet checks are strict and context-aware; no permissive fallback to unrelated ValueSets is used
- If a slice declares `binding.valueSet` but the ValueSet is not loaded into the cache, validation fails explicitly with:
`FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
- If no resolvable binding context exists for a slice (`fixedUri` and `binding.valueSet` both missing), validation fails explicitly with:
`FHIR_TASK_INPUT_CODING_SYSTEM_NOT_IN_VALUE_SET`
- Inputs already failing structural checks (`FHIR_TASK_INPUT_REQUIRED_CODING_SYSTEM_AND_CODING_CODE`) are not re-reported by terminology checks

- **Task.input Fixed Constraint Validation (`fixedUri` / `fixedCode`)**:
- Validates actual `Task.input.type.coding.system` and `Task.input.type.coding.code` pairs against fixed constraints declared in the referenced `StructureDefinition` slices
- Validation direction is **Task → StructureDefinition** (actual instance values are checked against allowed fixed pairs)
- **System mismatch for existing code**:
- If a Task input code exists in SD constraints, but with a different system
- Error: `FHIR_TASK_INPUT_FIXED_URI_MISMATCH`
- **Code mismatch for existing system**:
- If a Task input system exists in SD constraints, but with a different code
- Error: `FHIR_TASK_INPUT_FIXED_CODE_MISMATCH`
- **Pair not allowed by SD constraints**:
- If a non-BPMN Task input `(system, code)` pair is not defined by any SD `fixedUri/fixedCode` constraint
- Error: `FHIR_TASK_INPUT_PAIR_NOT_ALLOWED_BY_SD`
- **Validation Behavior**:
- Check runs only when profile can be resolved and fixed constraints are extractable from the referenced `StructureDefinition`
- BPMN message inputs (`http://dsf.dev/fhir/CodeSystem/bpmn-message`) are excluded from this specific pair-not-allowed check because they are validated in dedicated Task input rules


#### StructureDefinition Resources

Expand Down Expand Up @@ -702,6 +766,24 @@ According to FHIR profiling specification §5.1.0.14:
- No individual slice's maximum cardinality may exceed base element's maximum
- Error: `STRUCTURE_DEFINITION_SLICE_MAX_TOO_HIGH`

##### Binding and Fixed Coding Validation
- **`binding.valueSet` Reference Validation**:
- Checks whether `binding.valueSet` URLs in differential elements can be resolved to a known ValueSet in project resources
- For unresolved references with `strength="required"`:
- Warning: `STRUCTURE_DEFINITION_BINDING_VALUESET_UNRESOLVED`
- For unresolved references with non-required strengths (`extensible`, `preferred`, `example`):
- Info: `STRUCTURE_DEFINITION_BINDING_VALUESET_UNRESOLVED_NON_REQUIRED`
- Success: `SUCCESS` when the referenced ValueSet is resolvable
- **`fixedUri` / `fixedCode` Validation Against CodeSystems**:
- For differential elements using `fixedUri` on `*.system`, verifies that the referenced CodeSystem exists in project terminology cache
- If referenced CodeSystem is unknown:
- Info: `STRUCTURE_DEFINITION_FIXED_URI_CODESYSTEM_NOT_FOUND`
- Note: `fixedCode` validation is skipped for this path when CodeSystem is unknown
- If `fixedCode` is present and the CodeSystem is known, verifies that the code exists in that CodeSystem
- Error: `STRUCTURE_DEFINITION_FIXED_CODE_NOT_IN_CODESYSTEM`
- Success: `SUCCESS` when `fixedUri` resolves (and `fixedCode`, if present, is valid)


#### ValueSet Resources

ValueSet resources are validated against the DSF ValueSet base profile.
Expand Down Expand Up @@ -1023,6 +1105,59 @@ ValueSet resources are validated against the DSF ValueSet base profile.
- `dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginDefinition.java`
- The `getResourceVersion()` method extracts the resource version from the plugin version

#### Spring Configuration Registration

In the DSF runtime, the Camunda engine does not instantiate Java delegate or
listener classes directly. Spring creates those instances via `@Bean` methods
declared in `@Configuration` classes. For those beans to be available to the
BPE at runtime, the corresponding `@Configuration` classes must be explicitly
returned by `ProcessPluginDefinition#getSpringConfigurations()`. Forgetting
to add a `@Bean` for a BPMN-referenced class typically surfaces as a
`BeanCreationException` or `ClassNotFoundException` only at deployment time.

- **Every BPMN-referenced class must be provided as a `@Bean`**:
- The linter collects every `camunda:class` reference used by
`serviceTask`, `sendTask`, `messageEventDefinition`,
`camunda:executionListener` and `camunda:taskListener` elements in the
plugin's BPMN files (List 1).
- The linter calls `getSpringConfigurations()` to get the registered
`@Configuration` classes and inspects the return types of their
`@Bean` methods (List 2).
- For every class in List 1, the linter checks whether it is provided
as a `@Bean` (exact type or supertype) in any `@Configuration` from
List 2.
- If a BPMN-referenced class has **no matching `@Bean`** in any
registered configuration, this is reported as an error.
- Error: `PLUGIN_DEFINITION_SPRING_CONFIGURATION_MISSING`
- Success: `SUCCESS` when all BPMN delegate/listener references are
covered by a `@Bean` in a registered `@Configuration`, or when no
BPMN delegate/listener references exist.

- **Code Example**:
```java
@Override
public List<Class<?>> getSpringConfigurations() {
return List.of(DataSharingConfig.class, DataSharingVariablesConfig.class);
}
```
```java
@Configuration
public class DataSharingConfig {

// Every class referenced via camunda:class in the BPMN must have
// a corresponding @Bean here (or in another registered @Configuration).
@Bean
public SelectDicTargets selectDicTargets() {
return new SelectDicTargets(api);
}

@Bean
public SelectDmsTarget selectDmsTarget() {
return new SelectDmsTarget(api);
}
}
```

#### Leftover Resource Detection

The linter performs project-level analysis to identify unreferenced resources:
Expand Down