Skip to content

Access control

We use CASL package to handle Access Control in our project.

On this page, we will explore the process of setting up CASL.

WARNING

Please read the CASL documentation carefully and grasp the basic concept of Access Control before proceeding.

Integrating CASL in the Application

You can find access control-related configuration in the src/plugins/casl directory.

  1. ability.js: This file will read the abilities from the user-permissions local storage and define the Ability instance using the defineAbility function. You can also add your custom static abilities here.

  2. CaslHelpers.js: This file provides can and canViewNavMenuGroup methods to check whether a user is authorized to perform a specific action.

Define Rules

File: ability.js

js
import { AbilityBuilder, defineAbility } from "@casl/ability";
import { jsonParser } from "@/@core/utils/helpers.js";

const userAbilities = jsonParser(localStorage.getItem("user-permissions"));
const generatedUserAbilities = new AbilityBuilder(userAbilities);

export default defineAbility((can, cannot) => {
  can("manage", "all"); // For Demo Purpose
  if (generatedUserAbilities && generatedUserAbilities.length) {
    generatedUserAbilities.map((item) => {
      can(item.action, item.subject);
    });
  }
});

To define your custom abilities, you can use the can method to add pairs of actions and subjects with varying conditions. This is especially useful if your rules are dynamic and stored in a database.

In CASL, manage and all are unique terms. All stands for any subject, and manage for any activity.

The user can still access all routes if they have the specified abilities, or you haven't defined the action and subject for the route.

can Method

You can use can method if you want show/hide content based on user's permissions:

vue

<template>
  <div v-if="can('remove', 'todo')">
    <button @click="removeTodo">Remove Todo</button>
  </div>
</template>
<script setup>
  import {useAbility} from "@casl/vue";

  const {can} = useAbility();

  const removeTodo = () => { /* logic to remove todo form */
  };

</script>

Update ability

Whenever a user logs in (and out), we must ensure the Ability instance updates with the new rules.

Create a new Ability instance

Suppose that the server returns the user with specific permissions on login:

js
import {useAbility} from "@casl/vue";

// successful API response 👇
const userAbilities = [
    {
        action: "create",
        subject: "Blog",
    }
];

// Use composable
const ability = useAbility();

// Update the ability using `update` method
ability.update(userAbilities);

WARNING

Ability updates will be lost on page refresh. One way to solve this is by using localStorage to store the data:

jsx
// successful API response 👇
const userAbilities = [
    {
        action: "create",
        subject: "Blog",
    }
];

// UseAbility Hook
const ability = useAbility(AbilityContext)

// Update the ability using `update` method and store it in local storage
ability.update(userAbilities);
localStorage.setItem("user-permissions", userAbilities)

The key name we use to store data in local storage is user-permissions.

If you prefer a different key name, ensure it matches the one used in the src/plugins/casl/abilities.js to access user abilities.

Resetting ability

To reset the ability on logout, pass an empty array to the ability update method.

js

// Use composable
const ability = useAbility()

// Update the ability using `update` method
ability.update([])

// Remove "userAbilities" from local storage
localStorage.removeItem("user-permissions", null )

Protected Routes

We have configured a beforeEach guard that monitors routing events across the application. When a user opens a new page it fires a route guard to check if a user is authenticated.

You can inspect src/router/index.js file to see our configured router.beforeEach hook.

To protect routes based on ability, add action and subject meta to the route using a route block. In this example, we are protecting the Article page based on defined abilities.

vue

<script setup>
<route>
{
  meta: {
  action: ["create", "read", "delete"],
  subject: "Article",
  }
}
</route>
</script>

<template>
  <p>Article Page</p>
</template>

Current Setting

To ensure flexibility and control over the content shown in our demo, we have assigned manage and all abilities to our Ability instance. Since there are no specific action and subject metadata for our routes, users can access all available routes

You can add your own action and subject meta for routes to add route protection.

manage and all are special keywords in CASL. manage represents any action and all represents any subject.

Side Navigation Access Control

This way you can restrict users' access to navigation menus based on their ability very easily.

All you have to do is navigate to the src/navigation/index.js file and add action, and subject properties to the nav item.

js

export default [
  {
    title: "Email",
    icon: "Sms",
    to: "apps/email",
    action: "read",
    subject: "email",
  },
]

Users can now see the Email item on the sidebar if they can read the email.

Visible or Hidden ?

Navigation groups are designed to automatically show or hide themselves based on their children. However, you can also specify resources and actions for them if desired.

Note

  • Hidden: This indicates that the ACL returned false, meaning the user does not have the ability to view the item.

  • Can be viewed: This indicates that the ACL returned true, allowing the user to view the item.

✅ A navigation group is visible if both the group and its sub-items can be viewed.

✅ A navigation group is hidden if the group can be viewed, but its sub-items are hidden.

✅ A navigation group is hidden if the group itself is hidden, regardless of whether its sub-items can be viewed or are hidden.

COPYRIGHT