Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Create openregister.csr#1

Merged
MWest2020 merged 1 commit into
ConductionNL:mainfrom
MWest2020:patch-1
Sep 4, 2024
Merged

Create openregister.csr#1
MWest2020 merged 1 commit into
ConductionNL:mainfrom
MWest2020:patch-1

Conversation

@MWest2020

Copy link
Copy Markdown
Member

certificate signing request for the nextcloud ppstore

@MWest2020 MWest2020 merged commit f710c70 into ConductionNL:main Sep 4, 2024
rubenvdlinde pushed a commit that referenced this pull request Jul 30, 2025
WilcoLouwerse added a commit that referenced this pull request Feb 9, 2026
Update setAuthorization() methods to accept and decode JSON strings when loading entities from the database, preventing type errors during entity hydration.

Fixes: "Argument #1 ($authorization) must be of type ?array, string given"
rubenvdlinde added a commit that referenced this pull request May 3, 2026
…1420)

Adds four openspec changes implementing the audit's top hardcoded-
functionality findings + ADR-019 + ADR-025.

- openspec/changes/register-resolver-service/ (4 files, 560 lines)
  publishable RegisterResolverService that absorbs the
  `getValueString(...register/schema...)` pattern duplicated across
  13 call sites in opencatalogi (5 controllers), pipelinq (8
  services/jobs), and docudesk (OpenRegisterResolver). Five public
  methods (resolveRegisterId/SchemaId/Register/Schema/Pair),
  request-scoped caching, multi-tenant scoping via MultiTenancyTrait,
  three structured exceptions (MissingConfig / RegisterNotFound /
  SchemaNotFound), enumerateAppConfigs() for admin tooling.

- openspec/changes/pluggable-integration-registry/ (4 files, 472 lines)
  Implements hydra ADR-019 — two-sided integration registry.
  IntegrationProvider PHP interface + IntegrationRegistry service
  + DI tag auto-registration; migrates the 8 built-in NC types
  (files/notes/tasks/calendar/mail/contacts/deck/talk) from the
  hardcoded `LinkedEntityService::TYPE_COLUMN_MAP`; routes external
  providers through OpenConnector; advertises capabilities via OCS;
  CI parity gate ensures backend ↔ frontend pairing.

- openspec/changes/i18n-source-of-truth/ (4 files, 510 lines)
  Implements hydra ADR-025 (source-of-truth half). Adds
  `sourceLanguage` schema-property modifier + per-object override
  via `_translationMeta` + new `source_language` column on
  openregister_translations + render envelope `_meta.languageMeta`
  + `X-Source-Language` response header + automatic outdated-status
  flip on source-value change. Closes the gap from audit R4.

- openspec/changes/i18n-api-language-negotiation/ (4 files, 437 lines)
  Implements hydra ADR-025 (API contract half). Adds `?_lang=` /
  `?language=` query params with explicit precedence over
  Accept-Language, lax fallback (no 406), and a write-side
  `X-Translation-Target-Language` header for scalar-body PATCH/PUT
  to non-default languages. Conflicting body+header → 400.

