Skip to content

Commit b5187eb

Browse files
authored
fix(policy): only include field selections for evaluating field-level policies if the corresponding fields are selected (#1979)
1 parent 3e699e7 commit b5187eb

File tree

2 files changed

+52
-8
lines changed

2 files changed

+52
-8
lines changed

packages/runtime/src/enhancements/node/policy/policy-utils.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -1183,7 +1183,7 @@ export class PolicyUtil extends QueryUtils {
11831183

11841184
if (this.hasFieldLevelPolicy(model)) {
11851185
// recursively inject selection for fields needed for field-level read checks
1186-
const readFieldSelect = this.getFieldReadCheckSelector(model);
1186+
const readFieldSelect = this.getFieldReadCheckSelector(model, args.select);
11871187
if (readFieldSelect) {
11881188
this.doInjectReadCheckSelect(model, args, { select: readFieldSelect });
11891189
}
@@ -1311,17 +1311,20 @@ export class PolicyUtil extends QueryUtils {
13111311
}
13121312

13131313
// get a merged selector object for all field-level read policies
1314-
private getFieldReadCheckSelector(model: string) {
1314+
private getFieldReadCheckSelector(model: string, fieldSelection: Record<string, any> | undefined) {
13151315
const def = this.getModelPolicyDef(model);
13161316
let result: any = {};
13171317
const fieldLevel = def.fieldLevel?.read;
13181318
if (fieldLevel) {
1319-
for (const def of Object.values(fieldLevel)) {
1320-
if (def.entityChecker?.selector) {
1321-
result = deepmerge(result, def.entityChecker.selector);
1322-
}
1323-
if (def.overrideEntityChecker?.selector) {
1324-
result = deepmerge(result, def.overrideEntityChecker.selector);
1319+
for (const [field, def] of Object.entries(fieldLevel)) {
1320+
if (!fieldSelection || fieldSelection[field]) {
1321+
// field is selected, merge the field-level selector
1322+
if (def.entityChecker?.selector) {
1323+
result = deepmerge(result, def.entityChecker.selector);
1324+
}
1325+
if (def.overrideEntityChecker?.selector) {
1326+
result = deepmerge(result, def.overrideEntityChecker.selector);
1327+
}
13251328
}
13261329
}
13271330
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
3+
describe('issue 1978', () => {
4+
it('regression', async () => {
5+
const { prisma, enhance } = await loadSchema(
6+
`
7+
model User {
8+
id Int @id
9+
posts Post[]
10+
secret String @allow('read', posts?[published])
11+
@@allow('all', true)
12+
}
13+
14+
model Post {
15+
id Int @id
16+
author User @relation(fields: [authorId], references: [id])
17+
authorId Int
18+
published Boolean @default(false)
19+
@@allow('all', true)
20+
}
21+
`,
22+
{ logPrismaQuery: true }
23+
);
24+
25+
const user1 = await prisma.user.create({
26+
data: { id: 1, secret: 'secret', posts: { create: { id: 1, published: true } } },
27+
});
28+
const user2 = await prisma.user.create({
29+
data: { id: 2, secret: 'secret' },
30+
});
31+
32+
const db = enhance();
33+
await expect(db.user.findFirst({ where: { id: 1 } })).resolves.toMatchObject({ secret: 'secret' });
34+
await expect(db.user.findFirst({ where: { id: 1 }, select: { id: true } })).resolves.toEqual({ id: 1 });
35+
36+
let r = await db.user.findFirst({ where: { id: 2 } });
37+
expect(r.secret).toBeUndefined();
38+
r = await db.user.findFirst({ where: { id: 2 }, select: { id: true } });
39+
expect(r.secret).toBeUndefined();
40+
});
41+
});

0 commit comments

Comments
 (0)