Skip to content

Commit 9144504

Browse files
authored
Merge pull request #41 from federated-catalogue-enhancements-2026/merge-to-upstream/CAT-FR-AU-Schema-Activation
Enhancement [CAT-FR-AU] Document schema-module admin toggles in the architecture chapters
2 parents 34d14e2 + 26ca4b9 commit 9144504

6 files changed

Lines changed: 95 additions & 4 deletions

File tree

federated-catalogue/src/docs/architecture/chapters/04_solution_strategy.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,9 @@ Non-RDF schemas are uploaded and managed through the same `/schemas` endpoint as
126126
* **RDF content types** (`application/rdf+xml`, `application/ld+json`, `text/turtle`) are processed by the `SchemaStore` — parsed as RDF, type-detected, and subject to the integrity constraints described above.
127127
* **Non-RDF content types** (`application/schema+json`, `application/xml`) bypass RDF parsing entirely. The raw file is stored in the schema File Store, and metadata (content type, upload timestamp) is persisted in the Metadata Store. An auto-generated identifier (`urn:schema:sha256:<hash>`) is assigned based on content hash, providing deduplication.
128128

129-
Non-RDF schemas appear in the schema listing (`GET /schemas`) under their own type categories (`jsonSchemas`, `xmlSchemas`) and can be retrieved, replaced, and deleted through the standard schema CRUD endpoints. However, they are **not used for automatic SHACL verification** of credentials — they serve as stored reference schemas for external tooling or manual validation.
129+
Non-RDF schemas appear in the schema listing (`GET /schemas`) under their own type categories (`jsonSchemas`, `xmlSchemas`) and can be retrieved, replaced, and deleted through the standard schema CRUD endpoints. They are **not used for automatic SHACL verification** of credentials, but the on-demand validation endpoint (`POST /assets/validate`) does invoke the corresponding validator for non-RDF assets — JSON Schema for `application/json` assets, XML Schema for `application/xml` assets — when the caller selects a matching schema.
130+
131+
Each of the four schema languages exposed through `/schemas` (SHACL, JSON Schema, XML Schema, OWL) has an administrative on/off toggle managed through the admin schema-modules page; see ADR 16 for the toggle semantics and the asymmetric role of the OWL toggle (which gates role resolution rather than a validator).
130132

131133
==== Schema Validation Checks
132134
Each schema before adding it, will be analysed. The Analysis process should check first

federated-catalogue/src/docs/architecture/chapters/05_building_block_view.adoc

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1547,6 +1547,8 @@ flowchart TB
15471547
style EXT fill:#e3f2fd,stroke:#42a5f5
15481548
....
15491549

1550+
NOTE: The `Schema validation enabled?` branch combines two gates. The caller's `verifySchema` request parameter selects whether SHACL runs at all; the administrative SHACL module toggle (see ADR 16) then gates that branch independently. When `verifySchema=true` and SHACL is administratively disabled, the request short-circuits with `400 Bad Request` body `module_disabled:SHACL` before reaching the SHACL composite. The same pattern applies on the on-demand validation endpoint via `AssetValidationServiceImpl`.
1551+
15501552
===== Docker Compose configuration
15511553

