Repository navigation
fix(access): enforce the access ratchets on the import records, first aid and the import guards (D23) - #1124
Merged
Conversation
…d the import guards (D23) LvsResult and OsoImportDossier get authorization blocks (read and write by coordinators and compliance-officers, plus the pupil's own LVS result), FirstAidIncident enforces its reporter's self-read, the verify/accept/reject guards test the declared coordinators group, and LvsResult and FirstAidIncident drop appendOnly so their lifecycles run. The DossierNote audience pins learn the care-team entry. Closes six of development's twelve red tests.
This was referenced Sep 27, 2026
fix(register): repair what the serial landing of round one still leaves wrong (replaces #1006)
#1126
Merged
Contributor
Quality Report — ConductionNL/learniq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 107/107 | |||
| npm | ✅ | ✅ 747/747 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| 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 on any branch from the Actions tab, or locally with npx playwright test. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-27 18:32 UTC
Download the full PDF report from the workflow artifacts.
This was referenced Sep 27, 2026
Merged
rubenvdlinde
added a commit
that referenced
this pull request
Sep 28, 2026
…its tree equals fix/access-control-ratchet-compliance)
Contributor
Quality Report — ConductionNL/learniq @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ❌ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| build | ✅ | ||||
| check-specs | ✅ | ||||
| format | ✅ | ||||
| check-l10n-js | ✅ | ||||
| check-schema-l10n | ✅ | ||||
| composer | ✅ | ✅ 107/107 | |||
| npm | ✅ | ✅ 747/747 | |||
| app:check-code | ⏭️ | ||||
| info.xml | ✅ | ||||
| REUSE | ❌ | ||||
| lockfile sync | ✅ | ||||
| PHPUnit | ❌ | ||||
| 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 on any branch from the Actions tab, or locally with npx playwright test. |
||||
| Hydra gates | ❌ |
Quality workflow — 2026-09-28 07:24 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope
Six of the twelve red tests on
developmentare access rules that four round-one schemas never met. This PR applies the ratchets' own rules to them, as decision D23 asks, and changes no other schema's access.What changes
LvsResultauthorizationblock, so the register cascade applied:instructors,hr,compliance-officers,team-leadsread and wrote every row, a pupil read nothing.appendOnly: truecoordinators,compliance-officersand the pupil's own row (learnerId); create and update:coordinators,compliance-officers; no delete; not append-onlyOsoImportDossiercoordinators,compliance-officers. No self-read:learnerEckIdis not a Nextcloud userFirstAidIncidentinstructors,compliance-officers;appendOnly: true{authenticated, match: {reportedBy: $userId}}; not append-only. Writers unchangedLvsResultVerifyGuard,OsoImportAcceptGuard,OsoImportRejectGuardcoordinator, a group nobody is incoordinators, the declared groupDossierNotepins inDeclaredAudienceEnforcedTest,RbacScopeKindsRegisterTestVersions: the three schemas
0.1.0to0.2.0,info.version0.28.1to0.29.0.Access narrows or stays equal for every group except two, both named by their specs.
coordinatorsgain read on both import records, because the verify, accept and reject guards name them as the actor and they cannot act on a row they cannot find (ImportRecordAccessTest::testNoGroupGainsReadExceptTheGuardsActorpins that nothing else gains read). The pupil gains read on their own LVS results, whichlvs-import-contractspecified.instructors,hrandteam-leadslose read and write on both import records.Writers follow D23, not the cascade:
coordinatorsandcompliance-officers. The nextcloud-app requirement that cascade schemas keep the cascade writers gets an explicit exception for these two records (spec delta in this change).Tests red before, green now
DeclaredAudienceEnforcedTest::testNoSchemaDeclaresAnAudienceWithoutAnAuthorizationBlock,::testEverySelfMatchIsEnforced,::testGroupOnlyBlocksAddThePersonTheRowIsAboutGuardGroupsAreDeclaredTest::testEveryGroupAGuardTestsIsDeclaredLifecycleSchemasAreNotAppendOnlyTest::testNoSchemaIsAppendOnlyAndTransitionsRbacScopeKindsRegisterTest::testDossierNoteGainsCareTeamPropertyAndReadEntryNew tests, each shown red on
development's register and guards before the change:ImportRecordAccessTest(5 tests),testSingularCoordinatorGroupIsDeniedin each of the three guard tests.LifecycleSchemasAreNotAppendOnlyTestrunsLvsResult.verifyandFirstAidIncident.startHandling.Verification (on this branch,
d949174a)composer check:strict(through the lane semaphore,TMPDIR=$PWD/.tmp): exit 1, from two inherited reds only.CaseListenerRegistrarcoupling 13, present ondevelopment;round1-landing-repairs(stacked on this PR) moves the bridge that causes it.round1-landing-repairs:AttendanceThresholdRegisterTest::testThresholdCrossedNotificationNotifiesMentorAndCoordinator,DataMappingProfilePresetsRegisterTest::testExistingSeedsUnchangedAndFiveAdded,LvsResultRegisterTest::testDataExchangeJobTargetDescribesLvsResults,OsoImportDossierRegisterTest::testDataExchangeJobTargetDescribesOsoImport,UwlrEduvBasispoortRegisterTest::testExistingTimetableImportSeedsUnchanged,::testTargetDescriptionsNameNewConnections. No new failure.npm run lint: exit 0 (19 inherited warnings).npm run format: exit 0.npm run test:l10n: no such script.npm run check:specs,check:schema-l10n,check:l10n-js: exit 0.openspec validate access-control-ratchet-compliance: valid.opsx-verify(headless, code level; no live instance for this lane): 10 of 10 tasks, 3 of 3 requirements implemented, every scenario pinned by a named test, design decisions D1 to D4 followed. 0 critical, 0 warnings. One suggestion, the LVS score freeze listener below.HYDRA_GATE_BASE_REF=origin/development --scope-to-diff: exit 4, 43 of 44 applicable gates ran. gate-16 spec-coverage, gate-47 security-change-has-tests, gate-51, gate-101 pass. The reds are inherited: gate-108 (threeEntitlementobjects inlearniq_mock_register.jsonstill carryorderLineId, which feat(payments): retire orders for shillinq; raise fee contributions there and grant entitlements on its settled signal (D19) #1082 dropped from the schema; this PR does not touch the mock register), gate-113 (six@e2e excludelines inopenspec/specs/payments/spec.mdcite tests feat(payments): retire orders for shillinq; raise fee contributions there and grant entitlements on its settled signal (D19) #1082 deleted), gate-112 newman-reach, gate-53 and gate-68 (the gate's own Node script fails to load as an ES module on this box).Inherited, not touched
phpmd's
CaseListenerRegistrarcoupling, the six landing-repair tests above,check:schema-l10nreporting 9 strings fewer than its baseline, and the gate reds listed above.Follow-ups named, not built
appendOnlyletscoordinatorsandcompliance-officersedit an imported LVS score. Before, nobody could, but nobody could verify either. A freeze listener in the style ofAssessmentResultIntegrityListenerwould keep the score fixed after import.coordinator10 times,mentor,examboard,compliance-officer,study-advisor,exam-board), so those notifications reach nobody. Same class as the guard groups fixed here; not in D23's scope.Landing
round1-landing-repairsis stacked on this branch; land this one first. Both touchlib/Settings/learniq_register.json. D25 (integriq takes over data exchange) is another round-three lane and may touchDataExchangeJob; this PR does not.OpenSpec change:
openspec/changes/access-control-ratchet-compliance/. Decision D23 (Ruben, 2026-09-27).CI (read once, 2026-09-28)
PHPUnit in CI: 1929 tests, 6 failures, exactly the six listed above for #1126; phpmd red on the inherited
CaseListenerRegistrarcoupling. The CI PHPUnit job also runs with coverage, wherefailOnRiskyandbeStrictAboutCoverageMetadataturn 10 tests into risky ones (CourseSharingControllerTest,StoreControllerTest,CourseShareExportServiceTest,CourseMetadataFilterTest,CourseStorePublisherTest: they execute classes their#[CoversClass]does not list). That makes the job exit 1 even with zero failures. None of those files is touched here; it is inherited from the course store changes and does not show locally with--no-coverage. CI Hydra Gates runs full-tree and reports the same inherited set as a local full run (gate-3, gate-25, gate-49, gate-55, gate-62, gate-108); none is on a file this PR touches.🤖 Generated with Claude Code