Skip to content

fix(orm): use raw alias for join keys when PK/FK has field access policy#2675

Open
lsmith77 wants to merge 1 commit into
zenstackhq:devfrom
lsmith77:fix-deny-allow-joins
Open

fix(orm): use raw alias for join keys when PK/FK has field access policy#2675
lsmith77 wants to merge 1 commit into
zenstackhq:devfrom
lsmith77:fix-deny-allow-joins

Conversation

@lsmith77
Copy link
Copy Markdown
Contributor

@lsmith77 lsmith77 commented May 19, 2026

fix #2674

When a PK or FK field carries a @deny or @allow field-level policy, the
policy handler wraps it in CASE WHEN … THEN NULL. This caused include to
silently return empty arrays because join conditions evaluated as fk = NULL.

Fix: Two-part:

  1. The policy handler emits a __zs_raw_<field> alias (the raw column value)
    alongside the potentially-nulled expression for any PK or FK field that has
    a field access policy.

  2. Join condition builders (buildJoinPairs, SQLite's buildRelationJoinFilter,
    M2M path in the lateral-join dialect) use the raw alias selectively:

    • HasMany (ownedByModel = false): raw alias on both sides — the child's
      FK may be denied to hide which parent it belongs to, but the parent must
      still be able to fetch its children.
    • BelongsTo (ownedByModel = true): raw alias only on the relation's PK
      side; the FK side stays as a plain ref. Denying the FK is designed to hide
      the relation entirely (preserves pre-existing behavior verified by
      field-level-policy.test.ts).

Regression tests added in tests/regression/test/issue-2674.test.ts.

Note: externalIdMapping in the issue title is a REST API option unrelated to
the fix; the root cause is purely at the ORM layer.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where field-level authorization on primary/foreign keys could break fetching related records; nested relations now load correctly while denied key fields resolve to null.
  • New Features
    • Improved handling of key fields under field-level policies so join conditions and includes remain reliable.
  • Tests
    • Added regression tests validating policy-aware behavior for nested includes and denied keys.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36fc8532-930d-421d-8b96-b38a332090c0

📥 Commits

Reviewing files that changed from the base of the PR and between c109d4a and 531e8d1.

📒 Files selected for processing (5)
  • packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts
  • packages/orm/src/client/crud/dialects/sqlite.ts
  • packages/orm/src/client/query-utils.ts
  • packages/plugins/policy/src/policy-handler.ts
  • tests/regression/test/issue-2674.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/orm/src/client/crud/dialects/sqlite.ts
  • packages/plugins/policy/src/policy-handler.ts
  • packages/orm/src/client/query-utils.ts

📝 Walkthrough

Walkthrough

Adds detection and selection of raw column aliases for PK/FK fields with conditional read policies, and updates join-pair construction and dialect join filters (lateral + SQLite) to use those raw aliases; includes regression tests covering three @deny + include scenarios.

Changes

Fix @deny policies on PK/FK fields breaking includes

Layer / File(s) Summary
Query utilities - raw-alias join key detection and construction
packages/orm/src/client/query-utils.ts
Introduces JOIN_KEY_RAW_PREFIX constant and exports fieldHasConditionalReadPolicy, joinKeyRef, and updates buildJoinPairs to detect and route join references through raw-alias columns when fields have @deny/@allow policy.
Policy handler - select raw column aliases
packages/plugins/policy/src/policy-handler.ts
Updates createSelectAllFieldsWithPolicies to append raw column selections for PK/FK fields guarded by field-level policies, exposing unmodified values alongside policy-wrapped results.
Lateral join dialect - use raw aliases for M2M joins
packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts
Refactors buildRelationJoinFilter M2M predicate to use joinKeyRef-computed raw-alias references instead of direct ${alias}.${field} string synthesis.
SQLite dialect - use raw aliases for all join types
packages/orm/src/client/crud/dialects/sqlite.ts
Refactors buildRelationJoinFilter to use joinKeyRef for both M2M and FK-pair join conditions, replacing potentially CASE-wrapped field references with stable raw aliases.
Regression test - three @deny + include scenarios
tests/regression/test/issue-2674.test.ts
Adds vitest suite with three test cases: @deny on Post PK with HasMany includes, @deny on Comment FK with BelongsTo includes, and @deny on both PK and FK, verifying nested include results populate correctly when denied keys are null.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop on raw aliases bright and small,
When @deny casts shadows over the wall,
Joins still find their steady tune,
Includes return beneath the moon,
Keys stay hidden, but relations stand tall.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: using raw aliases for join keys when PK/FK fields have field access policies.
Linked Issues check ✅ Passed The PR fully addresses issue #2674 by implementing the raw alias mechanism to prevent policy-nulled PK/FK values from breaking join conditions in include queries.
Out of Scope Changes check ✅ Passed All changes are directly scoped to resolving issue #2674: policy-aware join key handling, raw alias emission, and regression tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/orm/src/client/crud/dialects/sqlite.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/orm/src/client/crud/dialects/lateral-join-dialect-base.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

packages/plugins/policy/src/policy-handler.ts

Oops! Something went wrong! :(

ESLint: 9.29.0

ESLint couldn't find an eslint.config.(js|mjs|cjs) file.

From ESLint v9.0.0, the default configuration file is now eslint.config.js.
If you are using a .eslintrc.* file, please follow the migration guide
to update your configuration file to the new format:

https://eslint.org/docs/latest/use/configure/migration-guide

If you still have problems after following the migration guide, please stop by
https://eslint.org/chat/help to chat with the team.

  • 2 others

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lsmith77 lsmith77 force-pushed the fix-deny-allow-joins branch from 022e5dd to c109d4a Compare May 19, 2026 18:13
@lsmith77 lsmith77 changed the title fix(orm): use raw alias for join keys when PK/FK has @allow or @deny field policy fix(orm): use raw alias for join keys when PK/FK has field access policy May 19, 2026
@lsmith77 lsmith77 force-pushed the fix-deny-allow-joins branch from c109d4a to 531e8d1 Compare May 19, 2026 19:10
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.

@deny on PK/FK fields breaks include when external_id_mapping is configured — related records silently return empty

1 participant