CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-casl--ability

Isomorphic authorization JavaScript library which restricts what resources a given user is allowed to access

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

CASL Ability

CASL (pronounced "castle") is an isomorphic authorization JavaScript library that enables fine-grained access control through a declarative rule-based approach. It supports both simple claim-based authorization and complex subject and attribute-based authorization (ABAC), providing a fluent API for defining permissions with MongoDB-style query operators.

Package Information

  • Package Name: @casl/ability
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @casl/ability

Core Imports

import { 
  createMongoAbility, 
  defineAbility, 
  AbilityBuilder, 
  PureAbility,
  ForbiddenError,
  subject,
  detectSubjectType,
  createAliasResolver,
  wrapArray,
  mongoQueryMatcher,
  fieldPatternMatcher
} from "@casl/ability";

// Type imports
import type {
  AnyMongoAbility,
  MongoAbility,
  AbilityTuple,
  Subject,
  SubjectType,
  Abilities,
  ForcedSubject,
  CanParameters
} from "@casl/ability";

// HKT namespace import
import * as hkt from "@casl/ability/hkt";

For CommonJS:

const { 
  createMongoAbility, 
  defineAbility, 
  AbilityBuilder, 
  PureAbility,
  ForbiddenError,
  subject,
  detectSubjectType,
  createAliasResolver,
  wrapArray,
  mongoQueryMatcher,
  fieldPatternMatcher
} = require("@casl/ability");

Basic Usage

import { createMongoAbility, defineAbility, AbilityBuilder } from "@casl/ability";

// Method 1: Factory function with rules array
const ability = createMongoAbility([
  { action: 'read', subject: 'Post' },
  { action: 'create', subject: 'Post', conditions: { authorId: '${user.id}' } },
  { action: 'delete', subject: 'Post', inverted: true, conditions: { published: true } }
]);

// Method 2: Functional DSL
const ability2 = defineAbility((can, cannot) => {
  can('read', 'Post');
  can('create', 'Post');
  cannot('delete', 'Post', { published: true });
});

// Method 3: Builder pattern
const { can, cannot, build } = new AbilityBuilder(createMongoAbility);
can('read', 'Post');
can('update', 'Post', { authorId: 'user123' });
cannot('delete', 'Post', { published: true });
const ability3 = build();

// Check permissions
if (ability.can('read', 'Post')) {
  console.log('Can read posts');
}

if (ability.cannot('delete', { __type: 'Post', published: true })) {
  console.log('Cannot delete published posts');
}

Architecture

CASL is built around several key components:

  • Core Classes: PureAbility (base class) and specialized ability classes for different use cases
  • Rule System: Declarative rules with actions, subjects, conditions, and field restrictions
  • Matchers: Pluggable condition matching (MongoDB queries, custom matchers) and field pattern matching
  • Builder API: AbilityBuilder for fluent rule construction and defineAbility functional DSL
  • Error Handling: ForbiddenError with detailed context for permission violations
  • Type System: Full TypeScript support with generic constraints and type inference

Capabilities

Core Ability Management

Create and manage ability instances with rule-based permissions. The foundation for all authorization logic.

function createMongoAbility<T = AnyMongoAbility>(
  rules?: RawRuleOf<T>[],
  options?: AbilityOptionsOf<T>
): T;

function defineAbility<T = AnyMongoAbility>(
  define: (can: DefineRule<T>, cannot: DefineRule<T>) => void | Promise<void>,
  options?: AbilityOptionsOf<T>
): T | Promise<T>;

class PureAbility<A extends Abilities = Abilities, Conditions = AnyObject> {
  can(...args: CanParameters<A>): boolean;
  cannot(...args: CanParameters<A>): boolean;
  relevantRuleFor(...args: CanParameters<A>): Rule<A, Conditions> | null;
  update(rules: RawRuleFrom<A, Conditions>[]): this;
}

Core Ability Management

Permission Checking

Check permissions against subjects with optional conditions and field-level access control.

