【问题标题】:NestJS + CASL + Mongoose: CASL cannot infer subject type from Mongoose SchemaNestJS + CASL + Mongoose:CASL 无法从 Mongoose Schema 推断主题类型
【发布时间】:2021-09-29 18:48:37
【问题描述】:

上下文

我已经使用 Mongoose 和 NestJS 定义了一个 Cat 架构:

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 授权的能力工厂:

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>,
    });
  }
}

问题

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

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 无法使用能力构建器的InferSubject 接口中的Cat 类来推断我的 Mongoose 模型的主题类型。但我不知道使用哪个类来正确推断主题类型。

我错过了什么?

【问题讨论】:

  • 自从这个问题发布以来,我已经迁移到 TypeORM,现在功能可以正常工作了。
  • 有人对此提出异议吗?

标签: mongoose authorization nestjs casl


【解决方案1】:

所以关键是在casl能力工厂中注入cat模型而不是nest js注入。 所以像这样的工厂应该可以工作:

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'>,
      });
    }
  }

【讨论】:

    猜你喜欢
    • 2022-01-24
    • 1970-01-01
    • 2014-12-27
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    相关资源
    最近更新 更多