Skip to content

feat(stuf): a connected system writes cases and documents through integriq (decision 206) - #3801

Merged
rubenvdlinde merged 5 commits into
developmentfrom
refactor/r2-stuf-writes-through-integriq
Oct 12, 2026
Merged

rubenvdlinde merged 5 commits into
developmentfrom
refactor/r2-stuf-writes-through-integriq

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What this does

Decision 206 (Ruben, 12 Oct): another system may write cases and documents into dossiq over StUF, but only through integriq and authenticated. This is the dossiq half. The integriq half is ConductionNL/integriq#2662, and it lands first.

Stacked on #3775 (refactor/r2-stuf-provider-retired, head ebf7359), which had not landed when this branch was cut. It contains origin/development as of 102aba0. Land it after #3736, #3775 and integriq#2662.

  • Integriq authenticates the sender and maps the StUF message onto a generic operation: create, update or add-document. Dossiq reads that operation from integriq's event. It parses no StUF and has no StUF write route of its own.
  • ConnectedSystemCaseWriter carries the operation out as the background service account (BackgroundServiceAccount::runAs) with OpenRegister's access rules on. No _rbac: false, no system elevation. It is generic code named by what it does (decision 182): nothing in it names StUF or a procedure.
  • StufProviderWriteListener answers integriq's two write events, only for a source a stufEndpoint names, and puts the outcome back on the event with its kind (refused, forbidden, not found, conflict).
  • A stufEndpoint opts in with acceptsWrites, off by default. Its zaaktypeMappings decide which case type an incoming case opens on. The new documentTypeMappings decide the document type. An unmapped zaaktype or document type is refused. The schema moves to 1.2.0.
  • A case a connected system opened keeps originSystem and originIdentifier (new, generic case properties), so later messages from that system find it by the identifier it gave. Any other case is found by its dossiq case number.
  • An update changes the title, the description and the planned end date, and nothing else. Type, status, handler and the dates a term hangs on stay dossiq's.
  • A document takes the same screened upload path as a handler's upload (DossierUploadHandler::uploadOne), so the executable check and the confidentiality rule apply.

Refused, and how

Situation Outcome
Endpoint without acceptsWrites forbidden, nothing read
No usable background service account forbidden, nothing read
Event with an empty operation refused
An integriq from before integriq#2662 (its events have no operation) the call fails, integriq answers a server fault, nothing is written
Unmapped zaaktype or document type, no title, no identifier, no content refused
Unknown case on update or document add not found
Second create with the same identifier on the same connection conflict
Store fails exception, so integriq answers a server fault and never a confirmation

Integriq turns each kind into a Fo03 with HTTP 400, 403, 404 or 409.

Rollout, for an administrator

  1. Land integriq#2662 first. With an older integriq no write is carried out: the zaaksysteem gets a server fault.
  2. Set the background service account in dossiq's admin settings, if it is not set. It must be in the group dossiq-background-service.
  3. On the stufEndpoint: set acceptsWrites to true, check zaaktypeMappings (dossiq case type identifier or uuid to the zaaksysteem's zaaktype name or code) and fill documentTypeMappings (the zaaksysteem's document type name to a dossiq document type id).
  4. On the integriq source: configuration.acceptsProviderRequests true and a WS-Security UsernameToken.
  5. Point the zaaksysteem's kennisgevingen at /apps/integriq/api/stuf-zkn/provider/{sourceId}/cases. The old dossiq addresses (/apps/dossiq/api/stuf/zaken and /cases) take queries only: integriq refuses a write that arrives through them.

Which cases a connected system may change (decision 215)

An update or a document add may name any case by its dossiq case number, not only a case the same system opened. Ruben confirmed this on 12 Oct (decision 215, question Q-R2-4).

Inherited, reported not fixed

Gate 119

No procedure-named class is added or removed, so the gate 119 count does not move.

