javascript - NestJs 基于用户属性和策略的 CASL 授权守卫

标签 javascript typescript authorization nestjs casl

我正在尝试在 NestJS 和 CASL 中为列表/获取端点实现一个基于通用策略的防护。
我正在关注这里的文档 https://docs.nestjs.com/security/authorization#integrating-casl .
一个用户可以是多个组的一部分。我需要检查 ArticleBooks拥有 groupId 的实体匹配任何用户组。并且仅当用户的组列表匹配 Article.id 时才允许用户阅读它们和 Books.id但是我无法得到它似乎正在工作

// User Object - from request.user after decoded from AuthGuard('jwt')

user = {
  groups: ['department-1', 'department-2']

}
// Article Entity
export class Article extends UUIDBase {
  @Column({ nullable: false })
  groupId: string

  @Column({ nullable: true })
  description: string

  @Column({ nullable: true })
  name: string
}
// Books Entity
export class Books extends UUIDBase {
  @Column({ nullable: false })
  groupId: string

  @Column({ nullable: true })
  description: string

  @Column({ nullable: true })
  name: string

  @Column({ nullable: true })
  description: string
}
// CASL-ability.factory.ts
type Subjects = typeof Articles | typeof User | Articles | User | 'all'

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

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

    console.log('USER IS ADMIN', user.groups)
    if (user.groups.includes(Groups.ADMIN)) {
      can(Action.Manage, 'all') // read-write access to everything
    } else {
      can(Action.Read, 'all') // read-only access to everything
    }

    return build()
  }
}
// @CheckPolicies decorator
interface IPolicyHandler {
  handle(ability: AppAbility): boolean
}

type PolicyHandlerCallback = (ability: AppAbility, can?, user?) => boolean

export declare type PolicyHandler = IPolicyHandler | PolicyHandlerCallback

import { SetMetadata } from '@nestjs/common'

export const CHECK_POLICIES_KEY = 'check_policy'
export const CheckPolicies: any = (...handlers: PolicyHandler[]) =>
  SetMetadata(CHECK_POLICIES_KEY, handlers)
// PoliciesGuard.ts

@Injectable()
export class PoliciesGuard implements CanActivate {
  constructor(
    private reflector: Reflector,
    private caslAbilityFactory: CaslAbilityFactory,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const policyHandlers =
      this.reflector.get<PolicyHandler[]>(
        CHECK_POLICIES_KEY,
        context.getHandler(),
      ) || []

    const { user } = context.switchToHttp().getRequest()
    const ability = this.caslAbilityFactory.createForUser(user)

    return policyHandlers.every(handler =>
      this.execPolicyHandler(handler, ability),
    )
  }

  private execPolicyHandler(handler: PolicyHandler, ability: AppAbility) {
    if (typeof handler === 'function') {
      return handler(ability)
    }
    return handler.handle(ability)
  }
}
我在文章 Controller 中像这样使用它
@ApiTags('Articles')
@Controller('Articles')
@UseGuards(AuthGuard('jwt'))
export class ArticlesController {
  constructor(public service: ArticlesService) {}
  @Get('/articles/:id')
  @UseGuards(PoliciesGuard)
  @CheckPolicies((ability: AppAbility) => {
    ability.can(Action.Read, Articles)
  })
  async listArticles(@Query() query, @Param() param) {
    return this.service.listArticles(query, param)
  }

  @Get('/articles/:id')
  @UseGuards(PoliciesGuard)
  @CheckPolicies((ability: AppAbility) => {
    ability.can(Action.Read, Articles)
  })
  async getArticle(@Param() params, @Query() query) {
    return this.service.getArticle(params, query)
  }

我在 Books Controller 中像这样使用它
@ApiTags('Books')
@Controller('Books')
@UseGuards(AuthGuard('jwt'))
export class BooksController {
  constructor(public service: BooksService) {}
  @Get('/books/:id')
  @UseGuards(PoliciesGuard)
  @CheckPolicies((ability: AppAbility) => {
    ability.can(Action.Read, Books)
  })
  async listBooks(@Query() query, @Param() param, @Body() body) {
    return this.service.listBooks(query, param, body)
  }

  @Get('/books/:id')
  @UseGuards(PoliciesGuard)
  @CheckPolicies((ability: AppAbility) => {
    ability.can(Action.Read, Books)
  })
  async getBooks(@Param() params, @Query() query) {
    return this.service.getBooks(params, query)
  }

最佳答案

尝试改变@CheckPolicies((ability: AppAbility) => {ability.can(Action.Read, Books)})通过在 ability.can... 之前添加返回或删除括号。
有了你所拥有的,该策略处理程序总是返回 undefined ,所以它总是给你一个 Unauthorized .

关于javascript - NestJs 基于用户属性和策略的 CASL 授权守卫,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65125367/

相关文章:

java - 我如何创建受方法安全保护的 DAO 的安全版本和不安全版本

java - Payload.getIssuee() 已弃用

javascript - 使用 JavaScript、JQuery 从网页中抓取评论

javascript - 如何使用 JADE 显示和访问各个键值

javascript - JQuery - 在没有 if 条件的情况下在选择选项中选择数据的随机代码生成器

javascript - 如何在输入更改后立即使用 javascript 调用函数?

TypeScript:用其他东西替换命名空间

typescript - typescript 界面中的 "constructor"

javascript - 在 Angular 6 中显示来自 api 的数据时出现问题

java - 如何更改生成 JWT token 的端点地址