question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

integrating CASL in NestJs not working (only works with typeorm)

See original GitHub issue

I 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 :

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 ?

Discord_H5h0uJqpeR

msedge_oSBfa8t24y

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:10 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
stalniycommented, Dec 25, 2021

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

0reactions
thorizercommented, Feb 10, 2022

@Wenish I’m using prisma instead of mongoose now, I didn’t try Casl with prisma yet

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found