type CanParameters<A extends Abilities> = 
  | [action: A, subject?: Subject]
  | [action: A, subject: Subject, field?: string];

interface Rule<A extends Abilities, Conditions> {
  readonly action: string | string[];
  readonly subject?: string | string[];
  readonly conditions?: Conditions;
  readonly fields?: string[];
  readonly inverted: boolean;
  readonly reason?: string;
  matchesConditions(object: any): boolean;
  matchesField(field: string): boolean;
}

Permission Checking

Fluent Rule Building

Build complex permission rules using fluent builder pattern or functional DSL approach.

class AbilityBuilder<T extends AnyAbility> {
  rules: RawRuleOf<T>[];
  can: DefineRule<T>;
  cannot: DefineRule<T>;
  build(options?: AbilityOptionsOf<T>): T;
}

type DefineRule<T extends AnyAbility> = (
  action: Parameters<T['can']>[0],
  subject?: Parameters<T['can']>[1],
  conditionsOrFields?: any,
  fields?: string | string[]
) => void;

Fluent Rule Building

Error Handling

Handle permission violations with detailed error context and customizable messages.

class ForbiddenError<T extends AnyAbility = AnyAbility> extends Error {
  readonly ability: T;
  readonly action: string;
  readonly subject: any;
  readonly field?: string;
  readonly subjectType: string;

  static setDefaultMessage(messageOrFn: string | ErrorMessageFactory): void;
  static from<U extends AnyAbility>(ability: U): ForbiddenError<U>;
  
  setMessage(message: string): this;
  throwUnlessCan(...args: Parameters<T['can']>): void;
  unlessCan(...args: Parameters<T['can']>): this | undefined;
}

Error Handling

Advanced Utilities

Advanced features for serialization, database integration, and field-level access analysis.

// Rule serialization (from @casl/ability/extra)
function packRules<T extends RawRule<any, any>>(
  rules: T[], 
  packSubject?: (subject: any) => any
): PackRule<T>[];

function unpackRules<T extends RawRule<any, any>>(
  rules: PackRule<T>[], 
  unpackSubject?: (subject: any) => any
): T[];

// Field analysis
function permittedFieldsOf<T extends AnyAbility>(
  ability: T,
  action: Parameters<T['can']>[0],
  subject: Parameters<T['can']>[1],
  options?: { fieldsFrom?: any }
): string[];

// Query generation
function rulesToQuery<T extends AnyAbility, Q>(
  ability: T,
  action: Parameters<T['can']>[0],
  subjectType: string,
  convert: (rule: Rule<any, any>) => Q
): Q | null;

Advanced Utilities

Utility Functions

Core utility functions for subject type handling, array processing, and alias management.

/**
 * Set subject type on object for explicit type detection
 * @param type - Subject type name to set
 * @param object - Object to tag with subject type
 * @returns Tagged object with forced subject type
 */
function subject<T extends string, U extends Record<PropertyKey, any>>(
  type: T, 
  object: U
): U & ForcedSubject<T>;

/**
 * Detect subject type from object instance
 * @param object - Object to detect type from
 * @returns String representation of subject type
 */
function detectSubjectType(object: Exclude<Subject, SubjectType>): string;

/**
 * Create action alias resolver for expanding action shortcuts
 * @param aliasMap - Map of aliases to action arrays
 * @param options - Optional configuration
 * @returns Function that expands aliases to full action arrays
 */
function createAliasResolver(
  aliasMap: AliasesMap, 
  options?: AliasResolverOptions
): (action: string | string[]) => string[];

/**
 * Wrap value in array if not already an array
 * @param value - Value to wrap
 * @returns Array containing the value
 */
function wrapArray<T>(value: T[] | T): T[];

// Matcher functions
function mongoQueryMatcher<T = AnyObject>(conditions: MongoQuery<T>): MatchConditions<T>;
function fieldPatternMatcher(fields: string[]): MatchField<string>;

// Support types
interface AliasResolverOptions {
  skipValidate?: boolean;
  anyAction?: string;
}

