Skip to content

RLS #1

Open
Open
RLS#1
@jiashengguo

Description

@jiashengguo

The Prisma extension library ZenStack we are building happens to share the same view with wladiston andLiam-Tait’s solution to use the schema as the single source of truth. 🤝

This is how the sample schema should be represented in ZenStack now:

enum Role {
    USER
    ADMIN
}

model Cart {
    id String @id @default(uuid())
    customer Profile @relation(fields: [customerId], references: [id])
    customerId String

    //Carts are only visible by owners
    @@allow('read', customerId == auth().id)

    //Admins can do anything with carts
    @@allow('all', auth().role == 'ADMIN')
}

The only differences are:

  1. We use @@Allow and @@deny instead of @@security
  2. We don’t generate RLS for now. Instead, we add a transparent proxy on Prisma client.

For the multitenancy issues, we invited a Collection Predict Expression to express it. Here is how it looks now:

enum Role {
    USER
    ADMIN
}

model User {
    id            String    @id @default(cuid())
    spaces SpaceUser[]
    
    // can be created by anyone, even not logged in
    @@allow('create', true)

    // can be read by users sharing any space
    @@allow('read', spaces?[space.members?[user == auth()]])

    // full access by oneself
    @@allow('all', auth() == this)
}

model Space {
    id String @id @default(uuid())
    members SpaceUser[]

    // require login
    @@deny('all', auth() == null)

    // everyone can create a space
    @@allow('create', true)

    // any user in the space can read the space
    @@allow('read', members?[user == auth()])

    // space admin can update and delete
    @@allow('update,delete', members?[user == auth() && role == ADMIN])
}

/*
 * Model representing membership of a user in a space
 */
model SpaceUser {
    id String @id @default(uuid())
    space Space @relation(fields:[spaceId], references: [id], onDelete: Cascade)
    spaceId String
    user User @relation(fields: [userId], references: [id], onDelete: Cascade)
    userId String
    role Role

    // require login
    @@deny('all', auth() == null)

    // space admin can create/update/delete
    @@allow('create,update,delete', space.members?[user == auth() && role == ADMIN])

    // user can read entries for spaces which he's a member of
    @@allow('read', space.members?[user == auth()])
}

If you feel interested, there is a tutorial post I wrote about it:

How to build a collaborative SaaS product using Next.js and ZenStack's access control policy

We would really appreciate it if you could share your opinions by commenting or joining our Discord to help us make ZenStack the right thing to solve your problems.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions