Role Based Access Control with dynamic Roles
Asked Answered
V

1

7

We have a nest application that should implement rbac. I added a basic guard and decorator for it based on the guard docs: https://docs.nestjs.com/guards. The problem: This only allows static roles.

Our goal: In our case, we have a Contract entity. This contract should be loaded solely if the contract's contractor (<User>) or the contractor's supervisor (<User>) try to access it. I don't want to implement something like if (contract.contractor.id === user.id) and so on because we have lots of different cases which would make it a mess over time.

It looks like the following:

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

Of course, ROLES.SUPERVISOR is only a string which then gets matched with the user's static roles. So here's the question: How can I implement something like this with dynamic roles, like the supervisor role which is dynamic in context only for certain items.

Vortex answered 3/10, 2019 at 13:51 Comment(2)
Any results my friend :)Grieve
Were you able to find a solution?Bozuwa
C
0

You need to approach this conceptually first.

I think that you should write a method that would be capable to calculate what is a specific user for a specific contract.

Super simple example:

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

    return null;
}

In your guard, from context, you realize which user and contract request is about, utilize this method to get ROLE, and then compare the result with requested roles.

NOW, you explicitly said that you do not want to use this approach (to check for every single combination of user and contract).

I think that you should improve this getUserRoleForContract method implementation now, based on the knowledge you have about your entities' relationships.

From your question, I see CONTRACTOR and SUPERVISOR. This fit based on that (limited) information:

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

This would work under the assumption that every user relation on the contract is really ManyToOne and is named the same as the ROLE name, but lowercase (and the assumption is that ROLES enum values are the same uppercase strings).

All in all, I hope this helps and answers the question.

EDIT2: I fixed the URL this points to.

I have just put the answer here: What would be the best way to create an isAuthor guard for nestjs? . It covers a larger solution for RBAC in Nest.js (including this scenario).

Cytoplasm answered 27/9, 2022 at 18:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.