Skip to content

Research and prototype asset-level RBAC for read-only SPARQL (#33224) - #33279

Draft
fmcardoso wants to merge 7 commits into
mainfrom
fmcardoso/add-asset-level-rbac-for-read-only-sparql-querie
Draft

fmcardoso wants to merge 7 commits into
mainfrom
fmcardoso/add-asset-level-rbac-for-read-only-sparql-querie

Conversation

@fmcardoso

@fmcardoso fmcardoso commented Sep 14, 2026

Copy link
Copy Markdown
Member

Summary

Related to #33224 · Companion chat integration: https://github.com/open-metadata/ai-platform/issues/1299

Draft for design/security/performance feedback—not production asset RBAC. This PR adds research, a proposed ADR, and a test-only experiment for read-only SPARQL over caller-authorized RDF facts. Existing admin-only and glossary-scoped boundaries are unchanged. No production endpoint, schema, POM, configuration or UI changes are introduced. This PR should not close #33224 yet.

Reading guide

Required semantics

Use the effective authenticated caller and ordinary OpenMetadata discovery, field and detail restrictions. There is no least-restrictive override for RDF.

Authorization must apply before evaluation, not by filtering result rows: a hidden asset can otherwise change COUNT/ASK, joins, EXISTS, or connect two visible assets through a path. For example, if A→B→C and A→D, and B is hidden, A has one visible direct upstream and no visible path to C through B.

  • Relationships, shared facts and structured child nodes need explicit governing resource/field rules.
  • Brief metadata lag from existing asynchronous RDF updates is acceptable; an incomplete projection or truncated retrieval must not produce a supposedly complete COUNT/ASK.
  • Evaluate authorization per request using catalog-backed attributes and existing cache/invalidation behavior. Do not add RDF-induced revocation delay.
  • Initially rebuild the caller-specific model per request, reusing the RDF projection—not regenerating it. No cross-request authorized-model cache is proposed.

Candidate design and why we selected it for the experiment

Caller + catalog-backed permissions
  → authorized candidate resources
  → bounded retrieval from graph/knowledge
  → explicit fact/field/ownership admission
  → fresh sanitized local Jena model
  → restricted SELECT/ASK evaluation

Why this baseline: once admission and query confinement are correct, hidden facts are physically absent from the model the query reads. It reuses installed Jena ARQ and the existing local-query pattern, without initially repartitioning the projection or shipping policy decisions across a new Fuseki trust boundary. Unknown mappings, ownership conflicts and retrieval-budget overflow fail closed.

This is a simplicity/auditability choice, not a demonstrated performance win or approved production architecture. The complete field mapping and the query profile still need security validation.

Alternatives and the additional primary-source web check

Option What the sources establish Why not selected as the first implementation
Jena Permissions Jena 6.2.0 retired-module list: last release was 5.6.0. No supported drop-in module for this checkout's 6.2.0 stack.
Fuseki graph ACLs Official docs: user-to-named-graph read access. Assets currently share the knowledge graph; graph ACLs alone cannot express asset/field/edge rules without projection changes.
Local filtered dataset wrapper 6.2.0 DatasetGraphFilteredView: predicate-filtered dataset access. Requires proof that all engine read routes are filtered and still needs data locally.
Custom server-side Fuseki/TDB2 filtering Tuple-filter docs and 6.2.0 SecurityContextView: query-context filtering exists. Strong fallback if materialization is too costly, but trusted caller transport, OM policy/field integration, and path/optimizer/extension coverage remain unproved. No speedup has been measured.
Query rewriting / narrower templates ARQ evaluation extensions: algebra transforms and custom execution hooks exist. Not turnkey RBAC. Filtering visible endpoints does not remove hidden intermediate path nodes. A narrower supported language could help selective retrieval, but needs correctness proof and agreement with #1299.
Alternative store with built-in controls Stardog documents named-graph ACLs and property-based masking. Masking is not equivalent to removing hidden assets/facts. Its property-protection docs explicitly list zero-length-path, full-text and edge-property limitations and say that feature should not be considered production-ready. Migrating stores does not eliminate OM policy integration.

Important correction from the second pass: Fuseki is not inherently limited to a static registry. Its 6.2.0 AuthorizationService is pluggable and DataAccessCtl.controlledDataset accepts it. Dynamic integration need not mirror policies into static ACLs. The research addendum, pushed in 4e409314cd7, corrects the earlier overbroad static-registry characterization and records the additional sources and performance decision gate.

Research conclusion: this bounded second pass found no clearly better drop-in solution. It does keep custom server-side filtering and a narrower query contract on the table if measurements reject local materialization. It is not an exhaustive survey or a proof of safety for any alternative.

Evidence now included in the PR

112 tests passed locally: 56 in-process + the same 56 with Fuseki-backed retrieval, zero failures/errors/skips. This is a recorded worktree run, not a claim about PR CI.

  • Four-table fixture built with production RDF translation/lineage code; real PolicyEvaluator, but test-supplied catalog/policy context—not full caller/role resolution.
  • COUNT, ASK, joins, EXISTS, inverse/transitive/cyclic paths and hidden-only mutation invariance.
  • Conflicting structured-node owners/kinds and catalog-resource collisions rejected.
  • 22 unsupported query forms rejected, including tested nested SERVICE/GRAPH/dataset/property/extension-function forms.
  • Retrieval confined to graph/knowledge; overflow and unknown fact mappings fail closed.
  • Opt-in Fuseki 6.2.0 image built from current-checkout RDF-store sources; one isolated container with a 1 GiB memory cap, 384 MiB heap and 256 MiB tmpfs. Remote suite remains disabled unless explicitly enabled.
  • Fuseki loads/serves the fixture and runs retrieval CONSTRUCTs. User-query evaluation happens on the local sanitized model, not inside Fuseki.

The test report includes commands, image provenance and formatting verification. TDB2 literal canonicalization required reading the unrestricted reference through the same store. One arm64 run on four tables and coarse container-memory samples are not scale or per-request latency evidence. No production API, ingestion or UI tests apply to this test-only scope.

Concrete gaps found

  • Tag application: om:labelType / om:tagState are projected onto shared tag nodes, losing the owning asset context needed for authorization. Normal populated attributes currently fail closed.
  • Lineage details: SQL/column-lineage facts describe an edge involving two assets; source-asset visibility alone is insufficient.
  • Field permissions: all mapped fixture predicates currently use VIEW_BASIC. Finer-grained usage/sample-data/tests/queries/custom-property restrictions are not demonstrated.
  • Caller integration: actual roles, teams, personas, owner conditions, catalog loading, admin/bot/reviewer behavior and search-RBAC equivalence remain untested end to end.
  • Consistency/freshness: permission changes, multi-pod invalidation, multiple retrieval reads during rebuild/promotion, incomplete projection detection and remote cancellation remain open.
  • Security boundary: passing the query-profile cases does not establish complete confinement for arbitrary SPARQL or all engine extensions.

Latency and scalability: decision gate, not a solved problem

The initial target is roughly 2 seconds for the complete server-side graph request, including candidate discovery, authorization, RDF retrieval, model construction, evaluation and serialization. LLM SPARQL generation and answer composition are excluded. Scope, concurrency and the eventual SLO percentile still need agreement.

We do not yet know whether this approach meets that target. Per-asset policy/DB work and copying an authorized graph can both dominate; existing caches do not prove otherwise.

Next, combine real authorization integration with a bounded performance experiment:

  1. Increasing scopes, initially 100 / 1,000 / 10,000 assets as resources permit; lookup, traversal and catalog-wide aggregation queries.
  2. Cold/warm p50 and p95, then modest concurrency; separate each processing stage and record DB calls, retrieval requests/bytes/triples, and model memory.
  3. Check whether existing bulk/search-backed authorization can reduce work without changing effective permissions or losing completeness.
  4. If representative queries routinely exceed the budget, compare selective complete retrieval, a narrower contract, or server-side filtering before implementing the endpoint.

Do not meet the target with silent candidate truncation, partial aggregates, weaker permissions, or an unreviewed cross-request model cache. Earlier experiment-document wording that postpones all scale measurements until mapping is complete is superseded by this combined integration/performance next step.

Open contract decision: discovery versus direct-read permissions

Unresolved: should SPARQL include assets the caller can discover through search/browse, assets they can directly read, or an explicitly defined combination?

The code investigation found different authorization paths: direct reads evaluate policies against a specific resource, while discovery uses type-level checks and filtering. Search-policy compilation may skip unsupported conditions. Consequently, discovery results must not be assumed equivalent to direct-read permissions: omitted deny conditions can admit extra candidates, while omitted allow conditions can exclude readable assets.

Rechecking every candidate can remove unauthorized assets, but cannot recover assets missing from candidate discovery. This matters for complete COUNT/ASK answers. Full policy compilation alone also does not establish completeness when the search index may lag.

  • Authorization/product maintainers agree which semantics the graph tool must follow, including field/detail restrictions and any intentional discovery/read differences.
  • Validate the relevant paths using real users, roles and resources; explicitly test mismatches rather than assuming equivalence.
  • Establish complete candidate retrieval for the agreed semantics. Use exhaustive, bounded catalog enumeration as an experimental baseline; fail on exceeded limits rather than silently truncate. Defer search-based optimization until its completeness is demonstrated.

This is a clarification required to implement “ordinary OpenMetadata permissions,” not approval to choose the more permissive path or silently redefine discovery. The first direct-read alignment test will provide evidence, not settle the product contract.

Questions for peers

  1. Does the candidate admission model match normal OM discovery and field/detail rules? Which canonical bulk authorization path should it reuse?
  2. How should tag-application and lineage-detail ownership be represented? Is a targeted projection fix unavoidable?
  3. What catalog/query scope and concurrency should the two-second target cover? Is unrestricted catalog-wide aggregation required for Move UI module into its own maven module #1299?
  4. Is server-side filtering worth prototyping now, or only if the baseline misses the budget? What engine coverage would be required to trust it?
  5. Which existing projection-state and cache mechanisms can provide complete reads and ordinary revocation behavior without new synchronization infrastructure?
  6. What typed query/error/completeness contract does the chat integration require?

Checklist

  • Issue, ADR, research, alternatives and experimental evidence linked.
  • Test-only experiment and opt-in Fuseki suite pushed.
  • Maintainer agreement on architecture and fact permissions.
  • Real authorization integration and representative latency/memory evidence.
  • Completeness, consistency, freshness and cancellation validated.
  • Typed API contract and production security review.

UI screenshots: not applicable.


Summary by Gitar

  • Documentation:
    • Added research document for RDF authorization and asset-level SPARQL RBAC
    • Added ADR and guided implementation plan for authorized SPARQL asset RBAC

This will update automatically on new commits.

- Records the proposed (not accepted) design for asset-level, read-only
  SPARQL access requested in OpenMetadata #33224, distinguishing it from
  the existing admin-only and glossary-scoped query paths.
- Captures source-derived feasibility findings, the A/B/C/D visibility
  example, alternative architectures, and open decisions requiring
  owner sign-off before implementation.
- Lays out a small-commit implementation roadmap and cross-repo
  contract checklist with ai-platform #1299 to guide future work.
- Captures a bounded web/source research pass on Jena/Fuseki access
  control facilities (jena-permissions, Fuseki graph ACL, TDB tuple
  filters, DatasetGraphFilteredView) evaluated against the repo's
  pinned Jena 6.2.0 and current RDF projection architecture.
- Compares candidates and recommends a request-local sanitized-model
  approach over adapter-based filtering, since only the former
  confines reads by construction without unverified engine coverage.
- Proposes a minimal, unapproved experiment with concrete acceptance
  tests to validate the recommended approach before it becomes an ADR.
- Feeds the pending ADR at docs/adr/2026-09-14-authorized-sparql.md
  and flags open engineering unknowns (predicate-to-field mapping,
  rebuild consistency, cross-pod cache invalidation) that block it.
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — workflow failed

Validated commit 9c2f27a7682031d248b83700c573f1ee0db1ec51 in Playwright run 34836232482, attempt 1.

✅ 0 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky

⚙️ No test failures. This run failed in CI infrastructure or reporting (see Pipeline and setup failures), not because of your changes — no test action needed from you.

Pipeline and setup failures (6)

  • The build job finished with status failure.
  • Duration-aware shard planning finished with status skipped.
  • Fixture cache restoration finished with status skipped.
  • Seeded fixture preparation finished with status skipped.
  • The Playwright shard matrix was unexpectedly skipped.
  • No expected Playwright shards were declared.

Performance

⚪ Performance metrics unavailable; see the CI and reporting failures above.

Shard Passed Failed Flaky Skipped Lifecycle failed Lifecycle flaky

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@fmcardoso
fmcardoso marked this pull request as draft September 14, 2026 11:07
Adds an opt-in Testcontainers-based subclass that reruns the sanitized
sanitized-model experiment suite with retrieval against an isolated,
memory-capped Fuseki 6.2.0 container instead of in-process Jena,
closing the previously open question of whether the approach holds
against a real triple store. Updates the ADR and experiment writeup
with the run results, commands, and a new finding that TDB2
canonicalizes literal terms, which required reading the unrestricted
reference through the same store to keep comparisons valid.
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@@ -0,0 +1,445 @@
package org.openmetadata.service.rdf;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: New Java files missing Apache-2.0 license header

The six new .java files start directly with the package statement, while existing test files in this tree (e.g. AsyncOperationsConfigurationTest.java, BindJsonTest.java) begin with the standard Apache-2.0 / Collate license header block, which the repo's Java conventions require. Add the standard header to each new file so spotless/checkstyle and the license convention pass.

Was this helpful? React with 👍 / 👎

@fmcardoso fmcardoso changed the title Add asset level rbac for read only sparql querie Research and prototype asset-level RBAC for read-only SPARQL (#33224) Sep 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@@ -0,0 +1,614 @@
package org.openmetadata.service.rdf;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Quality: New IT file missing Apache-2.0 license header

RdfAuthorizationAlignmentIT.java starts directly with the package declaration and has no Apache-2.0 license header, which java.md requires for all Java files (and which most IT files in this tree carry). Add the standard Collate/Apache-2.0 header block above the package statement.

Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 2 findings

Research and prototype for asset-level SPARQL RBAC with comprehensive documentation, ADR, design alternatives and 112 passing tests. Add Apache-2.0 license headers to the six new Java test files (SanitizedModelBuilder.java, SanitizedModelExperimentTest.java, SanitizedModelFixture.java, SanitizedModelFusekiTest.java, SanitizedQueryProfile.java, PolicyContextFixture.java) and the IT file (RdfAuthorizationAlignmentIT.java) to align with repo conventions.

💡 Quality: New Java files missing Apache-2.0 license header

📄 openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelBuilder.java:1 📄 openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelExperimentTest.java:1 📄 openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelFixture.java:1 📄 openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelFusekiTest.java:1 📄 openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedQueryProfile.java:1 📄 openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/PolicyContextFixture.java:1

The six new .java files start directly with the package statement, while existing test files in this tree (e.g. AsyncOperationsConfigurationTest.java, BindJsonTest.java) begin with the standard Apache-2.0 / Collate license header block, which the repo's Java conventions require. Add the standard header to each new file so spotless/checkstyle and the license convention pass.

💡 Quality: New IT file missing Apache-2.0 license header

📄 openmetadata-integration-tests/src/test/java/org/openmetadata/service/rdf/RdfAuthorizationAlignmentIT.java:1

RdfAuthorizationAlignmentIT.java starts directly with the package declaration and has no Apache-2.0 license header, which java.md requires for all Java files (and which most IT files in this tree carry). Add the standard Collate/Apache-2.0 header block above the package statement.

🤖 Prompt for agents
Code Review: Research and prototype for asset-level SPARQL RBAC with comprehensive documentation, ADR, design alternatives and 112 passing tests. Add Apache-2.0 license headers to the six new Java test files (`SanitizedModelBuilder.java`, `SanitizedModelExperimentTest.java`, `SanitizedModelFixture.java`, `SanitizedModelFusekiTest.java`, `SanitizedQueryProfile.java`, `PolicyContextFixture.java`) and the IT file (`RdfAuthorizationAlignmentIT.java`) to align with repo conventions.

1. 💡 Quality: New Java files missing Apache-2.0 license header
   Files: openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelBuilder.java:1, openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelExperimentTest.java:1, openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelFixture.java:1, openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedModelFusekiTest.java:1, openmetadata-service/src/test/java/org/openmetadata/service/rdf/SanitizedQueryProfile.java:1, openmetadata-service/src/test/java/org/openmetadata/service/security/policyevaluator/PolicyContextFixture.java:1

   The six new `.java` files start directly with the `package` statement, while existing test files in this tree (e.g. AsyncOperationsConfigurationTest.java, BindJsonTest.java) begin with the standard Apache-2.0 / Collate license header block, which the repo's Java conventions require. Add the standard header to each new file so spotless/checkstyle and the license convention pass.

2. 💡 Quality: New IT file missing Apache-2.0 license header
   Files: openmetadata-integration-tests/src/test/java/org/openmetadata/service/rdf/RdfAuthorizationAlignmentIT.java:1

   RdfAuthorizationAlignmentIT.java starts directly with the package declaration and has no Apache-2.0 license header, which java.md requires for all Java files (and which most IT files in this tree carry). Add the standard Collate/Apache-2.0 header block above the package statement.

Review coverage

Rules No rules evaluated

Functional validation Not enabled · Set up

Auto-approval Not enabled · Set up

Options

Display: compact → Counting what did not apply, without listing it.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

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.

Add asset-level RBAC for read-only SPARQL queries

1 participant