integrating CASL in NestJs not working (only works with typeorm)
See original GitHub issueI followed nestjs docs to integrate CASL V5.4.3 to my NestJs backend but it’s not working.
To debug the issue, I made casl-ability.factory.spec.ts with jest, and all the tests fails.
ability.can returns false for any action when the user is not administrator.
you can run the tests by running : npm run test:casl
Full code source on github : https://github.com/thorizer/nest-rest-mongo-casl
casl-ability.factory.ts
import {
Ability,
AbilityBuilder,
AbilityClass,
ExtractSubjectType,
InferSubjects,
} from "@casl/ability";
import { Injectable } from "@nestjs/common";
import { Action, Roles } from "./casl.roles";
import { Article } from "../modules/article/article.model";
import { User } from "../modules/user/user.model";
type Subjects =
| InferSubjects<typeof Article | typeof User>
| Article
| User
| "all";
export type AppAbility = Ability<[Action, Subjects]>;
@Injectable()
export class CaslAbilityFactory {
createForUser(user: User) {
const { can, cannot, build } = new AbilityBuilder<AppAbility>(
Ability as AbilityClass<AppAbility>,
);
// this.logger.error(`createForUser: ${JSON.stringify(user)}`);
if (user.role === Roles.admin) {
can(Action.Manage, "all");
} else if (user.role === Roles.customer) {
can(Action.Read, Article);
cannot(Action.Read, Article, { isPublished: false }).because(
"can't read not published articles",
);
can(Action.Read, Article, {
isPublished: false,
authorId: user._id,
}).because("can read not published self articles");
}
can(Action.Update, Article, { authorId: user._id });
cannot(Action.Delete, Article, { isPublished: true }).because(
"can't delete published",
);
return build({
detectSubjectType: (item) =>
item.constructor as ExtractSubjectType<Subjects>,
});
}
}
casl-ability.factory.spec.ts
import { CaslAbilityFactory } from "./casl-ability.factory";
import { Article } from "../modules/article/article.model";
import { User } from "../modules/user/user.model";
import { Action, Roles } from "./casl.roles";
import { Logger } from "@nestjs/common";
describe("CaslAbilityFactory", () => {
it("should be defined", () => {
expect(new CaslAbilityFactory()).toBeDefined();
});
});
describe("Permissions", () => {
let user: User;
let own_article_published: Article;
let own_article_non_published: Article;
let article_non_published: Article;
let article_published: Article;
let ability;
const caslAbilityFactoryInstance = new CaslAbilityFactory();
describe("when user is an admin", () => {
beforeEach(() => {
user = {
_id: "61be2e1bf9c85c7510071a2f",
role: Roles.admin,
name: "jhon",
password: "123456",
email: "jhon@example.com",
username: "jhon",
avatar: "http://www.gravatar.com/avatar/1",
date: new Date("2021-12-18T18:53:15.895Z"),
};
ability = caslAbilityFactoryInstance.createForUser(user);
});
it("can do anything", () => {
expect(ability.can(Action.Manage, "all")).toBe(true);
});
});
describe("when user is a customer", () => {
beforeEach(() => {
user = {
_id: "61be2eeef9c85c7510071a32",
role: Roles.customer,
name: "jennifer",
password: "123456",
email: "jennifer@example.com",
username: "jennifer",
avatar: "http://www.gravatar.com/avatar/2",
date: new Date("2021-12-18T18:53:15.895Z"),
};
ability = caslAbilityFactoryInstance.createForUser(user);
own_article_published = {
articleName: "article1",
authorId: user._id,
isPublished: true,
};
own_article_non_published = {
articleName: "article2",
authorId: user._id,
isPublished: false,
};
article_non_published = {
articleName: "article3",
authorId: "61be30d3022cdfeb8a640793",
isPublished: false,
};
article_published = {
articleName: "article4",
authorId: "61be30d3022cdfeb8a640793",
isPublished: true,
};
});
it("can read published articles or non published self articles", () => {
// the first test is successful, it shouldn't return null but it does
expect(ability.relevantRuleFor(Action.Read, article_published)).toBe(
null,
);
expect(ability.can(Action.Read, article_published)).toBe(true);
expect(ability.can(Action.Read, own_article_non_published)).toBe(true);
expect(ability.can(Action.Read, own_article_published)).toBe(true);
expect(ability.can(Action.Read, article_non_published)).toBe(false);
});
});
});
Test results :
apparently , I’ not the only one who have this problem:
Seems like it works well with typeorm but not with mongoose , typegoose or prisma ?
- someone yesterday on official nestjs discord having problems with casl prisma (ability.can always false) demo: https://github.com/RobertCharron/minimal-project
- another issue on stack overflow: https://stackoverflow.com/questions/69381918/nestjs-casl-mongoose-casl-cannot-infer-subject-type-from-mongoose-schema
Issue Analytics
- State:
- Created 2 years ago
- Comments:10 (3 by maintainers)
Top Results From Across the Web
javascript - TYPEORM : Problem with condition on abilities casl
I want to allow update of SlotEntity only if user have a permission with the slot company, UserEntity have a relation OneToOne with ......
Read more >getjerry/nest-casl: Casl integration for NestJS - GitHub
Assuming authentication handled by AuthGuard. AccessGuard expects user to at least exist, if not authenticated user obtained from request acess will be denied....
Read more >Extensible and secure authorization with Nestjs and CASL
A secure and flexible authorization mechanism ensures not only granting proper privileges for a user but it can changing and revoking them ...
Read more >How to Manage User Access in NestJS - YouTube
In this video we'll take a deep dive into integrating CASL (authorization ABAC library) into the popular NestJS framework.
Read more >Authorization | NestJS - A progressive Node.js framework
Integrating CASL # · Admins can manage (create/read/update/delete) all entities · Users have read-only access to everything · Users can update their articles...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I have plans for that. But unfortunately, I don’t have enough free time to do this.
Maybe in Jan or in Feb I’ll do it
@Wenish I’m using prisma instead of mongoose now, I didn’t try Casl with prisma yet