diff --git a/.esformatter b/.esformatter new file mode 100644 index 0000000..586cf19 --- /dev/null +++ b/.esformatter @@ -0,0 +1,295 @@ +{ + "preset": "jquery", + + "indent": { + "value": "\t", + "ArrayExpression": 1, + "AssignmentExpression": 1, + "BinaryExpression": 1, + "ConditionalExpression": 1, + "CallExpression": 1, + "CatchClause": 1, + "DoWhileStatement": 1, + "ForInStatement": 1, + "ForStatement": 1, + "FunctionDeclaration": 1, + "FunctionExpression": 1, + "IfStatement": 1, + "MemberExpression": 1, + "MultipleVariableDeclaration": 1, + "ObjectExpression": 1, + "ReturnStatement": 1, + "SwitchCase": 1, + "SwitchStatement": 1, + "TopLevelFunctionBlock": 1, + "TryStatement": 1, + "WhileStatement": 1 + }, + "lineBreak": { + "value": "\n", + "before": { + "AssignmentExpression": ">=1", + "AssignmentOperator": 0, + "BlockStatement": 0, + "CallExpression": -1, + "ConditionalExpression": ">=1", + "CatchOpeningBrace": 0, + "CatchClosingBrace": ">=1", + "CatchKeyword": 0, + "DeleteOperator": ">=1", + "DoWhileStatement": ">=1", + "DoWhileStatementOpeningBrace": 0, + "DoWhileStatementClosingBrace": ">=1", + "EndOfFile": -1, + "EmptyStatement": -1, + "FinallyOpeningBrace": 0, + "FinallyClosingBrace": ">=1", + "ForInStatement": ">=1", + "ForInStatementExpressionOpening": 0, + "ForInStatementExpressionClosing": 0, + "ForInStatementOpeningBrace": 0, + "ForInStatementClosingBrace": ">=1", + "ForStatement": ">=1", + "ForStatementExpressionOpening": 0, + "ForStatementExpressionClosing": "<2", + "ForStatementOpeningBrace": 0, + "ForStatementClosingBrace": ">=1", + "FunctionExpression": 0, + "FunctionExpressionOpeningBrace": 0, + "FunctionExpressionClosingBrace": ">=1", + "FunctionDeclaration": ">=1", + "FunctionDeclarationOpeningBrace": 0, + "FunctionDeclarationClosingBrace": ">=1", + "IfStatement": ">=1", + "IfStatementOpeningBrace": 0, + "IfStatementClosingBrace": ">=1", + "ElseIfStatement": 0, + "ElseIfStatementOpeningBrace": 0, + "ElseIfStatementClosingBrace": ">=1", + "ElseStatement": 0, + "ElseStatementOpeningBrace": 0, + "ElseStatementClosingBrace": ">=1", + "LogicalExpression": -1, + "ObjectExpressionClosingBrace": -1, + "Property": -1, + "ReturnStatement": -1, + "SwitchOpeningBrace": 0, + "SwitchClosingBrace": ">=1", + "ThisExpression": -1, + "ThrowStatement": ">=1", + "TryOpeningBrace": 0, + "TryClosingBrace": ">=1", + "VariableName": ">=1", + "VariableValue": 0, + "VariableDeclaration": ">=1", + "VariableDeclarationWithoutInit": ">=1", + "WhileStatement": ">=1", + "WhileStatementOpeningBrace": 0, + "WhileStatementClosingBrace": ">=1" + }, + "after": { + "AssignmentExpression": ">=1", + "AssignmentOperator": 0, + "BlockStatement": 0, + "CallExpression": -1, + "CatchOpeningBrace": ">=1", + "CatchClosingBrace": ">=0", + "CatchKeyword": 0, + "ConditionalExpression": ">=1", + "DeleteOperator": ">=1", + "DoWhileStatement": ">=1", + "DoWhileStatementOpeningBrace": ">=1", + "DoWhileStatementClosingBrace": 0, + "EmptyStatement": -1, + "FinallyOpeningBrace": ">=1", + "FinallyClosingBrace": ">=1", + "ForInStatement": ">=1", + "ForInStatementExpressionOpening": "<2", + "ForInStatementExpressionClosing": -1, + "ForInStatementOpeningBrace": ">=1", + "ForInStatementClosingBrace": ">=1", + "ForStatement": ">=1", + "ForStatementExpressionOpening": "<2", + "ForStatementExpressionClosing": -1, + "ForStatementOpeningBrace": ">=1", + "ForStatementClosingBrace": ">=1", + "FunctionExpression": ">=1", + "FunctionExpressionOpeningBrace": ">=1", + "FunctionExpressionClosingBrace": -1, + "FunctionDeclaration": ">=1", + "FunctionDeclarationOpeningBrace": ">=1", + "FunctionDeclarationClosingBrace": ">=1", + "IfStatement": ">=1", + "IfStatementOpeningBrace": ">=1", + "IfStatementClosingBrace": ">=1", + "ElseIfStatement": ">=1", + "ElseIfStatementOpeningBrace": ">=1", + "ElseIfStatementClosingBrace": ">=1", + "ElseStatement": ">=1", + "ElseStatementOpeningBrace": ">=1", + "ElseStatementClosingBrace": ">=1", + "LogicalExpression": -1, + "ObjectExpressionOpeningBrace": -1, + "Property": 0, + "ReturnStatement": -1, + "SwitchOpeningBrace": ">=1", + "SwitchClosingBrace": ">=1", + "ThisExpression": 0, + "ThrowStatement": ">=1", + "TryOpeningBrace": ">=1", + "TryClosingBrace": 0, + "VariableDeclaration": ">=1", + "WhileStatement": ">=1", + "WhileStatementOpeningBrace": ">=1", + "WhileStatementClosingBrace": ">=1" + } + }, + "whiteSpace": { + "value": " ", + "removeTrailing": 1, + "before": { + "ArrayExpressionOpening": 0, + "ArrayExpressionClosing": 1, + "ArrayExpressionComma": 0, + "ArgumentComma": 0, + "ArgumentList": 1, + "ArgumentListArrayExpression": 1, + "ArgumentListFunctionExpression": 1, + "ArgumentListObjectExpression": 1, + "AssignmentOperator": 1, + "BinaryExpression": 0, + "BinaryExpressionOperator": 1, + "BlockComment": 1, + "CallExpression": -1, + "CatchParameterList": 0, + "CatchOpeningBrace": 1, + "CatchClosingBrace": 1, + "CatchKeyword": 1, + "CommaOperator": 0, + "ConditionalExpressionConsequent": 1, + "ConditionalExpressionAlternate": 1, + "DoWhileStatementOpeningBrace": 1, + "DoWhileStatementClosingBrace": 1, + "DoWhileStatementConditional": 1, + "EmptyStatement": 0, + "ExpressionClosingParentheses": 1, + "FinallyOpeningBrace": 1, + "FinallyClosingBrace": 1, + "ForInStatement": 1, + "ForInStatementExpressionOpening": 1, + "ForInStatementExpressionClosing": 0, + "ForInStatementOpeningBrace": 1, + "ForInStatementClosingBrace": 1, + "ForStatement": 1, + "ForStatementExpressionOpening": 1, + "ForStatementExpressionClosing": 0, + "ForStatementOpeningBrace": 1, + "ForStatementClosingBrace": 1, + "ForStatementSemicolon": 0, + "FunctionDeclarationOpeningBrace": 1, + "FunctionDeclarationClosingBrace": 1, + "FunctionExpressionOpeningBrace": 1, + "FunctionExpressionClosingBrace": 1, + "IfStatementConditionalOpening": 1, + "IfStatementConditionalClosing": 1, + "IfStatementOpeningBrace": 1, + "IfStatementClosingBrace": 1, + "ElseStatementOpeningBrace": 1, + "ElseStatementClosingBrace": 1, + "ElseIfStatementOpeningBrace": 1, + "ElseIfStatementClosingBrace": 1, + "MemberExpressionClosing": 1, + "LineComment": 1, + "LogicalExpressionOperator": 1, + "ObjectExpressionClosingBrace": 1, + "Property": 1, + "PropertyValue": 1, + "ParameterComma": 0, + "ParameterList": 1, + "SwitchDiscriminantOpening": 1, + "SwitchDiscriminantClosing": 0, + "ThrowKeyword": 1, + "TryOpeningBrace": 1, + "TryClosingBrace": 1, + "UnaryExpressionOperator": 0, + "VariableName": 1, + "VariableValue": 1, + "WhileStatementConditionalOpening": 1, + "WhileStatementConditionalClosing": 0, + "WhileStatementOpeningBrace": 1, + "WhileStatementClosingBrace": 1 + }, + "after": { + "ArrayExpressionOpening": 1, + "ArrayExpressionClosing": 0, + "ArrayExpressionComma": 1, + "ArgumentComma": 1, + "ArgumentList": 1, + "ArgumentListArrayExpression": 1, + "ArgumentListFunctionExpression": 1, + "ArgumentListObjectExpression": 1, + "AssignmentOperator": 1, + "BinaryExpression": 0, + "BinaryExpressionOperator": 1, + "BlockComment": 1, + "CallExpression": 0, + "CatchParameterList": 0, + "CatchOpeningBrace": 1, + "CatchClosingBrace": 1, + "CatchKeyword": 1, + "CommaOperator": 1, + "ConditionalExpressionConsequent": 1, + "ConditionalExpressionTest": 1, + "DoWhileStatementOpeningBrace": 1, + "DoWhileStatementClosingBrace": 1, + "DoWhileStatementBody": 1, + "EmptyStatement": 0, + "ExpressionOpeningParentheses": 1, + "FinallyOpeningBrace": 1, + "FinallyClosingBrace": 1, + "ForInStatement": 1, + "ForInStatementExpressionOpening": 0, + "ForInStatementExpressionClosing": 1, + "ForInStatementOpeningBrace": 1, + "ForInStatementClosingBrace": 1, + "ForStatement": 1, + "ForStatementExpressionOpening": 0, + "ForStatementExpressionClosing": 1, + "ForStatementClosingBrace": 1, + "ForStatementOpeningBrace": 1, + "ForStatementSemicolon": 1, + "FunctionReservedWord": 0, + "FunctionName": 0, + "FunctionExpressionOpeningBrace": 1, + "FunctionExpressionClosingBrace": 0, + "FunctionDeclarationOpeningBrace": 1, + "FunctionDeclarationClosingBrace": 1, + "IfStatementConditionalOpening": 1, + "IfStatementConditionalClosing": 1, + "IfStatementOpeningBrace": 1, + "IfStatementClosingBrace": 1, + "ElseStatementOpeningBrace": 1, + "ElseStatementClosingBrace": 1, + "ElseIfStatementOpeningBrace": 1, + "ElseIfStatementClosingBrace": 1, + "MemberExpressionOpening": 1, + "LogicalExpressionOperator": 1, + "ObjectExpressionClosingBrace": 0, + "PropertyName": 0, + "PropertyValue": 0, + "ParameterComma": 1, + "ParameterList": 1, + "SwitchDiscriminantOpening": 0, + "SwitchDiscriminantClosing": 1, + "ThrowKeyword": 1, + "TryOpeningBrace": 1, + "TryClosingBrace": 1, + "UnaryExpressionOperator": 0, + "VariableName": 1, + "WhileStatementConditionalOpening": 0, + "WhileStatementConditionalClosing": 1, + "WhileStatementOpeningBrace": 1, + "WhileStatementClosingBrace": 1 + } + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b171f..254333a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## 0.3.0 Primary motivation here is to begin work on a version of autohost that will work well with a hypermedia library ( [hyped](https://github.com/leankit-labs/hyped) ). This is a breaking change because of several structural and naming changes to how resources get modeled. +### prerelease 16 + * Bug fix - public path was getting registered before middleware causing static resources under public to get served regardless of authorization. + * Formatting (esformatter and then manual clean up) + ### prerelease 15 Bug fix - passport should not attempt to initialize when no auth provider is passed to init. @@ -74,4 +78,4 @@ Eliminate "feature" that prefixes action URLs with resource name - ultimately a ## 0.2.0 -Rewritten from scratch to address several design flaws and lack of testability in the library. New design focuses on a general approach to resource handling and then passing off loaded resources to various transport adapters which then determine how to route incoming requests/messages to the correct resource/action. \ No newline at end of file +Rewritten from scratch to address several design flaws and lack of testability in the library. New design focuses on a general approach to resource handling and then passing off loaded resources to various transport adapters which then determine how to route incoming requests/messages to the correct resource/action. diff --git a/demo/index.js b/demo/index.js index a067f72..c1eec40 100644 --- a/demo/index.js +++ b/demo/index.js @@ -1,12 +1,12 @@ var host = require( '../src/index.js' ); var authProvider = require( 'autohost-nedb-auth' )( {} ); - -var redis = require( 'redis' ).createClient(); // assumes a locally running redis server -var RedisStore = require( 'connect-redis' )( host.session ); -var store = new RedisStore( { - client: redis, - prefix: 'ah:' - } ); +var hyped = require( 'hyped' )(); +// var redis = require( 'redis' ).createClient(); // assumes a locally running redis server +// var RedisStore = require( 'connect-redis' )( host.session ); +// var store = new RedisStore( { +// client: redis, +// prefix: 'ah:' +// } ); try { host.init( { @@ -19,16 +19,19 @@ try { anonymous: [ '/$', '/js', '/css' ], sessionId: 'myapp.sid', sessionSecret: 'youdontevenknow', - sessionStore: store, + noOptions: true, + urlStrategy: hyped.urlStrategy + // sessionStore: store, }, - authProvider ); - - + authProvider ) + .then( hyped.addResources ); + hyped.setupMiddleware( host ); + // }, require( 'autohost-nedb-auth' )( {} ) ); // }, require( 'autohost-riak-auth' )( - // { appName: 'ahdemo', + // { appName: 'ahdemo', // riak: { nodes: [ // { host: 'ubuntu' } // ] } // } ) ); -} catch( e ) { console.log( e.stack ); } \ No newline at end of file +} catch( e ) { console.log( e.stack ); } diff --git a/gulpfile.js b/gulpfile.js index c69b34e..c243953 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,7 +14,7 @@ function cover( done ) { function runSpecs() { // jshint ignore : line return gulp.src( [ './spec/*.spec.js' ], { read: false } ) - .pipe( mocha( { reporter: 'spec' } ) ); + .pipe( mocha( { reporter: 'spec' } ) ); } function writeReport( cb, openBrowser, tests ) { @@ -24,7 +24,7 @@ function writeReport( cb, openBrowser, tests ) { } ) .pipe( istanbul.writeReports() ) .on( 'end', function() { - if( openBrowser ) { + if ( openBrowser ) { open( './coverage/lcov-report/index.html' ); } cb(); @@ -53,12 +53,26 @@ gulp.task( 'test', function() { .on( 'error', process.exit.bind( process, 1 ) ); } ); +var esformatter = require( 'gulp-esformatter' ); + +gulp.task( 'format', [ 'format:source', 'format:specs' ] ); + +gulp.task( 'format:source', function() { + return gulp.src( './src/**/*' ) + .pipe( esformatter() ) + .pipe( gulp.dest( 'src' ) ); +} ); + +gulp.task( 'format:specs', function() { + // return gulp.src( specFilePaths ) + // .pipe( esformatter() ) + // .pipe( gulp.dest( "client/spec" ) ); +} ); + gulp.task( 'test-watch', function() { gulp.watch( [ './src/**/*', './spec/*.spec.js' ], [ 'continuous-test' ] ); } ); -gulp.task( 'default', [ 'continuous-coverage', 'coverage-watch' ], function() { -} ); +gulp.task( 'default', [ 'continuous-coverage', 'coverage-watch' ], function() {} ); -gulp.task( 'specs', [ 'continuous-test', 'test-watch' ], function() { -} ); \ No newline at end of file +gulp.task( 'specs', [ 'continuous-test', 'test-watch' ], function() {} ); diff --git a/package.json b/package.json index cdb86dd..3b51dae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "autohost", - "version": "0.3.0-15", + "version": "0.3.0-16", "description": "Resource driven, transport agnostic host", "main": "src/index.js", "dependencies": { @@ -11,6 +11,7 @@ "express": "^4.7.2", "express-session": "^1.7.2", "fount": "0.0.4", + "gulp-esformatter": "^1.0.1", "lodash": "^2.4.1", "multer": "^0.1.3", "node-uuid": "^1.4.1", @@ -32,6 +33,7 @@ "gulp-istanbul": "^0.5.0", "gulp-mocha": "^0.5.2", "gulp-mocha-phantomjs": "^0.4.0", + "hyped": "0.1.0-6", "open": "0.0.5", "passport-http": "^0.2.2", "passport-http-bearer": "^1.0.1", diff --git a/spec/http.adapter.spec.js b/spec/http.adapter.spec.js index a0d7da4..fab64c4 100644 --- a/spec/http.adapter.spec.js +++ b/spec/http.adapter.spec.js @@ -1,10 +1,11 @@ -var should = require( 'should' ); //jshint ignore:line -var requestor = require( 'request' ).defaults( { jar: true } ); +var should = require( 'should' ); //jshint ignore:line +var requestor = require( 'request' ).defaults( { jar: false } ); var metrics = require( 'cluster-metrics' ); +var path = require( 'path' ); var port = 88988; var config = { port: port, - anonymous: [ '/api/forward' ] + anonymous: [ '/api/test/forward' ] }; var authProvider = require( './auth/mock.js' )( config ); var passport = require( '../src/http/passport.js' )( config, authProvider, metrics ); @@ -22,11 +23,11 @@ var userRoles = function( user, roles ) { describe( 'with http adapter', function() { var cookieExpiresAt = new Date( Date.now() + 60000 ); var cleanup = function() { - userRoles( 'userman', [] ); - actionRoles( 'test.call', [] ); - actionRoles( 'test.forward', [] ); - actionRoles( 'test.echo', [] ); - }; + userRoles( 'userman', [] ); + actionRoles( 'test.call', [] ); + actionRoles( 'test.forward', [] ); + actionRoles( 'test.echo', [] ); + }; before( function() { authProvider.tokens = { 'blorp': 'userman' }; @@ -35,17 +36,17 @@ describe( 'with http adapter', function() { method: 'get', url: '/test/call/:one/:two', handle: function( env ) { - env.reply( { - data: 'ta-da!', + env.reply( { + data: 'ta-da!', headers: { 'test-header': 'look a header value!' }, cookies: { 'an-cookies': { - value: 'chocolate chip', - options: { - expires: cookieExpiresAt, - path: '/api', - domain: 'herpdederp.com' - } - } } + value: 'chocolate chip', + options: { + expires: cookieExpiresAt, + path: '/api', + domain: 'herpdederp.com' + } + } } } ); } }, { routes: {} } ); @@ -54,7 +55,7 @@ describe( 'with http adapter', function() { url: '/test/forward/:one/:two', handle: function( env ) { env.forwardTo( { - url: 'http://userman:herp@localhost:88988/api/test/call/10/20' + url: 'http://userman:hi@localhost:88988/api/test/call/10/20' } ); } }, { routes: {} } ); @@ -65,6 +66,7 @@ describe( 'with http adapter', function() { env.reply( { data: 'echo-echo-echo-echo-echo-o-o-o-o-o-o-oooooo' } ); } }, { routes: {} } ); + http.static( '/files', path.join( __dirname, './public' ) ); http.middleware( "/", function( req, res, next ) { req.context.noSoupForYou = req.query.deny; next(); @@ -79,11 +81,11 @@ describe( 'with http adapter', function() { actionRoles( 'test.call', [ 'admin' ] ); userRoles( 'userman', [ 'guest' ] ); requestor.get( { - url: 'http://userman:hi@localhost:88988/api/test/call/10/20' - }, function( err, resp ) { - result = resp; - done(); - } ); + url: 'http://userman:hi@localhost:88988/api/test/call/10/20' + }, function( err, resp ) { + result = resp; + done(); + } ); } ); it( 'should tell user to take a hike', function() { @@ -97,6 +99,31 @@ describe( 'with http adapter', function() { after( cleanup ); } ); + describe( 'when getting a static file with invalid credentials', function() { + var result; + + before( function( done ) { + actionRoles( 'test.call', [ 'admin' ] ); + userRoles( 'userman', [ 'guest' ] ); + requestor.get( { + url: 'http://localhost:88988/files/txt/hello.txt' + }, function( err, resp ) { + result = resp; + done(); + } ); + } ); + + it( 'should reject as unauthorized', function() { + result.body.should.equal( 'Unauthorized' ); + } ); + + it( 'should return 401', function() { + result.statusCode.should.equal( 401 ); + } ); + + after( cleanup ); + } ); + describe( 'when making a request with adequate permissions', function() { var result; var headers; @@ -105,15 +132,15 @@ describe( 'with http adapter', function() { actionRoles( 'test.call', [ 'guest' ] ); userRoles( 'userman', [ 'guest' ] ); requestor.get( { - url: 'http://localhost:88988/api/test/call/10/20', - headers: { - 'Authorization': 'Bearer blorp' - } - }, function( err, resp ) { - headers = resp.headers; - result = new Buffer( resp.body, 'utf-8' ).toString(); - done(); - } ); + url: 'http://localhost:88988/api/test/call/10/20', + headers: { + 'Authorization': 'Bearer blorp' + } + }, function( err, resp ) { + headers = resp.headers; + result = new Buffer( resp.body, 'utf-8' ).toString(); + done(); + } ); } ); it( 'should return action response', function() { @@ -125,7 +152,7 @@ describe( 'with http adapter', function() { } ); it( 'should return expected cookies', function() { - headers[ 'set-cookie' ].should.eql( [ 'an-cookies=chocolate%20chip; Domain=herpdederp.com; Path=/api; Expires=' + cookieExpiresAt.toUTCString() ] ); + headers[ 'set-cookie' ][ 0 ].should.eql( 'an-cookies=chocolate%20chip; Domain=herpdederp.com; Path=/api; Expires=' + cookieExpiresAt.toUTCString() ); } ); after( cleanup ); @@ -138,14 +165,14 @@ describe( 'with http adapter', function() { actionRoles( 'test.call', [ 'guest' ] ); userRoles( 'userman', [ 'guest' ] ); requestor.get( { - url: 'http://localhost:88988/api/test/call/10/20?deny=true', - headers: { - 'Authorization': 'Bearer blorp' - } - }, function( err, resp ) { - result = resp; - done(); - } ); + url: 'http://localhost:88988/api/test/call/10/20?deny=true', + headers: { + 'Authorization': 'Bearer blorp' + } + }, function( err, resp ) { + result = resp; + done(); + } ); } ); it( 'should tell user to take a hike', function() { @@ -158,7 +185,7 @@ describe( 'with http adapter', function() { after( cleanup ); } ); - + describe( 'when making a request to a pattern route with adequate permissions', function() { var result; @@ -166,15 +193,15 @@ describe( 'with http adapter', function() { actionRoles( 'test.call', [ 'guest' ] ); userRoles( 'userman', [ 'guest' ] ); requestor.get( { - url: 'http://localhost:88988/api/test/echo/10/20', - headers: { - 'Authorization': 'Bearer blorp' - } - }, function( err, resp ) { - result = resp; - result = new Buffer( resp.body, 'utf-8' ).toString(); - done(); - } ); + url: 'http://localhost:88988/api/test/echo/10/20', + headers: { + 'Authorization': 'Bearer blorp' + } + }, function( err, resp ) { + result = resp; + result = new Buffer( resp.body, 'utf-8' ).toString(); + done(); + } ); } ); it( 'should return action response', function() { @@ -191,11 +218,11 @@ describe( 'with http adapter', function() { actionRoles( 'test.call', [ 'guest' ] ); userRoles( 'userman', [ 'guest' ] ); requestor.get( { - url: 'http://localhost:88988/api/test/forward/10/20' - }, function( err, resp ) { - result = new Buffer( resp.body, 'utf-8' ).toString(); - done(); - } ); + url: 'http://localhost:88988/api/test/forward/10/20' + }, function( err, resp ) { + result = new Buffer( resp.body, 'utf-8' ).toString(); + done(); + } ); } ); it( 'should return action response', function() { @@ -206,4 +233,4 @@ describe( 'with http adapter', function() { } ); after( http.stop ); -} ); \ No newline at end of file +} ); diff --git a/src/api.js b/src/api.js index 3bcd247..1879a51 100644 --- a/src/api.js +++ b/src/api.js @@ -1,23 +1,23 @@ // TODO: this module needs a lot of clean up :( var _ = require( 'lodash' ); var path = require( 'path' ); -var when = require('when'); +var when = require( 'when' ); var nodeWhen = require( 'when/node' ); var fs = require( 'fs' ); var debug = require( 'debug' )( 'autohost:api' ); var readDirectory = nodeWhen.lift( fs.readdir ); var resources = {}; var wrapper = { - actionList: {}, - addAdapter: addAdapter, - clearAdapters: clearAdapters, - loadModule: loadModule, - loadResources: loadResources, - resources: resources, - start: start, - startAdapters: startAdapters, - stop: stop, -}; + actionList: {}, + addAdapter: addAdapter, + clearAdapters: clearAdapters, + loadModule: loadModule, + loadResources: loadResources, + resources: resources, + start: start, + startAdapters: startAdapters, + stop: stop, + }; var config; var adapters = []; var host; @@ -29,7 +29,7 @@ function addAdapter( adapter ) { //jshint ignore:line function attachPath( target, filePath ) { var dir = path.dirname( filePath ); - if( _.isArray( target ) ) { + if ( _.isArray( target ) ) { _.each( target, function( item ) { item._path = dir; } ); @@ -46,18 +46,21 @@ function clearAdapters() { //jshint ignore:line function getActions( resource ) { var list = wrapper.actionList[ resource.name ] = []; _.each( resource.actions, function( action, actionName ) { - list.push( [ resource.name, actionName ].join( '.' ) ); - } ); + list.push( [ resource.name, actionName ].join( '.' ) ); + } ); } function deepMerge( target, source ) { // jshint ignore:line _.each( source, function( val, key ) { var original = target[ key ]; - if( _.isObject( val ) ) { - if( original ) { deepMerge( original, val ); } - else { target[ key ] = val; } + if ( _.isObject( val ) ) { + if ( original ) { + deepMerge( original, val ); + } else { + target[ key ] = val; + } } else { - target[ key ] = ( original === undefined ) ? val : original; + target[ key ] = ( original === undefined ) ? val : original; } } ); } @@ -69,16 +72,16 @@ function getArguments( fn ) { // returns a list of resource files from a given parent directory function getResources( filePath ) { - if( fs.existsSync( filePath ) ) { + if ( fs.existsSync( filePath ) ) { return readDirectory( filePath ) .then( function( contents ) { return _.map( contents, function( item ) { var resourcePath = path.join( filePath, item, 'resource.js' ); - if( fs.existsSync( resourcePath ) ) { + if ( fs.existsSync( resourcePath ) ) { return resourcePath; } }.bind( this ) ); - }.bind( this ) ); + }.bind( this ) ); } else { return when( [] ); } @@ -87,11 +90,11 @@ function getResources( filePath ) { // loads internal resources, resources from config path and node module resources function loadAll( resourcePath ) { var loadActions = [ loadResources( resourcePath ) ] || []; - if( config.modules ) { + if ( config.modules ) { _.each( config.modules, function( mod ) { - var modPath = require.resolve( mod ); - loadActions.push( loadModule( modPath ) ); - } ); + var modPath = require.resolve( mod ); + loadActions.push( loadModule( modPath ) ); + } ); } return when.all( loadActions ); } @@ -106,12 +109,12 @@ function loadModule( resourcePath ) { // jshint ignore:line var modFn = require( resourcePath ); var args = getArguments( modFn ); args.shift(); - if( args.length ) { + if ( args.length ) { return fount.resolve( args ) .then( function( deps ) { var argList = _.map( args, function( arg ) { - return deps[ arg ]; - } ); + return deps[ arg ]; + } ); argList.unshift( host ); var mod = modFn.apply( modFn, argList ); attachPath( mod, resourcePath ); @@ -122,7 +125,7 @@ function loadModule( resourcePath ) { // jshint ignore:line attachPath( mod, resourcePath ); return when( mod ); } - } catch ( err ) { + } catch (err) { console.error( 'Error loading resource module at %s with: %s', resourcePath, err.stack ); return when( [] ); } @@ -143,14 +146,14 @@ function loadResources( filePath ) { //jshint ignore:line function normalizeResources( list ) { var flattened = _.flatten( list ); _.each( flattened, function( resource ) { - resources[ resource.name ] = resource; - getActions( resource ); - } ); + resources[ resource.name ] = resource; + getActions( resource ); + } ); return resources; } function processModule( mod ) { // jshint ignore:line - if( mod && mod.name ) { + if ( mod && mod.name ) { return processResource( mod, path.dirname( mod._path ) ); } else { debug( 'Skipping resource at %s - no valid metadata provided', mod._path ); @@ -160,12 +163,12 @@ function processModule( mod ) { // jshint ignore:line function processResource( resource ) { //jshint ignore:line var meta = _.map( adapters, function( adapter ) { - if( _.isArray( resource ) ) { + if ( _.isArray( resource ) ) { return _.reduce( resource, function( acc, x ) { - return deepMerge( x, adapter.resource( x, resource._path, resources ) ); - }, {} ); + return deepMerge( x, adapter.resource( x, resource._path, resources ) ); + }, {} ); } else { - return adapter.resource( resource, resource._path, resources ); + return adapter.resource( resource, resource._path, resources ); } } ); var container = {}; @@ -179,10 +182,10 @@ function processResources() { function reduce( acc, resource ) { //jshint ignore:line _.each( resource, function( val, key ) { - if( acc[ key ] ) { + if ( acc[ key ] ) { _.each( val, function( list, prop ) { - acc[ key ][ prop ] = list; - } ); + acc[ key ][ prop ] = list; + } ); } else { acc[ key ] = val; } @@ -194,10 +197,10 @@ function start( resourcePath, auth ) { //jshint ignore:line wrapper.actionList = {}; return loadAll( resourcePath ) .then( normalizeResources ) - .then( function ( list ) { + .then( function() { var meta = processResources(); host.actions = wrapper.actionList; - if( auth ) { + if ( auth ) { auth.updateActions( wrapper.actionList ) .then( function() { startAdapters( auth ); @@ -209,22 +212,24 @@ function start( resourcePath, auth ) { //jshint ignore:line } ); } -function stop() { +function stop() { // jshint ignore:line _.each( adapters, function( adapter ) { - adapter.stop(); - } ); + adapter.stop(); + } ); } -function startAdapters( auth ) { //jshint ignore:line +function startAdapters( auth ) { // jshint ignore:line _.each( adapters, function( adapter ) { - adapter.start( config, auth ); - } ); + adapter.start( config, auth ); + } ); } -function trimString( str ) { return str.trim(); } +function trimString( str ) { + return str.trim(); +} -function trim( list ) { - return ( list && list.length ) ? _.filter( list.map( trimString ) ) : []; +function trim( list ) { // jshint ignore:line + return ( list && list.length ) ? _.filter( list.map( trimString ) ) : []; } module.exports = function( ah, cfg ) { @@ -232,4 +237,4 @@ module.exports = function( ah, cfg ) { host = ah; fount = ah.fount; return wrapper; -}; \ No newline at end of file +}; diff --git a/src/http/adapter.js b/src/http/adapter.js index 914c647..4bc0987 100644 --- a/src/http/adapter.js +++ b/src/http/adapter.js @@ -20,9 +20,9 @@ var wrapper = { function buildActionUrl( resourceName, actionName, action, resource, resources ) { var prefix = config.apiPrefix === undefined ? 'api' : config.apiPrefix; - if( _.isRegExp( action.url ) ) { + if ( _.isRegExp( action.url ) ) { return regex.prefix( http.buildUrl( config.urlPrefix || '', prefix ), action.url ); - } else if( config.urlStrategy ) { + } else if ( config.urlStrategy ) { var url = config.urlStrategy( resourceName, actionName, action, resources ); prefix = hasPrefix( url ) ? '' : prefix; return http.buildUrl( prefix, url ); @@ -40,8 +40,8 @@ function buildActionAlias( resourceName, actionName ) { function buildPath( pathSpec ) { var hasLocalPrefix; pathSpec = pathSpec || ''; - if( _.isArray( pathSpec ) ) { - hasLocalPrefix = pathSpec[0].match( /^[.]\// ); + if ( _.isArray( pathSpec ) ) { + hasLocalPrefix = pathSpec[ 0 ].match( /^[.]\// ); pathSpec = path.join.apply( {}, pathSpec ); } pathSpec = pathSpec.replace( /^~/, process.env.HOME ); @@ -60,26 +60,26 @@ function checkPermissionFor( user, context, action ) { } ); } -function getUserString( user ) { +function getUserString( user ) { // jshint ignore:line return user.name ? user.name : JSON.stringify( user ); } -function hasPrefix( url ) { +function hasPrefix( url ) { // jshint ignore:line var prefix = http.buildUrl( config.urlPrefix || '', config.apiPrefix || '' ); return url.indexOf( prefix ) === 0; } -function start() { +function start() { // jshint ignore:line http.start( config, passport ); } -function stop() { +function stop() { // jshint ignore:line http.stop(); } -function wireupResource( resource, basePath, resources ) { +function wireupResource( resource, basePath, resources ) { // jshint ignore:line var meta = { routes: {} }; - if( resource.resources && resource.resources !== '' ) { + if ( resource.resources && resource.resources !== '' ) { var directory = buildPath( [ basePath, resource.resources ] ); http.static( '/' + resource.name, directory ); meta.path = { url: '/' + resource.name, directory: directory }; @@ -90,7 +90,7 @@ function wireupResource( resource, basePath, resources ) { return meta; } -function wireupAction( resource, actionName, action, meta, resources ) { +function wireupAction( resource, actionName, action, meta, resources ) { // jshint ignore:line var url = buildActionUrl( resource.name, actionName, action, resource, resources ); var alias = buildActionAlias( resource.name, actionName ); meta.routes[ actionName ] = { method: action.method, url: url }; @@ -103,15 +103,15 @@ function wireupAction( resource, actionName, action, meta, resources ) { var envelope = new HttpEnvelope( req, res ); action.handle.apply( resource, [ envelope ] ); }; - if( authStrategy ) { + if ( authStrategy ) { checkPermissionFor( req.user, req.context, alias ) .then( function( pass ) { - if( pass ) { + if ( pass ) { debug( 'HTTP activation of action %s (%s %s) for %s granted', alias, action.method, url, getUserString( req.user ) ); respond(); } else { debug( 'User %s was denied HTTP activation of action %s (%s %s)', getUserString( req.user ), alias, action.method, url ); - res.status( 403 ).send( "User lacks sufficient permissions" ); + res.status( 403 ).send( 'User lacks sufficient permissions' ); } } ); } else { @@ -123,11 +123,11 @@ function wireupAction( resource, actionName, action, meta, resources ) { module.exports = function( cfg, auth, httpLib, req, meter ) { config = cfg; authStrategy = auth; - if( auth ) { + if ( auth ) { passport = passportFn( cfg, auth, meter ); } http = httpLib; metrics = meter; HttpEnvelope = require( './httpEnvelope.js' )( req ); return wrapper; -}; \ No newline at end of file +}; diff --git a/src/http/http.js b/src/http/http.js index d3f93e8..2c86b55 100644 --- a/src/http/http.js +++ b/src/http/http.js @@ -17,18 +17,18 @@ function buildUrl() { var idx = 0, cleaned = [], segment; - while( idx < arguments.length ) { + while (idx < arguments.length) { segment = arguments[ idx ]; - if( segment.substr( 0, 1 ) === '/' ) { + if ( segment.substr( 0, 1 ) === '/' ) { segment = segment.substr( 1 ); } - if( segment.substr( segment.length-1, 1 ) === '/' ) { + if ( segment.substr( segment.length - 1, 1 ) === '/' ) { segment = segment.substring( 0, segment.length - 1 ); } - if( !_.isEmpty( segment ) ) { + if ( !_.isEmpty( segment ) ) { cleaned.push( segment ); } - idx ++; + idx++; } return cleaned.length ? '/' + cleaned.join( '/' ) : ''; } @@ -61,14 +61,14 @@ function createAuthMiddlewareStack() { // adaptation of express's initializing middleware // the original approach breaks engine-io function expressInit( req, res, next ) { // jshint ignore:line - req.next = next; - req.context = {}; - // patching this according to how express does it - /* jshint ignore:start */ - req.__proto__ = expreq; - res.__proto__ = expres; - /* jshint ignore:end */ - next(); + req.next = next; + req.context = {}; + // patching this according to how express does it + /* jshint ignore:start */ + req.__proto__ = expreq; + res.__proto__ = expres; + /* jshint ignore:end */ + next(); } function initialize() { @@ -76,49 +76,56 @@ function initialize() { var public = path.resolve( cwd, ( config.static || './public' ) ); config.tmp = path.resolve( cwd, ( config.temp || './tmp' ) ); - wrapper.static( '/', public ); - - _.each( middleware, function( m ) { m( wrapper.app ); } ); + _.each( middleware, function( m ) { + m( wrapper.app ); + } ); // apply user-supplied middleware - _.each( userMiddleware, function( m ) { m( wrapper.app ); } ); - _.each( routes, function( r ) { r(); } ); - _.each( paths, function( p ) { p(); } ); + _.each( userMiddleware, function( m ) { + m( wrapper.app ); + } ); + _.each( routes, function( r ) { + r(); + } ); + wrapper.static( '/', public ); + _.each( paths, function( p ) { + p(); + } ); } // this might be the worst thing to ever happen to anything ever // this is adapted directly from express layer.match -function parseAhead( router, req, done ){ - var idx = 0; - var stack = router.stack; - var params = {}; - var method = req.method ? req.method.toLowerCase() : undefined; - next(); +function parseAhead( router, req, done ) { + var idx = 0; + var stack = router.stack; + var params = {}; + var method = req.method ? req.method.toLowerCase() : undefined; + next(); - function next() { - var layer = stack[idx++]; - if (!layer) { - // strip dangling query params - params = _.transform( params, function( acc, v, k ) { - acc[ k ] = v.split( '?' )[ 0 ]; return acc; - }, {} ); - return done( params ); - } + function next() { // jshint ignore:line + var layer = stack[ idx++ ]; + if ( !layer ) { + // strip dangling query params + params = _.transform( params, function( acc, v, k ) { + acc[ k ] = v.split( '?' )[ 0 ]; return acc; + }, {} ); + return done( params ); + } - if (layer.method && layer.method !== method) { - return next(); + if ( layer.method && layer.method !== method ) { + return next(); + } + layer.match( req.originalUrl ); + params = _.merge( params, layer.params ); + next(); } - layer.match( req.originalUrl ); - params = _.merge( params, layer.params ); - next(); - } } // apply prefix to url if one exists function prefix( url ) { - if( config.urlPrefix ) { + if ( config.urlPrefix ) { var prefixIndex = url.indexOf( config.urlPrefix ); - var prefix = prefixIndex === 0 ? '' : config.urlPrefix; - return buildUrl( prefix, url ); + var appliedPrefix = prefixIndex === 0 ? '' : config.urlPrefix; + return buildUrl( appliedPrefix, url ); } else { return url; } @@ -129,7 +136,7 @@ function preprocessPathVariables( req, res, next ) { var original = req.param; req.preparams = params; req.param = function( name, dflt ) { - return params[ name ] || original( name, dflt ); + return params[ name ] || original( name, dflt ); }; next(); } ); @@ -150,7 +157,7 @@ function registerMiddleware( filter, callback ) { debug( 'MIDDLEWARE: %s mounted at %s', ( callback.name || 'anonymous' ), filter ); router.use( filter, callback ); }; - if( wrapper.app ) { + if ( wrapper.app ) { fn( wrapper.app ); } middleware.push( fn ); @@ -161,7 +168,7 @@ function registerUserMiddleware( filter, callback ) { debug( 'MIDDLEWARE: %s mounted at %s', ( callback.name || 'anonymous' ), filter ); router.use( filter, callback ); }; - if( wrapper.app ) { + if ( wrapper.app ) { fn( wrapper.app ); } userMiddleware.push( fn ); @@ -175,20 +182,20 @@ function registerRoute( url, verb, callback ) { url = prefix( url ); debug( 'ROUTE: %s %s -> %s', verb, url, ( callback.name || 'anonymous' ) ); wrapper.app[ verb ]( url, function( req, res ) { - if( config && config.handleRouteErrors ) { + if ( config && config.handleRouteErrors ) { try { callback( req, res ); - } catch ( err ) { + } catch (err) { metrics.meter( errors ).record(); debug( 'ERROR! route: %s %s failed with %s', verb, url, err.stack ); res.status( 500 ).send( 'An error occurred at route ' + verb + ' ' + url + '.' ); } } else { callback( req, res ); - } + } } ); }; - if( wrapper.app ) { + if ( wrapper.app ) { fn( wrapper.app ); } routes.push( fn ); @@ -202,7 +209,7 @@ function registerStaticPath( url, filePath ) { // jshint ignore:line wrapper.app.use( url, express.static( target ) ); }; paths.push( fn ); - if( wrapper.app ) { + if ( wrapper.app ) { fn(); } } @@ -210,12 +217,12 @@ function registerStaticPath( url, filePath ) { // jshint ignore:line function start( cfg, pass ) { config = cfg; wrapper.passport = pass; - if( cfg.parseAhead ) { + if ( cfg.parseAhead ) { registerMiddleware( '/', preprocessPathVariables ); } // if using an auth strategy, move cookie and session middleware before passport middleware // to take advantage of sessions/cookies and avoid authenticating on every request - if( pass ) { + if ( pass ) { middlewareLib.useCookies( registerMiddleware ); middlewareLib.useSession( registerMiddleware ); _.each( wrapper.passport.getMiddleware( '/' ), function( m ) { @@ -233,7 +240,7 @@ function start( cfg, pass ) { } function stop() { - if( wrapper.server ) { + if ( wrapper.server ) { wrapper.server.close(); wrapper.server = undefined; } @@ -263,4 +270,4 @@ module.exports = function( req, mw, metric ) { middlewareLib = mw; return wrapper; -}; \ No newline at end of file +}; diff --git a/src/http/httpEnvelope.js b/src/http/httpEnvelope.js index cd45b74..2351712 100644 --- a/src/http/httpEnvelope.js +++ b/src/http/httpEnvelope.js @@ -14,21 +14,21 @@ function HttpEnvelope( req, res ) { this.session = req.session; this.responseStream = res; this._original = { - req: req, - res: res - }; + req: req, + res: res + }; - [req.params, req.query].forEach(function(source){ - Object.keys(source).forEach(function(key){ + [ req.params, req.query ].forEach( function( source ) { + Object.keys( source ).forEach( function( key ) { var val = source[ key ]; - if( !this.data[ key ] ) { + if ( !this.data[ key ] ) { this.data[ key ] = val; } this.params[ key ] = val; - }.bind(this)); - }.bind(this)); + }.bind( this ) ); + }.bind( this ) ); - if( req.extendHttp ) { + if ( req.extendHttp ) { _.each( req.extendHttp, function( val, key ) { this[ key ] = val; }.bind( this ) ); @@ -40,7 +40,7 @@ HttpEnvelope.prototype.forwardTo = function( options ) { }; HttpEnvelope.prototype.redirect = function( statusCode, url ) { - if(url === undefined){ + if ( url === undefined ) { url = statusCode; statusCode = 302; } @@ -49,12 +49,12 @@ HttpEnvelope.prototype.redirect = function( statusCode, url ) { HttpEnvelope.prototype.reply = function( envelope ) { var code = envelope.statusCode || 200; - if( envelope.headers ) { + if ( envelope.headers ) { _.each( envelope.headers, function( v, k ) { this._original.res.set( k, v ); }.bind( this ) ); } - if( envelope.cookies ) { + if ( envelope.cookies ) { _.each( envelope.cookies, function( v, k ) { this._original.res.cookie( k, v.value, v.options ); }.bind( this ) ); @@ -73,4 +73,4 @@ HttpEnvelope.prototype.replyWithFile = function( contentType, fileName, fileStre module.exports = function( req ) { request = req; return HttpEnvelope; -}; \ No newline at end of file +}; diff --git a/src/http/middleware.js b/src/http/middleware.js index 273f736..7997b05 100644 --- a/src/http/middleware.js +++ b/src/http/middleware.js @@ -1,5 +1,5 @@ var bodyParser = require( 'body-parser' ); -var cookies = require('cookie-parser'); +var cookies = require( 'cookie-parser' ); var sessionLib = require( 'express-session' ); var multer = require( 'multer' ); var wrapper = { @@ -11,7 +11,7 @@ var wrapper = { var config, metrics, session, cookieParser; function applyCookieMiddleware( attach ) { // jshint ignore: line - if( !config.noCookies ) { + if ( !config.noCookies ) { attach( '/', cookieParser ); } } @@ -20,12 +20,12 @@ function applyMiddelware( attach, hasAuth ) { // jshint ignore: line // add a timer to track ALL requests attach( '/', requestMetrics ); - if( !hasAuth ) { + if ( !hasAuth ) { applyCookieMiddleware( attach ); } // turn on body parser unless turned off by the consumer - if( !config.noBody ) { + if ( !config.noBody ) { attach( '/', bodyParser.urlencoded( { extended: false } ) ); attach( '/', bodyParser.json() ); attach( '/', bodyParser.json( { type: 'application/vnd.api+json' } ) ); @@ -34,19 +34,19 @@ function applyMiddelware( attach, hasAuth ) { // jshint ignore: line } ) ); } - if( !hasAuth ) { + if ( !hasAuth ) { applySessionMiddleware( attach ); } // turn on cross origin unless turned off by the consumer - if( !config.noCrossOrigin ) { + if ( !config.noCrossOrigin ) { attach( '/', crossOrigin ); } } function applySessionMiddleware( attach ) { // jshint ignore: line // turn on sessions unless turned off by the consumer - if( !config.noSession ) { + if ( !config.noSession ) { attach( '/', session ); } } @@ -57,16 +57,16 @@ function crossOrigin( req, res, next ) { // jshint ignore: line next(); } -function configure( cfg ) { +function configure( cfg ) { // jshint ignore:line config = cfg; cfg.sessionStore = cfg.sessionStore || new sessionLib.MemoryStore(); - session = sessionLib( { - name: config.sessionId || 'ah.sid', - secret: config.sessionSecret || 'authostthing', - saveUninitialized: true, - resave: true, - store: cfg.sessionStore - } ); + session = sessionLib( { + name: config.sessionId || 'ah.sid', + secret: config.sessionSecret || 'authostthing', + saveUninitialized: true, + resave: true, + store: cfg.sessionStore + } ); } function requestMetrics( req, res, next ) { // jshint ignore: line @@ -74,9 +74,9 @@ function requestMetrics( req, res, next ) { // jshint ignore: line res.setMaxListeners( 0 ); var timerKey = [ req.method.toUpperCase(), req.url, 'timer' ].join( ' ' ); metrics.timer( timerKey ).start(); - res.once( 'finish', function() { - metrics.timer( timerKey ).record(); - } ); + res.once( 'finish', function() { + metrics.timer( timerKey ).record(); + } ); next(); } @@ -86,4 +86,4 @@ module.exports = function( meter ) { return wrapper; }; -module.exports.sessionLib = sessionLib; \ No newline at end of file +module.exports.sessionLib = sessionLib; diff --git a/src/http/passport.js b/src/http/passport.js index 679dd2a..43422e2 100644 --- a/src/http/passport.js +++ b/src/http/passport.js @@ -2,7 +2,9 @@ var _ = require( 'lodash' ); var when = require( 'when' ); var passport = require( 'passport' ); var debug = require( 'debug' )( 'autohost:passport' ); -var noOp = function() { return when( true ); }; +var noOp = function() { + return when( true ); + }; var userCountCheck = noOp; var authorizationErrorCount = 'autohost.authorization.errors'; var authorizationErrorRate = 'autohost.authorization.error.rate'; @@ -17,7 +19,7 @@ var metrics; function authConditionally( req, res, next ) { // jshint ignore:line // if previous middleware has said to skip auth OR // a user was attached from a session, skip authenticating - if( req.skipAuth || req.user ) { + if ( req.skipAuth || req.user ) { next(); } else { metrics.timer( authenticationTimer ).start(); @@ -28,21 +30,21 @@ function authConditionally( req, res, next ) { // jshint ignore:line function getAuthMiddleware( uri ) { var list = [ - { path: uri, fn: passportInitialize }, - { path: uri, fn: passportSession } - ] - .concat( _.map( anonPaths, function( pattern ) { - return { path: pattern, fn: skipAuthentication }; - } ) ) - .concat( [ { path: uri, fn: whenNoUsers }, - { path: uri, fn: authConditionally }, - { path: uri, fn: getRoles } ] ); + { path: uri, fn: passportInitialize }, + { path: uri, fn: passportSession } + ] + .concat( _.map( anonPaths, function( pattern ) { + return { path: pattern, fn: skipAuthentication }; + } ) ) + .concat( [ { path: uri, fn: whenNoUsers }, + { path: uri, fn: authConditionally }, + { path: uri, fn: getRoles } ] ); return list; } function getRoles( req, res, next ) { // jshint ignore:line var userName = _.isObject( req.user.name ) ? req.user.name.name : req.user.name; - if( userName === 'anonymous' ) { + if ( userName === 'anonymous' ) { req.user.roles = [ 'anonymous' ]; next(); } else { @@ -52,7 +54,7 @@ function getRoles( req, res, next ) { // jshint ignore:line metrics.counter( authorizationErrorCount ).incr(); metrics.meter( authorizationErrorRate ).record(); metrics.timer( authorizationTimer ).record(); - debug( 'Failed to get roles for %s with %s', getUserString( user ), err.stack ); + debug( 'Failed to get roles for %s with %s', getUserString( req.user ), err.stack ); res.status( 500 ).send( 'Could not determine user permissions' ); } ) .then( function( roles ) { @@ -65,7 +67,7 @@ function getRoles( req, res, next ) { // jshint ignore:line } function getSocketRoles( user ) { - if( user.name === 'anonymous' ) { + if ( user.name === 'anonymous' ) { return when( [ 'anonymous' ] ); } else { metrics.timer( authorizationTimer ).start(); @@ -85,7 +87,7 @@ function getSocketRoles( user ) { } } -function getUserString( user ) { +function getUserString( user ) { // jshint ignore:line return user.name ? user.name : JSON.stringify( user ); } @@ -95,7 +97,7 @@ function resetUserCount() { function skipAuthentication( req, res, next ) { // jshint ignore:line req.skipAuth = true; - if( !req.user ) { + if ( !req.user ) { debug( 'Skipping authentication and assigning user anonymous to request %s %s', req.method, req.url ); req.user = { id: 'anonymous', @@ -109,7 +111,7 @@ function skipAuthentication( req, res, next ) { // jshint ignore:line function whenNoUsers( req, res, next ) { // jshint ignore:line userCountCheck() .then( function( hasUsers ) { - if( hasUsers ) { + if ( hasUsers ) { userCountCheck = noOp; next(); } else { @@ -131,16 +133,16 @@ module.exports = function( config, authPlugin, meter ) { authProvider.initPassport( passport ); passport.serializeUser( authProvider.serializeUser ); passport.deserializeUser( authProvider.deserializeUser ); - if( config.anonymous ) { + if ( config.anonymous ) { anonPaths = _.isArray( config.anonymous ) ? config.anonymous : [ config.anonymous ]; } else { anonPaths = []; } withAuthLib( authProvider ); return { - getMiddleware: getAuthMiddleware, - getSocketRoles: getSocketRoles, - hasUsers: userCountCheck, - resetUserCheck: resetUserCount - }; -}; \ No newline at end of file + getMiddleware: getAuthMiddleware, + getSocketRoles: getSocketRoles, + hasUsers: userCountCheck, + resetUserCheck: resetUserCount + }; +}; diff --git a/src/http/regex.js b/src/http/regex.js index b6c7b8e..5092968 100644 --- a/src/http/regex.js +++ b/src/http/regex.js @@ -9,10 +9,10 @@ function getRegex( pattern ) { function applyPrefix( prefix, pattern ) { var original = parseRegex( pattern ); - if( !prefix ) { + if ( !prefix ) { return pattern; } else { - if( original.slice( 0, 1 ) === '^' ) { + if ( original.slice( 0, 1 ) === '^' ) { var trimmed = original.slice( 1, original.length ); var separator = trimmed.indexOf( '\/' ) === 1 ? '' : '/'; return getRegex( '^' + prefix + separator + trimmed ); @@ -26,4 +26,4 @@ module.exports = { create: getRegex, parse: parseRegex, prefix: applyPrefix -}; \ No newline at end of file +}; diff --git a/src/index.js b/src/index.js index efcf27e..b0591d1 100644 --- a/src/index.js +++ b/src/index.js @@ -14,31 +14,31 @@ var httpAdapter, socketAdapter; var initialized, api; var middleware = middlewareLib( metrics ); var wrapper = { - actions: undefined, - auth: undefined, - config: undefined, - fount: internalFount, - init: initialize, - metrics: metrics, - request: request, - meta: undefined, - http: httpFn( request, middleware, metrics ), - socket: undefined, - session: middlewareLib.sessionLib, - on: onEvent - }; + actions: undefined, + auth: undefined, + config: undefined, + fount: internalFount, + init: initialize, + metrics: metrics, + request: request, + meta: undefined, + http: httpFn( request, middleware, metrics ), + socket: undefined, + session: middlewareLib.sessionLib, + on: onEvent +}; function initialize( cfg, authProvider, fount ) { //jshint ignore:line api = require( './api.js' )( wrapper, cfg ); wrapper.fount = fount || internalFount; - if( initialized ) { + if ( initialized ) { api.startAdapters(); return when( api.resources ); } else { wrapper.config = cfg; wrapper.stop = api.stop; middleware.configure( cfg ); - if( when.isPromiseLike( authProvider ) ) { + if ( when.isPromiseLike( authProvider ) ) { return authProvider .then( function( result ) { wrapper.auth = result; @@ -63,9 +63,9 @@ function setup( authProvider ) { //jshint ignore:line api.addAdapter( httpAdapter ); // API metadata - if( !config.noOptions ) { + if ( !config.noOptions ) { wrapper.http.middleware( '/api', function( req, res, next ) { - if( req.method === 'OPTIONS' || req.method === 'options' ) { + if ( req.method === 'OPTIONS' || req.method === 'options' ) { res.status( 200 ).send( wrapper.meta ); } else { next(); diff --git a/src/websocket/adapter.js b/src/websocket/adapter.js index 87516e8..cd4efd4 100644 --- a/src/websocket/adapter.js +++ b/src/websocket/adapter.js @@ -6,12 +6,12 @@ var SocketEnvelope; var _ = require( 'lodash' ); var debug = require( 'debug' )( 'autohost:websocket-adapter' ); var wrapper = { - name: 'http', - action: wireupAction, - resource: wireupResource, - start: start, - stop: stop -}; + name: 'http', + action: wireupAction, + resource: wireupResource, + start: start, + stop: stop + }; function buildActionAlias( resourceName, actionName ) { return [ resourceName, actionName ].join( '.' ); @@ -24,7 +24,7 @@ function buildActionTopic( resourceName, action ) { function checkPermissionFor( user, context, action ) { debug( 'Checking %s\'s permissions for %s', getUserString( user ), action ); return authStrategy.checkPermission( user, action, context ) - .then( null, function(err) { + .then( null, function( err ) { debug( 'Error during check permissions: %s', err.stack ); return false; } ) @@ -33,27 +33,27 @@ function checkPermissionFor( user, context, action ) { } ); } -function getUserString( user ) { +function getUserString( user ) { // jshint ignore:line return user.name ? user.name : JSON.stringify( user ); } -function start() { +function start() { // jshint ignore:line socket.start( authStrategy ); } -function stop() { +function stop() { // jshint ignore:line socket.stop(); } -function wireupResource( resource ) { +function wireupResource( resource ) { // jshint ignore:line var meta = { topics: {} }; _.each( resource.actions, function( action, actionName ) { - wireupAction( resource, actionName, action, meta ); - } ); + wireupAction( resource, actionName, action, meta ); + } ); return meta; } -function wireupAction( resource, actionName, action, meta ) { +function wireupAction( resource, actionName, action, meta ) { // jshint ignore:line var topic = buildActionTopic( resource.name, action ); var alias = buildActionAlias( resource.name, actionName ); @@ -65,10 +65,10 @@ function wireupAction( resource, actionName, action, meta ) { var envelope = new SocketEnvelope( topic, message, socket ); action.handle.apply( resource, [ envelope ] ); }; - if( authStrategy ) { - checkPermissionFor( socket.user, context, alias ) + if ( authStrategy ) { + checkPermissionFor( socket.user, {}, alias ) .then( function( pass ) { - if( pass ) { + if ( pass ) { debug( 'WS activation of action %s for %s granted', alias, getUserString( socket.user ) ); respond(); } else { @@ -89,4 +89,4 @@ module.exports = function( cfg, auth, sock, meter ) { metrics = meter; SocketEnvelope = require( './socketEnvelope.js' ); return wrapper; -}; \ No newline at end of file +}; diff --git a/src/websocket/socket.js b/src/websocket/socket.js index b438a6f..f6de331 100644 --- a/src/websocket/socket.js +++ b/src/websocket/socket.js @@ -19,79 +19,79 @@ var wrapper = { wrapper.clients.lookup = {}; -function addClient( socket ) { +function addClient( socket ) { // jshint ignore:line wrapper.clients.push( socket ); eventChannel.publish( 'socket.client.connected', { socket: socket } ); } -function socketIdentified( id, socket ) { +function socketIdentified( id, socket ) { // jshint ignore:line wrapper.clients.lookup[ id ] = socket; eventChannel.publish( 'socket.client.identified', { id: id, socket: socket } ); } -function notifyClients( message, data ) { +function notifyClients( message, data ) { // jshint ignore:line debug( 'Notifying %d clients: %s %s', wrapper.clients.length, message, JSON.stringify( data ) ); _.each( wrapper.clients, function( client ) { - client.publish( message, data ); - } ); + client.publish( message, data ); + } ); } -function onTopic( topic, handle ) { +function onTopic( topic, handle ) { // jshint ignore:line debug( 'TOPIC: %s -> %s', topic, ( handle.name || 'anonymous' ) ); wrapper.topics[ topic ] = handle; - if( socketIO ) { + if ( socketIO ) { socketIO.on( topic, handle ); } } -function removeClient( socket ) { +function removeClient( socket ) { // jshint ignore:line var index = wrapper.clients.indexOf( socket ); - if( index >= 0 ) { + if ( index >= 0 ) { wrapper.clients.splice( index, 1 ); } - if( socket.id ) { + if ( socket.id ) { delete wrapper.clients.lookup[ socket.id ]; } eventChannel.publish( 'socket.client.closed', { id: socket.id, socket: socket } ); } -function sendToClient( id, message, data ) { +function sendToClient( id, message, data ) { // jshint ignore:line debug( 'Sending to clients %s: %s %s', id, message, JSON.stringify( data ) ); var socket = wrapper.clients.lookup[ id ]; - if( !socket ) { + if ( !socket ) { socket = wrapper.clients.find( clients, function( client ) { return client.user.id === id || client.user.name === id; } ); } - if( socket ) { + if ( socket ) { socket.publish( message, data ); return true; - } + } return false; } -function start() { - if( config.socketio || config.socketIO || config.socketIo ) { +function start() { // jshint ignore:line + if ( config.socketio || config.socketIO || config.socketIo ) { socketIO = require( './socketio.js' )( config, wrapper, http.passport ); socketIO.config( http ); } - if( config.websocket || config.websockets ) { + if ( config.websocket || config.websockets ) { websocket = require( './websocket' )( config, wrapper, http.passport ); websocket.config( http ); } } -function stop() { +function stop() { // jshint ignore:line _.each( wrapper.clients, function( socket ) { - if( socket ) { - socket.removeAllListeners(); - socket.close(); - } - } ); - if( socketIO ) { + if ( socket ) { + socket.removeAllListeners(); + socket.close(); + } + } ); + if ( socketIO ) { socketIO.stop(); } - if( websocket ) { + if ( websocket ) { websocket.stop(); } } @@ -100,4 +100,4 @@ module.exports = function( cfg, httpLib ) { config = cfg; http = httpLib; return wrapper; -}; \ No newline at end of file +}; diff --git a/src/websocket/socketEnvelope.js b/src/websocket/socketEnvelope.js index 284e6b5..818c7b6 100644 --- a/src/websocket/socketEnvelope.js +++ b/src/websocket/socketEnvelope.js @@ -11,9 +11,9 @@ function SocketEnvelope( topic, message, socket ) { this.responseStream = new SocketStream( this.replyTo, socket ); this.session = socket.session; this._original = { - message: message, - socket: socket - }; + message: message, + socket: socket + }; } SocketEnvelope.prototype.forwardTo = function( /* options */ ) { @@ -21,7 +21,7 @@ SocketEnvelope.prototype.forwardTo = function( /* options */ ) { }; SocketEnvelope.prototype.reply = function( envelope ) { - if( this._original.message.data ) { + if ( this._original.message.data ) { this._original.socket.publish( this.replyTo, envelope ); } else { this._original.socket.publish( this.replyTo, envelope.data ); @@ -33,4 +33,4 @@ SocketEnvelope.prototype.replyWithFile = function( contentType, fileName, fileSt fileStream.pipe( this.responseStream ); }; -module.exports = SocketEnvelope; \ No newline at end of file +module.exports = SocketEnvelope; diff --git a/src/websocket/socketStream.js b/src/websocket/socketStream.js index de940a5..dc0a8a1 100644 --- a/src/websocket/socketStream.js +++ b/src/websocket/socketStream.js @@ -3,7 +3,7 @@ var util = require( 'util' ); util.inherits( SocketStream, Writable ); -function SocketStream( replyTo, socket ) { +function SocketStream( replyTo, socket ) { // jshint ignore:line Writable.call( this, { decodeStrings: false, objectMode: true } ); this._socket = socket; this._index = 0; @@ -12,21 +12,21 @@ function SocketStream( replyTo, socket ) { SocketStream.prototype._write = function( chunk, encoding, callback ) { var envelope = { - index: this._index ++ + index: this._index++ }; - if( !chunk ) { + if ( !chunk ) { envelope.end = true; } else { envelope.data = chunk; } this._socket.publish( this._replyTo, envelope ); - if( callback ) { + if ( callback ) { callback(); } - if( envelope.end ) { + if ( envelope.end ) { this.emit( 'finish' ); } return true; }; -module.exports = SocketStream; \ No newline at end of file +module.exports = SocketStream; diff --git a/src/websocket/socketio.js b/src/websocket/socketio.js index 1eb0a4f..d8b14f1 100644 --- a/src/websocket/socketio.js +++ b/src/websocket/socketio.js @@ -14,24 +14,24 @@ function acceptSocket( socket ) { // grab user from request socket.user = handshake.user || { - id: 'anonymous', - name: 'anonymous' - }; - + id: 'anonymous', + name: 'anonymous' + }; + // copy session from request socket.session = handshake.session; // copy cookies from request from middleware socket.cookies = {}; - if( handshake.headers.cookie ) { + if ( handshake.headers.cookie ) { _.each( handshake.headers.cookie.split( ';' ), function( cookie ) { - var crumbs = cookie.split( '=' ); - socket.cookies[ crumbs[ 0 ].trim() ] = crumbs[ 1 ].trim(); - } ); + var crumbs = cookie.split( '=' ); + socket.cookies[ crumbs[ 0 ].trim() ] = crumbs[ 1 ].trim(); + } ); } // attach roles to user on socket - if( authStrategy ) { + if ( authStrategy ) { authStrategy.getSocketRoles( socket.user ) .then( function( roles ) { socket.user.roles = roles; @@ -51,7 +51,7 @@ function acceptSocket( socket ) { debug( 'Closing socket.io client (user: %s)', JSON.stringify( socket.user ) ); socket.removeAllListeners(); socket.disconnect( true ); - registry.remove( socket ); + registry.remove( socket ); }; // if client identifies itself, register id @@ -66,24 +66,24 @@ function acceptSocket( socket ) { // subscribe to registered topics _.each( registry.topics, function( callback, topic ) { - if( callback ) { - socket.on( topic, function( data ) { - callback( data, socket ); - } ); - } - } ); + if ( callback ) { + socket.on( topic, function( data ) { + callback( data, socket ); + } ); + } + } ); socket.publish( 'server.connected', { user: socket.user } ); socket.on( 'disconnect', function() { debug( 'socket.io client disconnected (user: %s)', JSON.stringify( socket.user ) ); socket.removeAllListeners(); - registry.remove( socket ); + registry.remove( socket ); } ); } function authSocketIO( req, allow ) { var allowed; - if( authStrategy ) { + if ( authStrategy ) { middleware .use( '/', function( hreq, hres, next ) { debug( 'Setting socket.io connection user to %s', hreq.user ); @@ -91,7 +91,7 @@ function authSocketIO( req, allow ) { next(); } ) .handle( req, req.res, function( err ) { - if( err ) { + if ( err ) { debug( 'Error in authenticating socket.io connection %s', err.stack ); allow( err ); } else { @@ -113,8 +113,10 @@ function configureSocketIO( http ) { function handle( topic, callback ) { _.each( registry.clients, function( client ) { - client.on( topic, function( data ) { callback( data, client ); } ); - } ); + client.on( topic, function( data ) { + callback( data, client ); + } ); + } ); } function stop() { @@ -127,8 +129,8 @@ module.exports = function( cfg, reg, auth ) { authStrategy = auth; registry = reg; return { - config: configureSocketIO, - on: handle, - stop: stop - }; -}; \ No newline at end of file + config: configureSocketIO, + on: handle, + stop: stop + }; +}; diff --git a/src/websocket/websocket.js b/src/websocket/websocket.js index 8523687..877fc9e 100644 --- a/src/websocket/websocket.js +++ b/src/websocket/websocket.js @@ -1,8 +1,8 @@ -var authStrategy, - registry, - socketServer, - middleware, - config; +var authStrategy; +var registry; +var socketServer; +var middleware; +var config; var _ = require( 'lodash' ); var WS = require ( 'websocket' ).server; var ServerResponse = require( 'http' ).ServerResponse; @@ -17,19 +17,19 @@ function acceptSocketRequest( request ) { var protocol = request.requestedProtocols[ 0 ]; var socket = request.accept( protocol, request.origin ); - + socket.user = request.user || { - id: 'anonymous', - name: 'anonymous' - }; - + id: 'anonymous', + name: 'anonymous' + }; + // grab session and cookies parsed from middleware socket.session = request.session; socket.cookies = request.cookies; // attach roles to user on socket - if( authStrategy ) { + if ( authStrategy ) { authStrategy.getSocketRoles( socket.user ) .then( function( roles ) { socket.user.roles = roles; @@ -41,7 +41,7 @@ function acceptSocketRequest( request ) { // reprocess generic message with topic sent socket.on( 'message', function( message ) { - if( message.type === 'utf8' ) { + if ( message.type === 'utf8' ) { var json = JSON.parse( message.utf8Data ); this.emit( json.topic, json.data, socket ); } @@ -50,7 +50,7 @@ function acceptSocketRequest( request ) { // normalize socket publishing interface socket.publish = function( topic, message ) { var payload = JSON.stringify( { topic: topic, data: message } ); - this.sendUTF(payload); + this.sendUTF( payload ); }; var originalClose = socket.close; @@ -58,7 +58,7 @@ function acceptSocketRequest( request ) { debug( 'Closing websocket client (user: %s)', JSON.stringify( socket.user ) ); socket.removeAllListeners(); originalClose(); - registry.remove( socket ); + registry.remove( socket ); }; // if client identifies itself, register id @@ -72,39 +72,41 @@ function acceptSocketRequest( request ) { // subscribe to registered topics _.each( registry.topics, function( callback, topic ) { - if( callback ) { - socket.on( topic, function( data, socket ) { callback( data, socket ); } ); + if ( callback ) { + socket.on( topic, function( data, socket ) { + callback( data, socket ); + } ); } } ); socket.publish( 'server.connected', { user: socket.user } ); socket.on( 'close', function() { debug( 'websocket client disconnected (user: %s)', JSON.stringify( socket.user ) ); - registry.remove( socket ); + registry.remove( socket ); } ); debug( 'Finished processing websocket connection attempt' ); } function configureWebsocket( http ) { - if( config.websockets || config.websocket ) { + if ( config.websockets || config.websocket ) { middleware = authStrategy ? http.getAuthMiddleware() : http.getMiddleware(); - socketServer = new WS( { - httpServer: http.server, - autoAcceptConnections: false - } ); + socketServer = new WS( { + httpServer: http.server, + autoAcceptConnections: false + } ); socketServer.on( 'request', handleWebSocketRequest ); } } -function handleWebSocketRequest( request ) { +function handleWebSocketRequest( request ) { // jshint ignore:line // if this doesn't end in websocket, we should ignore the request, it isn't for this lib - if( !/websocket[\/]?$/.test( request.resourceURL.path ) ) { + if ( !/websocket[\/]?$/.test( request.resourceURL.path ) ) { debug( 'Websocket connection attempt (%s) does not match allowed URL /websocket', request.resourceURL.path ); return; } // check origin - if( !allowOrigin( request.origin ) ) { + if ( !allowOrigin( request.origin ) ) { debug( 'Websocket origin (%s) does not match allowed origin %s', request.origin, config.origin ); request.reject(); return; @@ -112,10 +114,10 @@ function handleWebSocketRequest( request ) { var response = new ServerResponse( request.httpRequest ); response.assignSocket( request.socket ); - if( authStrategy ) { + if ( authStrategy ) { middleware .handle( request.httpRequest, response, function( err ) { - if( err || !request.httpRequest.user ) { + if ( err || !request.httpRequest.user ) { debug( 'Websocket connection rejected: authentication required' ); request.reject( 401, 'Authentication Required', { 'WWW-Authenticate': 'Basic' } ); } else { @@ -127,10 +129,10 @@ function handleWebSocketRequest( request ) { } ); } else { request.user = { - id: 'anonymous', - name: 'anonymous', - roles: [] - }; + id: 'anonymous', + name: 'anonymous', + roles: [] + }; acceptSocketRequest( request ); } } @@ -144,7 +146,7 @@ module.exports = function( cfg, reg, auth ) { authStrategy = auth; registry = reg; return { - config: configureWebsocket, - stop: stop - }; -}; \ No newline at end of file + config: configureWebsocket, + stop: stop + }; +};