Skip to main content

Roles & Permissions

Marblism uses ZenStack to implement a powerful and flexible permission system. This system is based on the auth() function and @@allow decorators in your Zenstack data model.

Understanding auth()

The auth() function represents the currently authenticated user. It allows you to reference the current user's properties in your permission rules.

Using @@allow Decorators

The @@allow decorators define access rules for your models. They take two arguments:

  1. The operation type ("create", "read", "update", "delete", or "all")
  2. A boolean expression that determines if the operation is allowed

Here's an example from the User model:

model User {
// ... other fields ...

@@allow("all", auth().id == this.id)
@@allow('all', auth().globalRole == 'ADMIN')
@@allow("create", globalRole != 'ADMIN')
@@allow("read", true)
}

Let's break down these rules:

  • @@allow("all", auth().id == this.id): Allows users to perform all operations on their own record
  • @@allow('all', auth().globalRole == 'ADMIN'): Allows admins to perform all operations on any user record
  • @@allow("create", globalRole != 'ADMIN'): Allows any user to create non-admin users
  • @@allow("read", true): Allows any user to read information of all user records

Protecting Other Models

To protect other models, you can use similar @@allow decorators. For example:

model Post {
id String @id @default(uuid())
title String
content String
authorId String
author User @relation(fields: [authorId], references: [id])

@@allow('all', auth().id == authorId)
@@allow('read', true)
}

This setup:

  • Allows users to create, update, and delete their own posts
  • Allows anyone to read posts

Remember to always start with the most restrictive permissions and then add exceptions as needed.

Best Practices

  1. Be explicit about your permissions
  2. Use auth().id to reference the current user
  3. Consider using roles for more complex permission structures
  4. Test your permissions thoroughly to ensure they work as expected

By understanding and properly implementing these concepts, you can create a secure and flexible permission system for your application.

Automatic Permission Application

It's important to note that these permissions are automatically applied when using the ctx.database Prisma client. This means that when you query the database using this client, the results are automatically filtered according to the logged-in user and the permissions applied to the model.

For example, consider the following query:

const posts = await ctx.database.Post.findMany();

In this case, the findMany() query will only return posts that the current user has permission to read, based on the rules defined in the Post model.