Skip to content

Commit 8ad3cdb

Browse files
committed
fix: should not reject "update" when there's only field-level override but no model-level policy
Fixes #1014
1 parent 9db52db commit 8ad3cdb

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export class PolicyUtil {
319319
/**
320320
* Checks if the given model has a policy guard for the given operation.
321321
*/
322-
hasAuthGuard(model: string, operation: PolicyOperationKind): boolean {
322+
hasAuthGuard(model: string, operation: PolicyOperationKind) {
323323
const guard = this.policy.guard[lowerCaseFirst(model)];
324324
if (!guard) {
325325
return false;
@@ -328,6 +328,21 @@ export class PolicyUtil {
328328
return typeof provider !== 'boolean' || provider !== true;
329329
}
330330

331+
/**
332+
* Checks if the given model has any field-level override policy guard for the given operation.
333+
*/
334+
hasOverrideAuthGuard(model: string, operation: PolicyOperationKind) {
335+
const guard = this.requireGuard(model);
336+
switch (operation) {
337+
case 'read':
338+
return Object.keys(guard).some((k) => k.startsWith(FIELD_LEVEL_OVERRIDE_READ_GUARD_PREFIX));
339+
case 'update':
340+
return Object.keys(guard).some((k) => k.startsWith(FIELD_LEVEL_OVERRIDE_UPDATE_GUARD_PREFIX));
341+
default:
342+
return false;
343+
}
344+
}
345+
331346
/**
332347
* Checks model creation policy based on static analysis to the input args.
333348
*
@@ -731,7 +746,7 @@ export class PolicyUtil {
731746
preValue?: any
732747
) {
733748
let guard = this.getAuthGuard(db, model, operation, preValue);
734-
if (this.isFalse(guard)) {
749+
if (this.isFalse(guard) && !this.hasOverrideAuthGuard(model, operation)) {
735750
throw this.deniedByPolicy(
736751
model,
737752
operation,
@@ -904,7 +919,7 @@ export class PolicyUtil {
904919
*/
905920
tryReject(db: Record<string, DbOperations>, model: string, operation: PolicyOperationKind) {
906921
const guard = this.getAuthGuard(db, model, operation);
907-
if (this.isFalse(guard)) {
922+
if (this.isFalse(guard) && !this.hasOverrideAuthGuard(model, operation)) {
908923
throw this.deniedByPolicy(model, operation, undefined, CrudFailureReason.ACCESS_POLICY_VIOLATION);
909924
}
910925
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { loadSchema } from '@zenstackhq/testtools';
2+
3+
describe('issue 1014', () => {
4+
it('update', async () => {
5+
const { prisma, enhance } = await loadSchema(
6+
`
7+
model User {
8+
id Int @id() @default(autoincrement())
9+
name String
10+
posts Post[]
11+
}
12+
13+
model Post {
14+
id Int @id() @default(autoincrement())
15+
title String
16+
content String?
17+
author User? @relation(fields: [authorId], references: [id])
18+
authorId Int? @allow('update', true, true)
19+
20+
@@allow('read', true)
21+
}
22+
`,
23+
{ logPrismaQuery: true }
24+
);
25+
26+
const db = enhance();
27+
28+
const user = await prisma.user.create({ data: { name: 'User1' } });
29+
const post = await prisma.post.create({ data: { title: 'Post1' } });
30+
await expect(db.post.update({ where: { id: post.id }, data: { authorId: user.id } })).toResolveTruthy();
31+
});
32+
33+
it('read', async () => {
34+
const { prisma, enhance } = await loadSchema(
35+
`
36+
model Post {
37+
id Int @id() @default(autoincrement())
38+
title String @allow('read', true, true)
39+
content String
40+
}
41+
`,
42+
{ logPrismaQuery: true }
43+
);
44+
45+
const db = enhance();
46+
47+
const post = await prisma.post.create({ data: { title: 'Post1', content: 'Content' } });
48+
await expect(db.post.findUnique({ where: { id: post.id } })).toResolveNull();
49+
await expect(db.post.findUnique({ where: { id: post.id }, select: { title: true } })).resolves.toEqual({
50+
title: 'Post1',
51+
});
52+
});
53+
});

tests/integration/tests/tsconfig.template.json

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)