What is @casl/ability?
@casl/ability is a powerful and flexible library for managing access control in JavaScript applications. It allows you to define what various users can or cannot do in your application, making it easier to implement role-based access control (RBAC) and other permission systems.
What are @casl/ability's main functionalities?
Define Abilities
This feature allows you to define what actions a user can or cannot perform on specific resources. In this example, the user can read articles but cannot delete them.
const { AbilityBuilder, Ability } = require('@casl/ability');
const { can, cannot, build } = new AbilityBuilder(Ability);
can('read', 'Article');
cannot('delete', 'Article');
const ability = build();
console.log(ability.can('read', 'Article')); // true
console.log(ability.can('delete', 'Article')); // false
Check Abilities
This feature allows you to check if a user has the ability to perform a specific action on a resource. The example shows how to check if a user can read or delete an article.
const { Ability } = require('@casl/ability');
const ability = new Ability([
{ action: 'read', subject: 'Article' },
{ action: 'delete', subject: 'Article', inverted: true }
]);
console.log(ability.can('read', 'Article')); // true
console.log(ability.can('delete', 'Article')); // false
Define Abilities with Conditions
This feature allows you to define abilities with specific conditions. In this example, the user can read articles only if they are published.
const { AbilityBuilder, Ability } = require('@casl/ability');
const { can, build } = new AbilityBuilder(Ability);
can('read', 'Article', { published: true });
const ability = build();
console.log(ability.can('read', { published: true })); // true
console.log(ability.can('read', { published: false })); // false
Integrate with Frontend Frameworks
This feature allows you to integrate @casl/ability with frontend frameworks like React. The example shows how to conditionally render a component based on the user's abilities.
import { Ability } from '@casl/ability';
import { Can } from '@casl/react';
const ability = new Ability([
{ action: 'read', subject: 'Article' }
]);
function App() {
return (
<Can I="read" a="Article" ability={ability}>
<p>You can read this article!</p>
</Can>
);
}
export default App;
Other packages similar to @casl/ability
accesscontrol
AccessControl is a Node.js module that provides a flexible and intuitive way to manage role-based access control (RBAC) and attribute-based access control (ABAC). It is similar to @casl/ability in that it allows you to define and check permissions, but it focuses more on roles and attributes.
rbac
RBAC is a simple and flexible role-based access control library for Node.js. It allows you to define roles and permissions and check if a user has a specific role or permission. Compared to @casl/ability, RBAC is more focused on role management and less on fine-grained permissions.
This package is the core of CASL. It includes logic responsible for checking and defining permissions.
Installation
npm install @casl/ability
pnpm install @casl/ability
yarn add @casl/ability
Documentation
This README file contains only basic information about the package. If you need an in depth introduction, please visit CASL's documentation.
Getting Started
Note: the best way to get started is to read the Guide in the official documentation. In this README file, you will find just basic information.
Note: all the examples below are written in ES6 using ES modules but CASL also has a sophisticated support for TypeScript, read CASL TypeScript support for details.
1. Define Abilities
Lets define Ability
for a blog website where visitors:
- can read blog posts
- can manage (i.e., do anything) own posts
- cannot delete a post if it was created more than a day ago
import { AbilityBuilder, createMongoAbility } from '@casl/ability';
import { User } from '../models';
function defineAbilitiesFor(user) {
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
can('read', 'BlogPost');
can('manage', 'BlogPost', { author: user.id });
cannot('delete', 'BlogPost', {
createdAt: { $lt: Date.now() - 24 * 60 * 60 * 1000 }
});
return build();
});
Do you see how easily business requirements were translated into CASL's rules?
And yes, Ability
class allow you to use some MongoDB operators to define conditions. Don't worry if you don't know MongoDB, it's not required and explained in details in Defining Abilities
2. Check Abilities
Later on you can check abilities by using can
and cannot
methods of Ability
instance.
import { BlogPost, ForbiddenError } from '../models';
const user = getLoggedInUser();
const ability = defineAbilitiesFor(user)
ability.can('read', 'BlogPost');
const post = new BlogPost({ title: 'What is CASL?' });
ability.cannot('read', post);
ForbiddenError.from(ability).throwUnlessCan('read', post);
Of course, you are not restricted to use only class instances in order to check permissions on objects. See Introduction for the detailed explanation.
3. Database integration
CASL has a complementary package [@casl/mongoose] which provides easy integration with MongoDB and [mongoose].
import { accessibleRecordsPlugin } from '@casl/mongoose';
import mongoose from 'mongoose';
mongoose.plugin(accessibleRecordsPlugin);
const user = getUserLoggedInUser();
const ability = defineAbilitiesFor(user);
const BlogPost = mongoose.model('BlogPost', mongoose.Schema({
title: String,
author: mongoose.Types.ObjectId,
content: String,
createdAt: Date,
hidden: { type: Boolean, default: false }
}))
const posts = await Post.accessibleBy(ability).where({ hidden: false });
const hiddenPosts = await Post.find({ hidden: true }).accessibleBy(ability);
const updatablePosts = await Post.accessibleBy(ability, 'update');
See Database integration for details.
4. Advanced usage
CASL is incrementally adoptable, that means you can start your project with simple claim (or action) based authorization and evolve it later, when your app functionality evolves.
CASL is composable, that means you can implement alternative conditions matching (e.g., based on joi, ajv or pure functions) and field matching (e.g., to support alternative syntax in fields like addresses.*.street
or addresses[0].street
) logic.
See Advanced usage for details.
5. Performance and computational complexity
CASL checks are quite fast, thanks to underlying rule index structure. The estimated complexity of different operations can be found below:
Operation | Complexity | Notes |
---|
Ability creation time | O(n) | n - amount of rules |
Check by action and subject type (e.g., ability.can('read', 'Todo') ) | O(1) | |
Check by action and subject object (e.g., ability.can('read', todo) ) | O(m + k) + O(p) | m - amount of rules for the same pair of action and subject; k - amount of operators in conditions; O(p) - complexity of used operators (e.g., $in implementation is more complex than $lt ) |
Want to help?
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing
License
MIT License