Skip to content

Fix current authorization of preloaded generated relationship targets - #98

Merged
Dastari merged 2 commits into
mainfrom
test/generated-relation-authorization-20261001
Oct 1, 2026
Merged

Dastari merged 2 commits into
mainfrom
test/generated-relation-authorization-20261001

Conversation

@Dastari

@Dastari Dastari commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Generated nullable relationships returned preloaded objects before current target authorization or complete ownership-key resolution. A forged snapshot could therefore expose a denied or cross-tenant target. Resolve the authoritative target through the existing typed query/uncached batch loader, recheck current entity/row authority, and preflight selected target fields with their actual child contexts. Denial preserves an authorized parent, nulls its nullable child and attaches a sanitized relationship-path error. Missing/mismatched targets ordinarily return null without errors; SQL-complete visibility treats hidden rows as absent without an unauthorized existence probe.

Remove generated parents' pool-only eager preloads, which could query a denied target and fail the whole parent before nullable traversal. Keep explicit preload APIs source compatible. Apply current SQL visibility before relation paging/counting and include it in batch identity alongside DbAuthContext. Callback-only/prefiltered to-many policies require the existing host AuthorizedScanConfig: examine bounded database batches to exhaustion, retain only the requested visible window, and fail closed on an exhausted budget rather than returning partial counts or false end-of-data. Residual scans run per parent; SQL-complete loads remain batched. Existing SDL and cursor formats are unchanged. Policy callbacks may run during preflight and ordinary resolution; both receive the actual child context/arguments/alias/path. No cross-statement snapshot isolation under concurrent external DML is promised.

This is a separately reviewable dependent PR based on #96 at 4617b332014758387a3c271a7b30a5aff4873453 for the two-crate regression fixture. #96's narrow cross-crate fix remains unchanged. It does not depend on PR A or B–D, add joined/computed queries/group continuation/private view adapters, or change private repository roots. ORM/macros are aligned at 0.33.5. Version order: #96 (0.33.4), this fix (0.33.5), #97 (0.34.0); realign/rebase the pending patches if the owner chooses a different merge order. No release publication is authorized or performed.

Runnable SQL-free host example:

cargo run --manifest-path crates/graphql-orm-macros/fixtures/cross-crate-relations/Cargo.toml --locked -p cross-crate-source-models --no-default-features --features sqlite --example authorized_links

The example populates a forged cached snapshot, resolves the current target through a composite tenant/identity binding, then changes host target authority and demonstrates parent-preserving nullable denial with an alias/fragment. Public declarations remain the existing GraphQLEntity/GraphQLRelations and Database policy APIs. New doc-hidden runtime helpers support generated code; consumers do not call ORM-private methods or supply query SQL.

Executed verification (jobs=2, line-tables-only debug, incremental=0, disk-backed TMPDIR/target caches):

  • External fixture cargo test --manifest-path crates/graphql-orm-macros/fixtures/cross-crate-relations/Cargo.toml --locked -p cross-crate-source-models --no-default-features --features sqlite: 6 tests passed. The separate --features postgres command also has 6 passed, using only labelled disposable test-owned containers with ownership-checked cleanup. One generated illustrative search doctest is ignored in each lane; no backend execution tests are ignored.
  • Regressions cover entity denial before target I/O, a generated parent with an absent target table, forged/stale/manual preloads, a primed application HashMapCache, complete ownership-key mismatch/reassignment, reused schema/loader after permission changes, current/expired and two differently authorized identities, row denial, selected field denial, sanitized provider failures, aliases/fragments, skip/include selections, SQL-complete and bounded callback pages/counts, and budget exhaustion. The retained SQLite native tracing fixture measures two actual target SELECTs across multiple parents, one dispatch per source relation.
  • cargo test -p graphql-orm --locked --no-default-features --features sqlite --lib --test authorization_mode --test composite_relations --test conditional_relations --test recursive_relations --test computed_order_and_complex_relations --test bidirectional_join_relations --test authorized_pagination: 54 passed, one pre-existing large release-mode pagination benchmark ignored.
  • cargo test -p graphql-orm-macros --locked --no-default-features --features sqlite --lib: 6 passed.
  • Warnings-denied Clippy for ORM/macros libraries and the external fixture's all-targets lanes, separately selecting sqlite, postgres, mssql: passed. MSSQL is compile-only, no live MSSQL execution claimed.
  • Warnings-denied Rustdoc for ORM/macros, separately selecting those same three backends: passed. Root and external-fixture formatting, documentation checks (191 files), generated inventory, explicit backend dependency trees and workspace dependency architecture: passed.
  • bash scripts/check-package-release-policy.sh 4617b332014758387a3c271a7b30a5aff4873453 and bash scripts/check-semver.sh 4617b332014758387a3c271a7b30a5aff4873453: passed. Never workspace all-features.

Known limits: callbacks requiring exact complete counts must finish within the host scan budget; SQL-complete policies are the bounded batched path. Explicit bulk snapshots are no longer reused for GraphQL authority. Other application loader cache types are not authoritative and trigger fresh-query fallback. Scope guards remain host-owned; no credentials, transport authority, policy language, sibling changes, data migration or releases.

  • Documentation updated

Documentation impact: current generated relationship authority contract, migration/compatibility notes, changelog, SQL-free executable example and generated package inventory. Additional static query/group/private-view requirements remain independent workstreams in #91.

Additional explicit compatibility evidence: cargo semver-checks -p graphql-orm --manifest-path crates/graphql-orm/Cargo.toml --baseline-root <owned-4617b33-baseline>/crates/graphql-orm --default-features --release-type patch actually builds/parses 0.33.4 and 0.33.5 and passes 223 checks (30 inapplicable checks skipped). Issue #100 separately tracks no-analysis invocation behavior without explicit package selection. The reviewable implementation head remains unchanged.

@Dastari
Dastari changed the base branch from fix/cross-crate-generated-relations-20261001 to main October 1, 2026 07:18
@Dastari
Dastari merged commit 6e7a3ea into main Oct 1, 2026
10 checks passed
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