-
-
Notifications
You must be signed in to change notification settings - Fork 16
Description
Description
When using array-related functions (has, hasSome) in access policies with PostgreSQL, the policy evaluation fails with different errors depending on the function used:
has()function: Producesmalformed array literalerrorhasSome()function: ProducesUnsupported argument expression: arrayerror
These functions are essential for implementing role-based access control with array fields in the AuthUser type.
Environment
- ZenStack version: 3.2.1
- Database: PostgreSQL
- Node.js version: 24.x
Schema to Reproduce
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
plugin policy {
provider = '@zenstackhq/plugin-policy'
}
enum Role {
admin
editor
viewer
}
type AuthUser {
id String
roles Role[]
@@auth
}
model User {
id String @id @default(cuid())
name String
email String? @unique
posts Post[]
profile Profile?
@@allow('read', auth() != null)
@@allow('update', auth().id == this.id)
// has() produces "malformed array literal" error
@@allow('all', has(auth().roles, admin))
}
model Profile {
id String @id @default(cuid())
userId String @unique
bio String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@allow('read', auth() != null)
@@allow('create', userId == auth().id)
@@allow('update', userId == auth().id)
// has() produces "malformed array literal" error
@@allow('all', has(auth().roles, admin))
}
model Post {
id String @id @default(cuid())
title String
authorId String
author User @relation(fields: [authorId], references: [id])
@@allow('read', auth() != null)
@@allow('create', authorId == auth().id)
// hasSome() produces "Unsupported argument expression: array" error
@@allow('update', hasSome(auth().roles, [admin, editor]))
}Code to Reproduce
import { ZenStackClient } from '@zenstackhq/orm';
import { schema } from './zenstack/schema';
import { Role } from 'zenstack/models'
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const db = new ZenStackClient(schema, {
dialect: { pool, type: 'postgres' }
});
// Set auth context with roles array
const userId = 'user-123'
const userDb = db.$setAuth({
id: userId ,
roles: [ Role.editor ],
});
// has() - "malformed array literal" error
// This happens during policy evaluation for nested mutations
await userDb.user.update({
data: {
profile: {
upsert: {
create: { bio: 'Hello' },
update: { bio: 'Hello' },
where: { userId },
},
},
},
where: { id: userId },
});
// hasSome() - "Unsupported argument expression: array" error
const postId = 'post-123'
await userDb.post.update({
data: { title: 'Updated Title' },
where: { id: postId },
});Error Messages
has() function
Error: Failed to execute query: error: malformed array literal: "admin"
reason: 'db-query-error',
dbErrorCode: '22P02',
dbErrorMessage: 'malformed array literal: "admin"',
sql: `... cast(ARRAY[$1,$2] as "public"."Role"[]) @> ($3) ...`,
detail: 'Array value must start with "{" or dimension information.',
hasSome() function
Error: Failed to execute query: Error: Unsupported argument expression: array
reason: 'not-supported',
dbErrorMessage: 'Unsupported argument expression: array',
Root Cause Analysis
has() in @zenstackhq/orm
Location: @zenstackhq/orm/dist/index.js - has() function
var has = __name((eb, args) => {
const [field, search2] = args;
// ...
return eb(field, "@>", [search2]); // ← Problem here
}, "has");When search2 is a Kysely Expression object (returned from eb.val()), wrapping it in [search2] creates a JavaScript array containing the Expression object rather than a proper SQL array value. This causes PostgreSQL to receive a malformed array literal.
hasSome() in @zenstackhq/plugin-policy
Location: @zenstackhq/plugin-policy/dist/index.js - transformCallArg() function
transformCallArg(eb, arg, context) {
if (ExpressionUtils3.isLiteral(arg)) { return eb.val(arg.value); }
if (ExpressionUtils3.isField(arg)) { return eb.ref(arg.field); }
if (ExpressionUtils3.isCall(arg)) { return this.transformCall(arg, context); }
if (this.isAuthMember(arg)) { /* ... */ }
// Missing: handling for ExpressionUtils.isArray(arg)
throw createUnsupportedError(`Unsupported argument expression: ${arg.kind}`);
}The function does not handle array expressions (ExpressionUtils.isArray(arg)), which are required for hasSome([admin, editor]) syntax.
Summary
| Function | Error | Affected Package |
|---|---|---|
has() |
malformed array literal |
@zenstackhq/orm |
hasSome() |
Unsupported argument expression: array |
@zenstackhq/plugin-policy |