具有动态角色的基于角色的访问控制

san*_*oco 7 javascript roles rbac typescript nestjs

我们有一个应该实现 rbac 的 Nest 应用程序。我根据守卫文档为其添加了一个基本的守卫和装饰器: https: //docs.nestjs.com/guards。问题:这仅允许静态角色。

我们的目标:在我们的例子中,我们有一个Contract实体。仅当合同的承包商 ( <User>) 或承包商的主管 ( <User>) 尝试访问该合同时,才应加载该合同。我不想实施诸如此类的事情,if (contract.contractor.id === user.id)因为我们有很多不同的情况,随着时间的推移,这会让事情变得一团糟。

它看起来像下面这样:

@Get()
@Roles(ROLES.ADMIN, ROLES.CONTRACTOR, ROLES.SUPERVISOR)
getContractById(...): Contract {
    return this.contractService.findById(...);
}
Run Code Online (Sandbox Code Playgroud)

当然,ROLES.SUPERVISOR只是一个字符串,然后与用户的静态角色进行匹配。所以问题是:如何使用动态角色来实现类似的功能,例如主管角色,它仅在某些项目的上下文中是动态的。

Gra*_*ent 0

您需要首先从概念上解决这个问题。

我认为您应该编写一个能够计算特定合约的特定用户是什么的方法。

超级简单的例子:

getUserRoleForContract(user: User, contract: Contract): ROLES {
    // your logic goes here
    if (contract.contractor.id === user.id) {
         return ROLES.CONTRACTOR;
    } else if (...) {...}

    return null;
}
Run Code Online (Sandbox Code Playgroud)

在你的守卫中,从上下文中,你知道哪个用户和合约请求是关于哪个用户和合约请求,利用此方法获取 ROLE,然后将结果与请求的角色进行比较。

现在,您明确表示您不想使用这种方法(检查用户和合同的每个组合)。

我认为您getUserRoleForContract现在应该根据您对实体关系的了解来改进此方法的实现。

从你的问题中,我看到承包商和主管。此拟合基于该(有限)信息:

getUserRoleForContract(user: User, contract: Contract): ROLES {
    const allAvailableRoles = [ROLES.CONTRACTOR, ROLES.SUPERVISOR];

    for (let role of allAvailableRoles) {
        if (contract[role.toLowerCase()].id === user.id) {
            return role;
    }

    return null;
}
Run Code Online (Sandbox Code Playgroud)

这将在假设合约上的每个用户关系实际上是 ManyToOne 且名称与 ROLE 名称相同但小写的情况下起作用(并且假设 ROLES 枚举值是相同的大写字符串)。

总而言之,我希望这对您有所帮助并回答问题。

EDIT2:我修复了 this 指向的 URL。

我刚刚将答案放在这里:What would be the best way to create an isAuthor Guard for Nestjs? 。它涵盖了 Nest.js 中 RBAC 的更大解决方案(包括此场景)。