BE-538: HashQL: Permission system integration#8882
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
8dd3f9b to
929d239
Compare
1f33b16 to
7a01031
Compare
PR SummaryHigh Risk Overview Runtime authorization graft ( Server/router wiring: drops a dedicated Supporting changes: property-protection filters use a narrower Reviewed by Cursor Bugbot for commit d74c496. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 81f4f86. Configure here.
There was a problem hiding this comment.
Pull request overview
Integrates the graph authorization policy system into HashQL execution by patching compiled Postgres queries at runtime with actor-specific policy admission (permit/forbid WHERE predicates) and property protection masking (CASE WHEN–driven JSONB key removal inside the entity_editions LATERAL subquery). This also wires HashQL REST execution to require an authenticated actor header, builds PolicyComponents per request, and expands/updates the test infrastructure and snapshots to cover the new behavior.
Changes:
- Add a typed, continuation-passing prepared-query patch pipeline (
PreparedQueryPatch) and anAuthorizationPatchlayer to graft policy predicates + property masking into compiled SQL. - Rework Postgres projections to expose
entity_editionsas aCROSS JOIN LATERAL (SELECT ee.<col> AS <col> ...)so masking can targetproperties/property_metadata, and add auxiliary join planning for auth needs. - Update REST HashQL endpoint to require
X-Authenticated-User-Actor-Id, build policy context per request, propagate property protection config, and add/adjust unit + HTTP + snapshot tests.
Reviewed changes
Copilot reviewed 78 out of 78 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/graph/integration/postgres/email_filter_protection.rs | Update protection config helpers to return Arc<PropertyProtectionFilterConfig> and use the new property-filter path enum. |
| tests/graph/http/tests/hashql.http | Add negative auth tests (missing header / invalid actor) and update existing requests to send actor header. |
| tests/graph/benches/graph/scenario/runner.rs | Update benchmark settings to use Arc<PropertyProtectionFilterConfig>. |
| libs/@local/hashql/eval/tests/ui/postgres/filter/provides_drives_select_and_joins.snap | Snapshot updates reflecting entity_editions as a LATERAL subquery. |
| libs/@local/hashql/eval/tests/ui/postgres/filter/data_island_provides_without_lateral.snap | Snapshot updates reflecting entity_editions as a LATERAL subquery. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/transpile_hash_default.snap | New snapshot for default protection mask SQL + auxiliary params. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/resolve_path_uuid.snap | New snapshot for protection path resolution (Uuid). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/resolve_path_type_base_urls.snap | New snapshot for protection path resolution (TypeBaseUrls). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/resolve_expression_text.snap | New snapshot for protection parameter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/resolve_expression_actor_id.snap | New snapshot for protection actor-id lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_property_filter_case_when.snap | New snapshot for per-property CASE mask fragment. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_filter_not_equal.snap | New snapshot for protection NotEqual lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_filter_nested_all.snap | New snapshot for nested All lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_filter_in.snap | New snapshot for protection In lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_filter_equal.snap | New snapshot for protection Equal lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/protection/lower_filter_any_disjunction.snap | New snapshot for protection Any lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_joins_no_laterals_with_auth.snap | New snapshot for auxiliary-join insertion (no laterals, auth joins present). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_joins_inserts_before_laterals.snap | New snapshot for inserting auth joins before continuation laterals. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_from_with_entity_ids_and_editions.snap | New snapshot validating FROM assembly with ids + editions lateral. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_from_with_entity_editions.snap | New snapshot validating FROM assembly with editions lateral. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_from_editions_before_continuations.snap | New snapshot validating editions lateral precedes continuation laterals. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/projections/build_from_base_only.snap | New snapshot for base-only FROM. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/optimize_single_web.snap | New snapshot for policy optimization (single web id). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/optimize_single_entity.snap | New snapshot for policy optimization (single entity uuid). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/optimize_multiple_webs.snap | New snapshot for policy optimization (batched web ids). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/optimize_multiple_entities.snap | New snapshot for policy optimization (batched entity uuids). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/is_of_type_overlap.snap | New snapshot for (base_url, version) pairing check via array_positions overlap. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/is_of_base_type_any.snap | New snapshot for base-url membership via ANY(base_urls). |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/filter_not_negation.snap | New snapshot for NOT policy filter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/filter_any_disjunction.snap | New snapshot for Any policy filter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/filter_all_conjunction.snap | New snapshot for All policy filter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/created_by_principal_with_actor.snap | New snapshot for created-by principal check with actor. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/created_by_principal_anonymous.snap | New snapshot for created-by principal check with anonymous/public actor. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/constraint_web.snap | New snapshot for web-scoped constraint lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/constraint_web_with_created_by.snap | New snapshot for web constraint + created-by filter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/constraint_exact_entity.snap | New snapshot for exact-entity constraint lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/constraint_any_with_type_filter.snap | New snapshot for any-entity constraint with type filter lowering. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/algebra_permits_and_forbids.snap | New snapshot for combined permit + forbid algebra result. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/algebra_constrained_permits_only.snap | New snapshot for constrained-permits-only algebra result. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/policy/algebra_blank_permit_with_forbids.snap | New snapshot for blank-permit + forbids algebra result. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/integration/patch_with_policy_and_protection.snap | New integration snapshot showing both WHERE graft + property masking. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/integration/patch_protection_with_type_base_urls.snap | New integration snapshot ensuring auth joins are in-scope for masking. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/integration/patch_instance_admin_bypasses_protection.snap | New integration snapshot for instance-admin bypass of masking. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/integration/patch_blank_permit_no_protection.snap | New integration snapshot for minimal patching on allow-all/no mask. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/integration/patch_blank_forbid_denies_all.snap | New integration snapshot for deny-all blank forbid with defense-in-depth masking. |
| libs/@local/hashql/eval/tests/ui/postgres/authorization/.spec.toml | Mark authorization UI snapshot suite as skipped and name the suite. |
| libs/@local/hashql/eval/src/postgres/tests.rs | Introduce shared Postgres compiler test fixture + SQL lint helper. |
| libs/@local/hashql/eval/src/postgres/projections.rs | Rework entity_editions join into LATERAL subquery + add AuxiliaryProjections and auth join materialization. |
| libs/@local/hashql/eval/src/postgres/prepared.rs | Add prepared query representation with patch-layer pipeline (HList/CPS) and query registry iteration helpers. |
| libs/@local/hashql/eval/src/postgres/parameters.rs | Add AuxiliaryParameters for runtime policy/protection parameter binding. |
| libs/@local/hashql/eval/src/postgres/mod.rs | Export new authorization + prepared query plumbing, and remove prior property-mask-on-compiler approach. |
| libs/@local/hashql/eval/src/postgres/filter/tests.rs | Migrate filter tests to shared fixture and remove old property-mask test path. |
| libs/@local/hashql/eval/src/postgres/authorization/tests.rs | Add mock authorization store + end-to-end patching tests and reports. |
| libs/@local/hashql/eval/src/postgres/authorization/protection/tests.rs | Add unit tests and snapshots for protection lowering. |
| libs/@local/hashql/eval/src/postgres/authorization/protection/mod.rs | Add property protection lowering into SQL mask expressions + join demands. |
| libs/@local/hashql/eval/src/postgres/authorization/policy/tests.rs | Add unit tests and snapshots for policy translation + optimizations. |
| libs/@local/hashql/eval/src/postgres/authorization/policy/mod.rs | Implement policy constraint/filter translation to SQL plus optimization batching. |
| libs/@local/hashql/eval/src/postgres/authorization/mod.rs | Implement AuthorizationPatch patch layer (WHERE admission + projection masking graft). |
| libs/@local/hashql/eval/src/orchestrator/request/mod.rs | Add iterator module for request parameter iteration. |
| libs/@local/hashql/eval/src/orchestrator/request/iter.rs | Add ExactSizeIterator adapter for chaining compiled + auxiliary params. |
| libs/@local/hashql/eval/src/orchestrator/request/graph_read.rs | Execute SQL with chained compiled + auxiliary parameters via query_raw. |
| libs/@local/hashql/eval/package.json | Move Rust workspace deps from devDependencies to dependencies for eval package. |
| libs/@local/hashql/eval/Cargo.toml | Adjust dependency grouping/ordering; ensure auth/store deps are available where needed. |
| libs/@local/graph/type-fetcher/src/store.rs | Add AsRef<T> forwarding impl to satisfy new REST store bounds. |
| libs/@local/graph/store/src/filter/protection.rs | Introduce PropertyFilterEntityQueryPath and narrow protection filter path types. |
| libs/@local/graph/store/src/filter/parameter.rs | Update conversions to handle new PropertyFilterExpressionList type. |
| libs/@local/graph/store/src/filter/mod.rs | Update property-filter-to-entity-filter conversion to use new path type. |
| libs/@local/graph/postgres-store/src/store/postgres/query/table.rs | Add EntityEditions::ALL for explicit projection selection in LATERAL editions subquery. |
| libs/@local/graph/postgres-store/src/store/postgres/query/expression/table_reference.rs | Add TableName::from_table helper for const construction. |
| libs/@local/graph/postgres-store/src/store/postgres/query/expression/conditional.rs | Add array_positions() SQL function support and uuid PostgresType. |
| libs/@local/graph/postgres-store/src/store/postgres/mod.rs | Make filter_protection an Arc and export a PostgresClient alias. |
| libs/@local/graph/postgres-store/src/lib.rs | Enable additional nightly features used by new const helpers/arrays. |
| libs/@local/graph/api/src/rest/mod.rs | Wire HashQL routes into REST router and pass filter_protection via extensions; update store bounds to provide Postgres client access. |
| libs/@local/graph/api/src/rest/hashql/mod.rs | Require authenticated actor header, build PolicyComponents, apply AuthorizationPatch, and execute via store pool. |
| libs/@local/graph/api/src/rest/hashql/error.rs | Improve diagnostics for store acquire and auth-context creation (actor-not-found vs infra). |
| libs/@local/graph/api/src/rest/hashql/compile.rs | Collect per-query action permissions from compiled artifacts to drive policy resolution. |
| libs/@local/graph/api/openapi/openapi.json | Add required X-Authenticated-User-Actor-Id header to HashQL endpoint spec. |
| apps/hash-graph/src/subcommand/server.rs | Pass filter_protection into REST dependencies and update store bounds/types accordingly. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// This traverses the right spine of `CrossJoin` nodes to find the | ||
| /// insertion point (the innermost non-LATERAL join tree), appends | ||
| /// authorization joins there, and reassembles the LATERAL chain on top. | ||
| pub(crate) fn build_joins(&self, mut from: FromItem<'static>) -> FromItem<'static> { |
7a01031 to
df982ed
Compare
81f4f86 to
0f60303
Compare
df982ed to
ea95719
Compare
0f60303 to
5a0812f
Compare

🌟 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_policiessystem used by the REST entity endpoints.🚫 Blocked by
entity_edition_cache#8854 (BE-596: Materialize entity aggregates intoentity_edition_cache)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/)entity_editionsLATERAL subquery, stripping protected property keys frompropertiesandproperty_metadatacolumnsPreparedQueryPatch): typed layer composition where each layer receives anextcontinuation, registers join demands before materialization, and can rewrite the FROM tree afterVec<Box<dyn ToSql + Sync>>sidepiece onPreparedQuery, chained as borrowed refs during encoding, separate from the compiler'sParameterssystemREST API wiring (
graph/api/src/rest/hashql/)PolicyComponentsbuilt per-request from the authenticated actor and query-derived actionsPropertyProtectionFilterConfigread from the store pool settings (respectsHASH_GRAPH_SKIP_FILTER_PROTECTION)actor_not_found(ERROR/400) vsauthorization_context_failed(BUG/500), withDetermineActorchecking inner cause to distinguish store errors from genuine not-foundProjection system (
eval/src/postgres/projections.rs)AuxiliaryProjections: tracks compiled join aliases, detects existing joins (punch-through), registers new join demandsbuild_joins: materializes auth joins into the FROM tree viamem::replaceon CrossJoin right spine, maintaining LATERAL ordering (non-LATERAL before LATERAL)find_from_by_alias: recursive FROM tree walker for locating graft targetsTest infrastructure
CompilationFixtureinpostgres/tests.rsused by both filter and authorization testsMockStoreimplementingPrincipalStore+PolicyStorewith strict assertions (actor UUID, action, principal context)Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
PropertyProtectionFilterConfigusesHashMapinternally, 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?
❓ How to test this?
yarn dev:backend)cd tests/graph/http && yarn reset-database && yarn httpyac send --all tests/hashql.httpcargo test --lib --package hashql-eval -- postgres