Skip to content

feat(property-source): BRP relations are read behind a setting on the source (decision 220) - #2667

Merged
rubenvdlinde merged 1 commit into
developmentfrom
refactor/r2-brp-relations
Oct 12, 2026
Merged

rubenvdlinde merged 1 commit into
developmentfrom
refactor/r2-brp-relations

Conversation

@rubenvdlinde

@rubenvdlinde rubenvdlinde commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

What this does

Decision 220 (Ruben, 12 Oct): dossiq's conflict-of-interest check reads BRP relations through integriq. This is the integriq half. The dossiq half is ConductionNL/dossiq#3844, and it lands after this one.

Cut from origin/development a4a22ca, which holds #2665.

  • BrpRelationReader::read asks the brp-haalcentraal source who a person's partner, parents and children are. It posts RaadpleegMetBurgerservicenummer to /personen and asks for three fields: partners.burgerservicenummer, ouders.burgerservicenummer and kinderen.burgerservicenummer. Nothing else.
  • The read runs only when the source carries configuration.relationFields: true. Without it the registry is not asked. Only the boolean true counts.
  • The answer is a plain array: available, reason and relations (relation partner, parent or child, with a burgerservicenummer). A caller that reaches the class by name needs none of integriq's types.
  • A question that was not answered is never an empty family. available: false comes with relations-not-enabled, no-source, unreachable or invalid-identifier.
  • BrpPropertySource::resolve now leaves partners, ouders and kinderen fields out of its question. A schema property could declare them before, around any setting. That read caches its answer per person for 15 minutes, so a relation in it would have reached the next reader of that person.
  • The seeded source's mock answer gains a partner, two parents and a child, with test-range numbers. The contract test reads that answer from the fragment.
  • RegistrySourceGateway gains readUnrecorded() and configuration().

Privacy

Relation numbers are personal data about people who are not part of the case.

  • Only the fields the check uses. Three relation numbers. No names, no dates.
  • The answer is never stored. The call goes through readUnrecorded(), which passes persistLog: false to the call service. The call log keeps bodies during an investigation window and keeps a failed request for a replay, so a recorded call could hold both the question and the answer. The read also stays out of the property value cache.
  • The log holds no number. The reader writes one line per read: property-source.brp.relations.read, with the source slug and the outcome. A test asserts that no burgerservicenummer and no part of the answer reaches the logger, on a good read and on a failed one.
  • A consequence to know: a relation read does not show on the source's call log page, and it does not count against the source's rate limit state. The log line above is its trace. See the open question below.

Which BRP authorisation the municipality needs

Set relationFields only when the municipality's autorisatiebesluit from RvIG covers the burgerservicenummer of:

  • the parents (BRP elements 02.01.20 and 03.01.20, categories Ouder1 and Ouder2),
  • the partner (element 05.01.20, category Huwelijk/geregistreerd partnerschap),
  • the children (element 09.01.20, category Kind),

for a task that includes checking a worker's personal interest in a case. Corrected 12 October: these numbers were first written from memory. They are now checked against an autorisatietabel RvIG publishes (https://publicaties.rvig.nl/media/13550/download), which lists Burgerservicenummer under exactly these four rubrics. The municipality's own autorisatiebesluit is the authority on what it may ask. Without that authorisation Haal Centraal refuses the fields, and the read answers unreachable.

Rollout, for an administrator

  1. Nothing changes on upgrade. No source carries the setting, so nothing is asked.
  2. To switch the read on: open the brp-haalcentraal source and add "relationFields": true to its configuration.
  3. To switch it off again: remove the key or set it to false.

Verified

  • Red first: refactor/r2/logs/r1-red.log (exit 2, 13 errors and 1 failure, the reader class and the gateway methods did not exist).
  • tests/Unit/PropertySource and tests/Unit/Settings: 314 tests, exit 0 (r1-green.log).
  • composer check:strict, run stage by stage in the foreground because no build slot came free in 15 minutes (r1-strict.log): check:no-legacy-types, check:routes, lint, phpcs, phpmd, phpstan and psalm exit 0 each. The full PHPUnit suite exits 2: 5,464 tests, 11 errors, all in tests/Unit/AppInfo and all the same TypeError in tests/Helpers/AppContainerInjection.php:93 (a mock container assigned to a typed property). They fail the same way on origin/development on this machine and touch none of this PR's files. CI is the check for them.
  • No route, schema or version change, so no npm check applies.

Not verified

  • No call was made to the Haal Centraal proefomgeving. The live pass is a task in openspec/changes/brp-relations-read/tasks.md.
  • Whether the call service's mock path honours persistLog: false was not exercised; the unit test asserts the flag reaches CallService::call.

Answered (Q-R2-8, decision 229)

A relation read leaves a call log record without bodies. The follow-up pull request builds that switch and replaces readUnrecorded() with readWithoutBodies().

… source (decision 220)

BrpRelationReader asks the brp-haalcentraal source for the partner, parent
and child numbers of one person, and only when the source carries
configuration.relationFields true. The call stays out of the call log and
the value cache, and the log line holds no number and no part of the answer.
The person read leaves relation fields out of its question.
@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/integriq @ 8620c2e

Check PHP Vue Security License Tests
lint ✅
phpcs ✅
phpmd ✅
psalm ✅
phpstan ✅
phpmetrics ✅
eslint ✅
stylelint ✅
build ✅
build ✅
check-specs ✅
test-l10n ✅
format ✅
check-schema-l10n ✅
check-l10n-js ✅
composer ✅ ✅ 143/143
npm ✅ ✅ 647/647
app:check-code ⏭️
info.xml ✅
REUSE ❌
lockfile sync ✅
PHPUnit ✅ 5463/5463 · 1 skipped
Newman ✅ 130/130
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 ✅
Hydra gates (axe) ✅

Quality workflow — 2026-10-12 15:41 UTC

Download the full PDF report from the workflow artifacts.

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