casl: Rule with class and conditions doesn't work
First at all, sorry if I forgot something.
Describe the bug When I define rules using a class name in typescript, in that rules where there are some condition always return false.
To Reproduce
I have these classes User and Post
enum Role {
ADMIN = 'admin',
USER = 'user'
}
class User {
id: number;
login: string;
role: Role;
}
class Post {
title: string;
text: string;
authrorId: number;
}
I define the rules in a static class method called createForUser as follow:
enum Action {
MANAGE = 'manage',
CREATE = 'create',
READ = 'read',
UPDATE = 'update',
DELETE = 'delete'
}
type Subjects = typeof Post | typeof User | Post | User | 'all';
type AppAbility = Ability<[Action, Subjects}>;
class CaslAbilityFactory {
static createForUser(user: User) {
cosnt { can, cannot, build } = new AbilityBuilder<AppAbility>(Ability as AbilityClass<AppAbility>);
if (user.role === Role.ADMIN) {
can(Action.MANAGE, 'all');
} else {
can(Action.READ, Post);
cannot(Action.DELETE, Post);
}
can(Action.UPDATE, Post, { authorId: user.id });
return build();
}
}
When I check the ability of READ a post, with a user with Role USER, it works as expected. The problem is when I check to update a post as follow:
const user: User = new User();
user.id = 1;
user.login = 'user01';
user.role = Role.USER;
const post: Post = new Post();
post.title = "Dummy post";
post.text = "Something interesting";
post.authorId = user.id;
const ability = CaslAbilityFactory.createForUser(user);
console.log(ability.can(Action.READ, Post)); // ---> Prints true and expected true.
console.log(ability.can(Action.UPDATE, post)); // ---> Prints false, but expected true
But if we add 'Post' to the type Subjects and change Post for 'Post' as following:
type Subjects = typeof Post | typeof User | Post | User | 'Post' | 'all';
class CaslAbilityFactory {
...
can(Action.UPDATE, 'Post', { authorId: user.id });
...
return build();
}
console.log(ability.can(Action.READ, Post)); // ---> Prints true and expected true.
console.log(ability.can(Action.UPDATE, post)); // ---> Now prints true and expected true
I tried too of downgrade @casl/ability to version 4.1.6 and the first code (using Post in the rule specification) works as expected.
Interactive example Here is a link to the interactive example in codesandobx. https://codesandbox.io/s/casl-bug-3txfo?file=/src/index.ts
CASL Version
@casl/ability - 5.1.1
Environment:
node - 12.18.4
typescript - 4.1.3
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 21 (9 by maintainers)
Casl subjects still be as you have it. Check how I handle this here: https://github.com/davidgpgr94/nestjs-blog-api/blob/master/src/posts/controllers/posts.controller.ts#L81 https://github.com/davidgpgr94/nestjs-blog-api/blob/master/src/casl/casl-ability.factory.ts#L13
Hello there! Have similar problem discussed above. Can’t find a solution. Kindly ask you to help with it. Here is a link to the description: https://stackoverflow.com/questions/70584146/nest-js-authorization-with-casl-doesnt-work-as-expected
@ThomasEddie you didn’t specify
detectSubjectTypefunction:@ThomasEddie You need to send a instance of BlogEntity. What you have is like you were doing
ability.cannot(Action.Delete, BlogEntity), but you need doability.cannot(Action.Delete, blogEntityInstance)for thecannot(Action.Delete, BlogEntity, { isPublished: true });rule to be executed. You will need to make some changes in your guard to send to the handler that instance. I hope I have explained myself well.fixed in 5.1.2
To be able to do it (without typescript errors) I had to add
// @ts-ignorebecausedetectSubjectTypeexpects a string, buttype.constructorreturns a FunctionThat is! When there is no “instance conditions” (don’t know how name it exactly 😅) it works fine.