Verified

  • ConnectedSystemCaseWriterTest, StufProviderWriteListenerTest, StufProviderQueryListenerTest, CrossAppListenerRegistrarTest: 52 tests, 189 assertions, 2 skipped (both already skipped on development), green. The writer, the document filer, the service account and the case type resolver are the production classes.
  • The created case and the updated case are validated against the merged register schema dossiq ships (RealSchemaValidator), with a control that the same validator refuses a wrong intakeChannel and a non-string originSystem.
  • Every store call in the create test ran as dossiq-achtergrond and without an access-scope override.
  • The registrar test asserts that both write events reach StufProviderWriteListener.
  • phpcs and phpmd on the changed files: clean. check:schema-l10n, test:l10n, check:l10n-js: green. Schema digests regenerated.
  • composer check:strict, once, on 1a39a85 (refactor/r2/logs/w2-strict.log): lint, refusal-grounds-snapshot, phpcs and phpmd green, full suite 8153 tests green. psalm and phpstan each reported the same 4 errors, all in the new StufProviderWriteListener (the event type was not narrowed). Fixed in the next commit and rechecked with psalm and phpstan on that file and its test, all green. The full run was repeated after merging development, see the next line.
  • COMPOSER_PROCESS_TIMEOUT=0 composer check:strict, once more, on db1f4bb after merging development 3b974a4 (refactor/r2/logs/w3-strict.log): exit 0, ALL CHECKS PASSED. lint, refusal-grounds-snapshot, phpcs, phpmd, psalm and phpstan green, full suite 8379 tests, 80 skipped.
  • CI, read once on db1f4bb: one new red, Frontend Tests (unit). caseSearchDeclarations.spec.js asks every case property to declare an input control, and originSystem and originIdentifier declared none. Fixed in the next commit (exact match, text control). After it: that spec 8 tests exit 0, tests/Unit/Settings, tests/Unit/Listener and ConnectedSystemCaseWriterTest 759 tests exit 0, check:schema-l10n exit 0. The full vitest suite was not run locally, so CI's frontend job is the check for it. The other two reds on that read, Hydra Gates and check:debt-ratchet, are red on development's own push run for 3b974a4.
  • After that merge: l10n:build, check:schema-l10n (0 uncovered, baseline 0) and test:l10n exit 0. Train 3 took the two integriqSource catalogue strings from this branch, and the catalogues hold one copy of each.
  • The merge: the integriq event stubs cite dossiq's own StUF requirements, as train 3 did for the stubs it took (gate 46). Register info.version goes 0.20.37 to 0.20.38 for stufEndpoint 1.2.0 and the two case origin fields. No repair step or migration is added, so appinfo/info.xml does not move (gate 110).
  • Not verified: a live creeerZaak, updateZaak and voegZaakdocumentToe on an instance with both apps. Owed as one live pass. Whether OpenRegister's access rules let the background group write the document records is part of that pass.

…egriq, as the service account (decision 206)

Integriq authenticates the sender and maps the StUF message onto a generic
operation. ConnectedSystemCaseWriter carries it out as the background service
account with the access rules on. A stufEndpoint opts in with acceptsWrites.
Dossiq has no StUF write route of its own.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ b62dd8f

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ❌
phpstan ❌
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
check-vue3-compile ✅
test-l10n ✅
format ❌
check-schema-l10n ✅
check-l10n-js ✅
check-debt-ratchet ❌
check-types-e2e ✅
composer ✅ ✅ 109/109
npm ✅ ✅ 659/659
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌ 8148/8149 · 4 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-12 08:19 UTC

Download the full PDF report from the workflow artifacts.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ 8aad9e4

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
check-vue3-compile ✅
test-l10n ✅
format ❌
check-schema-l10n ✅
check-l10n-js ✅
check-debt-ratchet ❌
check-types-e2e ✅
composer ✅ ✅ 109/109
npm ✅ ✅ 659/659
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌ 8148/8149 · 4 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-12 08:43 UTC

Download the full PDF report from the workflow artifacts.

…uf-writes-through-integriq

The integriq event stubs cite dossiq's own StUF requirements (gate 46), as
train 3 did for the stubs it took. Register version 0.20.38 for the
stufEndpoint 1.2.0 fragment and the case origin fields.
caseSearchDeclarations.spec.js asks every case property for an input control
or a place on its silent list. Both fields are looked up by exact match.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/dossiq @ b950cba

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
check-manifest ✅
check-vue3-compile ✅
test-l10n ✅
format ✅
check-schema-l10n ✅
check-l10n-js ✅
check-debt-ratchet ❌
check-types-e2e ✅
composer ✅ ✅ 109/109
npm ✅ ✅ 659/659
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ❌ 8373/8375 · 4 skipped
Newman ⏭️
Playwright ⏭️ deferred: E2E runs locally and on the promotion path only. This pull request targets development, so the suite is asked once per promotion into beta and main rather than once per push per open pull request. Run it locally with npx playwright test, or from the Actions tab on a branch with no open pull request into development.
Hydra gates ❌

Quality workflow — 2026-10-12 13:40 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 0ca7cf9 into development Oct 12, 2026
46 of 50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant