github stalniy/casl @casl/ability@5.1.0

latest releases: @casl/prisma@2.0.1, @casl/vue@3.0.0, @casl/react@7.0.0...
5 years ago

5.1.0 (2020-12-26)

Bug Fixes

  • ability: ensure default field matcher can match fields with partial patterns inside (362f49f), closes #388
  • ability: replaces getters with functions to ensure terser properly minifies them (386ecb6)
  • angular: fixes sourcemap generation for the code built by ngc (7715263), closes #387 #382
  • build: ensure dist is updated before (0a879f7)
  • conditions: moves logic related to compare complex types to @ucast/mongo2js (9bd6a1b)
  • condtions: adds interpreter for $and parsing instruction (3166a32)
  • extra: makes permittedFieldsOf to iterate from the end of array (81e6409)
  • package: removes engine section that points to npm@6 (eecd12a), closes #417
  • README: removes explanation duplicated from intro guide (6315aa7)
  • types: ensure ForceSubject generic parameter is preserved in resulting d.ts files (e97e5fe)
  • types: makes parameters of AbilityClass optional (e7d0815)

Code Refactoring

  • extra: makes fieldsFrom option to be mandatory for permittedFieldsOf [skip release] (df29b0d)
  • ruleIndex: detectSubjectType option is now responsible only for detecting subject type from objects [skip release] (ebeaadc)
  • ruleIndex: removes possibility to pass subject to rulesFor and possibleRulesFor [skip release] (b8c324d)
  • types: restricts which utility types are exported by library (e98618f)

Features

  • builder: improves typings for AbilityBuilder [skip release] (ebd4d17), closes #379
  • builder: improves typings of AbilityBuilder['can'] and AbilityBuilder['cannot'] methods [skip release] (98ffbfc), closes #333
  • esm: adds ESM support for latest Node.js through exports prop in package.json (cac2506), closes #331
  • extra: adds rulesToAST that converts rules into @ucast AST (55fd6ee), closes #350

Performance Improvements

  • ability: creates conditions and field matcher lazily (4ae7799)
  • ability: replaces object for storing index with ES6 Map (d1fa117)
  • events: converts LinkedItem into POJO and regular functions (6f2de73)
  • events: handles event removal in space efficient way (71246e2)
  • events: moves out side-effect from LinkedItem constructor (3657c7f)
  • events: utilizes LinkedList for storing event handlers (e2fd265)
  • extra: replaces object with Set in permittedFieldsOf (a9260d1)
  • rule: ensures conditions/field matcher created only when we have object/field to check (110a69d)
  • ruleIndex: removes subject type detection from _buildIndexFor (13fe934)
  • rules: improves merging logic of rules of subject and manage all (6f8a13a)

Reverts

  • builder: reverts back AbilityBuilder generic parameter (aa7b45f)
  • extra: makes rulesToQuery return an object with Object prototype (dcb7254)

