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 = var permissions = //gives "userid" the ability to do "allowedAction" on "resourceid" var result = permissions // 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 //denys user ability to do deniedAction var result = permissions //can == false var can = permissions //clear user ability to do neutralAction var result = permissionsclear'userid''resourceid''neutralAction' //can == null var can = permissions
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 = var Permits = { //assume store has an upsert function which takes an object with an id property permissionsid = path Store } //assume store gets entire table as an array with getAll() var permissions = //any new permissions will be upserted into database permissions
Resource Types
You may want to organize your resources by types and scope your permission methods to those types.
var Permits = //your global permissions, this object works on the default permission type: 'default' var permissions = //create permission types called books, which will still be accessible from the permissions object. var books = permissionstype'books' //these changes will trigger onChange callback on the permissions object books //true books //access book types from the main permissions object. always will be consistent with //other resource types, since they share the same object. permissionstype'books' //this does the exact same thing permissions //returns a permissions object //{ // userid:'someuser',resourceid:'booktitle',action:'canRead',allowed:true, type:'books' //} //or use the default permission type permissions
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 nulltype:'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
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)