Skip to content

[Feature Request] Permission control for Users with polymorphic roles #1745

@aladinelkhalil

Description

@aladinelkhalil

Is your feature request related to a problem? Please describe.
So if you look at the Ad model below, I would like to replace the hardcoded value 'STORE' with the buyerType variable. This value currently exists only for Companies with companyType 'Buyer', in the Buyer model it is the type field.

What I'm wishing for is, that I can either replace the hardcoded value 'STORE' here in the Ad model (this one 👇)

@@allow('all', auth().company.companyType == 'Buyer' && 'STORE' in buyerTypes)

with

auth().company.buyer.type / auth().buyer.type

OR I can duplicate the type field in the Company model, make it optional and call it buyerType BuyerType? and then be able to do this

@@allow('all', auth().company.companyType == 'Buyer' && auth().company.buyerType in buyerTypes)

Currently this gives the following error when I try it.
comparison between fields of different models is not supported in model-level "read" rules (zmodel)

Describe the solution you'd like
See above.

Describe alternatives you've considered
Currently, my only solution is

@@allow("all", auth().company.buyerType == "RESTAURANT" && "RESTAURANT" in buyerTypes)
@@allow("all", auth().company.buyerType == "STORE" && "STORE" in buyerTypes)
@@allow("all", auth().company.buyerType == "WHOLESALER" && "WHOLESALER" in buyerTypes)

which is hard to scale and maintain.

Additional context

schema.zmodel

enum BuyerType {
    STORE
    RESTAURANT
    WHOLESALER
}

enum ChainStore {
    ALL
    CHAINSTORE_1
    CHAINSTORE_2
    CHAINSTORE_3
}

abstract model Id {
    id String @id @default(cuid())
}

abstract model Base extends Id {
    createdAt DateTime @default(now())
    updatedAt DateTime @updatedAt
}

model Ad extends Base {
    serial       Int          @unique @default(autoincrement())
    description  String
    batchNumber  String
    deliveryDate DateTime?
    expiryDate   DateTime
    buyerTypes   BuyerType[]
    chainStores  ChainStore[]
    listPrice    Float
    isSold       Boolean      @default(false)

    supplier     Supplier     @relation(fields: [supplierId], references: [id])
    supplierId   String       @default(auth().companyId)

    @@allow('all', auth().company.companyType == 'Buyer' && 'STORE' in buyerTypes)
    @@allow('all', auth().company.companyType == 'Supplier' && auth().companyId == supplierId)
    @@allow('all', auth().isAdmin)
}

model Company extends Base {
    name               String @unique
    organizationNumber String @unique
    users              User[]

    companyType        String
    @@delegate(companyType)

    @@allow('read, update', auth().companyId == id)
    @@allow('all', auth().isAdmin)
}

model Buyer extends Company {
    storeName  String
    type       BuyerType
    chainStore ChainStore @default(ALL)

    @@allow('read, update', auth().company.companyType == 'Buyer' && auth().companyId == id)
    @@allow('all', auth().isAdmin)
}

model Supplier extends Company {
    ads Ad[]

    @@allow('all', auth().company.companyType == 'Supplier' && auth().companyId == id)
    @@allow('all', auth().isAdmin)
}

model User extends Base {
    firstName String
    lastName  String
    email     String   @unique
    username  String   @unique
    password  String   @password @omit
    isAdmin   Boolean  @default(false)

    company   Company? @relation(fields: [companyId], references: [id])
    companyId String?

    @@allow('read', auth().id == id)
    @@allow('read', auth().companyId == companyId)
    @@allow('all', auth().isAdmin)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions