Skip to content

H-4825: Implement initial meta-policy handling #7589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

TimDiekmann
Copy link
Member

🌟 What is the purpose of this PR?

This PR implements meta-permissions for policy management in HASH's authorization system. It establishes who can create, update, and delete policies by introducing a new Policy entity type in Cedar schema and implementing resource constraints that scope policy management permissions to appropriate actor roles.

The implementation follows the principle that policy management should be restricted by resource constraints - web administrators can manage policies within their web scope, while preventing unauthorized global policy creation that could lead to privilege escalation.

🔗 Related links

🚫 Blocked by

  • N/A

🔍 What does this change?

Cedar Schema Updates

  • Add Policy entity type with actions: Set<String> attribute
  • Add policy management actions: createPolicy, viewPolicy, updatePolicy, archivePolicy, deletePolicy
  • Add delete as top-level action (placeholder for future delete actions)

Authorization System Enhancements

  • New Action Hierarchy: Implement policy-specific actions (CreatePolicy, ViewPolicy, etc.) as children of base actions
  • Meta-Resource Constraints: Add MetaResourceConstraint and MetaResourceFilter for policy-based permissions
  • Policy Meta-Resource: Add PolicyMetaResource for Cedar entity representation of policies
  • Resource Constraint Integration: Extend ResourceConstraint enum with Meta variant

Policy Store Implementation

  • Authorization Checks: All policy CRUD operations now validate permissions using meta-policies
  • Scope Enforcement: Web administrators can only manage policies within their web scope
  • Global Policy Protection: System-wide policies are protected by forbid policies

Security Model

  • Privilege Escalation Prevention: Global forbid policies prevent creation of dangerous meta-policies
  • Web Admin Scoping: Policy management permissions are scoped to specific web contexts
  • System Actor Permissions: Limited system machine permissions for temporary policy creation

Core Components

  • libs/@local/graph/authorization/src/policies/resource/meta.rs: New meta-resource implementation
  • Policy action definitions with proper parent-child relationships
  • Cedar expression tree support for HasAction filtering
  • Enhanced error handling for policy operations

🐾 Next steps

  • Add resource-ID based policy management for fine-grained permissions

🛡 What tests cover this?

  • Integration Tests: tests/hash-backend-integration/src/tests/graph/authorization/policy.test.ts
    • Policy creation within web scope (permitted)
    • Global policy creation (forbidden)
    • Policy querying and filtering
    • Policy CRUD operations with proper authorization
  • Unit Tests: libs/@local/graph/postgres-store/tests/principals/policies.rs
    • Policy resolution through role hierarchies
    • Meta-resource constraint validation
    • Action hierarchy testing
  • Resource Constraint Tests: libs/@local/graph/authorization/src/policies/resource/meta.rs
    • Meta-resource filter serialization/deserialization
    • Cedar expression generation for policy constraints

❓ How to test this?

  1. Checkout the branch and run the test suite
  2. Create a test user with web administrator role
  3. Try creating a policy scoped to their web (should succeed)
  4. Try creating a global policy (should fail with "Permission to create policy was denied")
  5. Try creating a policy with dangerous actions like CreateWeb (should fail due to global forbid policy)
  6. Verify policy querying returns only policies the user has permission to view

@TimDiekmann TimDiekmann requested review from hashdotai and Copilot July 11, 2025 14:35
@TimDiekmann TimDiekmann self-assigned this Jul 11, 2025
@github-actions github-actions bot added area/libs Relates to first-party libraries/crates/packages (area) type/eng > backend Owned by the @backend team area/tests New or updated tests area/tests > integration New or updated integration tests labels Jul 11, 2025
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds initial support for meta-policy handling by introducing a new Policy entity in the Cedar schema, meta‐resource constraints, and wiring those through the authorization store and engine.

  • Define Policy entity type and policy-specific actions (createPolicy, viewPolicy, etc.) in the schema.
  • Implement MetaResourceConstraint/MetaResourceFilter and PolicyMetaResource for Cedar integration.
  • Refactor the Postgres policy store to insert, update, archive, and delete policies with meta‐permission checks.

Reviewed Changes

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

Show a summary per file
File Description
tests/hash-backend-integration/src/tests/graph/authorization/policy.test.ts Update TS tests to cover web vs. global policy CRUD
libs/@local/graph/store/src/filter/mod.rs Extend filter logic to ignore meta constraints for non-policy types
libs/@local/graph/postgres-store/src/store/postgres/seed_policies.rs Seed initial meta-policies (system, global, web)
libs/@local/graph/postgres-store/src/store/postgres/mod.rs Refactor policy CRUD methods to use meta-components
libs/@local/graph/authorization/src/policies/store/error.rs Add NotAuthorized variants for create/update/delete
libs/@local/graph/authorization/src/policies/resource/meta.rs New meta-resource filter and constraint definitions
libs/@local/graph/authorization/src/policies/mod.rs Register PolicyId and PolicyMetaResource in Cedar
libs/@local/graph/authorization/schemas/policies.cedarschema Extend Cedar schema with Policy entity and actions
Comments suppressed due to low confidence (2)

