Skip to content

BE-538: HashQL: Permission system integration#8882

Open
indietyp wants to merge 34 commits into
bm/be-71-hashql-consider-merging-ast-stagesfrom
bm/be-538-hashql-permission-system-integration
Open

BE-538: HashQL: Permission system integration#8882
indietyp wants to merge 34 commits into
bm/be-71-hashql-consider-merging-ast-stagesfrom
bm/be-538-hashql-permission-system-integration

Conversation

@indietyp

@indietyp indietyp commented Jun 18, 2026

Copy link
Copy Markdown
Member

🌟 What is the purpose of this PR?

Integrates the authorization policy system into HashQL query execution. Compiled queries are now patched at runtime with actor-specific permit/forbid conditions (WHERE clause) and property masking (CASE WHEN projections), achieving parity with the existing PolicyComponents + Filter::for_policies system used by the REST entity endpoints.

Size note: This PR is large (75 files, ~4.8k added). If review is difficult, it can be split into 4 stacked PRs:

Production Tests Snapshots HTTP
PR1: Prerequisites (patch system, projections, shared test infra) ~800 ~500 ~30
PR2: Policy (permit/forbid WHERE clause) ~400 ~600 ~200
PR3: Protection (property masking graft) ~250 ~700 ~500
PR4: API wiring (REST integration, diagnostics) ~350 ~69

🚫 Blocked by

These PRs rework the underlying entity tables and query compilation that the authorization graft targets. Once they land, this PR will need to undergo some adjustments.

🔍 What does this change?

Authorization patching system (eval/src/postgres/authorization/)

  • Policy admission: translates Cedar-style permit/forbid policies into SQL WHERE predicates, with two-phase lowering (blank permit/forbid detection before expression allocation to avoid dead parameters)
  • Property masking: generates CASE WHEN expressions grafted into the entity_editions LATERAL subquery, stripping protected property keys from properties and property_metadata columns
  • HList/CPS pipeline (PreparedQueryPatch): typed layer composition where each layer receives a next continuation, registers join demands before materialization, and can rewrite the FROM tree after
  • Auxiliary parameters: Vec<Box<dyn ToSql + Sync>> sidepiece on PreparedQuery, chained as borrowed refs during encoding, separate from the compiler's Parameters system

REST API wiring (graph/api/src/rest/hashql/)

  • PolicyComponents built per-request from the authenticated actor and query-derived actions
  • PropertyProtectionFilterConfig read from the store pool settings (respects HASH_GRAPH_SKIP_FILTER_PROTECTION)
  • Proper diagnostics: actor_not_found (ERROR/400) vs authorization_context_failed (BUG/500), with DetermineActor checking inner cause to distinguish store errors from genuine not-found

Projection system (eval/src/postgres/projections.rs)

  • AuxiliaryProjections: tracks compiled join aliases, detects existing joins (punch-through), registers new join demands
  • build_joins: materializes auth joins into the FROM tree via mem::replace on CrossJoin right spine, maintaining LATERAL ordering (non-LATERAL before LATERAL)
  • find_from_by_alias: recursive FROM tree walker for locating graft targets

Test infrastructure

  • Shared CompilationFixture in postgres/tests.rs used by both filter and authorization tests
  • MockStore implementing PrincipalStore + PolicyStore with strict assertions (actor UUID, action, principal context)
  • 45 authorization unit tests (policy algebra, protection lowering, projection building, 5 E2E integration)
  • HTTP tests updated: all HashQL requests now authenticate, plus new tests for missing header and invalid actor

Pre-Merge Checklist 🚀

🚢 Has this modified a publishable library?

This PR:

  • does not modify any publishable blocks or libraries, or modifications do not need publishing

📜 Does this require a change to the docs?

The changes in this PR:

  • are internal and do not require a docs change

🕸️ Does this require a change to the Turbo Graph?

The changes in this PR:

  • do not affect the execution graph

⚠️ Known issues

  • PropertyProtectionFilterConfig uses HashMap internally, so SQL generation order for multiple protected properties is non-deterministic. Semantics are order-independent (concatenated mask expression), but query plans could theoretically differ between requests. The multi-property test uses structural assertions instead of snapshots to avoid flakiness.

🛡 What tests cover this?

  • 45 authorization unit tests: policy algebra (7), protection lowering (14), projection building (7), side-effect assertions (5), E2E integration (5), structural assertions (7)
  • 24 filter tests (migrated to shared infrastructure)
  • HTTP integration tests: missing auth header (400), invalid actor with diagnostic (400), authenticated queries (200)
  • All existing HashQL HTTP tests updated to pass actor ID

