Skip to content

Commit

Permalink
Fairly sizeable refactor (whilst maintaining existing interface)
Browse files Browse the repository at this point in the history
Separate out the code more logically and reduce function createion per request
  • Loading branch information
ciaranj committed Aug 21, 2011
1 parent f0ad8f4 commit 9b306d4
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 198 deletions.
6 changes: 2 additions & 4 deletions lib/authExecutionScope.js
Expand Up @@ -2,12 +2,10 @@
* Copyright(c) 2010 Ciaran Jessup <ciaranj@gmai.com>
* MIT Licensed
*/
var AuthExecutionScope= module.exports = function( scope, trace, request, response ) {
var AuthExecutionScope= module.exports = function( scope, trace ) {
this.executionResult= { authenticated: undefined };
this.scope= scope;
this._trace= trace;
this.request= request;
this.response= response;
}

/**
Expand All @@ -19,7 +17,7 @@ AuthExecutionScope.prototype.trace= function( message ) {
if( this.executionResult.currentStrategy ) {
messagePrefix= this.executionResult.currentStrategy + ": ";
}
this._trace( messagePrefix + message, this.request, this.response, this.scope, "***" )
this._trace( messagePrefix + message, this.scope, "***" )
}

AuthExecutionScope.prototype.fail= function(callback) {
Expand Down
99 changes: 99 additions & 0 deletions lib/auth_middleware.js
@@ -0,0 +1,99 @@
/*!
* Copyright(c) 2010 Ciaran Jessup <ciaranj@gmai.com>
* MIT Licensed
*/
var connect= require('connect')
, RequestMethods= require('./requestMethods')
, StrategyExecutor= require('./strategyExecutor')
, Tracing= require('./tracing');

/**
* Construct the authentication middleware.
* Construction can take 2 forms:
* auth(<Strategy>()|[<Strategy>()]) - A single configured strategy, or array of strategies.
* auth({ strategies:<Strategy>()|[<Strategy>()...]
* [trace: true|false|function(message, req, [scope])}]) - More powerful variant that allows for passing in other configuration options, none yet defined.
*/
module.exports = function(optionsOrStrategy) {

var i, strategies, strategyExecutor, options, traceFunction, server;

if( !optionsOrStrategy ) throw new Error("You must specify at least one strategy to use the authentication middleware, even if it is anonymous.");
// Constructor form 1
if( Array.isArray(optionsOrStrategy) || ( optionsOrStrategy.authenticate !== undefined &&
optionsOrStrategy.strategies === undefined ) ) {
strategies= Array.isArray(optionsOrStrategy) ? optionsOrStrategy : [optionsOrStrategy];
options= {trace: false};
}
else {
options= optionsOrStrategy
strategies= Array.isArray(optionsOrStrategy.strategies) ? optionsOrStrategy.strategies : [optionsOrStrategy.strategies];
}

if( !options.trace ) { // If options.trace is specified as false or undefined we no-op the messages.
traceFunction= Tracing.nullTrace;
}
else if( options.trace === true ) { // If options.trace is really true then we log out to console
traceFunction= Tracing.standardTrace;
}
else { // Custom provided trace function
traceFunction= options.trace;
}

// Construct the strategy executor.
strategyExecutor= new StrategyExecutor( strategies )

// Construct the middleware that adapts the request object to provide authentication primitives.
server= connect.createServer(
function auth(req, res, next) {

// Mix-in the static utility methods (the methods are directly on the request, and don't need the response object).
req.getAuthDetails= RequestMethods.getAuthDetails;
req.isAuthenticated= RequestMethods.isAuthenticated;
req.isUnAuthenticated= RequestMethods.isUnAuthenticated;
req.logout= RequestMethods.logout;

// If there is a session middleware, use it.
if( req.session && req.session.auth ) {
req._connect_auth= req.session.auth;
}
else {
// Create the auth holder if needed.
if( ! req.getAuthDetails() ) {
createAuthDetails(req);
}
}

// These methods require the request & response to be in their closures.
req.authenticate= function(strategy, opts, callback) {
RequestMethods.authenticate.call( this, strategy, opts, callback, strategyExecutor, res );
}

// Assign a tracer so if needed routes can trace.
req.getAuthDetails().trace= function( message, scope, linePrefix ) {
traceFunction( message, req, res, scope, linePrefix );
};

// Now we've added our requisite methods to the request, call the next part of the middleware chain
// (which may in fact be a middleware piece that enforces authentication!)
next();
});

// Some strategies require routes to be defined, so give them a chance to do so.
for(i=0;i< strategies.length; i++ ) {
if( strategies[i].setupRoutes ) {
strategies[i].setupRoutes(server);
}
}

return server;
};

// Utility functions
function createAuthDetails( request ) {
var auth= { scopedUsers: {} };
request._connect_auth= auth;
if( request.session ) {
request.session.auth= auth;
}
};
192 changes: 4 additions & 188 deletions lib/index.js
Expand Up @@ -6,195 +6,10 @@
/**
* Module dependencies.
*/
var connect= require('connect')
, fs= require('fs')
, StrategyExecutor= require('./strategyExecutor')
, Tracing= require('./tracing');
var Auth= require('./auth_middleware')
, fs= require('fs');

/**
* Construct the authentication middleware.
* Construction can take 2 forms:
* auth(<Strategy>()|[<Strategy>()]) - A single configured strategy, or array of strategies.
* auth({ strategies:<Strategy>()|[<Strategy>()...]
* [trace: true|false|function(message, req, [scope])}]) - More powerful variant that allows for passing in other configuration options, none yet defined.
*/
var Auth = module.exports = function(optionsOrStrategy) {

var strategies;
var options;
var trace;
if( !optionsOrStrategy ) throw new Error("You must specify at least one strategy to use the authentication middleware, even if it is anonymous.");
// Constructor form 1
if( Array.isArray(optionsOrStrategy) || ( optionsOrStrategy.authenticate !== undefined &&
optionsOrStrategy.strategies === undefined ) ) {
strategies= Array.isArray(optionsOrStrategy) ? optionsOrStrategy : [optionsOrStrategy];
options= {trace: false};
}
else {
options= optionsOrStrategy
strategies= Array.isArray(optionsOrStrategy.strategies) ? optionsOrStrategy.strategies : [optionsOrStrategy.strategies];
}

if( !options.trace ) { // If options.trace is specified as false or undefined we no-op the messages.
trace= Tracing.nullTrace;
}
else if( options.trace === true ) { // If options.trace is really true then we log out to console
trace= Tracing.standardTrace;
}
else { // Custom provided trace function
trace= options.trace;
}

// Otherwise we use the provided trace function.

var isAuthenticated = function( request, scope ) {
if( scope === undefined ) {
return (request.getAuthDetails().user) ? true : false;
}
else {
return (request.getAuthDetails().scopedUsers[scope] && request.getAuthDetails().scopedUsers[scope].user) ? true : false;
}
};

function createAuthDetails(req) {
var auth= { scopedUsers: {} };
req._connect_auth= auth;
if( req.session ) {
req.session.auth= auth;
}
};


var server= connect.createServer(
function auth(req, res, next) {
// Setup the utility methods
req.authenticate = function(strategy, opts, callback) {
var strategy, opts, callback;
var scope;

//ughhh pull this rubbish somewhere tidy...
if( strategy && opts && callback ) {
var type= typeof strategy;
if( strategy.constructor != Array ) {
strategy= [strategy]
}
scope= opts.scope;
}
else if( strategy && opts ) {
callback= opts;
var type= typeof strategy;
if( strategy.constructor == Array ) {
// do nothing
}
else if( type == 'string' ) {
strategy= [strategy];
}
else if( type == 'object') {
scope= strategy.scope
strategy= undefined;
}
}
else if( strategy ) {
callback= strategy;
strategy= undefined;
}

// Choose the first strategy defined if no strategy provided
if( !strategy && strategies.length >0 ) {
strategy= [strategies[0].name];
}

trace( "Authenticating ("+req.headers.host + req.url+")", req, res, scope, ">>>" );
if( isAuthenticated(req, scope) ) {
trace( "Authentication successful (Already Authenticated)", req, res, scope, "<<<" );
callback(null, true);
}
else {
strategyExecutor.authenticate(strategy, scope, trace, req, res, function (error, executionResult) {
//TODO: This needs tidying up, the HTTP strategies have bled...
if( executionResult) {
req.getAuthDetails().errorResponse= executionResult.errorResponse;
}

if(error) {
trace( "Authentication error: "+ error, req, res, scope, "<<<" );
callback(error);
}
else {
if( executionResult.authenticated === true ) {
trace( "Authentication successful", req, res, scope, "<<<" );
if( scope === undefined) {
req.getAuthDetails().user= executionResult.user;
}
else {
if( req.getAuthDetails().scopedUsers[scope] === undefined ) {
req.getAuthDetails().scopedUsers[scope] = {};
}
req.getAuthDetails().scopedUsers[scope].user= executionResult.user;
}
}
else if( executionResult.authenticated === false ) {
trace( "Authentication failed", req, res, scope, "<<<" );
}
else {
trace( "Authentication ongoing (Requires browser interaction)", req, res, scope, "<<<" );
}
callback(null, executionResult.authenticated)
}
});
}
};
req.getAuthDetails= function() {
return req._connect_auth
};
req.isAuthenticated= function(scope) {
return isAuthenticated( req, scope );
};
req.isUnAuthenticated= function(scope) {
return !isAuthenticated( req, scope );
};
req.logout= function(scope, callback) {
// Optional scope, optional callback
if( scope !== undefined && typeof scope == "function" ) {
callback= scope;
scope= undefined;
}
trace( "Logout", req, res, scope, "!!!" );

if( scope === undefined) {
delete req.getAuthDetails().user;
req.getAuthDetails().scopedUsers= {};
}
else {
delete req.getAuthDetails().scopedUsers[scope].user;
}
if( callback ) callback();
}

// If there is a session middleware, use it.
if( req.session && req.session.auth ) {
req._connect_auth= req.session.auth;
}
else {
// Create the auth holder if needed.
if( ! req.getAuthDetails() ) {
createAuthDetails(req);
}
}
next();
});

// Some strategies require routes to be defined, so give them a chance to do so.
for(var i=0;i< strategies.length; i++ ) {
if( strategies[i].setupRoutes ) {
strategies[i].setupRoutes(server);
}
}

var strategyExecutor = new StrategyExecutor( strategies )

return server;
};
module.exports= Auth;

/**
* Auto-load bundled strategies with getters.
Expand All @@ -213,6 +28,7 @@ function augmentAuthWithStrategy(filename, path) {
enumerable:true});
}
}

//TODO: Meh could make this recurse neatly over directories, but I'm lazy.
fs.readdirSync(__dirname + '/auth.strategies').forEach(function(filename){
augmentAuthWithStrategy(filename, '/auth.strategies')
Expand Down

0 comments on commit 9b306d4

Please sign in to comment.