zenstack icon indicating copy to clipboard operation
zenstack copied to clipboard

[ Bug ] Comparison Between Fields of Different Models Not Supported in Model-Level

Open benjollymore opened this issue 1 year ago • 3 comments

Description and expected behavior Cross model comparison on models for 'read' checks works for models with a direct relation to the model backing auth() (User in the following example).

It does not, however, work on models that do not have a direct relation to auth, and an error is thrown. The following behaviour can be observed with the schema below.

** Reproducable Schema **

model User extends Base {
      name              String
      email             String
      username          String
      superAdmin        SuperAdmin?
      companyAdmin      CompanyAdmin?
  
      @@allow('all', auth() == this)
  }

  model SuperAdmin extends Base {
      userId String @unique
      user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  
      @@allow('all', auth().superAdmin != null)
  }

    model Company extends Base {
        name String
        admins CompanyAdmin[]
        businessObjects BusinessObject[]

        @@allow('all', auth().superAdmin != null)
        @@allow('all', auth().companyAdmin.companyId == id) // cross model read error present here
    }

  model CompanyAdmin extends Base  {
      userId String @unique
      user User @relation(fields: [userId], references: [id], onDelete: Cascade)
      
      companyId String 
      company   Company @relation(fields: [companyId], references: [id])
  
      @@allow('all', auth().superAdmin != null)
      @@allow('all', auth().companyAdmin.companyId == companyId) // no error for cross model read here
  }

   model BusinessObject extends Base {
      company Company? @relation(fields: [companyId], references: [id])
      companyId String? 
  
      productNumber String
  
      @@allow('all', auth().superAdmin != null)
      @@allow('all', auth().companyAdmin.companyId == companyId) // cross model read error present here
 }

  abstract model Base {
      id String @id @default(uuid())
      createdAt DateTime @default(now())
      updatedAt DateTime @updatedAt
      deletedAt DateTime? @omit
  }

Environment (please complete the following information):

  • ZenStack version: 2.2.4
  • Prisma version: 5.13.0
  • Database type: Postgresql

Additional context Add any other context about the problem here.

benjollymore avatar Jun 28 '24 18:06 benjollymore

Looking at this for a bit longer (without a real understanding of Zenstack's inner machinations), is the following case where this is seen to be working actually cross model?

model CompanyAdmin extends Base  {
      userId String @unique
      user User @relation(fields: [userId], references: [id], onDelete: Cascade)
      
      companyId String 
      company   Company @relation(fields: [companyId], references: [id])
  
      @@allow('all', auth().superAdmin != null)
      @@allow('all', auth().companyAdmin.companyId == companyId) // no error for cross model read here
  }

I suppose we are comparing the companyAdmin related to auth() to other members of the CompanyAdmin model, so this may not really be a cross model comparison and the fact that it is related to auth() may be a red herring.

benjollymore avatar Jun 28 '24 19:06 benjollymore

There is no problem referring cross models. But its your responsibility to pass the right auth context to the getPrisma function. If you want to use nested related models (like in your example companyAdmin is a relation) you should query your db with unenhanced prisma client each request and pass it as auth context to the getPrisma function

Eliav2 avatar Jul 06 '24 08:07 Eliav2

@Eliav2 Thank you for the response and sorry for taking so long to get back to you myself.

I am making a query that picks up all the nested relations for the user and passes that to enhance.

That code looks something like this and is executed every request:

 const getPrisma() => {
   const user: TUserWithRelations = getSessionUser(); // user based on jwt with all relations fetched using vanilla prisma client
   return enhance(prisma, { user })
 }

The error that I was referring to gets picked up by the Zenstack VSCode plugin and also prevents pnpm zenstack generate from succeeding.

When running pnpm zenstack generate: image

VSCode plugin: image

The names of the following vary slightly from the example above but the structure is virtually identical.

The above is on v2.3.2.

benjollymore avatar Jul 25 '24 19:07 benjollymore

It seems this doesn't occur with v2.10.2 release anymore. Closing for now.

ymc9 avatar Jan 06 '25 07:01 ymc9