15521554
The default profile uses `docker-compose.yml` with `dev.env`. The strict profile adds the `docker-compose.strict.yml` overlay, which sets all verification flags to `true`:
@@ -1723,7 +1725,7 @@ Five delegate services in `fc-service-server` implement the admin endpoints, eac
17231725
| Service | Implements | Endpoints
17241726
| `AdminDashboardService` | `AdminApiDelegate` | `GET /admin/me` (access check), `GET /admin/stats` (metrics), `GET /admin/health` (component health), `GET /admin/keycloak-url`
17251727
| `TrustFrameworkAdminService` | `TrustFrameworkAdminApiDelegate` | List, toggle, and update trust frameworks; checks live connectivity per framework on list
1726-
| `SchemaValidationAdminService` | `SchemaValidationAdminApiDelegate` | List schema modules (SHACL, JSON Schema, XML Schema, OWL) with toggle and schema counts. SHACL, JSON Schema, and XML Schema toggles are enforced by on-demand validation (`POST /assets/validate`) via `AssetValidationService`; in upload verification, only SHACL is consulted. OWL toggles persist to `admin_config` but are not yet wired to any validation pipeline
1728+
| `SchemaValidationAdminService` | `SchemaValidationAdminApiDelegate` | List schema modules (SHACL, JSON Schema, XML Schema, OWL) with toggle and schema counts; toggle each module on/off; expose the per-ontology subclass contribution to each registered role for the OWL module. SHACL, JSON Schema, and XML Schema toggles are enforced by on-demand validation (`POST /assets/validate`) via `AssetValidationService` and return `400 module_disabled:<MODULE>` when off; in upload verification only SHACL is consulted, with the same error contract. The OWL toggle gates the runtime composite-ontology subclass walk in `CredentialVerificationStrategy.resolveRole` (see ADR 16)
17271729
| `GraphDatabaseAdminService` | `GraphDatabaseAdminApiDelegate` | Return current graph DB status; persist preferred backend to `admin_config` (switch takes effect on restart)
17281730
|===
17291731

@@ -1733,14 +1735,30 @@ These components in `fc-service-core` read admin-controlled settings during norm
17331735
|===
17341736
| Component | Triggered by | Effect of admin configuration
17351737
| `LoirePolicyEnforcer` | Loire envelope processing (per request, via `LoireCredentialProcessor`) | Consults `TrustFrameworkService.isEnabled("gaia-x")` to decide whether Loire-specific policies (issuer `did:web` restriction, trust-chain validation) apply at all; reads `trust_anchor_url` from the bundle's `properties` for `x5u` trust-chain validation
1736-
| `AssetValidationServiceImpl` | On-demand asset validation (`POST /assets/validate`) | Checks module state via `SchemaModuleConfigService` for `SHACL`, `JSON_SCHEMA`, and `XML_SCHEMA`. If a module is disabled, the corresponding validator path is skipped or rejected.
1737-
| `CredentialVerificationStrategy` | Credential verification | Reads SHACL module state via `SchemaModuleConfigService` (cached with `@Cacheable` — hot path). Disabling SHACL returns HTTP 400 on schema validation requests. JSON Schema, XML Schema, and OWL toggles are persisted but not yet wired to the verification pipeline. The Gaia-X enabled flag is consulted via `TrustFrameworkService` (see `LoirePolicyEnforcer` above)
1738+
| `AssetValidationServiceImpl` | On-demand asset validation (`POST /assets/validate`) | Checks module state via `SchemaModuleConfigService` for `SHACL`, `JSON_SCHEMA`, and `XML_SCHEMA`. If a module is disabled, the request is rejected with `ClientException` → `400 Bad Request`, body `module_disabled:<MODULE>` (see ADR 16).
1739+
| `CredentialVerificationStrategy` | Credential verification | Reads SHACL and OWL module state via `SchemaModuleConfigService` (cached with `@Cacheable` — hot path). Disabling SHACL returns the same `400 module_disabled:SHACL` contract on requests with `verifySchema=true`. Disabling OWL bypasses the runtime composite-ontology subclass walk in `resolveRole`; credentials whose type is only reachable through that walk fall through to `ResolvedRole.UNKNOWN` and are rejected by the verification entry point with `400 Bad Request` (see ADR 16). JSON Schema and XML Schema toggles do not bite on this path today — every credential is JSON-LD or JWT-wrapped JSON-LD and routes to SHACL. The Gaia-X enabled flag is consulted via `TrustFrameworkService`.
17381740
|===
17391741

17401742
NOTE: The graph backend switch (`graphstore.preferred.backend`) is not yet implemented in the Admin UI — the preference is persisted to `admin_config` but switching backends currently requires a manual config update and service restart.
17411743

17421744
The `trust_frameworks` and `admin_config` tables that back this configuration layer are documented under <<section-concepts,Cross-cutting Concepts — Admin Configuration Persistence>>.
17431745

