# [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