# [CASL Ability](https://stalniy.github.io/casl/) [![@casl/ability NPM version](https://badge.fury.io/js/%40casl%2Fability.svg)](https://badge.fury.io/js/%40casl%2Fability) [![](https://img.shields.io/npm/dm/%40casl%2Fability.svg)](https://www.npmjs.com/package/%40casl%2Fability) [![CASL Documentation](https://img.shields.io/badge/documentation-available-brightgreen.svg)](https://stalniy.github.io/casl/) [![CASL Join the chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/stalniy-casl/casl) This package is the core of CASL. It includes logic responsible for [checking and defining][intro] permissions. ## Installation ```sh npm install @casl/ability # or pnpm install @casl/ability # or 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](https://casl.js.org/). ## Getting Started **Note**: the best way to get started is to read the [Guide][intro] 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][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 ```js 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) { 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? 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][define-abilities] ### 2. Check Abilities Later on you can check abilities by using `can` and `cannot` methods of `Ability` instance. ```js import { BlogPost, ForbiddenError } from '../models'; const user = getLoggedInUser(); // app specific function const ability = defineAbilitiesFor(user) // true if ability allows to read at least one Post ability.can('read', 'BlogPost'); // true if there is no ability to read this particular blog post const post = new BlogPost({ title: 'What is CASL?' }); ability.cannot('read', post); // you can even throw an error if there is a missed ability 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][intro] for the detailed explanation. ### 3. Database integration CASL has a complementary package [@casl/mongoose] which provides easy integration with MongoDB and [mongoose]. ```js 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 Post.accessibleBy(ability).where({ hidden: false }); // you can also call it on existing query to enforce permissions const hiddenPosts = await Post.find({ hidden: true }).accessibleBy(ability); // you can even pass the action as a 2nd parameter. By default action is "read" const updatablePosts = await Post.accessibleBy(ability, 'update'); ``` See [Database integration][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][advanced-usage] for details. [joi]: https://www.npmjs.com/package/@hapi/joi [ajv]: https://www.npmjs.com/package/ajv ### 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](http://www.opensource.org/licenses/MIT) [contributing]: https://github.com/stalniy/casl/blob/master/CONTRIBUTING.md [define-abilities]: https://stalniy.github.io/casl/en/guide/define-rules [intro]: https://stalniy.github.io/casl/en/guide/intro [database-integration]: https://stalniy.github.io/casl/en/package/casl-mongoose [advanced-usage]: https://stalniy.github.io/casl/en/advanced/customize-ability [typescript-support]: https://stalniy.github.io/casl/en/advanced/typescript