Single-Role Scenario

In a Single-Role Scenario, each user in your application is assigned one role, and permissions are granted based on that role. This scenario is simpler because the user's role defines their access to resources without involving multiple roles or complex combinations.

Example Setup

Let's imagine you have a file where the authentication user roles for your application are defined. Here's how that could look:

export enum UserRoles {
  Admin = "admin",
  User = "user",
  Moderator = "moderator",
  BetaTester = "betaTester",
}

export interface AuthUser {
  userId: string;
  role: UserRoles;
  // other user properties
}

In this example, the AuthUser interface defines the authenticated user, including their role which is one of the UserRoles.

Next, let's define a resource model, say ToDoModel:

export type ToDoModel = {
  authorId: string;
  title: string;
  description: string;
};

Now, let’s consider how we structure the permissions for this model. Here’s how you would organize the access control:

access-control
├── permissions.config.ts
└── index.ts

In permissions.config.ts, you'll define the structure of your permissions for different resources. For instance:

// access-control/permissions.config.ts

import { ResourceConfig } from "rolebaker";

export interface MyResourceConfig extends ResourceConfig {
  resources: {
    todos: {
      dataType: ToDoModel;
      action: "read" | "write" | "delete";
    };
    betaResource: {
      dataType: string;
      action: "view";
    };
  };
}

Here, MyResourceConfig extends ResourceConfig and defines two resources: todos (representing a to-do list) and betaResource (a special resource only for betaTester roles). Each resource is associated with actions (like read, write, delete for todos or view for betaResource).

Assigning Permissions

Now, in index.ts, you’ll specify which users can perform which actions on these resources. Here’s how you can configure it:

// access-control/index.config.ts
import { MyResourceConfig } from "./permissions.config.ts";
import { bakeAuthorization } from "rolebaker";
import { UserRoles, AuthUser } from "your-auth-file";

export const { hasPermission } = bakeAuthorization<
  UserRoles,
  AuthUser,
  MyResourceConfig
>({
  userRoleMode: "singleRole", // This defines that users can only have one role at a time
  permissionsConfig: {
    admin: {
      todos: { read: true, write: true, delete: true },
    },
    moderator: {
      todos: { read: true, write: false, delete: false },
    },
    user: {
      todos: {
        read: true,
        write: false,
        delete: {
          checkFunction: (authUser, todo) => todo?.authorId === authUser.userId, // Allows a user to delete only their own todo
        },
      },
    },
    betaTester: {
      betaResource: { view: true }, // Only beta testers can view this resource
    },
  },
});

Permissions Configuration

Here’s what’s happening:

  • Admin can read, write, and delete todos.
  • Moderator can only read todos and cannot write or delete.
  • User can only read todos and has restricted delete permissions (they can only delete their own todos).
  • BetaTester can view the special betaResource but has no access to todos.

Has Permission?

The hasPermission function checks if a user has the right to perform a specific action on a resource. It takes the user, resource, and action as arguments and returns a boolean or evaluates custom logic for permission checks.

export const { hashPermission} = bakeAuthorization<...>(...)

// Example
const authorUser: AuthUser = { role: UserRoles.User, userId: "user1" };
const resourceData: ToDoModel = {
    authorId: "user1",
    title: "title",
    description: "description",
};
const isAllowed = hasPermission(authorUser, "todos", "delete", resourceData) // true