zenstackhq / zenstack

Fullstack TypeScript toolkit that enhances Prisma ORM with flexible Authorization layer for RBAC/ABAC/PBAC/ReBAC, offering auto-generated type-safe APIs and frontend hooks.
https://zenstack.dev
MIT License
2.07k stars 88 forks source link

Policy definition cannot be loaded from default location #421

Closed quinnvaughn closed 1 year ago

quinnvaughn commented 1 year ago

Description and expected behavior I am attempting to add the enhanced prisma client in the context of a graphql server, and I'm getting this error while attempting to codegen from graphql-codegen: "Context creation failed: Policy definition cannot be loaded from default location". I couldn't find anything on this error. It works fine when I just use the backend separate, and the codegen also works fine when using the normal prisma client.


function createZenstack(authUser: AuthUser | undefined) {
    return withPolicy(prisma, { user: authUser })
}

export async function createContext(req: IncomingMessage): Promise<Context> {
    const auth = req.headers.authorization?.split("Bearer ")[1] || ""
    const decoded = verifyToken(auth)
    let currentUser: User | null = null
    if (decoded?.id && typeof decoded.id === "string") {
        currentUser = await prisma.user.findUnique({
            where: {
                id: decoded.id,
            },
        })
    }
    return {
        prisma: createZenstack({ id: currentUser?.id }),
        currentUser,
    }
}

There is nothing in the examples about how to use this in GraphQL land, the only backend example is with express, so I'm not sure why this fails specifically with the codegen. The easiest temporary fix for now is to just not use the enhanced version but that defeats some of the point.

Environment (please complete the following information):

ymc9 commented 1 year ago

Hi @quinnvaughn , GQL hasn't been a focus yet but I'm interested in getting the main scenarios to work. By graphql-codegen you mean this one right?

The error is usually caused by a mismatch between ZenStack's code generation location and its runtime location. I'll need to build a repro to investigation. Is your project by any chance publicly accessible now? If not, I'll see if I can get something with a standard GQL setup.

quinnvaughn commented 1 year ago
  1. Yes
  2. Not public code, but I'm using Apollo Server's standalone server and passing in that function ^^ into the context like ({req}) => createContext(req) which I assume would be enough to replicate it along with some generic types. I'm using Apollo Client/React on the frontend, no weird setup.
quinnvaughn commented 1 year ago

Also possibly relevant, my zenstack code is in a zenstack folder under src ie src/zenstack/allFiles.zmodel. I just use a custom path for the npm script to specify the location.

ymc9 commented 1 year ago

Got it. Let me try it out, and will keep you updated on my findings. Thanks for reporting this!

jiashengguo commented 1 year ago

Hi, @quinnvaughn I tried to reproduce your problem with a simple ApolloServer and graphql-codegen as you used: https://github.com/jiashengguo/graphql-server-example

It worked fine on my machine. Can you try it in your environment?

BTW, as @ymc9 mentioned, this problem is usually caused by generated .zenstack folder that can’t be referenced by @zenstackhq/runtime package. Can you try to identify where the .zenstack folder is generated in your project by running:

find . -type d -name ".zenstack"

For your concern, the location of zmodelfile should not be an issue. ZenStack CLI supports specifying the schema file using --schema argument like:

npx zenstack generate --schema src/zenstack/schema.zmodel

I don’t quite sure how you did that by

use a custom path for the npm script.

If it's not the way above, can you try it instead to see whether it could solve your problem?

quinnvaughn commented 1 year ago

It seems to work when I change the code slightly to this:

import { prisma } from "./db"
import { verifyToken } from "./lib/jwt"
import { PrismaClient, User } from "@prisma/client"
import { withPolicy } from "@zenstackhq/runtime"
import { IncomingMessage } from "http"

export type Context = {
    prisma: PrismaClient
    currentUser: User | null
}

function createZenstack(authUser: User | undefined) {
    return withPolicy(prisma, { user: authUser })
}

export async function createContext(req: IncomingMessage): Promise<Context> {
    const auth = req.headers.authorization?.split("Bearer ")[1] || ""
    const decoded = verifyToken(auth)
    let currentUser: User | null = null
    if (decoded?.id && typeof decoded.id === "string") {
        currentUser = await prisma.user.findUnique({
            where: {
                id: decoded.id,
            },
        })
    }
    return {
        prisma: createZenstack(currentUser || undefined),
        currentUser,
    }
}

And completely ignore your AuthUser type. Although I'm not sure why you have it as AuthUser | undefined when a findUnique will return null, forces me to do the || undefined to play nice.

quinnvaughn commented 1 year ago

So actually this just made TS work, the prisma instance itself seems to not be working.

Also to answer your original question it was ./node_modules/@zenstackhq/runtime/node_modules/.zenstack

jiashengguo commented 1 year ago

It is supposed to be ./node_modules/.zenstack. Did you use the command I mentioned above running in the root folder of your repo: npx zenstack generate --schema src/zenstack/schema.zmodel

Regarding your question, I don't understand how it relates to the AuthUser type.

And can you give me more info about:

the prisma instance itself seems to not be working.

I feel it would be more efficient if you can create a public repo with a similar structure just to show the error so that I can use it to identify the problem. What do you think? 😄

jiashengguo commented 1 year ago

@quinnvaughn In the latest release 1.0.0-alpha.126, we made a change that might be relevant to this issue. Maybe you can update it, then try it again?

quinnvaughn commented 1 year ago

Sure I'll check it out. Sorry, been busy.

ymc9 commented 1 year ago

Closing for now until people hit it again.

quinnvaughn commented 1 year ago

Yeah this still doesn't work. Gives me the same error @ymc9