learn react ui logoLearnReactUI
Role Based Access Control (Permission)

Role Based Access Control (Permission) Example

This example explores how to implement Role-Based Access Control (RBAC) in a React application using CASL. Through a ToDo app example, it demonstrates defining roles, resources, and actions, creating permission structures, and using CASL's Ability to enforce rules. You'll also learn how to wrap components with Can for fine-grained control and dynamically update permissions based on user roles.

I’ve already talked about authorization models and authorization with React CASL. In this post, I’d like to deepen this topic a bit more

In my previous 2 blog posts, I explained Authorization Models and how we can do Routing level authorization with React CASL.

Authorization Models React Project Architecture 19 — (Authorization)

In this example, I will explain how we can authorize the components of the application through ToDO App.

Demo SS

[Demo])

1. Code Part

In this example we have users in different roles using our ToDo application, it is actually similar to the example in the previous post;

export const AUTHORIZATION_ROLES = {
  SUPER_ADMIN: "SuperAdmin",
  ADMIN: "Admin ",
  STANDART_USER: "User",
};

And let’s define the Subject/Resource that this will work on

export const AUTHORIZATION_RESOURCES = {
  TODO: "Todo",
};

And let’s list the operations we can perform on this Resource.

export const AUTHORIZATION_ACTIONS = {
  CREATE: "create",
  READ: "read",
  UPDATE: "update",
  DELETE: "delete",
};

What I’m going to do next is to create the Ability/Permission Structure. I’m here

  • Super Admin → All Skills
  • Admin → Not Only Delete Ability
  • User → Read Only Ability..
import { AbilityBuilder, Ability, AbilityClass } from "@casl/ability";
import { AUTHORIZATION_ROLES as roles } from "./roles";
import { AUTHORIZATION_ACTIONS as actions } from "./actions";
import { AUTHORIZATION_RESOURCES as resources } from "./resources";

export type AppAbility = Ability<[Actions, Subjects]>;
export const appAbility = Ability as AbilityClass<AppAbility>;

export default function defineRulesFor(role: string) {
  const { can, rules } = new AbilityBuilder(appAbility);

  if (role === roles.SUPER_ADMIN) {
    can(actions.DELETE, resources.TODO);
    can(actions.CREATE, resources.TODO);
    can(actions.UPDATE, resources.TODO);
    can(actions.READ, resources.TODO);
  } else if (role === roles.ADMIN) {
    can(actions.CREATE, resources.TODO);
    can(actions.UPDATE, resources.TODO);
    can(actions.READ, resources.TODO);
  } else if (role === roles.STANDART_USER) {
    can(actions.READ, resources.TODO);
  }

  return rules;
}

export function buildAbilityFor(role: string): AppAbility {
  return new appAbility(defineRulesFor(role), {
    // https://casl.js.org/v5/en/guide/subject-type-detection
    detectSubjectType: (object) => object!.type,
  });
}

Now after these we wrap them with buttons or the Can or Cannot ability on the components we want.

<Can do={AUTHORIZATION_ACTIONS.DELETE} on={AUTHORIZATION_RESOURCES.TODO}>
  <ODButtons.DeleteBulkButton
    entityName="Task"
    propName="Taskname"
    selectedEntities={selectedEntities}
    handleDeleteBulk={(deleteTasksIds: string[]) => {
      mutation.mutate(deleteTasksIds);
    }}
  />
</Can>

Of course, don’t forget to update the Ability when you change roles.

    const ability = useContext(AbilityContext);
    ability.update(defineRulesFor(updatedRole);