diff --git a/README.md b/README.md index ed7337c..6632dae 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # CanThey -A customizeable, flexible authorization checker - usable with or without express. +A customizeable, flexible authorization checker, usable with or without express. ## Install ``` @@ -7,12 +7,12 @@ npm install canthey ``` ## Authorization vs Authentication -Authentication is determining if a user is who they say they are. Authorization is determining whether or not said user is allowed to access / interact with the resource. This module assumes you've already handled authentication. +Authentication is determining if a user is who they say they are. Authorization is determining if an authenticated user is allowed to access/alter a specific resource. This module assumes you've already handled authentication. ## Function vs Express plugin -This can be used as a standalone function, seperate of Express. This is useful if you want to bake your own middleware or use it for non Express purposes. +CanThey can be used as a standalone function. This is useful if you want to bake your own middleware or use it outside of Express. -At its core, the function takes an ACL style string and a JSON object of permissions, and parses them against one another to see if they match. +At its core, the function takes an ACL style string and a JSON permissions object, and compares them for a match. ### Example ACL strings ``` @@ -22,7 +22,7 @@ products - music - albums - delete products::toys::action_figures::edit ``` -### Example permissions JSON: +### Example JSON permissions object ```js { "admins": @@ -40,9 +40,11 @@ products::toys::action_figures::edit } ``` -In these examples, the first three ACL strings would pass, the fourth would not. +In the above example, the first three ACL strings would pass, the fourth would not. -The function is looking against the JSON for either a name match to the same or higher depth of the ACL string, a boolean true, or a "*" at the same or higher level. For example, with the above given permission JSON, the ACL string of "products:toys" blocking a route that should have "action_figures" in the ACL string will allow the user through. +The function looks through the permission object for a boolean true, or a string match or "*" at the same or higher level of the ACL string. + +Be carefull with your permissions to ensure that you check them at the level you really want. For example, using the ACL string of "products:toys" with the above permissions object, attempting to protect a route that should require "action_figures" will pass, even though that isn't your intent. To get the granularity of check you want here, you need to use an ACL like "products:toys:action_figures". # The CanThey Function @@ -55,12 +57,12 @@ canThey(requiredACL, givenUserPermissions, opts); //returns true or false ## Parameters -* requiredACL - Required. The access level required, in a string of similar format to above.. -* givenUserPermissions - Required. The JSON representation of the user's permissions OR an array of JSON permissions. - * if this is an array, an internal function called canTheyCombiner is used to create a singular permissions object from many permissions objects. A great example of this is if you used role based permisisons and a user had many roles. +* requiredACL - Required. The access level required, as a string with or without splitBy characters. +* givenUserPermissions - Required. A JSON permissions object or array of permissions objects. + * If array, canTheyCombiner merges objects into a single permissions object prior to ACL comparision. A use case for this is for role based permisisons where a user has many roles. * opts - Optional. Each attribute in opts is optional, with a default value. - * splitBy - default: ":" - the string delimiter to split the ACL by. - * removeSpaces - default: true - whether or not to remove all spaces from the ACL. + * splitBy - default: ":" - The string delimiter to split the ACL by. + * removeSpaces - default: true - Whether or not to remove all spaces from the ACL. # Express Middleware There are two forms of Express Middleware available, as shown in the examples below. @@ -68,8 +70,8 @@ There are two forms of Express Middleware available, as shown in the examples be var Cte = require('canthey').Express var canThey = new Cte( { - onRoutecall: function, // to be explaiend later - this determines which middleware you're using - failureStatusCode: 403, //Defaults to 403 - what error code to send if they are not allowed access. + onRoutecall: function, // To be explaiend later - this determines which middleware you're using + failureStatusCode: 403, // Defaults to 403 - what error code to send if they are not allowed access. permissionsAttribute: 'userACL', /* Defaults to userACL - Required when not using onRouteCall. @@ -77,13 +79,13 @@ var canThey = new Cte( Resulting code is req[this.permissionsAttribute] - we expect you to assign the value in a prior middleware, probably when authenticating the user. */, - splitBy: ":", //Same as the canThey option - passed through. - removeSpaces: true //Same as the canThey option - passed through. + splitBy: ":", // Same as the canThey option - passed through. + removeSpaces: true // Same as the canThey option - passed through. } ); ``` -## Without onRoutecall +## Without onRouteCall ```js var Cte = require('canthey').Express; @@ -96,8 +98,8 @@ app.get('/admins/edit', canThey.do('admins:edit'), function(req, res){ ## With onRouteCall -onRouteCall is a function fired off whenever the middleware is called to determine: -* the ACL string requirements for the route _ (Required) _ +onRouteCall is a function fired when the middleware is called to allow dynamic determination of: +* the ACL string for the route (Required) * the user's JSON permissions (Not required if you're using the request object as the other middleware). ```js @@ -106,13 +108,13 @@ var canThey = new Cte( { onRouteCall: function(req, res, cb){ //Grab your ACL string - var fakeACL = 'admins:create'; + var dynamicACL = 'admins:create'; //And, optionally, grab the user's permissions. - var fakeUserPermissions = + var dynamciUserPermissions = { admins: "*" }; - cb(null, fakeACL, fakeUserPermissions); + cb(null, dynamicACL, dynamicUserPermissions); } } ); @@ -124,9 +126,9 @@ app.get('/admins/create', canThey.do, function(req, res){ Function with the following parameters: * req - Request object, passed for your convenience. -* res - Response object, also passed for your convenience. +* res - Response object, passed for your convenience. * cb - The callback is a function with the following expected responses: - * err - If populated, it will send the the failureStatusCode back to the user, but nothing else (silent fail). + * err - If populated, it will respond with the failureStatusCode, but nothing else (silent fail). * routeACL - Required - the route's ACL string. * permissions - Optional - if you are attaching permissions to the request object, you can ignore this. Otherwise, it is the resulting permissions for the user. diff --git a/lib/canTheyCombiner.js b/lib/canTheyCombiner.js index c9cbddc..9cdfc11 100644 --- a/lib/canTheyCombiner.js +++ b/lib/canTheyCombiner.js @@ -14,10 +14,13 @@ var merge = require('controlled-merge'), returns a javascript object */ function(permissions){ - if(!Array.isArray(permissions)) throw new Error('CanThey Combiner can only an array of javascript objects'); + + var msg = 'CanThey Combiner can only accept an array of javascript objects'; + + if(!Array.isArray(permissions)) throw new Error(msg); permissions.forEach(function(permission){ - if(typeof permission != 'object') throw new Error('CanThey Combiner can only accept an array of javascript objects'); + if(typeof permission != 'object') throw new Error(msg); }); return merge( diff --git a/lib/canTheyExpress.js b/lib/canTheyExpress.js index b68b125..e5d8d0c 100644 --- a/lib/canTheyExpress.js +++ b/lib/canTheyExpress.js @@ -18,7 +18,7 @@ module.exports = function(){ var self = this; if(arguments.length != 1 && !self.onRouteCall){ - throw new Error('CanThey.do can only be treated like middleware (req, res, next) passed if onRouteCall is set.'); + throw new Error('CanThey.do can only be treated like middleware (req, res, next passed) when onRouteCall is set.'); } if(self.onRouteCall){ diff --git a/test/test.js b/test/test.js index 4ae22a9..2f30e4e 100644 --- a/test/test.js +++ b/test/test.js @@ -185,7 +185,7 @@ describe('CanThey - express, onRouteCall is used', function(){ }).to.throw(); }); - it('should return 401 if userACL is undefined', function(){ + it('should return 403 if userACL is undefined', function(){ req.routeACL = null; canThey.do(req, res, next);