JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 2302
  • Score
    100M100P100Q115873F
  • License MIT

Simple ACL library for the browser inspired by Laravel's guards and policies.

Package Exports

  • browser-acl

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (browser-acl) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

browser-acl 🔒

build status Coverage Status Known Vulnerabilities NPM version

Simple ACL library for the browser inspired by Laravel's guards and policies.

Go to vue-browser-acl for the official Vue package.

Install

yarn add browser-acl

Setup

import Acl from 'browser-acl'
const acl = new Acl()

acl.rule('view', Post)
acl.rule('moderate', Post, (user) => user.isModerator())
acl.rule(['edit', 'delete'], Post, (user, post) => post.userId === user.id)
acl.rule('purgeInactive', user => user.isAdmin) 

Policies are also supported:

acl.policy({
  view: true,
  edit: (user, post) => post.userId === user.id),
}, Post)

Note: policies takes precedence over rules.

Usage

// true if user owns post
acl.can(user, 'edit', post)

// true if user owns at least posts
acl.some(user, 'edit', posts)

// true if user owns all posts
acl.every(user, 'edit', posts)

You can add mixins to your user class:

acl.mixin(User) // class not instance

user.can('edit', post)
user.can.some('edit', posts)
user.can.every('edit', posts)

Minification

The default subject mapper makes use of "poor-man's reflection", meaning it will use the name of the input subject's constructor to group the subjects.

When using webpack or similar this method can break if you are not careful. Since code minifiers will rename functions you have to make sure you only rely on the function to set up your rules and asking for permission.

Bad

This will break with minifiers since there is no way to know the subject name of the subject after minification. Typically it will be a single letter member.

acl.rule('edit', 'Post')

Better

This works with minifiers:

acl.rule('edit', Post)

Whatever Post is minified to that is what the rule will work on. As long as the same import is used throughout your code base it will work. With webpack for instance it most likely will be the same.

But we can do better.

Best

This works with minifiers:

acl.register(Post, 'Post')
acl.rule('create', 'Post') // <-- works as expected
acl.rule('edit', Post)     // <-- and so does this

This basically sets up an alias for Post so that you can refer to it as 'Post'. So even though Post may actually turn into t you can still refer to it as Post.

Alternatives to classes and constructor functions

You can also override the subjectMapper function and a property to you objects with the subject name.

See subjectMapper

Additional Parameters and Global Rules

You can define global rules by omitting the subject when defining rules.

acl.rule('purgeInactive', user => user.admin)
acl.can('purgeInactive')

Also you can pass additional parameters to the handler like this:

acl.rule('edit', Post, (user, post, verb, additionalParameter) => true)
acl.can('edit', post, additionalParameter)

However, you cannot combine the two without explicitly stating that you are defining a global rule. You do this by importing the special GlobalRule subject.

import {GlobalRule} from 'browser-acl'
acl.rule('purgeInactive', GlobalRule, user => user.admin)
acl.can('purgeInactive', GlobalRule, additionalParameter)

Note: When defining the rule you can omit it, but is is required for can. This is only in the case when you need to pass additional parameters.

API

Table of Contents

rule

You add rules by providing a verb, a subject and an optional test (that otherwise defaults to true).

If the test is a function it will be evaluated with the params: user, subject, and subjectName. The test value is ultimately evaluated for truthiness.

Examples:

acl.rule('create', Post)
acl.rule('edit', Post, (user, post) => post.userId === user.id)
acl.rule('edit', Post, (user, post, verb, additionalParameter, secondAdditionalParameter) => true)
acl.rule('delete', Post, false) // deleting disabled
acl.rule('purgeInactive', user => user.isAdmin) // global rule

Parameters

Returns Acl

policy

You can group related rules into policies for a subject. The policies properties are verbs and they can plain values or functions.

If the policy is a function it will be new'ed up before use.

  class Post {
    constructor() {
      this.view = true       // no need for a functon

      this.delete = false    // not really necessary since an abscent
                             // verb has the same result
    },
    edit(user, post, verb, additionalParameter, secondAdditionalParameter) {
      return post.id === user.id
    }
  }

Policies are useful for grouping rules and adding more complex logic.

Parameters

Returns Acl

register

Explicitly map a class or constructor function to a name.

You would want to do this in case your code is heavily minified in which case the default mapper cannot use the simple "reflection" to resolve the subject name.

Note: If you override the subjectMapper this is not used, bud it can be used manually through this.registry.

Parameters

can

Performs a test if a user can perform action on subject.

The action is a verb and the subject can be anything the subjectMapper can map to a subject name.

E.g. if you can to test if a user can delete a post you would pass the actual post. Where as if you are testing us a user can create a post you would pass the class function or a string.

  acl->can(user, 'create', Post)
  acl->can(user, 'edit', post)
  acl->can(user, 'edit', post, additionalParameter, secondAdditionalParameter)

Note that these are also available on the user if you've used the mixin:

  user->can('create', Post)
  user->can('edit', post)

Parameters

Returns any Boolean

some

Like can but subject is an array where only some has to be true for the rule to match.

Note the subjects do not need to be of the same kind.

Parameters

Returns any Boolean

every

Like can but subject is an array where all has to be true for the rule to match.

Note the subjects do not need to be of the same kind.

Parameters

Returns any Boolean

mixin

Mix in augments your user class with a can function object. This is optional and you can always call can directly on your Acl instance.

user.can()
user.can.some()
user.can.every()

Parameters

  • User Function A user class or contructor function

subjectMapper

Rules are grouped by subjects and this default mapper tries to map any non falsy input to a subject name.

This is important when you want to try a verb against a rule passing in an instance of a class.

  • strings becomes subjects
  • function's names are used for subject
  • objects's constructor name is used for subject

Override this function if your models do not match this approach.

E.g. say that you are using plain data objects with a type property to indicate the "class" of the object.

  acl.subjectMapper = s => typeof s === 'string' ? s : s.type

can will now use this function when you pass in your objects.

See register() for how to manually map classes to subject name.

Parameters

Returns string A subject

reset

Removes all rules, policies, and registrations

Returns Acl

removeRules

Remove rules for subject

Optionally limit to a single verb.

Parameters

Returns Acl

removePolicy

Remove policy for subject

Parameters

Returns Acl

removeAll

Convenience method for removing all rules and policies for a subject

Parameters

Returns Acl