CASL is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access
CASL (pronounced /ˈkæsəl/, like castle) is an isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access. It's designed to be incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization. It makes it easy to manage and share permissions across UI components, API services, and database queries.
Heavily inspired by cancan.
| Project | Status | Description | Supported envinronemnts |
|-------------------|--------------------------------------|-------------|-------------------------|
| @casl/ability | | CASL's core package | nodejs 8+ and ES5 compatible browsers (IE 9+) |
| @casl/mongoose |
| integration with Mongoose | nodejs 8+ |
| @casl/angular |
| integration with Angular | IE 9+ |
| @casl/react |
| integration with React | IE 9+ |
| @casl/vue |
| integration with Vue | IE 11+ (uses
WeakMap) | | @casl/aurelia |
WeakMap) |
A lot of detailed information about CASL, integrations and examples can be found in documentation.
Ask it in chat or on stackoverflow. Please don't ask questions in issues, the issue list of this repo is exclusively for bug reports and feature requests. Questions in the issue list may be closed immediately without answers.
CASL operates on the abilities level, that is what a user can actually do in the application. An ability itself depends on the 4 parameters (last 3 are optional):
prolong,
read). Very often it will be a list of words from CRUD -
create,
read,
updateand
delete.
Subscription,
BlogPost,
User).
BlogPost)
hiddenfield of
BlogPostbut not update
descriptionor
title)
Using CASL you can describe abilities using regular and inverted rules. Let's see how
Note: all the examples below will be written in TypeScript but CASL can be used in similar way in ES6+ and Nodejs environments.
Lets define
Abilityfor 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, Ability } from '@casl/ability' import { User } from '../models'; // application specific interfaces/**
@param user contains details about logged in user: its id, name, email, etc
/ function defineAbilitiesFor(user: User) { const { can, cannot, rules } = new AbilityBuilder(Ability);
// can read blog posts can('read', 'BlogPost'); // can manage (i.e., do anything) own posts can('manage', 'BlogPost', { author: user.id }); // cannot delete a post if it was created more than a day ago cannot('delete', 'BlogPost', { createdAt: { $lt: Date.now() - 24 * 60 * 60 * 1000 } });
return new Ability(rules); });
Do you see how easily business requirements were translated into CASL's rules?
Note: you can use class instead of string as a subject type (e.g.,
can('read', BlogPost))
And yes,
Abilityclass 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
Later on you can check abilities by using
canand
cannotmethods of
Abilityinstance.
// in the same file as above import { ForbiddenError } from '@casl/ability';const user = getLoggedInUser(); // app specific function const ability = defineAbilitiesFor(user);
class BlogPost { // business entity constructor(props) { Object.assign(this, props); } }
// true if ability allows to read at least one Post ability.can('read', 'BlogPost'); // the same as ability.can('read', BlogPost);
// true, if user is the author of the blog post ability.can('manage', new BlogPost({ author: user.id }));
// true if there is no ability to read this particular blog post const ONE_DAY = 24 * 60 * 60 * 1000; const postCreatedNow = new BlogPost({ createdAt: new Date() }); const postCreatedAWeekAgo = new BlogPost({ createdAt: new Date(Date.now() - 7 * ONE_DAY) });
// can delete if it's created less than a day ago ability.can('delete', postCreatedNow); // true ability.can('delete', postCreatedAWeekAgo); // false
// you can even throw an error if there is a missed ability ForbiddenError.from(ability).throwUnlessCan('delete', postCreatedAWeekAgo);
Of course, you are not restricted to use only class instances in order to check permissions on objects. See Introduction for the detailed explanation.
CASL has a complementary package @casl/mongoose which provides easy integration with MongoDB and mongoose.
import { AbilityBuilder } from '@casl/ability'; import { accessibleRecordsPlugin } from '@casl/mongoose'; import mongoose from 'mongoose';mongoose.plugin(accessibleRecordsPlugin);
const user = getUserLoggedInUser(); // app specific function
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 } }))
// returns mongoose Query, so you can chain it with other conditions const posts = await BlogPost.accessibleBy(ability).where({ hidden: false });
// you can also call it on existing query to enforce permissions const hiddenPosts = await BlogPost.find({ hidden: true }).accessibleBy(ability);
// you can even pass the action as a 2nd parameter. By default action is "read" const updatablePosts = await BlogPost.accessibleBy(ability, 'update');
See Database integration for details.
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.*.streetor
addresses[0].street) logic.
See Advanced usage for details.
Looking for examples? Check CASL examples repository.
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on guidelines for contributing.
If you'd like to help us sustain our community and project, consider to become a financial contributor on Open Collective
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
Copyright (c) 2017-present, Sergii Stotskyi