Replies: 2 comments 3 replies
-
I think I can share my experience because I had a similar situation. In my application, I had the following models
A user can be either an admin, a squad lead or a regular user. Each user could create as many goals as they want and can add tasks to their goals. So I needed to setup permission so that a regular user only can manage their goals and the tasks related to those goals. I am using Prisma and Nestjs so the example could be different than what you worked with. This is the service I am using to generate the permissions: import { AbilityBuilder, AbilityClass } from '@casl/ability';
import { PrismaAbility, Subjects } from '@casl/prisma';
import { Injectable } from '@nestjs/common';
import { Goal, PrismaClient, Task, User } from '@prisma/client';
const prisma = new PrismaClient();
export enum Action {
Manage = 'manage',
Create = 'create',
Read = 'read',
Update = 'update',
Delete = 'delete',
}
export type MySubjects = Subjects<{
User: User,
Goal: Goal,
Task: Task,
}> | 'all';
export type AppAbility = PrismaAbility<[Action, MySubjects]>;
@Injectable()
export class AbilityService {
async createForUser(user: User) {
const AppAbility = PrismaAbility as AbilityClass<AppAbility>;
const { can, build } = new AbilityBuilder(AppAbility as AbilityClass<AppAbility>);
switch (user.role) {
case 'ADMIN':
can(Action.Manage, 'all');
break;
case 'MEMBER':
await createUserPermissions();
break;
default:
break;
}
async function createUserPermissions() {
can(Action.Create, 'Goal');
can(Action.Read, 'Goal', { userId: user.id });
can(Action.Update, 'Goal', { userId: user.id });
can(Action.Delete, 'Goal', { userId: user.id });
const goals = await prisma.goal.findMany({ where: { userId: user.id } });
can(Action.Manage, 'Task', { goalId: { in: goals.map(g => g.id) } });
}
return build();
}
} As you can see, to be able to identify if the user can manage the tasks, I get the goals list from the database, and the following line construct to rule: can(Action.Manage, 'Task', { goalId: { in: goals.map(g => g.id) } }); In the service that update the database, I use the following: // Inject the service created earlier
const ability = await this.abilityFactory.createForUser(user);
try {
ForbiddenError.from(ability)
.setMessage('You do not have permissions to update tasks in this goal')
.throwUnlessCan(Action.Update, subject('Task', { goalId } as Task)); To cut the clutter, it will be something like this: ability.can(Action.Update, subject('Task', { goalId } as Task)); Hope that helps |
Beta Was this translation helpful? Give feedback.
-
If you're just asking if you are doing it right then yes, it was right but if you have some different validation like knowing if it is an admin then you have to add more requirements. import { AbilityBuilder, Ability } from '@casl/ability'
const { can, build } = new AbilityBuilder(Ability)
const ADMIN_ROLE_ID = 10;
can('create_repositories' ,'Organization', { id: 1, roleId: ADMIN_ROLE_ID })
export const ability = build() import { subject } from '@casl/ability'
import { ability } from ' *previous_example* '
const user = { role: ADMIN_ROLE_ID }
const organization = { id: 1, roleId: user.role }
ability.can('create_repositories', 'Organization', subject('Organization', organization)) // true |
Beta Was this translation helpful? Give feedback.
-
Lets say I have a rule
Only administrators can create repositories within organizations (c) GitHub
So, the first thing that comes to my mind is to create the next rule, using
AbilityBuilder
and use it like this
The question is - Am I doing it right?
The documentation says nothing about creating resources. All examples that exist show how to
read
ordelete
a resource, butcreate
is omitted.cc @stalniy
Beta Was this translation helpful? Give feedback.
All reactions