This project demonstrates how to use ZenStack in a RedwoodJS project. It's extended based on the blog app used through RedwoodJS's tutorial.
Please refer to this blog post for a general introduction.
-
Prepare project
Install CLI
cd api yarn add -D zenstack yarn add @zenstackhq/runtime
Bootstrap ZModel from
schema.prisma
cp db/schema.prisma ./schema.zmodel
-
Prepare model
Add the following section to
schema.zmodel
to output the generated Prisma schema to the default location of Redwood:plugin prisma { provider = '@core/prisma' output = './db/schema.prisma' }
Run
zenstack generate
and verify thatdb/schema.prisma
is updated.yarn zenstack generate
-
Add access policies
Note the added
@@allow
rules (all operations are denied by default).model Post { id Int @id @default(autoincrement()) title String body String comments Comment[] user User @relation(fields: [userId], references: [id]) userId Int createdAt DateTime @default(now()) published Boolean @default(true) // 🔐 Admin user can do everything to his own posts @@allow('all', auth().roles == 'admin' && auth() == user) // 🔐 Posts are visible to everyone if published @@allow('read', published) } model Comment { id Int @id @default(autoincrement()) name String body String post Post @relation(fields: [postId], references: [id]) postId Int createdAt DateTime @default(now()) // 🔐 Moderator user can do everything to comments @@allow('all', auth().roles == 'moderator') // 🔐 Everyone is allowed to view and create comments for published posts @@allow('create,read', post.published) }
See the next section for where the
auth()
function's value comes from.Rerun generation and migrate the database.
yarn zenstack generate yarn rw prisma migrate dev
-
Create access-policy-enhanced Prisma Client
Add the following function to
api/src/lib/db.js
:import { withPolicy } from '@zenstackhq/runtime' /* * Returns ZenStack wrapped Prisma Client with access policies enabled. */ export function authDb() { return withPolicy(db, { user: context.currentUser }) }
It uses the
withPolicy
API to create a Prisma Client wrapper (note thecontext.currentUser
is passed in as the current user, which determines what theauth()
function returns in the ZModel policy rules). -
Switch to relying on access policies for authorization
For example, remove authorization from
api/src/services/comments.js
and useauthDb()
helper to access db.export const deleteComment = ({ id }) => { - requireAuth({ roles: 'moderator' }) - return db.comment.delete({ where: { id } }) return authDb().comment.delete({ where: { id } }) }