Core Types

// Basic utility types
type AnyClass<ReturnType = any> = new (...args: any[]) => ReturnType;
type AnyObject = Record<PropertyKey, unknown>;
type AnyRecord = Record<PropertyKey, any>;

// Subject and ability types
type SubjectClass<N extends string = string> = AnyClass & { modelName?: N };
type SubjectType = string | SubjectClass;
type Subject = AnyRecord | SubjectType;
type AbilityTuple<X extends string = string, Y extends Subject = Subject> = [X, Y];
type Abilities = AbilityTuple | string;

// Rule definition types
interface RawRule<A extends Abilities, C = AnyObject> {
  action: A extends readonly [infer Action, any] ? Action : A;
  subject?: A extends readonly [any, infer S] ? S : string;
  conditions?: C;
  fields?: string | string[];
  inverted?: boolean;
  reason?: string;
}

// Type utilities
type Normalize<T extends Abilities> = T extends AbilityTuple ? T : [T, string];
type IfString<T, U> = T extends string ? U : never;

// Tagged interface support
interface ForcedSubject<T> {
  readonly __caslSubjectType__: T;
}

type TaggedInterface<T extends string> = ForcedSubject<T> |
  { readonly kind: T } |
  { readonly __typename: T };

type TagName<T> = T extends TaggedInterface<infer U> ? U : never;
type ExtractSubjectType<S extends Subject> = Extract<S, SubjectType> | TagName<S>;

type SubjectClassWithCustomName<T> = AnyClass & { modelName: T };
type InferSubjects<T, IncludeTagName extends boolean = false> =
  T | (T extends AnyClass<infer I>
    ? I | (IncludeTagName extends true
      ? T extends SubjectClassWithCustomName<infer Name> ? Name : TagName<I>
      : never)
    : TagName<T>);

// Parameter types
type Fn = (...args: any[]) => any;
type AbilityParameters<
  T extends Abilities,
  TupleFunction extends Fn,
  StringFunction extends Fn = () => 0,
  Else = IfString<T, Parameters<StringFunction>>
> = T extends AbilityTuple ? Parameters<TupleFunction> : Else;

type CanParameters<T extends Abilities, IncludeField extends boolean = true> =
  AbilityParameters<
    T,
    T extends AbilityTuple
      ? IncludeField extends true
        ? (action: T[0], subject: T[1], field?: string) => 0
        : (action: T[0], subject: T[1]) => 0
      : never,
    (action: Extract<T, string>) => 0
  >;

// MongoDB integration types
type MongoQuery<T = AnyObject> = Record<string, any>;
type MongoAbility<
  A extends AbilityTuple = AbilityTuple, 
  C extends MongoQuery = MongoQuery
> = PureAbility<A, C>;
type AnyMongoAbility = MongoAbility<any, any>;

// Matcher types
type MatchConditions<T extends {} = AnyRecord> = {
  (object: T): boolean;
  ast?: any; // Condition type from @ucast/mongo2js
};
type ConditionsMatcher<T> = (conditions: T) => MatchConditions;
type MatchField<T extends string> = (field: T) => boolean;
type FieldMatcher = <T extends string>(fields: T[]) => MatchField<T>;

// Rule index and event types (exported from main module)
interface Generics<T> {
  abilities: any;
  conditions: any;
}

type RawRuleOf<T extends AnyAbility> = RawRule<Generics<T>['abilities'], Generics<T>['conditions']>;
type RuleOf<T extends AnyAbility> = Rule<Generics<T>['abilities'], Generics<T>['conditions']>;

interface UpdateEvent<T extends AnyAbility> {
  rules: RawRuleOf<T>[];
}
type EventHandler<T> = (event: T) => void;
type Unsubscribe = () => void;

// Utility types
type AliasesMap = Record<string, string | string[]>;

Install with Tessl CLI

npx tessl i tessl/npm-casl--ability
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@casl/ability@6.7.x
Publish Source
CLI
Badge
tessl/npm-casl--ability badge