Skip to content

accessibleBy does not throw an error as expected when using createPrismaAbility #794

@benediktms

Description

@benediktms

Describe the bug
I am using casl with the Prisma plugin. So far I think everything is working really nicely, but I've noticed that that the accessibleBy function does not throw an error when a restricted resource is being accessed, instead it seems to simply narrow the search query, so that nothing is returned if the user does not have permission to access the resource.

To Reproduce
For example, here I've defined my ability:

type Action = 'read' | 'create' | 'update' | 'delete' | 'manage';

type AppSubjects =
  | 'all'
  | Subjects<{
      User: User;
      Client: Client;
	  // ...
    }>;

export type AppAbility = PureAbility<[Action, AppSubjects], PrismaQuery>;

// the following code is in a function that accepts a user object

const { can, cannot, build } = new AbilityBuilder<AppAbility>(
    createPrismaAbility,
);

// for each user role I define different abilities, for example this is for the Operator role
// A more liberal role e.g. an admin or something would be able to read everything to do with
// a client

can(['read', 'update'], 'Client', {
	operatorId: { equals: user.id },
}).because('You can only access your own clients.');


// then later on when fetching from the database I am constructing my query something like so:
const filter = accessibleBy(ability).Client
const where = {
	AND: [filter, { id: clientId }],
};

const client = await prisma.client.findFirstOrThrow({
    where,
    include: {
      operator: true
    },
});

Expected behavior
As mentioned in the documentation I am expecting a ForbiddenError to be thrown. However it seems that this query simply returns:

An operation failed because it depends on one or more records that were required but not found. Expected a record, found none.

suggesting that the can simply narrows the search results rather than throw an exception. Using a findFirst and not using any filters and then asserting afterwards like so:

ForbiddenError.from(ctx.ability).throwUnlessCan(
	'read',
	subject('Client', client),
);

seems to work, but this does not seem to be the intended behaviour.

CASL Version

"@casl/ability": "^6.5.0",
"@casl/prisma": "^1.4.0",
"@casl/react": "^3.1.0",

Environment:

node: ^18

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions