npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

permits

v0.1.2

Published

Simple and flexible syncronous permission system on arbitrary resources with arbitrary users

Downloads

13

Readme

Permits (Permissions)

Simple but powerful in memory permission manager and query interface. Allows you to set permissions for users on resources with arbitrary string ids. Also has change event callbacks for syncronizing with persistent store.

Install

npm install permits

Philosophy

This library assumes that all things are identified by unique string ids. There are 3 fundamental building blocks, the user, the resource and the action to be taken. This allows you to set and get which user can take what action on which resource. All data is stored in a nested object to reduce the total number of keys on a single object since there could be very very many. The entire nested structure can be easily filtered over to find any particular combination of entries.

Usage

  var Permits = require('permits')

  var permissions = Permits()

  //gives "userid" the ability to do "allowedAction" on "resourceid"
  var result = permissions.allow('userid','resourceid','allowedAction')

  // result is a permissions object in the form 
  // { 
  //   userid:'userid',
  //   resourceid:'resourceid',
  //   action:'action'
  //   allowed:true,
  //   type:'default' //optionally define a resource type, defaults to "default"
  //  }

  //can == true
  var can = permissions.can('userid','resourceid','allowedAction')

  //denys user ability to do deniedAction
  var result = permissions.deny('userid','resourceid','deniedAction')

  //can == false
  var can = permissions.can('userid','resourceid','deniedAction')

  //clear user ability to do neutralAction
  var result = permissions.clear('userid','resourceid','neutralAction')

  //can == null
  var can = permissions.can('userid','resourceid','neutralAction')

Restore and Persist

Restore permissions from a persistent data store and syncronizes any changes back into the database.

  //assume we have a persistent store
  var Store = require('permissionStore')
  var Permits = require('permits')
  
  function upsert(permission,path){
    //assume store has an upsert function which takes an object with an id property
    permissions.id = path.join('.')
    Store.upsert(permissions)
  }

  //assume store gets entire table as an array with getAll()
  var permissions = Permits(Store.getAll(),upsert)

 //any new permissions will be upserted into database
 permissions.allow(...)

Resource Types

You may want to organize your resources by types and scope your permission methods to those types.

  var Permits = require('permits')

  //your global permissions, this object works on the default permission type: 'default'
  var permissions = Permits(resume,onChange)

  //create permission types called books, which will still be accessible from the permissions object.
  var books = permissions.type('books')

  //these changes will trigger onChange callback on the permissions object
  books.allow('someuser','booktitle','canRead')

  //true
  books.can('someuser','booktitle','canRead')

  //access book types from the main permissions object. always will be consistent with
  //other resource types, since they share the same object.
  permissions.type('books').get('someuser','booktitle','canRead')
  //this does the exact same thing
  permissions.get('someuser','booktitle','canRead','books')

  //returns a permissions object
  //{
  //  userid:'someuser',resourceid:'booktitle',action:'canRead',allowed:true, type:'books'
  //}

  //or use the default permission type
  permissions.deny(/*etc...*/)
  

Permissions Object

This is how the permissions object is returned and emitted through change callbacks.

 { 
   userid:'userid',
   resourceid:'resourceid',
   action:'action'
   allowed:true,  //allowed can be true false or null
   type:'default' //optionally define a resource type, defaults to "default"
  }

Internally The permissions object resides in a nested object organized into this structure:

  {
    userid:{
      type:{
        resourceid:{
          action:true //true false or undefined
        }
      }
    }
  }

It is accessed through lodash .get and .set methods. It will also be returned as a path on change:

  path = [userid,type,resourceid,action]

You can use this to create a unique id for insertion into a traditional database.

API

Initialize

Permits takes 3 options, an array of permissions, a callback function which gets executed every time permissions change and a string to define the default permissions type.

Permits(resume,upsert,defaultType)

  • resume(optional, array) - An array of permission objects to restore previous state.
  • upsert(optional, function) - A callback function which can take 2 parameters function upsert(permission,path){}
    • permission - A permissions object which was just updated, in the form

       { 
         userid:'userid',
         resourceid:'resourceid',
         action:'action'
         allowed:true,  //allowed can be true false or null
         type:'default' //optionally define a resource type, defaults to "default"
        }
    • path - the unique path to this permission object as an array. Use to create your own ID in the form: [userid,type,resourceid,action]

  • defaultType(optional, string) - defaults to the string 'default'. Set to whatever your default permissions resource type should be.

Type

Creating a resource type is optional, by default just initialize the permits class and it is ready to be used. If you need the ability to define seperate resources then use this function. All functions on the returned object will be scoped to whatever resource type you define. It has the same API as the permits object.

  var books = permits.type('books')
  //use books like a permits object, it has all the same functions

Set

Multiple ways to set new permissions, they all trigger on change callback. Type is optional and defaults to the either the default type, or the custom type.

permits.set(userid,resourceid,action,allowed,type)
permits.allow(userid,resourceid,action,type)
permits.deny(userid,resourceid,action,type)
permits.clear(userid,resourceid,action,type)

Get

Gets full permissions object. Type is optional to override the default type.

permits.get(userid,resourceid,action,type)

Can

Get a true, false or null answer for if a user can do something on a resource. Type is optional to override default type.

var result = permits.can(userid,resourceid,action,type)

Queries

There are many helper queries to get lists of permissions. These iterate over the entire structure, scoped to the resource type. Type is optional to override the default type.

permits.getByUser(userid,type)
permits.getByResource(resourceid,type)
permits.getByUserAndResource(userid,resourceid,type)
permits.getByUserAndAction(userid,action,type)
permits.getByResourceAndAction(resourceid,action,type)

  • returns - An array of permission objects, or an empty array if none are found.

Filter

If you need more search options you can filter directly on any permission parameter. All parameters optional, if none provided all permissions will be returned, which is the same as permit.list(). One caveat is that if you are filtering from a typed permissions, then it will only filter over that type. Use the root permits object to search over all types.

  var result = permits.filter({
    userid:'userid',         //optional userid to match, searches all users if omitted.
    resourceid:'resourceid', //optional resource id to match, searches all resources if omitted.
    action:'action',         //optional action to match, searches all actions if omitted.
    allowed:true,            //optional allowed to match, can be true or false. Searches all allowed states if omitted.
    type:'type',             //optional type to match. Searches all default type if omitted.
  })

List

Get the entire permissions store as a list of permission objects. If using a typed permission, then it will only return the entirety of that type. Use the root permits object to get entire list. Type is optional.

var list = permits.list(type)