我如何访问 CASL 条件的嵌套属性

问题描述

我似乎无法使用条件规则访问嵌套对象。 如果文章评论用户具有相同的 ID,我希望用户有权删除文章。 这些只是一些要测试的组成类...

这是我的代码

import { defineAbility,AbilityBuilder } from '@casl/ability';

import { Ability,AbilityClass,ExtractSubjectType,InferSubjects } from '@casl/ability';

export class Article {
    static readonly modelName = "Article";
    static readonly __typename = "Article";
    constructor(  id: number,title: string,content: string,user: User,comment: Comment) {
            this.id = id
            this.title = title
            this.content = content
            this.user = user
            this.comment = comment
        }
  id: number
  title: string
  content: string
  user: User
  comment: Comment
}

export class User {
    static readonly modelName = "User"
    static readonly __typename = "User";
    constructor (id: number,name: string,comment: Comment) {
            this.id = id
            this.name = name
            this.comment = comment
        }
  id: number
  name: string
  comment: Comment
}

export class Comment {
    static readonly modelName = "Comment"
    static readonly __typename = "Comment";
  constructor(id: number,authorId: number) {
        this.id = id
        this.content = content
        this.authorId = authorId
    }
    id: number
  content: string
  authorId: number
}

type Action = 'create' | 'read' | 'update' | 'delete';
type Subjects = InferSubjects<typeof Article | typeof Comment| typeof User,true>;

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


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

    can('delete',Article,{ comment: {id: user.comment.id}})

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

我正在测试它:

const comment = new Comment(0,'a',0)
const user = new User(1,'sd',comment);
const article = new Article(2,'asd',user,comment)
const ability = createForUser(user);

console.log(ability.can('delete',article))// false

我在某处看到我需要做这样的事情: can('delete',{ 'comment.id': user.comment.id}) 但是当我说'对象文字可能只指定已知属性,''comment.id''不存在于'string[]'类型

解决方法

您可以发现 https://casl.js.org/v5/en/advanced/typescript 上的“带点符号的嵌套字段”部分很有用。总之,当你使用for notation和typescript一起定义条件时,你需要创建一个自定义类型:

type FlatArticle = Article & {
  'comment.id': Article['comment']['id']
};

can<FlatArticle>('read',Article,{ 'comment.id': 1 });
,

最终是这样解决的:

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

export type Subject = InferSubjects<typeof Article | 
                                     typeof Comment | 
                                     typeof User | 
                                     'all';

export type AppAbility = PureAbility<[Action,Subject]>;
export const AppAbility = Ability as AbilityClass<AppAbility>;

然后像这样定义规则:

createForUser(user: User) {
  const { can,cannot,build } = new AbilityBuilder(AppAbility);
  
  
  can(Action.Read,{ 'comment.id': 1 });

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