References:
.claude/audit-2026-05-03/research/R4-or-i18n-source-of-truth.md
.claude/audit-2026-05-03/research/R5-or-api-language-negotiation.md
.claude/audit-2026-05-03/04-hardcoded.md (top-10 findings #1-#10)
hydra/openspec/architecture/adr-019-integration-registry.md
hydra/openspec/architecture/adr-022-apps-consume-or-abstractions.md
hydra/openspec/architecture/adr-025-i18n-source-of-truth.md
rubenvdlinde added a commit that referenced this pull request May 12, 2026
…t + add BuiltIn provider tests

The ai-chat-companion-orchestrator change refactored McpToolsService from
an inline registers/schemas/objects CRUD service into a provider
aggregator: it now takes `array $providers` + relocates the CRUD logic to
lib/Mcp/BuiltIn/{Registers,Schemas,Objects}ToolProvider. The old 62-test
McpToolsServiceTest still constructed the pre-refactor signature
(RegisterService as arg #1) → 63 ArgumentCountErrors.

- McpToolsServiceTest: rewritten against the new API — listTools()
  aggregation + `{appId}.` namespace enforcement (drops + warns),
  callTool() routing/success-wrap/InvalidArgumentException-on-unknown/
  exception-to-isError/debug-log/first-match-wins, invokeTool() flat
  envelope + no-throw-on-unknown, addProvider() runtime registration.
- New tests/Unit/Mcp/BuiltIn/{Registers,Schemas,Objects}ToolProviderTest:
  cover the relocated CRUD logic — getAppId(), the single namespaced
  descriptor + inputSchema, each action (list/get/create/update/delete)
  delegating to the mocked service, missing-param + unknown-action
  throwing InvalidArgumentException; the objects provider also covers the
  register+schema requirement and setRegister/setSchema scoping.
rubenvdlinde added a commit that referenced this pull request May 18, 2026
Brings development from 4 failing required CI jobs (Vue eslint, SBOM,
PHPUnit ×2) to 0 failures, so the 7 open PRs that just rebased onto
development can also clear quality gates.

## src/main.js — Vue eslint: `import/first` (5 errors)

The xwiki-registry side-effect block was inserted between two import
groups, which violates `import/first`. Moved all `import` statements
above the side-effect code. No behavior change — the imports are
hoisted at parse time anyway, so execution order is identical.

## package.json + package-lock.json — SBOM: `npm ls` exit 1

`@conduction/nextcloud-vue@1.0.0-beta.46` declares `@vueuse/core:^10.0.0`
in its package.json, but its own internal `@nextcloud/dialogs@7.3.0`
transitively requires `@vueuse/core:^14.0.0`. npm installs 14.2.1 to
satisfy the transitive constraint, but that violates nc-vue's stated
range, so `npm ls` exits 1 and SBOM generation fails.

Added an `overrides` entry that re-declares nc-vue's `@vueuse/core`
constraint as `^14.0.0`, matching what's actually installed. Other
vueuse consumers (root, @nextcloud/vue, the v6.x dialogs branch) stay
on their own ranges (10.x / 11.x) — npm hoists them separately.

The upstream nc-vue package.json should be updated to match what its
own internal deps require — flagged as a separate follow-up against
the nc-vue repo (peer-dep narrowing).

## 13 test files — PHPUnit: 142 errors (constructor signature drift)

Pre-existing test debt where production constructors gained args /
changed types but the tests weren't updated. Same pattern as
LeafProvidersMetadataTest (PR #1562). Files fixed:

- `ToolRegistrationListenerTest` (3 err): ctor went 5 → 7 args, added
  `McpToolsService` + `LoggerInterface` mocks
- `GitHubHandlerTest` (30 err): ctor switched from `IClientService` to
  6 named args incl. `IClient` + `AttributionFormatter`
- `TextExtractionService{Coverage,Deep,Gap}Test` (47+46+16 err): ctor
  gained `EmlParser $emlParser` (from PR #1504), added the mock
- `ManifestServiceTest` (1 fail): production now requires explicit
  `x-openregister-manifest-user-fields` allowlist; added to fixture
- `AnnotationNotificationDispatcherTest` (1 fail, surfaced after the
  above): production switched from `isDuplicate()` polling to
  claim-first via `record()` throwing `DuplicateDispatchException`;
  mock updated

## tests/bootstrap-unit.php — 22 ZipStream TypeErrors

If the `forms` app is installed in the test environment, it ships
ZipStream v2 (`ZipStream\Option\Archive`) in its vendor tree.
PhpSpreadsheet's writer dispatcher then sees `class_exists(Archive)`
returns true and picks the v2 entry point, but calls v2 methods
against the v3 ZipStream that OR ships, causing
`Argument #1 (\$operationMode) must be of type ZipStream\OperationMode,
null given`.

Bootstrap now defensively strips the forms-app ZipStream v2 vendor
entries from all Composer autoloaders before any test runs. No-op in
CI environments where forms isn't installed.

## lib/Db/MagicMapper.php — remove debug file_put_contents calls

Two stray `file_put_contents('/tmp/or-debug.log', ...)` calls in the
constructor were silently writing to disk and (more visibly) triggering
"Permission denied" PHP warnings during test runs. Replaced with a
logger->warning call when the static `\$constructCount > 2` guard
trips. The guard itself is left in place — it covers a circular-
construction case under investigation in #1564.

## Verification

- ✅ `composer phpcs` → 814 files, 0 errors
- ✅ `npm run lint` → 0 errors (273 warnings, pre-existing)
- ✅ `npm ls --json --long --all --package-lock-only --omit=dev` → exit 0
- ✅ `phpunit -c phpunit-unit.xml` → 12,224 tests / 25,942 assertions /
  0 errors / 0 failures (was 142 errors)
rjzondervan added a commit that referenced this pull request May 21, 2026
…-side; surface root cause on rollback (#1593)

**Root cause of the operator-reported 500:**
The `oc_openregister_entities` table has TWO `NOT NULL`-with-no-default
columns (`category` and `updated_at`) that the existing detector flow
(`EntityRecognitionHandler::findOrCreateEntity`) always populates. The
new manual-entity flow only populated `detected_at`, so the insert
failed at the DB layer with two consecutive `SQLSTATE[HY000] 1364
Field 'X' doesn't have a default value` errors — one per column.

**Fix #1 — `category` is now server-derived, removed from the API:**
- `EntityRecognitionHandler::getCategoryForType()` is now
  `public static` (was `private`) so producers outside the detector
  pipeline can derive the same mapping without taking on the
  handler's DI graph.
- `ManualEntityService::lookupOrCreateEntity()` calls it on every new
  insert. Rationale: the operator UI rarely has the context to make a
  category decision, the detector flow already derives consistently,
  and exposing the field as operator-input was the original source of
  the bug (omitted = null = NOT NULL violation). Manual-entity rows
  and detector rows now share categories for identical types.
- `category` removed from the request body, the service signature, the
  `writeAuditTrails` signature, the controller body-parsing, and the
  unit tests. The audit trail still records `category` in
  `changed.fields` — read from the entity after insert, so the
  persisted value is captured verbatim.
- Operator-override on category is intentionally NOT exposed in v1; a
  follow-up if a concrete use case emerges.

**Fix #2 — `updated_at` populated alongside `detected_at`:**
- Same root cause class. Mirroring the detector flow's column
  population fixes it.

**Fix #3 — operator-debuggable rollback path:**
The service's `catch (Throwable $error)` block wrapped the original
exception as `ManualEntityException(REASON_INTERNAL_ERROR)` without
logging. Combined with the controller's `mapManualEntityException`
only logging the reason code (not the previous-exception message), a
500 left no debuggable trail in `nextcloud.log` — exactly what the
operator hit. Added a `LoggerInterface::error()` call inside the
service's rollback handler that captures `errorType`, `error`, and
`trace` of the underlying Throwable before re-wrapping. Future
operator-reported 500s will have a stack-trace-level log entry
keyed by reqId.

**Spec + docs updates:**
- `docs/Features/manual-entity-anonymisation.md` — request body shape
  loses the `category` line; added a note explaining the server-side
  derivation.
- `openspec/changes/manual-entity-anonymisation/proposal.md` — example
  body updated; the 400 error hint updated.
- `openspec/changes/manual-entity-anonymisation/design.md` — derivation
  rationale folded into the existing "lookup-or-create" section; the
  open-question on category resolved with a "not in the API at all"
  decision.
- `openspec/changes/manual-entity-anonymisation/specs/entity-relation-grondslagen/spec.md`
  — Requirement updated, "Category mismatch does NOT trigger an
  update" scenario replaced with "New catalogue row carries the
  server-derived category".
- `openspec validate manual-entity-anonymisation` clean.

**Verification:**
- PHPCS clean (4 touched files including the new logger call).
- PHPStan clean on the full diff.
- 44 unit tests / 145 assertions across
  `ManualEntityServiceTest|FileTextControllerManualEntityTest|ChunkTextMatcherTest|GdprEntityMapperTest|EntityRelationMapperTest`
  all green in the live `master-nextcloud-1` container.
- End-to-end smoke test in the dev environment:
  - Empty-match POST → HTTP 200 with `message`, catalogue row created.
  - Match POST → HTTP 201 with `matchCount=4, matchesSkipped=2` (the
    2 skipped were pre-existing detector hits at the same positions).
  - Idempotent re-POST → HTTP 201 with `matchCount=4, matchesSkipped=4`,
    `relations: []` (no duplicates).

Refs: #1593
rjzondervan added a commit that referenced this pull request May 26, 2026
…-side; surface root cause on rollback (#1593)

**Root cause of the operator-reported 500:**
The `oc_openregister_entities` table has TWO `NOT NULL`-with-no-default
columns (`category` and `updated_at`) that the existing detector flow
(`EntityRecognitionHandler::findOrCreateEntity`) always populates. The
new manual-entity flow only populated `detected_at`, so the insert
failed at the DB layer with two consecutive `SQLSTATE[HY000] 1364
Field 'X' doesn't have a default value` errors — one per column.

**Fix #1 — `category` is now server-derived, removed from the API:**
- `EntityRecognitionHandler::getCategoryForType()` is now
  `public static` (was `private`) so producers outside the detector
  pipeline can derive the same mapping without taking on the
  handler's DI graph.
- `ManualEntityService::lookupOrCreateEntity()` calls it on every new
  insert. Rationale: the operator UI rarely has the context to make a
  category decision, the detector flow already derives consistently,
  and exposing the field as operator-input was the original source of
  the bug (omitted = null = NOT NULL violation). Manual-entity rows
  and detector rows now share categories for identical types.
- `category` removed from the request body, the service signature, the
  `writeAuditTrails` signature, the controller body-parsing, and the
  unit tests. The audit trail still records `category` in
  `changed.fields` — read from the entity after insert, so the
  persisted value is captured verbatim.
- Operator-override on category is intentionally NOT exposed in v1; a
  follow-up if a concrete use case emerges.

**Fix #2 — `updated_at` populated alongside `detected_at`:**
- Same root cause class. Mirroring the detector flow's column
  population fixes it.

**Fix #3 — operator-debuggable rollback path:**
The service's `catch (Throwable $error)` block wrapped the original
exception as `ManualEntityException(REASON_INTERNAL_ERROR)` without
logging. Combined with the controller's `mapManualEntityException`
only logging the reason code (not the previous-exception message), a
500 left no debuggable trail in `nextcloud.log` — exactly what the
operator hit. Added a `LoggerInterface::error()` call inside the
service's rollback handler that captures `errorType`, `error`, and
`trace` of the underlying Throwable before re-wrapping. Future
operator-reported 500s will have a stack-trace-level log entry
keyed by reqId.

**Spec + docs updates:**
- `docs/Features/manual-entity-anonymisation.md` — request body shape
  loses the `category` line; added a note explaining the server-side
  derivation.
- `openspec/changes/manual-entity-anonymisation/proposal.md` — example
  body updated; the 400 error hint updated.
- `openspec/changes/manual-entity-anonymisation/design.md` — derivation
  rationale folded into the existing "lookup-or-create" section; the
  open-question on category resolved with a "not in the API at all"
  decision.
- `openspec/changes/manual-entity-anonymisation/specs/entity-relation-grondslagen/spec.md`
  — Requirement updated, "Category mismatch does NOT trigger an
  update" scenario replaced with "New catalogue row carries the
  server-derived category".
- `openspec validate manual-entity-anonymisation` clean.

**Verification:**
- PHPCS clean (4 touched files including the new logger call).
- PHPStan clean on the full diff.
- 44 unit tests / 145 assertions across
  `ManualEntityServiceTest|FileTextControllerManualEntityTest|ChunkTextMatcherTest|GdprEntityMapperTest|EntityRelationMapperTest`
  all green in the live `master-nextcloud-1` container.
- End-to-end smoke test in the dev environment:
  - Empty-match POST → HTTP 200 with `message`, catalogue row created.
  - Match POST → HTTP 201 with `matchCount=4, matchesSkipped=2` (the
    2 skipped were pre-existing detector hits at the same positions).
  - Idempotent re-POST → HTTP 201 with `matchCount=4, matchesSkipped=4`,
    `relations: []` (no duplicates).

Refs: #1593
rjzondervan added a commit that referenced this pull request May 28, 2026
Bumps the ddn/sapp constraint from dev-feat/text-replacement-api#25db760
(pre-strict-review snapshot of the per-PR branch) to
dev-work/text-replacement#07e5a74 — the tip of the SAPP fork's
work/text-replacement integration branch after all 10 upstream-PR
drafts (#1-#8 upstream-numbering / #1-#10 fork-PR numbering) were
merged and strict-review remediated.

What lands by switching the pin:
- Filter chain dispatch with array-form /Filter (PR #5)
- ASCIIHex, RunLength, ASCII85, LZW decoders + encoders (#1-#4)
- ToUnicode CMap + FontEncoding for Identity-H/V composite fonts (#6)
- TJ kerning-array flattening with multi-match per TJ + spec-tight
  numeric/hex/whitespace tokenisation (#7)
- Public replace_text_in_document() API with Helvetica subset-font
  fallback + 12-key diagnostic surface + rejected_substitutions input
  validation (#8)
- All chain-failure-propagation contracts (return false on every
  decoder failure path so downstream filters short-circuit cleanly)

Preparatory for the pdf-anonymisation change which depends on the
new replace_text_in_document API.
rubenvdlinde added a commit that referenced this pull request May 28, 2026
…jection allowlist) (#2008)

CRITICAL #1 — RevertHandler RBAC bypass (C1):
RevertHandler::revert() called findAcrossAllSources with _rbac:false and
_multitenancy:false, letting any authenticated user revert any object in
any tenant without permission checks. NotAuthorizedException was imported
but never thrown.

Fix:
- Add missing constructor with AuditTrailMapper, ContainerInterface,
  IEventDispatcher, MagicMapper, and new PermissionHandler dependency.
- Remove _rbac:false / _multitenancy:false flags — use tenant-scoped find.
- After the find, call permissionHandler->hasPermission(schema, 'update',
  object) and throw NotAuthorizedException when denied.

CRITICAL #2 — Client-supplied @self overwrites server-controlled fields (C2):

Two-layer fix:

Layer 1 — SaveObject::setSelfMetadata (primary):
The method accepted owner from raw client $selfData and applied it to the
entity before applyOwnerAttribution() ran. For logged-in REST requests,
applyOwnerAttribution() overrode it; for background/system contexts with
no IUserSession user, the client-supplied owner persisted. Removed the
owner setter from setSelfMetadata — applyOwnerAttribution() is the sole
authoritative owner setter.

Layer 2 — MagicMapper::prepareObjectDataForTable (defense-in-depth):
Client could inject @self.owner and @self.authorization via the @self
block. Added explicit strip of owner and authorization from $metadata
before the DB write. Also made the register/schema override unconditional
(previously only applied when empty) to prevent identity field injection.

Tests updated: SaveObjectDeepTest, SaveObjectTest, SaveObjectCoverageTest
(owner-setting assertions inverted to assert null), MagicMapperTest
(register/schema values changed to verify server-param override; owner
column asserted null).

gate output: php -l clean on all 7 files; phpstan no new errors (2
pre-existing errors in test files unchanged); phpunit 457/457 pass on
modified test files.

References wave-7 deep review (project_deep-review-findings-2026-05-27.md)
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant