### CONTRIBUTING # Contributing to CASL I would love for you to contribute to CASL and help make it even better than it is today! As a contributor, here are the guidelines I would like you to follow: - [Question or Problem?](#question) - [Issues and Bugs](#issue) - [Feature Requests](#feature) - [Submission Guidelines](#submit) - [Coding Rules](#rules) - [Commit Message Guidelines](#commit) ## Got a Question or Problem? Do not open issues for general support questions as I want to keep GitHub issues for bug reports and feature requests. You've got much better chances of getting your question answered on [support chat][chat] or [stackoverflow](https://stackoverflow.com/questions/tagged/casl). ## Found a Bug? If you find a bug in the source code, you can help by [submitting an issue](#submit-issue) or even better, you can [submit a Pull Request](#submit-pr) with a fix. ## Missing a Feature? You can *request* a new feature by [submitting an issue](#submit-issue) to this GitHub Repository. If you would like to *implement* a new feature, please submit an issue with a proposal for your work first, to be sure that somebody else hasn't started to do the same. ## Submission Guidelines ### Submitting an Issue Before you submit an issue, please search the issue tracker, maybe an issue for your problem already exists and the discussion might inform you of workarounds readily available. Please provide steps to reproduce for found bug (using http://plnkr.co, https://codesandbox.io/, https://repl.it/ or similar), this will help to understand and fix the issue faster. ### Submitting a Pull Request (PR) Before you submit your Pull Request (PR) consider the following guidelines: * Search [GitHub](https://github.com/stalniy/casl/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate effort. * Fork the project and setup it (CASL uses [pnpm](https://pnpm.js.org/) for monorepo management): ```sh # replace ${YOUR_GITHUB_USER_NAME} with your github username git clone git@github.com:${YOUR_GITHUB_USER_NAME}/casl.git # install pnpm, other ways at https://pnpm.js.org/en/installation npx pnpm add pnpm cd casl pnpm i -r pnpm run -r build # build all packages so local deps can be linked ``` * Make your changes in a new git branch (fork master branch): ```sh git checkout -b my-fix-branch master ``` * **include appropriate test cases**. * Follow defined [Coding Rules](#rules). * Run all test suites `pnpm run -r test` * Commit your changes using a descriptive commit message that follows defined [commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages using [semantic-release](https://semantic-release.gitbook.io/semantic-release/). * Push your branch to GitHub: ```sh git push origin my-fix-branch ``` * In GitHub, send a pull request to `casl:master`. * If somebody from project contributors suggest changes then: * Make the required updates. * Re-run all test suites to ensure tests are still passing. * Rebase your branch and force push to your GitHub repository (this will update your Pull Request). Basically you can use `git commit -a --amend` and `git push --force origin my-fix-branch` in order to keep single commit in the feature branch. That's it! Thank you for your contribution! ## Coding Rules To ensure consistency throughout the source code, keep these rules in mind as you are working: * All features or bug fixes **must be tested** by one or more specs (unit-tests). * All public API methods **must be documented** in docs-src/src/content/pages. * Project follows recommended eslint with [some exceptions](packages/dx/config/eslint.config.mjs). All these will be checked by CI when you submit a PR ## Commit Message Guidelines The project have very precise rules over how git commit messages can be formatted. This leads to **more readable messages** that are easy to follow when looking through the **project history**. But also, git history is used to **generate the change log**. The commit message format is borrowed from Angular projects and you can find [more details in this document][commit-message-format] [commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit# [github]: https://github.com/stalniy/casl [chat]: https://github.com/stalniy/casl/discussions --- ### README # CASL ## Do you like this package? [Support Ukraine 🇺🇦](https://prytulafoundation.org/en/home/support_page)

