omar-dulaimi / trpc-shield

🛡 A tRPC tool to ease the creation of permission layer.
MIT License
402 stars 10 forks source link

trpc shield + zenstack ? #14

Closed kishan-getstarted closed 1 year ago

kishan-getstarted commented 1 year ago

Feature request

is it possible to wrap the shield with zenstack ? check here: https://zenstack.dev/docs/guides/trpc

omar-dulaimi commented 1 year ago

Will look into it

omar-dulaimi commented 1 year ago

I tested it with this example https://github.com/zenstackhq/docs-tutorial-express

Here's what I did:

prisma/shield.ts

import { TRPCError } from "@trpc/server";
import { allow, deny, shield } from "trpc-shield";
import { Context } from "../server/context";

export const permissions = shield<Context>(
  {
    list: {
      query: {
        aggregate: allow,
        findFirst: allow,
        findMany: deny,
        findUnique: allow,
        groupBy: allow,
      },
      mutation: {
        createOne: deny,
        deleteMany: allow,
        deleteOne: allow,
        updateMany: allow,
        updateOne: allow,
        upsertOne: allow,
      },
    },
  },
  {
    fallbackRule: allow,
    fallbackError: new TRPCError({
      code: "UNAUTHORIZED",
      message: "Not allowed",
    }),
  }
);

server/routers/_app.ts

const permissionsMiddleware = t.middleware(permissions);
const errorWrappedProc = t.procedure
  .use(permissionsMiddleware)
   ....

That's all you need for the shield to work.

kishan-getstarted commented 1 year ago

Thanks for the example. I will check in upcoming week. Other thing I wanted to ask u is in the documentation it’s mentioned we can use namespace router. I have tried that with zenstack but it seems not to work.

Basically if I use trpc prisma generator it will give me all the routes I wanted for example I have 3 models

space, user, partner

they all will have router like FindMany, create, etc

how can I apply different rules in shield for each route..

one solution is to name each route differently but for that I need to go and manually change each router name (bcz I am using generator)

second solution is namespace router but its seems to not work.

can u provide some insight on that ?

appreciated your response buddy !!

Cheers 🥂

omar-dulaimi commented 1 year ago

Namespacing works fine. Just make sure the shield that gets generated matches the procedure names that Zenstack produces. With prisma-trpc-generator there's an option that let's you hide model name from procedure name(findMany instead of findManyUser); showModelNameInProcedure.

But since Zenstack uses its own implementation of the library, you would need to modify the generated shield to match the one I sent above. Basically just group them by router name

kishan-getstarted commented 1 year ago

Just show u the sample code below is the way I want this to be work

query: {
    space: {
      findMany: and(isAuthenticated, or(isAdmin, isEditor, isReader)),
    }
  },
  mutation: {
    space: {
      create: and(isAuthenticated, or(isAdmin, isEditor)),
      update: and(isAuthenticated, or(isAdmin, isEditor)),
    }

  }

The above is not working but when I do like

 query: {
    findMany: and(isAuthenticated, or(isAdmin, isEditor, isReader)),
  },
  mutation: {
    create: and(isAuthenticated, or(isAdmin, isEditor)),
    update: and(isAuthenticated, or(isAdmin, isEditor)),
  },

Its working fine..

My router config

export function createRouter<Config extends BaseConfig>(router: RouterFactory<Config>, procedure: ProcBuilder<Config>) {
    return router({
        partner: createPartnerRouter<Config>(router, procedure),
        space: createSpaceRouter<Config>(router, procedure),

    }
    );
}
kishan-getstarted commented 1 year ago

Ohh my bad!! The namespace should be at the root level. It works fine. I am good to close this PR.

Thanks, buddy for the help.. Really appreciated