Search code examples
node.jsmongodbtypescriptmongoose-schemauser-permissions

what is the best way to build multiple permission group on mongodb?


I started a complex project management application and I have the challenge of building resource permissions management for different types of user profiles.

My challenge is:

User story

  • John is an user with a common user profile.
  • John creates a project in the application.
  • John creates several tasks and adds them to the project.
  • John adds an user responsible for each task.
  • Added users must have access to the project and the tasks to which they have been added.
  • John creates a specific task and adds it as a subtask to one of the project's tasks.
  • In this subtask John adds an user as responsible, automatically that user must have access to the subtask, the task and the project.
  • And at any time, John can restrict access to a project resource, such as defining that a specific user can only view tasks

The way I started. I created a specification pattern for each use case, where I inform the variables and it returns a true or false answer.

However, I have to request for each resource and, in my opinion, this is not performative. What I mentioned is one of the simplest cases, there are others that are more complex.


  canEditTaskOnProject(): boolean {
    if (!this.project) {
      console.error(
        `Project not provided on ${TaskPermission.name}.${this.canEditTaskOnProject.name}`
      );
      return false;
    }
    return new ProjectLeader(this.project, this.userId)
      .or(new Creator(this.task, this.userId))
      .or(new FullAccessTaskPermission(this.project, this.userId))
      .or(new TaskResponsible(this.task, this.userId))
      .or(
        new RestrictTaskPermission(this.project, this.userId).and(
          new Creator(this.task, this.userId).or(
            new TaskResponsible(this.task, this.userId)
          )
        )
      )
      .or(
        new ReadAndWriteTaskPermission(this.project, this.userId).and(
          new TaskResponsible(this.task, this.userId)
        )
      )
      .isSatisfiedBy(this.userId);
  }

I would very much like suggestions from experienced people who have already done something similar. I am a beginner in the area and in the company where I work, there are no seniors.

Thank you in advance!


Solution

  • I found the best way using casl:

     import { defineAbility } from '@casl/ability';
    
     export default defineAbility((can, cannot) => {
      can('read', 'Article'); 
      cannot('read', 'Article', { published: false }); // inverted rule
     });