From 9bad74da3a2c2e6798675f9c9f0bcadc5886ad63 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:46:35 +0800 Subject: [PATCH 1/3] fix: policy generation error when field-level rules contain "this" expression --- packages/runtime/src/validation.ts | 12 ++++++ .../access-policy/policy-guard-generator.ts | 9 +++++ .../with-policy/field-level-policy.test.ts | 30 +++++++++++++++ .../tests/regression/issue-665.test.ts | 38 +++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 tests/integration/tests/regression/issue-665.test.ts diff --git a/packages/runtime/src/validation.ts b/packages/runtime/src/validation.ts index 33115f8e9..83a05cc48 100644 --- a/packages/runtime/src/validation.ts +++ b/packages/runtime/src/validation.ts @@ -32,3 +32,15 @@ export function hasAllFields(obj: any, fields: string[]) { } return fields.every((f) => obj[f] !== undefined && obj[f] !== null); } + +/** + * Check if the given objects have equal values for the given fields. Returns + * false if either object is nullish or is not an object. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function allFieldsEqual(obj1: any, obj2: any, fields: string[]) { + if (!obj1 || !obj2 || typeof obj1 !== 'object' || typeof obj2 !== 'object') { + return false; + } + return fields.every((f) => obj1[f] === obj2[f]); +} diff --git a/packages/schema/src/plugins/access-policy/policy-guard-generator.ts b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts index 870dc40df..6cd602ea6 100644 --- a/packages/schema/src/plugins/access-policy/policy-guard-generator.ts +++ b/packages/schema/src/plugins/access-policy/policy-guard-generator.ts @@ -79,6 +79,7 @@ export default class PolicyGenerator { { name: 'type QueryContext' }, { name: 'type DbOperations' }, { name: 'hasAllFields' }, + { name: 'allFieldsEqual' }, { name: 'type PolicyDef' }, ], moduleSpecifier: `${RUNTIME_PACKAGE}`, @@ -486,6 +487,14 @@ export default class PolicyGenerator { for (const rule of [...allows, ...denies]) { for (const expr of [...this.allNodes(rule)].filter((node): node is Expression => isExpression(node))) { + if (isThisExpr(expr) && !isMemberAccessExpr(expr.$container)) { + // a standalone `this` expression, include all id fields + const model = expr.$resolvedType?.decl as DataModel; + const idFields = getIdFields(model); + idFields.forEach((field) => addPath([field.name])); + continue; + } + // only care about member access and reference expressions if (!isMemberAccessExpr(expr) && !isReferenceExpr(expr)) { continue; diff --git a/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts b/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts index cc0eda275..209876f25 100644 --- a/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts +++ b/tests/integration/tests/enhancements/with-policy/field-level-policy.test.ts @@ -716,4 +716,34 @@ describe('With Policy: field-level policy', () => { expect.objectContaining({ x: 1, y: 3 }) ); }); + + it('this expression', async () => { + const { prisma, withPolicy } = await loadSchema( + ` + model User { + id Int @id + username String @allow("all", auth() == this) + @@allow('all', true) + } + ` + ); + + await prisma.user.create({ data: { id: 1, username: 'test' } }); + + // admin + let r = await withPolicy({ id: 1, admin: true }).user.findFirst(); + expect(r.username).toEqual('test'); + + // owner + r = await withPolicy({ id: 1 }).user.findFirst(); + expect(r.username).toEqual('test'); + + // anonymous + r = await withPolicy().user.findFirst(); + expect(r.username).toBeUndefined(); + + // non-owner + r = await withPolicy({ id: 2 }).user.findFirst(); + expect(r.username).toBeUndefined(); + }); }); diff --git a/tests/integration/tests/regression/issue-665.test.ts b/tests/integration/tests/regression/issue-665.test.ts new file mode 100644 index 000000000..8bd9f717b --- /dev/null +++ b/tests/integration/tests/regression/issue-665.test.ts @@ -0,0 +1,38 @@ +import { loadSchema } from '@zenstackhq/testtools'; + +describe('Regression: issue 665', () => { + it('regression', async () => { + const { prisma, withPolicy } = await loadSchema( + ` + model User { + id Int @id @default(autoincrement()) + admin Boolean @default(false) + username String @unique @allow("all", auth() == this) @allow("all", auth().admin) + password String @password @default("") @allow("all", auth() == this) @allow("all", auth().admin) + firstName String @default("") + lastName String @default("") + + @@allow('all', true) + } + ` + ); + + await prisma.user.create({ data: { id: 1, username: 'test', password: 'test', admin: true } }); + + // admin + let r = await withPolicy({ id: 1, admin: true }).user.findFirst(); + expect(r.username).toEqual('test'); + + // owner + r = await withPolicy({ id: 1 }).user.findFirst(); + expect(r.username).toEqual('test'); + + // anonymous + r = await withPolicy().user.findFirst(); + expect(r.username).toBeUndefined(); + + // non-owner + r = await withPolicy({ id: 2 }).user.findFirst(); + expect(r.username).toBeUndefined(); + }); +}); From fa142231ab5c8905131ad37ed20d8ee75db2a60f Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 4 Sep 2023 14:58:00 +0800 Subject: [PATCH 2/3] more fixes --- .../validator/expression-validator.ts | 21 +++- .../typescript-expression-transformer.ts | 26 ++++- .../validation/attribute-validation.test.ts | 102 ++++++++++++++++++ 3 files changed, 144 insertions(+), 5 deletions(-) diff --git a/packages/schema/src/language-server/validator/expression-validator.ts b/packages/schema/src/language-server/validator/expression-validator.ts index 63960996d..2921b7670 100644 --- a/packages/schema/src/language-server/validator/expression-validator.ts +++ b/packages/schema/src/language-server/validator/expression-validator.ts @@ -1,4 +1,4 @@ -import { BinaryExpr, Expression, ExpressionType, isBinaryExpr, isEnum } from '@zenstackhq/language/ast'; +import { BinaryExpr, Expression, ExpressionType, isBinaryExpr, isDataModel, isEnum } from '@zenstackhq/language/ast'; import { ValidationAcceptor } from 'langium'; import { isAuthInvocation } from '../../utils/ast-utils'; import { AstValidator } from '../types'; @@ -93,6 +93,25 @@ export default class ExpressionValidator implements AstValidator { break; } + + case '==': + case '!=': { + // disallow comparing model type with scalar type or comparison between + // incompatible model types + const leftType = expr.left.$resolvedType?.decl; + const rightType = expr.right.$resolvedType?.decl; + if (isDataModel(leftType) && isDataModel(rightType)) { + if (leftType != rightType) { + // incompatible model types + // TODO: inheritance case? + accept('error', 'incompatible operand types', { node: expr }); + } + } else if (isDataModel(leftType) || isDataModel(rightType)) { + // comparing model against scalar + accept('error', 'incompatible operand types', { node: expr }); + } + break; + } } } diff --git a/packages/schema/src/utils/typescript-expression-transformer.ts b/packages/schema/src/utils/typescript-expression-transformer.ts index 8dd6a4eb6..17be22406 100644 --- a/packages/schema/src/utils/typescript-expression-transformer.ts +++ b/packages/schema/src/utils/typescript-expression-transformer.ts @@ -2,6 +2,7 @@ import { ArrayExpr, BinaryExpr, BooleanLiteral, + DataModel, Expression, InvocationExpr, isEnumField, @@ -16,6 +17,7 @@ import { UnaryExpr, } from '@zenstackhq/language/ast'; import { ExpressionContext, getLiteral, isFromStdlib, isFutureExpr } from '@zenstackhq/sdk'; +import { getIdFields } from './ast-utils'; export class TypeScriptExpressionTransformerError extends Error { constructor(message: string) { @@ -94,10 +96,9 @@ export class TypeScriptExpressionTransformer { } } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - private this(expr: ThisExpr) { - // "this" is mapped to id comparison - return 'id'; + private this(_expr: ThisExpr) { + // "this" is mapped to the input argument + return 'input'; } private memberAccess(expr: MemberAccessExpr, normalizeUndefined: boolean) { @@ -306,6 +307,23 @@ export class TypeScriptExpressionTransformer { expr.left, normalizeUndefined )}) ?? false)`; + } else if ( + (expr.operator === '==' || expr.operator === '!=') && + (isThisExpr(expr.left) || isThisExpr(expr.right)) + ) { + // map equality comparison with `this` to id comparison + const _this = isThisExpr(expr.left) ? expr.left : expr.right; + const model = _this.$resolvedType?.decl as DataModel; + const idFields = getIdFields(model); + if (!idFields || idFields.length === 0) { + throw new TypeScriptExpressionTransformerError(`model "${model.name}" does not have an id field`); + } + let result = `allFieldsEqual(${this.transform(expr.left, false)}, + ${this.transform(expr.right, false)}, [${idFields.map((f) => "'" + f.name + "'").join(', ')}])`; + if (expr.operator === '!=') { + result = `!${result}`; + } + return result; } else { return `(${this.transform(expr.left, normalizeUndefined)} ${expr.operator} ${this.transform( expr.right, diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts index 889bdc910..13c19358e 100644 --- a/packages/schema/tests/schema/validation/attribute-validation.test.ts +++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts @@ -496,6 +496,108 @@ describe('Attribute tests', () => { } `) ).toContain('invalid operand type for "||" operator'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + @@allow('all', x == this) + } + `) + ).toContain('incompatible operand types'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + @@allow('all', this != x) + } + `) + ).toContain('incompatible operand types'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + b B? + @@allow('all', b == this) + } + model B { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain('incompatible operand types'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + b B? + @@allow('all', this != b) + } + model B { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain('incompatible operand types'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + b B? + c C? + @@allow('all', b == c) + } + model B { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + model C { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain('incompatible operand types'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + b B? + c C? + @@allow('all', b != c) + } + model B { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + model C { + id String @id + a A? @relation(fields: [aId], references: [id]) + aId String + } + `) + ).toContain('incompatible operand types'); }); it('policy filter function check', async () => { From 18b99a33b1b6d44d1eec7f826d5dac3630d046b2 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 4 Sep 2023 18:04:50 +0800 Subject: [PATCH 3/3] more fixes --- .../validator/expression-validator.ts | 34 ++++++++- .../src/language-server/zmodel-linker.ts | 35 +++++----- .../access-policy/expression-writer.ts | 70 ++++++++++++------- .../tests/generator/expression-writer.test.ts | 17 +---- .../tests/generator/prisma-generator.test.ts | 2 +- .../tests/generator/zmodel/schema.zmodel | 2 +- .../validation/attribute-validation.test.ts | 45 ++++++++++++ .../nextjs/test-project/postgres.zmodel | 2 +- .../nextjs/test-project/sqlite.zmodel | 2 +- .../frameworks/trpc/test-project/todo.zmodel | 2 +- 10 files changed, 146 insertions(+), 65 deletions(-) diff --git a/packages/schema/src/language-server/validator/expression-validator.ts b/packages/schema/src/language-server/validator/expression-validator.ts index 2921b7670..3d6343a9e 100644 --- a/packages/schema/src/language-server/validator/expression-validator.ts +++ b/packages/schema/src/language-server/validator/expression-validator.ts @@ -1,4 +1,14 @@ -import { BinaryExpr, Expression, ExpressionType, isBinaryExpr, isDataModel, isEnum } from '@zenstackhq/language/ast'; +import { + BinaryExpr, + Expression, + ExpressionType, + isBinaryExpr, + isDataModel, + isEnum, + isNullExpr, + isThisExpr, +} from '@zenstackhq/language/ast'; +import { isDataModelFieldReference } from '@zenstackhq/sdk'; import { ValidationAcceptor } from 'langium'; import { isAuthInvocation } from '../../utils/ast-utils'; import { AstValidator } from '../types'; @@ -106,8 +116,26 @@ export default class ExpressionValidator implements AstValidator { // TODO: inheritance case? accept('error', 'incompatible operand types', { node: expr }); } - } else if (isDataModel(leftType) || isDataModel(rightType)) { - // comparing model against scalar + + // not supported: + // - foo == bar + // - foo == this + if ( + isDataModelFieldReference(expr.left) && + (isThisExpr(expr.right) || isDataModelFieldReference(expr.right)) + ) { + accept('error', 'comparison between model-typed fields are not supported', { node: expr }); + } else if ( + isDataModelFieldReference(expr.right) && + (isThisExpr(expr.left) || isDataModelFieldReference(expr.left)) + ) { + accept('error', 'comparison between model-typed fields are not supported', { node: expr }); + } + } else if ( + (isDataModel(leftType) && !isNullExpr(expr.right)) || + (isDataModel(rightType) && !isNullExpr(expr.left)) + ) { + // comparing model against scalar (except null) accept('error', 'incompatible operand types', { node: expr }); } break; diff --git a/packages/schema/src/language-server/zmodel-linker.ts b/packages/schema/src/language-server/zmodel-linker.ts index 147893626..ba6396695 100644 --- a/packages/schema/src/language-server/zmodel-linker.ts +++ b/packages/schema/src/language-server/zmodel-linker.ts @@ -61,7 +61,7 @@ interface DefaultReference extends Reference { _nodeDescription?: AstNodeDescription; } -type ScopeProvider = (name: string) => ReferenceTarget | undefined; +type ScopeProvider = (name: string) => ReferenceTarget | DataModel | undefined; /** * Langium linker implementation which links references and resolves expression types @@ -342,7 +342,13 @@ export class ZModelLinker extends DefaultLinker { const resolvedType = node.left.$resolvedType; if (resolvedType && isDataModel(resolvedType.decl) && resolvedType.array) { const dataModelDecl = resolvedType.decl; - const provider = (name: string) => dataModelDecl.$resolvedFields.find((f) => f.name === name); + const provider = (name: string) => { + if (name === 'this') { + return dataModelDecl; + } else { + return dataModelDecl.$resolvedFields.find((f) => f.name === name); + } + }; extraScopes = [provider, ...extraScopes]; this.resolve(node.right, document, extraScopes); this.resolveToBuiltinTypeOrDecl(node, 'Boolean'); @@ -351,13 +357,16 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveThis( - node: ThisExpr, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - document: LangiumDocument, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - extraScopes: ScopeProvider[] - ) { + private resolveThis(node: ThisExpr, _document: LangiumDocument, extraScopes: ScopeProvider[]) { + // resolve from scopes first + for (const scope of extraScopes) { + const r = scope('this'); + if (isDataModel(r)) { + this.resolveToBuiltinTypeOrDecl(node, r); + return; + } + } + let decl: AstNode | undefined = node.$container; while (decl && !isDataModel(decl)) { @@ -369,13 +378,7 @@ export class ZModelLinker extends DefaultLinker { } } - private resolveNull( - node: NullExpr, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - document: LangiumDocument, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - extraScopes: ScopeProvider[] - ) { + private resolveNull(node: NullExpr, _document: LangiumDocument, _extraScopes: ScopeProvider[]) { // TODO: how to really resolve null? this.resolveToBuiltinTypeOrDecl(node, 'Null'); } diff --git a/packages/schema/src/plugins/access-policy/expression-writer.ts b/packages/schema/src/plugins/access-policy/expression-writer.ts index 0ae94b6ae..f986d5d66 100644 --- a/packages/schema/src/plugins/access-policy/expression-writer.ts +++ b/packages/schema/src/plugins/access-policy/expression-writer.ts @@ -279,18 +279,6 @@ export class ExpressionWriter { const leftIsFieldAccess = this.isFieldAccess(expr.left); const rightIsFieldAccess = this.isFieldAccess(expr.right); - if (leftIsFieldAccess && rightIsFieldAccess) { - if ( - isDataModelFieldReference(expr.left) && - isDataModelFieldReference(expr.right) && - expr.left.target.ref?.$container === expr.right.target.ref?.$container - ) { - // comparing fields from the same model - } else { - throw new PluginError(name, `Comparing fields from different models is not supported`); - } - } - if (!leftIsFieldAccess && !rightIsFieldAccess) { // compile down to a plain expression this.guard(() => { @@ -318,7 +306,8 @@ export class ExpressionWriter { $container: fieldAccess.$container, target: fieldAccess.member, $resolvedType: fieldAccess.$resolvedType, - } as ReferenceExpr; + $future: true, + } as unknown as ReferenceExpr; } // guard member access of `auth()` with null check @@ -349,10 +338,7 @@ export class ExpressionWriter { // right now this branch only serves comparison with `auth`, like // @@allow('all', owner == auth()) - const idFields = getIdFields(dataModel); - if (!idFields || idFields.length === 0) { - throw new PluginError(name, `Data model ${dataModel.name} does not have an id field`); - } + const idFields = this.requireIdFields(dataModel); if (operator !== '==' && operator !== '!=') { throw new PluginError(name, 'Only == and != operators are allowed'); @@ -389,15 +375,21 @@ export class ExpressionWriter { }); }); } else { - this.writeOperator(operator, fieldAccess, () => { - if (isDataModelFieldReference(operand) && !this.isPostGuard) { - // if operand is a field reference and we're not generating for post-update guard, - // we should generate a field reference (comparing fields in the same model) - this.writeFieldReference(operand); - } else { - this.plain(operand); - } - }); + if (this.equivalentRefs(fieldAccess, operand)) { + // f == f or f != f + // this == this or this != this + this.writer.write(operator === '!=' ? TRUE : FALSE); + } else { + this.writeOperator(operator, fieldAccess, () => { + if (isDataModelFieldReference(operand) && !this.isPostGuard) { + // if operand is a field reference and we're not generating for post-update guard, + // we should generate a field reference (comparing fields in the same model) + this.writeFieldReference(operand); + } else { + this.plain(operand); + } + }); + } } }, !isThisExpr(fieldAccess)); }); @@ -408,6 +400,32 @@ export class ExpressionWriter { ); } + private requireIdFields(dataModel: DataModel) { + const idFields = getIdFields(dataModel); + if (!idFields || idFields.length === 0) { + throw new PluginError(name, `Data model ${dataModel.name} does not have an id field`); + } + return idFields; + } + + private equivalentRefs(expr1: Expression, expr2: Expression) { + if (isThisExpr(expr1) && isThisExpr(expr2)) { + return true; + } + + if ( + isReferenceExpr(expr1) && + isReferenceExpr(expr2) && + expr1.target.ref === expr2.target.ref && + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (expr1 as any).$future === (expr2 as any).$future // either both future or both not + ) { + return true; + } + + return false; + } + // https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#compare-columns-in-the-same-table private writeFieldReference(expr: ReferenceExpr) { if (!expr.target.ref) { diff --git a/packages/schema/tests/generator/expression-writer.test.ts b/packages/schema/tests/generator/expression-writer.test.ts index e35f07269..d4a5fe5db 100644 --- a/packages/schema/tests/generator/expression-writer.test.ts +++ b/packages/schema/tests/generator/expression-writer.test.ts @@ -113,26 +113,13 @@ describe('Expression Writer Tests', () => { it('this reference', async () => { await check( ` - model User { id String @id } model Test { id String @id - @@allow('all', auth() == this) + @@allow('all', this == this) } `, (model) => model.attributes[0].args[1].value, - `(user == null) ? { OR: [] } : { id: user.id }` - ); - - await check( - ` - model User { id String @id } - model Test { - id String @id - @@deny('all', this != auth()) - } - `, - (model) => model.attributes[0].args[1].value, - `(user == null) ? { AND: [] } : { NOT: { id: user.id } }` + `{OR:[]}` ); await check( diff --git a/packages/schema/tests/generator/prisma-generator.test.ts b/packages/schema/tests/generator/prisma-generator.test.ts index 31dba058c..d95e78166 100644 --- a/packages/schema/tests/generator/prisma-generator.test.ts +++ b/packages/schema/tests/generator/prisma-generator.test.ts @@ -330,7 +330,7 @@ describe('Prisma generator test', () => { const post = dmmf.datamodel.models.find((m) => m.name === 'Post'); expect(post?.documentation?.replace(/\s/g, '')).toBe( - `@@allow('read', owner == auth()) @@allow('delete', ownerId == auth())`.replace(/\s/g, '') + `@@allow('read', owner == auth()) @@allow('delete', owner == auth())`.replace(/\s/g, '') ); const todo = dmmf.datamodel.models.find((m) => m.name === 'Todo'); diff --git a/packages/schema/tests/generator/zmodel/schema.zmodel b/packages/schema/tests/generator/zmodel/schema.zmodel index 9e2c6a803..8d73977e2 100644 --- a/packages/schema/tests/generator/zmodel/schema.zmodel +++ b/packages/schema/tests/generator/zmodel/schema.zmodel @@ -9,7 +9,7 @@ model Post extends Basic { title String content String? - @@allow('delete', ownerId == auth()) + @@allow('delete', owner == auth()) } model Todo extends Basic { diff --git a/packages/schema/tests/schema/validation/attribute-validation.test.ts b/packages/schema/tests/schema/validation/attribute-validation.test.ts index 13c19358e..cc908898b 100644 --- a/packages/schema/tests/schema/validation/attribute-validation.test.ts +++ b/packages/schema/tests/schema/validation/attribute-validation.test.ts @@ -553,6 +553,51 @@ describe('Attribute tests', () => { `) ).toContain('incompatible operand types'); + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + other A? @relation('other', fields: [otherId], references: [id]) + otherId String? @unique + holder A? @relation('other') + @@allow('all', other == this) + } + `) + ).toContain('comparison between model-typed fields are not supported'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + other A? @relation('other', fields: [otherId], references: [id]) + otherId String? @unique + holder A? @relation('other') + @@allow('all', this != other) + } + `) + ).toContain('comparison between model-typed fields are not supported'); + + expect( + await loadModelWithError(` + ${prelude} + model A { + id String @id + x Int + other A? @relation('other', fields: [otherId], references: [id]) + otherId String? @unique + holder A? @relation('other') + other1 A? @relation('other1', fields: [otherId1], references: [id]) + other1Id String? @unique + holder1 A? @relation('other1') + @@allow('all', other == other1) + } + `) + ).toContain('comparison between model-typed fields are not supported'); + expect( await loadModelWithError(` ${prelude} diff --git a/tests/integration/tests/frameworks/nextjs/test-project/postgres.zmodel b/tests/integration/tests/frameworks/nextjs/test-project/postgres.zmodel index c194ec561..68edc64d1 100644 --- a/tests/integration/tests/frameworks/nextjs/test-project/postgres.zmodel +++ b/tests/integration/tests/frameworks/nextjs/test-project/postgres.zmodel @@ -26,6 +26,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) + @@allow('all', auth() == author) @@allow('read', published) } diff --git a/tests/integration/tests/frameworks/nextjs/test-project/sqlite.zmodel b/tests/integration/tests/frameworks/nextjs/test-project/sqlite.zmodel index 690f7c557..ff83da9f8 100644 --- a/tests/integration/tests/frameworks/nextjs/test-project/sqlite.zmodel +++ b/tests/integration/tests/frameworks/nextjs/test-project/sqlite.zmodel @@ -26,6 +26,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) + @@allow('all', auth() == author) @@allow('read', published) } diff --git a/tests/integration/tests/frameworks/trpc/test-project/todo.zmodel b/tests/integration/tests/frameworks/trpc/test-project/todo.zmodel index 4ad089642..6840f8978 100644 --- a/tests/integration/tests/frameworks/trpc/test-project/todo.zmodel +++ b/tests/integration/tests/frameworks/trpc/test-project/todo.zmodel @@ -36,6 +36,6 @@ model Post { author User? @relation(fields: [authorId], references: [id]) authorId String? published Boolean @default(false) - @@allow('all', auth() == this) + @@allow('all', auth() == author) @@allow('read', published) }