libs/@local/graph/postgres-store/src/store/postgres/seed_policies.rs:264

  • [nitpick] The authenticated-view-meta policy name is reused for User, Machine, and Ai actor types. Consider making names unique (e.g., include the actor type) to avoid confusion when debugging or auditing.
            name: Some("authenticated-view-meta".to_owned()),

libs/@local/graph/postgres-store/src/store/postgres/seed_policies.rs:507

  • [nitpick] The default-web-meta policy name for all web roles may conflict if multiple roles seed it. Consider appending the web role name or ID to distinguish these policies.
        name: Some("default-web-meta".to_owned()),

Copy link

codecov bot commented Jul 11, 2025

Codecov Report

Attention: Patch coverage is 17.84512% with 732 lines in your changes missing coverage. Please review.

Project coverage is 55.06%. Comparing base (1b4e903) to head (736919a).
Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...cal/graph/postgres-store/src/store/postgres/mod.rs 0.00% 556 Missing ⚠️
...postgres-store/src/store/postgres/seed_policies.rs 0.00% 91 Missing ⚠️
.../graph/authorization/src/policies/resource/meta.rs 69.71% 53 Missing ⚠️
...ibs/@local/graph/authorization/src/policies/mod.rs 42.10% 11 Missing ⚠️
...cal/graph/authorization/src/policies/components.rs 0.00% 9 Missing ⚠️
...l/graph/authorization/src/policies/resource/mod.rs 57.14% 3 Missing and 3 partials ⚠️
...@local/graph/authorization/src/policies/context.rs 0.00% 3 Missing ⚠️
.../authorization/src/policies/principal/actor/mod.rs 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7589      +/-   ##
==========================================
- Coverage   55.22%   55.06%   -0.16%     
==========================================
  Files        1082     1083       +1     
  Lines       94786    95329     +543     
  Branches     4510     4513       +3     
==========================================
+ Hits        52341    52494     +153     
- Misses      41461    41848     +387     
- Partials      984      987       +3     
Flag Coverage Δ
apps.hash-ai-worker-ts 1.32% <ø> (ø)
apps.hash-api 0.00% <ø> (ø)
local.hash-backend-utils 3.69% <ø> (ø)
local.hash-graph-sdk 0.00% <ø> (ø)
local.hash-isomorphic-utils 0.00% <ø> (ø)
rust.hash-graph-api 4.20% <ø> (ø)
rust.hash-graph-authorization 61.99% <65.16%> (+0.23%) ⬆️
rust.hash-graph-postgres-store 20.09% <0.00%> (-0.44%) ⬇️
rust.hash-graph-store 32.63% <ø> (ø)
rust.hash-graph-validation 83.15% <ø> (ø)
rust.hashql-compiletest 51.45% <ø> (ø)
rust.hashql-eval 71.42% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@TimDiekmann TimDiekmann force-pushed the t/h-4148-replace-remaining-spicedb-implementation-with-new-interface branch from a8ab8d5 to 6d2c302 Compare July 14, 2025 12:45
@TimDiekmann TimDiekmann force-pushed the t/h-4825-explore-meta-permissions-who-can-manage-policies branch from 60786c7 to 1c0b4be Compare July 14, 2025 12:46
@TimDiekmann TimDiekmann requested a review from CiaranMn July 18, 2025 21:37
Base automatically changed from t/h-4148-replace-remaining-spicedb-implementation-with-new-interface to main July 21, 2025 08:47
@TimDiekmann TimDiekmann force-pushed the t/h-4825-explore-meta-permissions-who-can-manage-policies branch from 1c0b4be to 736919a Compare July 21, 2025 10:10
Copy link
Contributor

Benchmark results

@rust/hash-graph-benches – Integrations

representative_read_entity

