stalniy / casl

CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
https://casl.js.org/
MIT License
6.05k stars 270 forks source link

The 'isSet' filter for optional fields in Prisma throws an error #966

Closed p0thi closed 1 month ago

p0thi commented 2 months ago

Describe the bug Using the prisma isSet filter in my casl conditions leads to an error.

To Reproduce

For reference, behind this expandable spoiler is the ability builder code. Click to show. ```typescript import { AbilityBuilder, AbilityOptionsOf, PureAbility, fieldPatternMatcher, type AbilityTuple, } from "@casl/ability"; import { PrismaQuery, Subjects, prismaQuery } from "@casl/prisma"; import { User } from "@prisma/client"; // PrismaModels is a helper, that contains all Prisma Models. // ExtendedPrismaClient is my prisma client with extensions import type { ExtendedPrismaClient, PrismaModels } from "../prismaClient"; export type MFActions = "manage" | "read" | "update" | "create" | "delete"; export type MFSubjects = | "all" | Subjects<{ Foo: PrismaModels["Foo"]; }>; export type MFAbility = PureAbility< AbilityTuple, PrismaQuery >; export type CanParameters = Parameters; export type MFAllow = AbilityBuilder["can"]; export type MFForbid = AbilityBuilder["cannot"]; export interface MFCreateAbilityOptions { prisma: ExtendedPrismaClient; user?: User; allow: MFAllow; forbid: MFForbid; } export type AbilityCreator = ({ user, allow, forbid, }: MFCreateAbilityOptions) => void; export function createMFAbility( prisma: ExtendedPrismaClient, authenticatedUser?: User | null, ): MFAbility { const { can: allow, cannot: forbid, build, } = new AbilityBuilder(PureAbility); const buildOptions: AbilityOptionsOf = { fieldMatcher: fieldPatternMatcher, conditionsMatcher: prismaQuery, // all models from my Prisma extension have the __typename field detectSubjectType: (item) => item.__typename, }; const abilityCreators: AbilityCreator[] = [ // creator functions, that contain code, that runs things like allow("read", "Foo", fooWhere); ]; abilityCreators.forEach((creator) => creator({ prisma, user: authenticatedUser ?? undefined, allow, forbid, }), ); return build(buildOptions); } ```

I am using casl v6 prisma for my prisma objects. I have a prisma model like this:

model Foo {
  name  String
  active  Boolean?   @default(true)
}

So if the active flag is not defined in the database, prisma includes it as true in the query result. Now, if i want to allow read access only on Foos, that have the active flag set to false or don't have it at all (as it is optional), I would need to configure the ability like the following:

allow("read", "Foo", {
  OR: [{ active: { equals: true } }, { active: { isSet: false } }],
});

but the use of the isSet filter for optional fields leads to an error, when I check if the ability can read an object of type Foo.

To be precise, it is an Error: "equals" does not supports comparison of arrays and objects error, even though it works, when I only use { active: { equals: true } } as the condition without the isSet filter.

Expected behavior The use of the isSet filter for optional fields should not throw an exception.

CASL Version

@casl/ability - v 6.7.1 @casl/prisma - v 1.4.1

Environment: Node version 20.12.1

stalniy commented 2 months ago

isSet is currently not support

p0thi commented 2 months ago

I see, thanks for the heads up. The isSet filter is only available for prisma with mongodb. I would go ahead anyways and implement it and create a PR. Does that sound good @stalniy ?

stalniy commented 2 months ago

Yes, would be cool!