首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NestJS + CASL +猫鼬: CASL不能从猫鼬模式推断主语类型

NestJS + CASL +猫鼬: CASL不能从猫鼬模式推断主语类型
EN

Stack Overflow用户
提问于 2021-09-29 18:48:37
回答 1查看 1.1K关注 0票数 2

上下文

我使用Mongoose和NestJS定义了一个NestJS模式:

代码语言:javascript
复制
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';

export type CatDocument = Cat & Document;

@Schema()
export class Cat {
  @Prop({ required: true })
  name: string;

  @Prop({ required: true })
  breed: string;

  @Prop({ required: true })
  createdBy: string;

  // Other fields...
}

export const CatSchema = SchemaFactory.createForClass(Cat);

我还定义了一个使用CASL处理API授权的能力工厂:

代码语言:javascript
复制
import {
  Ability,
  AbilityBuilder,
  AbilityClass,
  ExtractSubjectType,
  InferSubjects,
} from '@casl/ability';
import { Injectable } from '@nestjs/common';
import { Cat } from '../cats/schemas/cat.schema';
import { User } from '../users/models/user.model';

type Subjects = InferSubjects<typeof Cat | typeof User> | 'all';

export enum Action {
  Manage = 'manage',
  Create = 'create',
  Read = 'read',
  Update = 'update',
  Delete = 'delete',
}

export type AppAbility = Ability<[Action, Subjects]>;

@Injectable()
export class CaslAbilityFactory {
  createForUser(user: User) {
    const { can, build } = new AbilityBuilder<
      Ability<[Action, Subjects]>
    >(Ability as AbilityClass<AppAbility>);

    can(Action.Read, Cat);
    can(Action.Create, Cat);
    can(Action.Update, Cat, {
      createdBy: user.id,
    });

    if (user.isAdmin()) {
      can(Action.Manage, 'all');
    }

    return build({
      detectSubjectType: (item) =>
        item.constructor as ExtractSubjectType<Subjects>,
    });
  }
}

问题

当我试图检查服务中的权限时(注意:用户不是管理员):

代码语言:javascript
复制
import {
  ForbiddenException,
  Injectable,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { EditCatInput } from './dto/edit-cat.input';
import { Cat, CatDocument } from './schemas/cat.schema';
import { Action, CaslAbilityFactory } from '../casl/casl-ability.factory';
import { User } from '../users/models/user.model';

@Injectable()
export class CatsService {
  constructor(
    private readonly configService: ConfigService,
    private readonly caslAbilityFactory: CaslAbilityFactory,
    @InjectModel(Cat.name) private catModel: Model<CatDocument>,
  ) {}

  // Other methods...

  async update(id: string, input: EditCatInput, user: User): Promise<Cat> {
    const updatedCat = await this.catModel
      .findOne({
        _id: { $eq: id },
        deleted: { $eq: false },
      })
      .exec();
    const ability = this.caslAbilityFactory.createForUser(user);
    if (!ability.can(Action.Update, updatedCat)) {
      throw new ForbiddenException(
        'You cannot edit this cat.',
      );
    }
    Object.assign(updatedCat, input);
    await updatedCat.save();

    return updatedCat;
  }
}

ability.can(Action.Update, cat)总是返回false,而应该是true

更多信息

当用户不是管理员时,ability.can为任何操作返回false

我的猜测是,CASL无法使用能力生成器的Cat接口中的InferSubject类推断出我的Mongoose模型的主题类型。但我不知道该用哪一门课来正确推断主语类型。

我错过了什么?

EN

回答 1

Stack Overflow用户

发布于 2022-02-08 18:31:21

因此,关键是在casl能力工厂中通过嵌套js注入猫模型。所以像这样的工厂应该能运作:

代码语言:javascript
复制
import {
    Ability,
    AbilityBuilder,
    AbilityClass,
    ExtractSubjectType,
    InferSubjects,
  } from '@casl/ability';
  import { Injectable } from '@nestjs/common';
  import { InjectModel } from '@nestjs/mongoose';
  import { Model } from 'mongoose';
  import { Cat, CatDocument } from '../cats/schemas/cat.schema';
  import { User } from '../users/models/user.model';
  
  export enum Action {
    Manage = 'manage',
    Create = 'create',
    Read = 'read',
    Update = 'update',
    Delete = 'delete',
  }
  
  @Injectable()
  export class CaslAbilityFactory {
    constructor(
        @InjectModel(Cat.name)
        private catModel: Model<CatDocument>,
    ) {}
    createForUser(user: User) {
      const { can, build } = new AbilityBuilder(
        Ability as AbilityClass<
          Ability<[Action, InferSubjects<typeof this.catModel> | 'all']>
        >,
      );
  
      can(Action.Read, this.catModel);
      can(Action.Create, this.catModel);
      can(Action.Update, this.catModel, {
        createdBy: user.id,
      });
  
      if (user.isAdmin()) {
        can(Action.Manage, 'all');
      }
  
      return build({
        detectSubjectType: (item) =>
          item.constructor as ExtractSubjectType<InferSubjects<typeof this.catModel> | 'all'>,
      });
    }
  }

完整示例项目:https://github.com/Wenish/nestjs-mongoose-casl

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69381918

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档