1746+
===== Schema-module admin toggles
1747+
1748+
The admin schema-modules page (proxied from the demo portal to `GET /admin/schema-validation` on the catalogue) lists four toggleable modules — SHACL, JSON Schema, XML Schema, OWL — and surfaces an additional read-only endpoint, `GET /admin/schema-validation/ontologies`, backed by `OntologyImpactService`.
1749+
1750+
The four toggles fall into two semantic classes (ADR 16):
1751+
1752+
* **Validators (SHACL, JSON Schema, XML Schema)** gate the corresponding validator wherever the catalogue would otherwise run it (on-demand validation, and credential verification with `verifySchema=true`). When off, the affected request returns `400 Bad Request` body `module_disabled:<MODULE>`. Stored schemas remain queryable regardless of toggle state.
1753+
1754+
* **OWL** gates the third tier of role resolution. When off, the runtime composite-ontology subclass walk in `CredentialVerificationStrategy.resolveRole` is skipped; only the registry index and `additional_roots` (ADR 10) are consulted. Credentials whose type is only reachable through the subclass walk fall through to `ResolvedRole.UNKNOWN` and are rejected by the verification entry point with `400 Bad Request`. Registry-direct types are unaffected.
1755+
1756+
`OntologyImpactService` parses each `ONTOLOGY`-type schema row with Jena and counts the `rdfs:subClassOf+` descendants reachable from each registered role's root URI (plus any `additional_roots`), aggregated across active trust-framework bundles. The result is a read-only map of *(ontology id, role name) → subclass count* so operators can see what the OWL toggle is gating before flipping it off.
1757+
1758+
The result surfaces two signals the UI uses to keep the toggle decision honest. Each ontology row carries a `parseError` flag — true when the ontology fails to parse or the per-root subclass walk exceeds its two-second SPARQL timeout. The UI shows a warning row in that case so an admin does not read "zero contributions" as "safe to disable OWL"; an unparseable ontology and a parseable-but-irrelevant ontology look identical in the contributions map otherwise. A top-level `noActiveBundles` flag is set when `TrustFrameworkRegistry.getActiveBundles()` returns empty, i.e. when no trust-framework bundle was loaded from the classpath at boot; the flag does *not* track the runtime trust-framework enabled toggle (the toggle flips the `trust_frameworks.enabled` DB row but does not unload bundles from the registry), so disabling every bundle through the admin page does not raise `noActiveBundles`. The two-second timeout is per registered role root, not per ontology, so a deeply cyclic `subClassOf` graph in one ontology cannot block the rest of the listing.
1759+
1760+
The SHACL toggle additionally gates the background revalidation sweep (see ADR 16): when SHACL is off, `RevalidationServiceImpl.handleTask` skips per-asset validation rather than revoke previously-conforming assets. The periodic sweep is not actively wired in the default production configuration today; the gate is pre-positioned for when it is. Re-queueing chunks skipped while SHACL was off lands together with production wiring of the sweep.
1761+
17441762
=== Level 3: Shared data structures
17451763

17461764
==== VerificationResults