CASL logo [](https://opencollective.com/casljs) [](https://codecov.io/gh/stalniy/casl) [](https://github.com/stalniy/casl/discussions)

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](https://github.com/CanCanCommunity/cancancan). ## Features * **Versatile**\ An incrementally adoptable and can easily scale between a simple claim based and fully featured subject and attribute based authorization. * **Isomorphic**\ Can be used on frontend and backend and complementary packages make integration with major Frontend Frameworks and Backend ORMs effortless * **TypeSafe**\ Written in TypeScript, what makes your apps safer and developer experience more enjoyable * **Tree shakable**\ The core is only 6KB mingzipped and can be even smaller! * **Declarative**\ Thanks to declarative rules, you can serialize and share permissions between UI and API or microservices ## Ecosystem | Project | Status | Description | Supported environments | |-------------------|--------------------------------------|-------------|-------------------------| | [@casl/ability] | [![@casl/ability-status]][@casl/ability-package] | CASL's core package | Node.js 18+, browsers IE 11+ (uses `Map`/`Set`) | | [@casl/mongoose] | [![@casl/mongoose-status]][@casl/mongoose-package] | integration with [Mongoose][mongoose] | Node.js 20 | | [@casl/prisma] | [![@casl/prisma-status]][@casl/prisma-package] | integration with [Prisma][prisma] | Node.js 20+ | | [@casl/angular] | [![@casl/angular-status]][@casl/angular-package] | integration with [Angular][angular] | Node.js 20+, browsers IE 11+ | | [@casl/react] | [![@casl/react-status]][@casl/react-package] | integration with [React][react] | Node.js 20+, browsers IE 11+ | | [@casl/vue] | [![@casl/vue-status]][@casl/vue-package] | integration with [Vue][vue] | Node.js 20+, browsers with `Symbol` support | [@casl/ability]: packages/casl-ability [@casl/mongoose]: packages/casl-mongoose [@casl/prisma]: packages/casl-prisma [@casl/angular]: packages/casl-angular [@casl/react]: packages/casl-react [@casl/vue]: packages/casl-vue [@casl/ability-status]: https://img.shields.io/npm/v/@casl/ability.svg [@casl/mongoose-status]: https://img.shields.io/npm/v/@casl/mongoose.svg [@casl/prisma-status]: https://img.shields.io/npm/v/@casl/prisma.svg [@casl/angular-status]: https://img.shields.io/npm/v/@casl/angular.svg [@casl/react-status]: https://img.shields.io/npm/v/@casl/react.svg [@casl/vue-status]: https://img.shields.io/npm/v/@casl/vue.svg [@casl/ability-package]: https://www.npmjs.com/package/@casl/ability [@casl/mongoose-package]: https://www.npmjs.com/package/@casl/mongoose [@casl/prisma-package]: https://www.npmjs.com/package/@casl/prisma [@casl/angular-package]: https://www.npmjs.com/package/@casl/angular [@casl/react-package]: https://www.npmjs.com/package/@casl/react [@casl/vue-package]: https://www.npmjs.com/package/@casl/vue ## Documentation A lot of detailed information about CASL, integrations and examples can be found in [documentation]. ## Have a question? Ask it in [support chat][support-chat] or on [stackoverflow](https://stackoverflow.com/questions/tagged/casl). 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 crash course 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): 1. User Action\ Describes what user can actually do in the app. User action is a word (usually a verb) which depends on the business logic (e.g., `prolong`, `read`). Very often it will be a list of words from CRUD - `create`, `read`, `update` and `delete`. 2. Subject\ The subject or subject type which you want to check user action on. Usually this is a business (or domain) entity name (e.g., `Subscription`, `BlogPost`, `User`). 3. Conditions\ An object or function which restricts user action only to matched subjects. This is useful when you need to give a permission on resources created by a user (e.g., to allow user to update and delete own `BlogPost`) 4. Fields\ Can be used to restrict user action only to matched subject's fields (e.g., to allow moderator to update `hidden` field of `BlogPost` but not update `description` or `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. ### 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 ```ts import { AbilityBuilder, createMongoAbility } 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, build } = new AbilityBuilder(createMongoAbility); // 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 build(); }); ``` 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, `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 // 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][intro] for the detailed explanation. ### 3. Database integration CASL has a complementary package [@casl/mongoose] which provides easy integration with MongoDB and [mongoose]. ```ts 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][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. Examples Looking for examples? Check [CASL examples](https://github.com/stalniy/casl-examples) repository. ## Want to help? 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](https://opencollective.com/casljs/contribute) ## Contributors ### Code Contributors This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)]. ### Financial Contributors Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/casljs/contribute)] #### Individuals #### Organizations Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/casljs/contribute)] ## License [MIT License](http://www.opensource.org/licenses/MIT) Copyright (c) 2017-present, Sergii Stotskyi [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 [documentation]: https://stalniy.github.io/casl/ [advanced-usage]: https://stalniy.github.io/casl/en/advanced/customize-ability [mongoose]: http://mongoosejs.com/ [vue]: https://vuejs.org [angular]: https://angular.io/ [react]: https://reactjs.org/ [prisma]: https://www.prisma.io/ [support-chat]: https://github.com/stalniy/casl/discussions ---