Skip to content

Commit 28c2bc8

Browse files
authored
fix: additional fixes and tests related to cross-model field comparison (#1496)
1 parent 54e1e02 commit 28c2bc8

File tree

2 files changed

+185
-6
lines changed

2 files changed

+185
-6
lines changed

packages/schema/src/plugins/enhancer/policy/utils.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,9 @@ function hasCrossModelComparison(expr: Expression) {
483483
}
484484

485485
function getSourceModelOfFieldAccess(expr: Expression) {
486-
if (isDataModel(expr.$resolvedType?.decl)) {
486+
// an expression that resolves to a data model and is part of a member access, return the model
487+
// e.g.: profile.age => Profile
488+
if (isDataModel(expr.$resolvedType?.decl) && isMemberAccessExpr(expr.$container)) {
487489
return expr.$resolvedType?.decl;
488490
}
489491

@@ -497,7 +499,7 @@ function getSourceModelOfFieldAccess(expr: Expression) {
497499
return getContainerOfType(expr, isDataModel);
498500
}
499501

500-
// direct field reference
502+
// direct field reference, return the model
501503
if (isDataModelFieldReference(expr)) {
502504
return (expr.target.ref as DataModelField).$container;
503505
}

tests/integration/tests/enhancements/with-policy/cross-model-field-comparison.test.ts

Lines changed: 181 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -769,8 +769,8 @@ describe('Cross-model field comparison', () => {
769769
await expect(db.user.update({ where: { id: 1 }, data: { age: 25 } })).toResolveTruthy();
770770
});
771771

772-
it('with auth', async () => {
773-
const { prisma, enhance } = await loadSchema(
772+
it('with auth case 1', async () => {
773+
const { enhance } = await loadSchema(
774774
`
775775
model User {
776776
id Int @id @default(autoincrement())
@@ -803,8 +803,7 @@ describe('Cross-model field comparison', () => {
803803
level Int
804804
@@allow('all', true)
805805
}
806-
`,
807-
{ preserveTsFiles: true }
806+
`
808807
);
809808

810809
await expect(enhance().post.create({ data: { title: 'P1' } })).toBeRejectedByPolicy();
@@ -820,4 +819,182 @@ describe('Cross-model field comparison', () => {
820819
})
821820
).toResolveTruthy();
822821
});
822+
823+
it('with auth case 2', async () => {
824+
const { prisma, enhance } = await loadSchema(
825+
`
826+
model User {
827+
id Int @id @default(autoincrement())
828+
teamMembership TeamMembership[]
829+
@@allow('all', true)
830+
}
831+
832+
model Team {
833+
id Int @id @default(autoincrement())
834+
permissions Permission[]
835+
assets Asset[]
836+
@@allow('all', true)
837+
}
838+
839+
model Asset {
840+
id Int @id @default(autoincrement())
841+
name String
842+
team Team @relation(fields: [teamId], references: [id])
843+
teamId Int
844+
@@allow('all', auth().teamMembership?[role.permissions?[name == 'ManageTeam' && teamId == this.teamId]])
845+
@@allow('read', true)
846+
}
847+
848+
model TeamMembership {
849+
id Int @id @default(autoincrement())
850+
role TeamRole?
851+
user User @relation(fields: [userId], references: [id])
852+
userId Int
853+
@@allow('all', true)
854+
}
855+
856+
model TeamRole {
857+
id Int @id @default(autoincrement())
858+
permissions Permission[]
859+
membership TeamMembership @relation(fields: [membershipId], references: [id])
860+
membershipId Int @unique
861+
@@allow('all', true)
862+
}
863+
864+
model Permission {
865+
id Int @id @default(autoincrement())
866+
name String
867+
team Team @relation(fields: [teamId], references: [id])
868+
teamId Int
869+
role TeamRole @relation(fields: [roleId], references: [id])
870+
roleId Int
871+
@@allow('all', true)
872+
}
873+
`
874+
);
875+
876+
const team1 = await prisma.team.create({ data: {} });
877+
const team2 = await prisma.team.create({ data: {} });
878+
879+
const user = await prisma.user.create({
880+
data: {
881+
teamMembership: {
882+
create: {
883+
role: {
884+
create: {
885+
permissions: { create: [{ name: 'ManageTeam', team: { connect: { id: team1.id } } }] },
886+
},
887+
},
888+
},
889+
},
890+
},
891+
});
892+
893+
const asset = await prisma.asset.create({
894+
data: { name: 'Asset1', team: { connect: { id: team1.id } } },
895+
});
896+
897+
const dbTeam1 = enhance({
898+
id: user.id,
899+
teamMembership: [{ role: { permissions: [{ name: 'ManageTeam', teamId: team1.id }] } }],
900+
});
901+
await expect(dbTeam1.asset.update({ where: { id: asset.id }, data: { name: 'Asset2' } })).toResolveTruthy();
902+
903+
const dbTeam2 = enhance({
904+
id: user.id,
905+
teamMembership: [{ role: { permissions: [{ name: 'ManageTeam', teamId: team2.id }] } }],
906+
});
907+
await expect(
908+
dbTeam2.asset.update({ where: { id: asset.id }, data: { name: 'Asset2' } })
909+
).toBeRejectedByPolicy();
910+
});
911+
912+
it('with auth case 3', async () => {
913+
const { prisma, enhance } = await loadSchema(
914+
`
915+
model User {
916+
id Int @id @default(autoincrement())
917+
teamMembership TeamMembership[]
918+
@@allow('all', true)
919+
}
920+
921+
model Team {
922+
id Int @id @default(autoincrement())
923+
permissions Permission[]
924+
assets Asset[]
925+
@@allow('all', true)
926+
}
927+
928+
model Asset {
929+
id Int @id @default(autoincrement())
930+
name String
931+
team Team @relation(fields: [teamId], references: [id])
932+
teamId Int
933+
@@allow('all', auth().teamMembership?[role.permissions?[name == 'ManageTeam' && team == this.team]])
934+
@@allow('read', true)
935+
}
936+
937+
model TeamMembership {
938+
id Int @id @default(autoincrement())
939+
role TeamRole?
940+
user User @relation(fields: [userId], references: [id])
941+
userId Int
942+
@@allow('all', true)
943+
}
944+
945+
model TeamRole {
946+
id Int @id @default(autoincrement())
947+
permissions Permission[]
948+
membership TeamMembership @relation(fields: [membershipId], references: [id])
949+
membershipId Int @unique
950+
@@allow('all', true)
951+
}
952+
953+
model Permission {
954+
id Int @id @default(autoincrement())
955+
name String
956+
team Team @relation(fields: [teamId], references: [id])
957+
teamId Int
958+
role TeamRole @relation(fields: [roleId], references: [id])
959+
roleId Int
960+
@@allow('all', true)
961+
}
962+
`
963+
);
964+
965+
const team1 = await prisma.team.create({ data: {} });
966+
const team2 = await prisma.team.create({ data: {} });
967+
968+
const user = await prisma.user.create({
969+
data: {
970+
teamMembership: {
971+
create: {
972+
role: {
973+
create: {
974+
permissions: { create: [{ name: 'ManageTeam', team: { connect: { id: team1.id } } }] },
975+
},
976+
},
977+
},
978+
},
979+
},
980+
});
981+
982+
const asset = await prisma.asset.create({
983+
data: { name: 'Asset1', team: { connect: { id: team1.id } } },
984+
});
985+
986+
const dbTeam1 = enhance({
987+
id: user.id,
988+
teamMembership: [{ role: { permissions: [{ name: 'ManageTeam', team: { id: team1.id } }] } }],
989+
});
990+
await expect(dbTeam1.asset.update({ where: { id: asset.id }, data: { name: 'Asset2' } })).toResolveTruthy();
991+
992+
const dbTeam2 = enhance({
993+
id: user.id,
994+
teamMembership: [{ role: { permissions: [{ name: 'ManageTeam', teamId: team2.id }] } }],
995+
});
996+
await expect(
997+
dbTeam2.asset.update({ where: { id: asset.id }, data: { name: 'Asset2' } })
998+
).toBeRejectedByPolicy();
999+
});
8231000
});

0 commit comments

Comments
 (0)