Skip to content

Commit

Permalink
fix: policy compilation error for deeply nested post-update rules (#1382
Browse files Browse the repository at this point in the history
)
  • Loading branch information
ymc9 authored Apr 25, 2024
1 parent 54c4bd5 commit 08471d5
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,6 @@ export interface ClientType<AppRouter extends AnyRouter, Context = AppRouter['_d
>;

};
createMany: {
useMutation: <T extends Prisma.PostCreateManyArgs>(
opts?: UseTRPCMutationOptions<
Prisma.PostCreateManyArgs,
TRPCClientErrorLike<AppRouter>,
Prisma.BatchPayload,
Context
>,
) => Omit<
UseTRPCMutationResult<
Prisma.BatchPayload,
TRPCClientErrorLike<AppRouter>,
Prisma.SelectSubset<T, Prisma.PostCreateManyArgs>,
Context
>,
'mutateAsync'
> & {
mutateAsync: <T extends Prisma.PostCreateManyArgs>(
variables: T,
opts?: UseTRPCMutationOptions<T, TRPCClientErrorLike<AppRouter>, Prisma.BatchPayload, Context>,
) => Promise<Prisma.BatchPayload>;
};
};
create: {

useMutation: <T extends Prisma.PostCreateArgs>(opts?: UseTRPCMutationOptions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,29 +61,6 @@ export interface ClientType<AppRouter extends AnyRouter, Context = AppRouter['_d
>;

};
createMany: {
useMutation: <T extends Prisma.UserCreateManyArgs>(
opts?: UseTRPCMutationOptions<
Prisma.UserCreateManyArgs,
TRPCClientErrorLike<AppRouter>,
Prisma.BatchPayload,
Context
>,
) => Omit<
UseTRPCMutationResult<
Prisma.BatchPayload,
TRPCClientErrorLike<AppRouter>,
Prisma.SelectSubset<T, Prisma.UserCreateManyArgs>,
Context
>,
'mutateAsync'
> & {
mutateAsync: <T extends Prisma.UserCreateManyArgs>(
variables: T,
opts?: UseTRPCMutationOptions<T, TRPCClientErrorLike<AppRouter>, Prisma.BatchPayload, Context>,
) => Promise<Prisma.BatchPayload>;
};
};
create: {

useMutation: <T extends Prisma.UserCreateArgs>(opts?: UseTRPCMutationOptions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,15 @@ export class ExpressionWriter {
}

private isFutureMemberAccess(expr: Expression): expr is MemberAccessExpr {
return isMemberAccessExpr(expr) && isFutureExpr(expr.operand);
if (!isMemberAccessExpr(expr)) {
return false;
}

if (isFutureExpr(expr.operand)) {
return true;
}

return this.isFutureMemberAccess(expr.operand);
}

private requireIdFields(dataModel: DataModel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,71 @@ describe('With Policy: post update', () => {
).toResolveTruthy();
});

it('collection predicate deep-nested post-update', async () => {
const { prisma, enhance } = await loadSchema(
`
model M1 {
id String @id @default(uuid())
value Int
m2 M2?
@@allow('read', true)
@@allow('update', value > 0 && future().m2.m3?[value > 0])
}
model M2 {
id String @id @default(uuid())
m1 M1 @relation(fields: [m1Id], references:[id])
m1Id String @unique
m3 M3[]
@@allow('all', true)
}
model M3 {
id String @id @default(uuid())
value Int
m2 M2 @relation(fields: [m2Id], references:[id])
m2Id String
@@allow('all', true)
}
`
);

const db = enhance();

await prisma.m1.create({
data: {
id: '1',
value: 1,
m2: {
create: { id: '1', m3: { create: [{ id: '1', value: 0 }] } },
},
},
});

await expect(
db.m1.update({
where: { id: '1' },
data: { value: 2 },
})
).toBeRejectedByPolicy();

await prisma.m3.create({
data: {
id: '2',
m2: { connect: { id: '1' } },
value: 1,
},
});

await expect(
db.m1.update({
where: { id: '1' },
data: { value: 2 },
})
).toResolveTruthy();
});

it('nested to-many', async () => {
const { enhance } = await loadSchema(
`
Expand Down
61 changes: 61 additions & 0 deletions tests/regression/tests/issue-1381.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { loadSchema } from '@zenstackhq/testtools';

describe('issue 1381', () => {
it('regression', async () => {
await loadSchema(
`
enum MemberRole {
owner
admin
}
enum SpaceType {
contractor
public
private
}
model User {
id String @id @default(cuid())
name String?
email String? @unique @lower
password String? @password @omit
memberships Membership[]
}
model Membership {
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
spaceId String
space Space @relation(fields: [spaceId], references: [id], onDelete: Cascade)
role MemberRole @deny("update", auth() == user)
@@id([userId, spaceId])
}
model Space {
id String @id @default(cuid())
name String
type SpaceType @default(private)
memberships Membership[]
options Option[]
}
model Option {
id String @id @default(cuid())
name String?
spaceId String?
space Space? @relation(fields: [spaceId], references: [id], onDelete: SetNull)
@@allow("update",
future().space.type in [contractor, public] &&
future().space.memberships?[space.type in [contractor, public] && auth() == user && role in [owner, admin]]
)
}
`,
{
provider: 'postgresql',
pushDb: false,
}
);
});
});

0 comments on commit 08471d5

Please sign in to comment.