BREAKING CHANGES

  • types: types AliasesMap, TaggedInterface, AbilityTupleType, ToAbilityTypes, AnyObject are no longer exported by the library

  • extra: makes fieldsFrom option to be mandatory for permittedFieldsO f. This reduces confusion around why permittedFieldsOf returns empty array when user can manage entity fields. So, now this logic is just explicit and clear

    Before

    import { defineAbility } from '@casl/ability';
    import { permittedFieldsOf } from '@casl/ability/extra';
    
    const ability = defineAbility((can) => {
      can('read', 'Article');
    });
    
    const fields = permittedFieldsOf(ability, 'read', 'Article'); // []

    After

    import { defineAbility } from '@casl/ability';
    import { permittedFieldsOf } from '@casl/ability/extra';
    
    const ability = defineAbility((can) => {
      can('read', 'Article');
    });
    
    const ARTICLE_FIELDS = ['id', 'title', 'description'];
    const fields = permittedFieldsOf(ability, 'read', 'Article', {
      fieldsFrom: rule => rule.fields || ARTICLE_FIELDS
    }); // ['id', 'title', 'description']
  • ruleIndex: string and class (or function constructor) are the only possible subject types for now. detectSubjectType is now responsible only for detecting subject type from object

    Before

    When providing subject type it was important to handle cases when passed in argument is a string or function. As an alternative it was possible to call built-in detectSubjectType which could catch this cases:

    import { Ability } from '@casl/ability';
    
    const ability = new Ability([], {
      detectSubjectType(object) {
        if (object && typeof object === 'object') {
          return object.__typename;
        }
    
        return detectSubjectType(object);
    });

    After

    There is no need to handle subject type values in detectSubjectType function anymore. It's now handled internally:

    import { Ability } from '@casl/ability';
    
    const ability = new Ability([], {
      detectSubjectType: object => object.__typename
    });

    Also it's important to note that if you want it's no longer possible to use classes and strings as subject types interchangably together as it was before. Now, if you want to use classes, you should use them everywhere:

    Before

    import { defineAbility } from '@casl/ability';
    
    class Post {}
    const ability = defineAbility((can) => {
      can('read', Post);
      can('update', 'Post');
    });
    
    ability.can('read', 'Post') // true
    ability.can('read', Post) // true
    ability.can('update', Post) // true

    After

    import { defineAbility } from '@casl/ability';
    
    class Post {}
    const ability = defineAbility((can) => {
      can('read', Post);
      can('update', 'Post');
    });
    
    ability.can('read', 'Post') // false, 'Post' and Post are considered different now
    ability.can('read', Post) // true
    ability.can('update', Post) // false
  • ruleIndex: rulesFor, possibleRulesFor, rulesToQuery, ruleToAST, rulesToFields accepts only subject type now!

    Before

    import { Ability } from '@casl/ability';
    
    const ability = new Ability([
      { action: 'read', subject: 'Post' }
    ]);
    
    class Post {}
    
    console.log(ability.rulesFor('read', new Post())); // [Rule]
    console.log(ability.rulesFor('read', 'Post')); // [Rule]

    After

      import { Ability } from '@casl/ability';
    
    const ability = new Ability([
      { action: 'read', subject: 'Post' }
    ]);
    
    class Post {}
    
    console.log(ability.rulesFor('read', new Post())); // throws exception
    console.log(ability.rulesFor('read', 'Post')); // [Rule]

    Other functions and methods have the same behavior

  • builder: changes main generic parameter to be a class instead of instance and makes defineAbility to accept options as the 2nd argument.

    Before

    import { AbilityBuilder, defineAbility, Ability } from '@casl/ability';
    
    const resolveAction = (action: string) => {/* custom implementation */ };
    const ability = defineAbility({ resolveAction }, (can) => can('read', 'Item'));
    const builder = new AbilityBuilder<Ability>(Ability);

    After

    import { AbilityBuilder, defineAbility, Ability } from '@casl/ability';
    
    const resolveAction = (action: string) => {/* custom implementation */ };
    const ability = defineAbility((can) => can('read', 'Item'), { resolveAction });
    const builder = new AbilityBuilder(Ability); // first argument is now mandatory!

    The 1st parameter to AbilityBuilder is now madatory. This allows to infer generic parameters from it and makes AbilityType that is built to be explicit.

  • builder: can and cannot methods of AbilityBuilder now restricts what fields and operators can be used inside conditions (i.e., MongoQuery). Also these methods now suggests object fields based on passed instance

    Before

    import { AbilityBuilder, Ability, AbilityClass } from '@casl/ability';
    
    interface Person {
      kind: 'Person'
      firstName: string
      lastName: string
      age: number
      address: {
        street: string
        city: string
      }
    }
    
    type AppAbility = Ability<['read', Person | Person['kind']]>;
    cons AppAbility = Ability as AbilityClass<AppAbility>;
    cons { can } = new AbilityBuilder(AppAbility);
    
    can('read', 'Person', {
      'address.street': 'Somewhere in the world',
      fistName: 'John' // unintentional typo
    });
    can('read', 'Person', ['fistName', 'lastName'], { // no intellisense for fields
      age: { $gt: 18 }
    })

    After

    Because provided keys in the example above doesn't exist on Person interface, TypeScript throws an error. So, we are safe from typos! But what about dot notation? It's also supported but in more typesafe way:

    import { AbilityBuilder, Ability, AbilityClass } from '@casl/ability';
    
    interface Person {
      kind: 'Person'
      firstName: string
      lastName: string
      age: number
      address: {
        street: string
        city: string
      }
    }
    
    type AppAbility = Ability<['read', Person | Person['kind']]>;
    cons AppAbility = Ability as AbilityClass<AppAbility>;
    cons { can } = new AbilityBuilder(AppAbility);
    
    interface PersonQuery extends Person {
      'address.street': Person['address']['street']
      'address.city': Person['address']['city']
    }
    
    can<PersonQuery>('read', 'Person', {
      'address.street': 'Somewhere in the world',
      fistName: 'John' // unintentional typo
    });
    can<PersonQuery>('read', 'Person', ['firstName', 'lastName'], {
      age: { $gt: 18 }
    })

    Intellisense and type checking for fields is also implemented! To be able to use wildcards in fields just add additional generic parameter:

    can<PersonQuery, 'address.*'>('read', 'Person', ['firstName', 'address.*'], {
      age: { $gt: 18 }
    })

Don't miss a new casl release

NewReleases is sending notifications on new releases.