Skip to content

graphql: add a tenant reachability filter to serviceAccounts - #3292

Draft
GregorShear wants to merge 1 commit into
greg/prefix-filter-refactorfrom
greg/gql-tenant-filter
Draft

graphql: add a tenant reachability filter to serviceAccounts#3292
GregorShear wants to merge 1 commit into
greg/prefix-filter-refactorfrom
greg/gql-tenant-filter

Conversation

@GregorShear

@GregorShear GregorShear commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Adds serviceAccounts(filter: { tenant: "acmeCo/" }), narrowing a listing to the accounts an organization reaches through the role-grant graph.

  • tenant_reachable_prefixes walks role grants from the caller's footholds within the tenant — their authorized prefixes clamped into its subtree — and keeps the prefixes reached with QueryServiceAccounts. Seeding from footholds rather than the tenant root means a branch-scoped caller witnesses only reach flowing from namespace they occupy; edges granted to sibling branches contribute nothing. For a caller whose foothold covers the tenant root, the walk is the tenant's full reach.
  • The reachable set is intersected with the caller's own authorized prefixes. intersect_prefixes keeps the deeper prefix of each overlapping pair, so the result feeds catalog_name ^@ ANY($1) without admitting anything outside either set. The filter only ever removes rows the caller could already see.
  • Naming a tenant requires at least one foothold — QueryServiceAccounts somewhere within (or covering) the tenant's namespace. Without a gate, filtering by an arbitrary tenant and observing which namespaces return rows would reveal its role-grant edges; the check is a function of the caller's own grants alone, so a denial reveals nothing about the tenant.

The filter lives on serviceAccounts only; promoting it into the shared PrefixFilter later is mechanical.

Unit tests cover the capability-scoped walk (including ancestor-edge inheritance, sibling-branch exclusion, and multi-seed union) and the intersection. An integration test asserts the filter keeps only the deeper deepCo/team/ branch of a broader authorization, never widens past the caller's access, admits a branch-scoped caller filtering by the enclosing tenant, denies tenants the caller holds nothing in, and rejects malformed input.

Dashboard consumer: estuary/ui#2042, which depends on this deploying first.

@GregorShear
GregorShear force-pushed the greg/gql-tenant-filter branch from 18eb725 to e516e83 Compare August 3, 2026 20:54
@GregorShear
GregorShear changed the base branch from master to greg/prefix-filter-refactor August 3, 2026 20:54
@GregorShear
GregorShear force-pushed the greg/gql-tenant-filter branch from e516e83 to db2611b Compare August 3, 2026 23:42
@GregorShear
GregorShear force-pushed the greg/gql-tenant-filter branch from db2611b to 5a74132 Compare August 3, 2026 23:44
serviceAccounts(filter: {tenant}) narrows a listing to the accounts an organization reaches through the role-grant graph. tenant_reachable_prefixes walks the graph from the caller's footholds within the tenant — their authorized prefixes clamped into its subtree — and keeps prefixes reached with QueryServiceAccounts, so a branch-scoped caller witnesses only reach flowing from namespace they occupy. The reachable set is intersected with the caller's own authorized prefixes, keeping the deeper prefix of each overlapping pair, so the filter only ever removes rows the caller could already see. Naming a tenant requires at least one foothold; the denial is a function of the caller's own grants alone and reveals nothing about the tenant, including whether it exists. Also documents the MAX_PREFIXES guard, which the intersection can newly trip when a broad caller prefix fans out into many deeper reachable prefixes.
@GregorShear
GregorShear force-pushed the greg/gql-tenant-filter branch from 5a74132 to 450ba60 Compare August 3, 2026 23:46
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.

1 participant