Skip to content

Commit

Permalink
adds onFailure callback support and others
Browse files Browse the repository at this point in the history
includes:
- adds support to pass an options object to done callback in an activity handler
  function.
- adds onFailure callback support in the above options object.
- adds a 401 unauthorized status code to all returned error objects.
- updates README.md
- refactors and adds unit tests (100% coverage!)
  • Loading branch information
kgraves committed Oct 20, 2015
1 parent 9e98b0b commit 383b50f
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 13 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,37 @@ can.configureActivities({
done(req.user !== undefined);
},

// you can pass a custom error message to the callback for a failure
/**
* You can pass a custom error message to the callback for a failure.
*/
'view.admin.page': function(req, done) {
if (req.user && req.user.role === 'admin') {
done(true);
} else {
done(false, 'admins only!');
}
},

/**
* You can pass an options object for further functionality.
*
* The following options are supported:
* {
* onFailure: function(req, res, next) {...}
* }
*
* Currently the only option that is recognized is an `onFailure` callback.
* This gives you more granular control when there is an unauthorized request.
* For example, one may have the need to redirect unauthorized requests to
* different endpoints, instead of relying on error handlers further down the
* line.
*/
'view.stats': function(req, done) {
if (req.user && req.user.isOwner(someModelObject)) {
done(true);
} else {
done(false, '', { onFailure: helpers.redirectToLogin });
}
}

});
Expand Down
32 changes: 27 additions & 5 deletions lib/candoo.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,34 @@

var util = require('util');

/**
* Candoo constructor.
*
* Prepares all internal data structures and constant values.
*/
var Candoo = function() {
// create activity registry
this._activityRegistry = {};
this._notRegisteredMessage = 'The activity (%s) is not registered';
this._unauthorizedStatusCode = 401;
};

// configure user defined activity handlers
/**
* Configure user defined activity handlers.
*
* @param {Object} config
*/
Candoo.prototype.configureActivities = function(config) {
this._activityRegistry = config;
};

/**
* Produces an express/connect middleware function for authorization.
*
* This function runs the function associated with the given `activity` to
* authorize an action.
*
* @param {String} activity The name of an activity registered with Candoo.
*/
Candoo.prototype.do = function(activity) {
var that = this;
Expand All @@ -31,18 +43,28 @@ Candoo.prototype.do = function(activity) {
// check the registry for the given activity
if (that._activityRegistry[activity]) {

// call handler and return result
// call handler function for the given `activity`.
var handler = that._activityRegistry[activity];
handler(req, function(isAuthorized, errorMessage) {
handler(req, function(isAuthorized, errorMessage, options) {
if (isAuthorized) {
next();
} else {
next(new Error(errorMessage));
// if `onfailure` callback is passed, call that. Otherwise pass an
// error to next.
if (options && options.onFailure) {
options.onFailure(req, res, next);
} else {
// create error with given `errorMessage` that has the unauthorized
// http status code.
var error = new Error(errorMessage);
error.status = that._unauthorizedStatusCode;
next(error);
}
}
});

} else {
// activity is not registered, throw error with relevant message
// activity is not registered, throw error with relevant message.
var message = util.format(that._notRegisteredMessage, activity);
next(new Error(message));
}
Expand Down
42 changes: 35 additions & 7 deletions spec/candoo_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ describe('candoo', function() {

it('should contain all defined functions', function() {
expect('_activityRegistry' in candoo).toEqual(true);
expect('_notRegisteredMessage' in candoo).toEqual(true);
expect('_unauthorizedStatusCode' in candoo).toEqual(true);
expect('configureActivities' in candoo).toEqual(true);
expect('do' in candoo).toEqual(true);
});

it('should define an empty object for the registry', function() {
var length = Object.keys(candoo._activityRegistry).length;
expect(candoo._activityRegistry instanceof Object).toBe(true);
expect(length).toEqual(0);
});

Expand All @@ -37,7 +40,8 @@ describe('candoo', function() {

it('should add a single activity to the registry', function() {
var activity = {
'view.profile': function(req, done) {}
'view.profile': null
// 'view.profile': function(req, done) {}
};

candoo.configureActivities(activity);
Expand All @@ -50,10 +54,10 @@ describe('candoo', function() {

it('should add a multiple activities to the registry', function() {
var activities = {
'view.profile': function(req, done) {},
'view.admin.page': function(req, done) {},
'view.settings': function(req, done) {},
'view.stats': function(req, done) {}
'view.profile': null,
'view.admin.page': null,
'view.settings': null,
'view.stats': null
};

candoo.configureActivities(activities);
Expand Down Expand Up @@ -83,10 +87,10 @@ describe('candoo', function() {
}
};

can.configureActivities(activity);
can._activityRegistry = activity;

var middleware = can.do('view.profile');
var req = { user: {}, params: {} };
var req = { user: {} };
var res = {};
var spy = sinon.spy();

Expand Down Expand Up @@ -122,6 +126,7 @@ describe('candoo', function() {
expect(spy.callCount).toEqual(1);
expect(args[0]).not.toEqual(undefined);
expect(args[0].message).toEqual(rejectMessage);
expect(args[0].status).toEqual(401);
});

it('should call next with an error for unregistered activity', function() {
Expand All @@ -131,6 +136,7 @@ describe('candoo', function() {
var res = {};
var next = sinon.spy();
var expectedMessage = util.format(can._notRegisteredMessage, activityName);
var expectedStatus = 401;

middleware(req, res, next);

Expand All @@ -139,6 +145,28 @@ describe('candoo', function() {
expect(args[0].message).toEqual(expectedMessage);
});

it('should fail and call the onFailure callback', function() {
var onFailureSpy = sinon.spy();
var activity = {
'view.profile': function(req, done) {
done(req.user !== undefined, null, { onFailure: onFailureSpy });
}
};

can._activityRegistry = activity;

var middleware = can.do('view.profile');
var req = {};
var res = {};
var spy = sinon.spy();

middleware(req, res, spy);

expect(onFailureSpy.callCount).toEqual(1);
expect(onFailureSpy.firstCall.args).toEqual([req, res, spy]);
expect(spy.callCount).toEqual(0);
});

});

});

0 comments on commit 383b50f

Please sign in to comment.