Function Value Mean Flame graphs
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1 $$29.2 \mathrm{ms} \pm 367 \mathrm{μs}\left({\color{gray}0.538 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1 $$29.4 \mathrm{ms} \pm 297 \mathrm{μs}\left({\color{gray}0.896 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1 $$29.4 \mathrm{ms} \pm 297 \mathrm{μs}\left({\color{gray}0.310 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1 $$31.2 \mathrm{ms} \pm 242 \mathrm{μs}\left({\color{red}7.88 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1 $$30.3 \mathrm{ms} \pm 241 \mathrm{μs}\left({\color{gray}2.62 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1 $$28.7 \mathrm{ms} \pm 270 \mathrm{μs}\left({\color{gray}-1.795 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2 $$29.0 \mathrm{ms} \pm 277 \mathrm{μs}\left({\color{gray}2.88 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1 $$29.3 \mathrm{ms} \pm 268 \mathrm{μs}\left({\color{gray}3.19 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1 $$29.1 \mathrm{ms} \pm 312 \mathrm{μs}\left({\color{gray}-0.024 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_linkless

Function Value Mean Flame graphs
entity_by_id 10000 entities $$21.3 \mathrm{ms} \pm 165 \mathrm{μs}\left({\color{lightgreen}-23.076 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1000 entities $$14.0 \mathrm{ms} \pm 54.9 \mathrm{μs}\left({\color{lightgreen}-12.515 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 1 entities $$14.1 \mathrm{ms} \pm 55.0 \mathrm{μs}\left({\color{lightgreen}-5.977 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$14.4 \mathrm{ms} \pm 65.2 \mathrm{μs}\left({\color{gray}-2.539 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 100 entities $$13.4 \mathrm{ms} \pm 65.2 \mathrm{μs}\left({\color{lightgreen}-5.399 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_complete_one_depth

Function Value Mean Flame graphs
entity_by_id 1 entities $$56.7 \mathrm{ms} \pm 270 \mathrm{μs}\left({\color{gray}-0.879 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 50 entities $$5.48 \mathrm{s} \pm 543 \mathrm{ms}\left({\color{red}1418 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$109 \mathrm{ms} \pm 1.25 \mathrm{ms}\left({\color{red}5.98 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 25 entities $$265 \mathrm{ms} \pm 1.03 \mathrm{ms}\left({\color{red}9.32 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 5 entities $$64.2 \mathrm{ms} \pm 242 \mathrm{μs}\left({\color{gray}-4.200 \mathrm{\%}}\right) $$ Flame Graph

scaling_read_entity_complete_zero_depth

Function Value Mean Flame graphs
entity_by_id 1 entities $$14.1 \mathrm{ms} \pm 48.7 \mathrm{μs}\left({\color{lightgreen}-5.604 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 50 entities $$17.6 \mathrm{ms} \pm 103 \mathrm{μs}\left({\color{gray}0.105 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 10 entities $$15.7 \mathrm{ms} \pm 126 \mathrm{μs}\left({\color{gray}4.07 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 25 entities $$17.2 \mathrm{ms} \pm 139 \mathrm{μs}\left({\color{gray}2.05 \mathrm{\%}}\right) $$ Flame Graph
entity_by_id 5 entities $$15.0 \mathrm{ms} \pm 122 \mathrm{μs}\left({\color{gray}1.04 \mathrm{\%}}\right) $$ Flame Graph

representative_read_entity_type

Function Value Mean Flame graphs
get_entity_type_by_id Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba $$8.59 \mathrm{ms} \pm 43.0 \mathrm{μs}\left({\color{gray}0.677 \mathrm{\%}}\right) $$ Flame Graph

representative_read_multiple_entities

Function Value Mean Flame graphs
link_by_source_by_property depths: DT=2, PT=2, ET=2, E=2 $$272 \mathrm{ms} \pm 1.57 \mathrm{ms}\left({\color{red}5.83 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=0, E=2 $$235 \mathrm{ms} \pm 1.14 \mathrm{ms}\left({\color{gray}1.56 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=255, PT=255, ET=255, E=255 $$287 \mathrm{ms} \pm 2.42 \mathrm{ms}\left({\color{gray}3.56 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=0, E=0 $$191 \mathrm{ms} \pm 1.02 \mathrm{ms}\left({\color{gray}2.50 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=0, ET=2, E=2 $$247 \mathrm{ms} \pm 1.50 \mathrm{ms}\left({\color{gray}0.900 \mathrm{\%}}\right) $$ Flame Graph
link_by_source_by_property depths: DT=0, PT=2, ET=2, E=2 $$259 \mathrm{ms} \pm 2.66 \mathrm{ms}\left({\color{gray}3.05 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=2, PT=2, ET=2, E=2 $$129 \mathrm{ms} \pm 1.04 \mathrm{ms}\left({\color{gray}0.510 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=0, ET=0, E=2 $$108 \mathrm{ms} \pm 983 \mathrm{μs}\left({\color{gray}2.42 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=255, PT=255, ET=255, E=255 $$148 \mathrm{ms} \pm 979 \mathrm{μs}\left({\color{gray}0.659 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=0, ET=0, E=0 $$99.1 \mathrm{ms} \pm 675 \mathrm{μs}\left({\color{gray}0.617 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=0, ET=2, E=2 $$116 \mathrm{ms} \pm 824 \mathrm{μs}\left({\color{gray}1.79 \mathrm{\%}}\right) $$ Flame Graph
entity_by_property depths: DT=0, PT=2, ET=2, E=2 $$124 \mathrm{ms} \pm 888 \mathrm{μs}\left({\color{gray}0.792 \mathrm{\%}}\right) $$ Flame Graph

Merged via the queue into main with commit 1ff268a Jul 21, 2025
90 checks passed
@TimDiekmann TimDiekmann deleted the t/h-4825-explore-meta-permissions-who-can-manage-policies branch July 21, 2025 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/libs Relates to first-party libraries/crates/packages (area) area/tests > integration New or updated integration tests 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