❓ How to test this?

  1. Start the stack (yarn dev:backend)
  2. Run HTTP tests: cd tests/graph/http && yarn reset-database && yarn httpyac send --all tests/hashql.http
  3. Verify: 10/10 pass, including auth header rejection and invalid actor diagnostic
  4. Run Rust tests: cargo test --lib --package hashql-eval -- postgres

@vercel

vercel Bot commented Jun 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hash Error Error Jun 30, 2026 12:50pm
petrinaut Ready Ready Preview Jun 30, 2026 12:50pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hashdotdesign-tokens Ignored Ignored Preview Jun 30, 2026 12:50pm

@vercel vercel Bot temporarily deployed to Preview – petrinaut June 18, 2026 15:18 Inactive
@github-actions github-actions Bot added area/apps > hash* Affects HASH (a `hash-*` app) area/libs Relates to first-party libraries/crates/packages (area) labels Jun 18, 2026
@indietyp indietyp force-pushed the bm/be-71-hashql-consider-merging-ast-stages branch from 7a01031 to df982ed Compare June 23, 2026 09:41
@indietyp indietyp force-pushed the bm/be-538-hashql-permission-system-integration branch from 81f4f86 to 0f60303 Compare June 23, 2026 09:41
Copilot AI review requested due to automatic review settings June 23, 2026 09:52
@indietyp indietyp force-pushed the bm/be-71-hashql-consider-merging-ast-stages branch from df982ed to ea95719 Compare June 23, 2026 09:52
@indietyp indietyp force-pushed the bm/be-538-hashql-permission-system-integration branch from 0f60303 to 5a0812f Compare June 23, 2026 09:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 78 out of 78 changed files in this pull request and generated 5 comments.

Comment thread libs/@local/hashql/eval/src/postgres/projections.rs
Comment thread libs/@local/hashql/eval/src/postgres/projections.rs
Comment thread libs/@local/hashql/eval/src/postgres/projections.rs
Comment thread libs/@local/hashql/eval/src/postgres/projections.rs
Comment thread libs/@local/graph/store/src/filter/protection.rs Outdated
@vercel vercel Bot temporarily deployed to Preview – petrinaut June 23, 2026 10:03 Inactive
Copilot AI review requested due to automatic review settings June 30, 2026 09:56
@indietyp indietyp force-pushed the bm/be-538-hashql-permission-system-integration branch from d74c496 to 267b920 Compare June 30, 2026 09:56
@indietyp indietyp force-pushed the bm/be-71-hashql-consider-merging-ast-stages branch from ea95719 to 1cb0e47 Compare June 30, 2026 09:56
unit: &mut PolicyTranslationUnit<'_, A>,
filter: &EntityResourceFilter,
) -> Expression {
match filter {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 78 out of 78 changed files in this pull request and generated 2 comments.

///
/// Accessors like [`Self::entity_editions`] register that a table is needed and return a
/// reference to it. The actual `FROM` tree is built once at the end via [`Self::build_from`].
#[derive(Debug, Clone)]
Comment on lines +539 to +544
pub(crate) const fn snapshot(&self) -> Self {
Self {
base: self.base.snapshot(),
..*self
}
}
@indietyp indietyp force-pushed the bm/be-538-hashql-permission-system-integration branch from 267b920 to b2e4747 Compare June 30, 2026 12:44
@indietyp indietyp force-pushed the bm/be-71-hashql-consider-merging-ast-stages branch from 1cb0e47 to 340c629 Compare June 30, 2026 12:44
Copilot AI review requested due to automatic review settings June 30, 2026 12:45
@indietyp indietyp force-pushed the bm/be-71-hashql-consider-merging-ast-stages branch from 340c629 to 3e715ff Compare June 30, 2026 12:45
@indietyp indietyp force-pushed the bm/be-538-hashql-permission-system-integration branch from b2e4747 to 1e921af Compare June 30, 2026 12:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 78 out of 78 changed files in this pull request and generated 2 comments.

Comment on lines +65 to +67
pub(crate) const fn snapshot(&self) -> Self {
Self { ..*self }
}
Comment on lines +539 to +544
pub(crate) const fn snapshot(&self) -> Self {
Self {
base: self.base.snapshot(),
..*self
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/apps > hash* Affects HASH (a `hash-*` app) area/apps > hash-graph area/apps area/libs Relates to first-party libraries/crates/packages (area) area/tests New or updated tests type/eng > backend Owned by the @backend team

Development

Successfully merging this pull request may close these issues.

3 participants