Appearance
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.
ability.js
: This file will read the abilities from theuser-permissions
local storage and define theAbility
instance using thedefineAbility
function. You can also add your custom static abilities here.CaslHelpers.js
: This file providescan
andcanViewNavMenuGroup
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
andall
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
andsubject
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
andall
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.