Description
Description and expected behavior
When adding field-level acces rules, specifically @deny
that accesses this
, the prisma query executed selects all fields in the model, but this does not include Prisma extension computed fields.
// schema
model User {
uuid String @id @default(uuid())
email String @unique @deny('read', auth().uuid != this.uuid) // <-- using `this` here
username String @unique
@@allow('read', true)
}
// prisma-client
createZenstackPrisma = (prisma: PrismaService, cls: ClsService)=> {
const extended = prisma.$extends(Prisma.defineExtension({
name: "urls-extension",
result: {
user: {
pageUrl: {
needs: { username: true },
compute: (e) => `example.com/${username}`,
},
},
},
}))
// technically we cannot provide an extended instance here according to typescript because it is mising e.g. .$on. Only the base instance has that.
// We still do that here to show that it also doesn't work with extensions applied before enhancing
const enhanced = enhance(extended, { user: cls.get("auth") }, {
logPrismaQuery: true, // note that prisma is also set to log "info" level
})
return enhanced
}
Not specifying select fields does not include computed fields:
await zenstack.user.findUniqueOrThrow({
where: {
uuid: '00000000-0000-0000-0000-000000000000',
},
})
// prisma:info [policy]
{
"where": {
"uuid": "00000000-0000-0000-0000-000000000000"
},
"select": { // selects all fields from the model by default, no pageUrl
"email": true,
"uuid": true,
"username": true
}
}
// EXPECTED output
{
"where": {
"uuid": "00000000-0000-0000-0000-000000000000"
},
"select": { // selects all fields from the model by default, no pageUrl
"email": true,
"uuid": true,
"username": true,
"pageUrl": true
}
}
Manually specifying select fields works as expected:
await zenstack.user.findUniqueOrThrow({
where: {
uuid: '00000000-0000-0000-0000-000000000000',
},
select: {
pageUrl: true,
email: true,
}
})
// prisma:info [policy]
{
"where": {
"uuid": "00000000-0000-0000-0000-000000000000"
},
"select": { // selects what I specified + what it needs to evaluate @deny expression
"pageUrl": true,
"email": true,
"uuid": true
} // note that later prisma also adds the `username` to the selection to compute the pageUrl
}
Environment (please complete the following information):
- ZenStack version: 2.14.2
- Prisma version: 6.7.0
- Database type: Postgresql
Additional Context
In the docs it mentions that Currently there's a limitation that computed fields are not governed by field-level access policies. This means that if you have a computed field that depends on a field that the current user cannot read, the computed field will still be calculated and returned. But this does not appear to be true, as it does not even select the field.
I suppose the generated model-meta from the zmodel doesn't include computed fields, so I'm not sure if there is an easy solution. Something that could work is e.g. a @@computed(['pageUrl'])
attribute so that the field is in the model metadata.