federated-catalogue/src/docs/architecture/chapters/07_deployment_view.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,9 @@ Replace `<portal-origin>` with your portal URL (e.g., `http://localhost:8088` fo
6060
==== Admin Console URL
6161

6262
The demo portal auto-derives the Admin Console URL from `keycloak.auth-server-url` and `keycloak.realm` (aliased as `admin.dashboard.keycloak-auth-server-url` / `admin.dashboard.keycloak-realm`). Override with `KEYCLOAK_ADMIN_CONSOLE_URL` (or `admin.dashboard.keycloak-admin-console-url`) if Keycloak is behind a different hostname.
63+
64+
=== Runtime Admin Configuration
65+
66+
Most catalogue features that an operator can flip at runtime are persisted in the `admin_config` PostgreSQL table — including the four schema-module toggles (`schema.module.SHACL.enabled`, `schema.module.JSON_SCHEMA.enabled`, `schema.module.XML_SCHEMA.enabled`, `schema.module.OWL.enabled`) and the per-framework enabled flag in `trust_frameworks`. Values written through the admin endpoints survive restarts; no new environment variables are introduced by this layer.
67+
68+
`federated-catalogue.verification.schema` (start-up boolean) and the four `schema.module.*.enabled` rows (admin-controlled booleans) operate at different layers. The start-up flag selects whether SHACL is consulted at all during credential upload; the admin module toggles then gate the corresponding validator wherever it would otherwise run. See ADR 16 for the toggle semantics, including the asymmetric role of the OWL toggle.

federated-catalogue/src/docs/architecture/chapters/08_concepts.adoc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,23 @@ The resulting list is short by design (typically three to ten entries) and stabl
7676
When the conformity body is silent on such a class — no validation component, no criteria document, no test payload — the safe default is a defensive mapping: route the class to the most semantically adjacent existing role via `additional_roots` so credentials of that type, if they ever appear, land somewhere rather than being rejected.
7777
Document the mapping inline with a pointer to what would un-block dropping it (typically: confirmation from the conformity body that the class is never issued at the top level).
7878

79+
==== Type Dispatch and the OWL Toggle
80+
81+
When a credential arrives, the catalogue maps its `credentialSubject` type URI to a trust-framework role through three tiers, in order:
82+
83+
1. **`TrustFrameworkRegistry.typeIndex`** — a hash lookup populated at boot by walking each bundle's `ontology.ttl` from every declared role down via `rdfs:subClassOf*`. Hits here cover every class statically reachable from a role at startup.
84+
2. **`additional_roots`** — sibling URIs declared in `framework.yaml` for cases where the ontology's class graph does not connect the sibling to the role (see ADR 10 for the `gx:DigitalServiceOffering` example). Also indexed at boot.
85+
3. **Composite-ontology subclass walk** — `ClaimValidator.resolveSubjectRole` runs a SPARQL `rdfs:subClassOf+` query against the runtime composite ontology assembled from `ONTOLOGY`-type schema rows. This is the only tier that consults schemas uploaded *after* boot.
86+
87+
The OWL administrative toggle (ADR 16) controls whether the catalogue admits **runtime extensions** to its registered type vocabulary.
88+
With OWL on, custom subclasses introduced through runtime-uploaded ontologies are recognised and routed to the registered role they extend.
89+
With OWL off, the catalogue accepts only the types statically known at boot — credentials whose type was introduced via a runtime ontology upload are rejected at the verification entry point with `400 Bad Request`.
90+
Tiers 1 and 2 continue to run in either state; only tier 3 (the subclass walk over the runtime composite ontology) is gated.
91+
The admin UI labels the toggle accordingly, so operators see the user-facing effect rather than the implementation tier it gates.
92+
93+
Disabling OWL therefore has a *narrow* but *strict* effect: framework-direct types (Participant, ServiceOffering, Resource for the Gaia-X 2511 bundle, and equivalents in any other bundle's `framework.yaml`) keep resolving and keep being accepted; custom subclasses defined exclusively in a runtime-uploaded ontology stop resolving and are rejected.
94+
Gating role resolution is the most visible effect a toggle on a type-vocabulary input can have, and the one that satisfies the demonstrable-effect requirement for runtime toggles.
95+
7996
=== Admin Configuration Persistence
8097

8198
Admin configuration is backed by two PostgreSQL tables. The `trust_frameworks` table holds structured trust framework definitions (columns: `id`, `name`, `service_url`, `api_version`, `timeout_seconds`, `enabled`, `created_at`, `updated_at`), seeded with a single Gaia-X entry. The `admin_config` table is a generic key-value store (`config_key` PK, `config_value`) for all other runtime settings:

federated-catalogue/src/docs/architecture/chapters/09_architecture_decisions.adoc

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,3 +518,39 @@ Routing on `content_kind` is unambiguous and preserves backward compatibility
518518
Overwrite semantics simplify the data model: no partial-merge conflicts, no ordering dependencies between enrichment uploads.
519519

520520
**Related:** ADR 4, ADR 7.
521+
522+
=== ADR 16: Schema-module admin toggles, with OWL gating role resolution
523+
524+
**Related:** ADR 10.
525+
526+
**Context:** Four schema languages — SHACL, JSON Schema, XML Schema, OWL — must each be enable/disable-able at runtime through the administration UI, and every toggle must have a demonstrable effect.
527+
528+
SHACL, JSON Schema, and XML Schema all fit a uniform validator shape: input runs against stored schemas of the matching type and a conformance verdict is returned.
529+
OWL is different — uploaded ontologies extend the type vocabulary the role resolver consults via `rdfs:subClassOf+`; OWL has no validation step of its own.
530+
A uniform `module_disabled` semantic therefore does not fit OWL: without separate semantics, the OWL toggle would persist in the database with no observable effect, which violates the demonstrable-effect requirement.
531+
532+
**Decision:** Expose all four toggles on the admin schema-modules page, but treat them as two semantic classes. The admin UI splits the two classes into separate sections so the asymmetry is visible to operators.
533+
534+
* **Validators (SHACL, JSON Schema, XML Schema):** the toggle gates the validator wherever the catalogue would otherwise run it — on-demand validation, and credential verification when the caller requests a schema check.
535+
When off, the affected endpoint returns `400 Bad Request` with body `module_disabled:<MODULE>`.
536+
Schema storage is unaffected; schemas remain queryable through the schema endpoints regardless of toggle state.
537+
538+
* **OWL:** the toggle gates the third tier of role resolution — the `rdfs:subClassOf+` walk over runtime-uploaded ontologies, on top of the registry index and `additional_roots` defined by ADR 10.
539+
When off, only tiers 1 and 2 are consulted; credentials whose type is only reachable through the subclass walk resolve to `ResolvedRole.UNKNOWN`, and the verification entry point rejects them with `400 Bad Request`.
540+
Registry-direct types (Participant, ServiceOffering, Resource for the Gaia-X 2511 bundle, and equivalents in any other bundle's `framework.yaml`) are unaffected.
541+
542+
**Consequences:**
543+
544+
* Disabling a validator is *permissive* (a check is skipped); disabling OWL is *restrictive* (the catalogue stops recognising custom subclasses).
545+
The asymmetry is intentional: gating role resolution is the strongest demonstrable effect for a type-vocabulary input, and a uniform "skip a check" semantic does not exist for OWL.
546+
547+
* The OWL toggle is independent of the caller's `verifySchema` and `verifySemantics` flags.
548+
Type dispatch is a configuration of the catalogue's type system, not a per-request validation step, so disabling OWL affects every internal caller of the verification path — asset upload, provenance ingest, asset metadata flow, base subscriber, and on-demand role resolution — independent of how those callers parameterise their request.
549+
550+
* The SHACL toggle also gates the periodic revalidation sweep over the existing claims graph.
551+
When SHACL is administratively disabled, `RevalidationServiceImpl.handleTask` short-circuits — the sweep skips per-asset validation rather than revoke previously-conforming assets against shapes the operator has explicitly silenced.
552+
The periodic sweep is not actively wired in the default production configuration today; the gate is pre-positioned for when it is.
553+
Re-sweeping the backlog of chunks skipped while SHACL was off (re-queueing them on the false→true transition) is a follow-up that lands together with production wiring of the sweep — it is a one-liner against `RevalidationService.startValidating()` at that point.
554+
555+
* On-demand validation (`POST /assets/validate`) rejects with `400 Bad Request` not only for `module_disabled:<MODULE>` but also for the broader "no validation module is enabled or applicable" case.
556+
Both are administrative-configuration mismatches under operator control, not server-side processing failures, so `ClientException` (400) is the appropriate mapping rather than `VerificationException` (422). 422 remains reserved for assets whose type is not validatable at all (not RDF, unsupported content type).

0 commit comments

Comments
 (0)