diff --git a/src/accesscontrol.js b/src/accesscontrol.js index dbf842946..b8c90a99b 100644 --- a/src/accesscontrol.js +++ b/src/accesscontrol.js @@ -15,11 +15,6 @@ exports = module.exports = { ROLE_OWNER: 'owner', - initialize: initialize, - uninitialize: uninitialize, - - accessTokenAuth: accessTokenAuth, - validateRoles: validateRoles, validateScope: validateScope, @@ -29,90 +24,9 @@ exports = module.exports = { }; var assert = require('assert'), - BasicStrategy = require('passport-http').BasicStrategy, - BearerStrategy = require('passport-http-bearer').Strategy, - clients = require('./clients'), - ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy, - ClientsError = clients.ClientsError, - DatabaseError = require('./databaseerror'), debug = require('debug')('box:accesscontrol'), - LocalStrategy = require('passport-local').Strategy, - passport = require('passport'), - tokendb = require('./tokendb'), - users = require('./users.js'), - UsersError = users.UsersError, _ = require('underscore'); -function initialize(callback) { - assert.strictEqual(typeof callback, 'function'); - - // serialize user into session - passport.serializeUser(function (user, callback) { - callback(null, user.id); - }); - - // deserialize user from session - passport.deserializeUser(function(userId, callback) { - users.get(userId, function (error, result) { - if (error) return callback(error); - - callback(null, result); - }); - }); - - // used when username/password is sent in request body. used in CLI tool login route - passport.use(new LocalStrategy(function (username, password, callback) { - if (username.indexOf('@') === -1) { - users.verifyWithUsername(username, password, function (error, result) { - if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); - if (error && error.reason === UsersError.WRONG_PASSWORD) return callback(null, false); - if (error) return callback(error); - if (!result) return callback(null, false); - callback(null, result); - }); - } else { - users.verifyWithEmail(username, password, function (error, result) { - if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); - if (error && error.reason === UsersError.WRONG_PASSWORD) return callback(null, false); - if (error) return callback(error); - if (!result) return callback(null, false); - callback(null, result); - }); - } - })); - - // Used to authenticate a OAuth2 client which uses clientId and clientSecret in the Authorization header - passport.use(new BasicStrategy(function (clientId, clientSecret, callback) { - clients.get(clientId, function (error, client) { - if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); - if (error) return callback(error); - if (client.clientSecret !== clientSecret) return callback(null, false); - callback(null, client); - }); - })); - - // Used to authenticate a OAuth2 client which uses clientId and clientSecret in the request body (client_id, client_secret) - passport.use(new ClientPasswordStrategy(function (clientId, clientSecret, callback) { - clients.get(clientId, function(error, client) { - if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); - if (error) { return callback(error); } - if (client.clientSecret !== clientSecret) { return callback(null, false); } - callback(null, client); - }); - })); - - // used for "Authorization: Bearer token" or access_token query param authentication - passport.use(new BearerStrategy(accessTokenAuth)); - - callback(null); -} - -function uninitialize(callback) { - assert.strictEqual(typeof callback, 'function'); - - callback(null); -} - function canonicalScope(scope) { var scopes = scope.split(','); scopes = scopes.map(function (s) { return s.replace(exports.SCOPE_ANY, exports.VALID_SCOPES.join(',')); }); @@ -132,28 +46,6 @@ function intersectScope(allowedScope, wantedScope) { return _.intersection(allowedScopes, wantedScopes).join(','); } -function accessTokenAuth(accessToken, callback) { - assert.strictEqual(typeof accessToken, 'string'); - assert.strictEqual(typeof callback, 'function'); - - tokendb.get(accessToken, function (error, token) { - if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, false); - if (error) return callback(error); - - users.get(token.identifier, function (error, user) { - if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); - if (error) return callback(error); - - // scopes here can define what capabilities that token carries - // passport put the 'info' object into req.authInfo, where we can further validate the scopes - var scope = intersectScope(user.scope, token.scope); - var info = { scope: scope, clientId: token.clientId }; - - callback(null, user, info); - }); - }); -} - function validateRoles(roles) { assert(Array.isArray(roles)); diff --git a/src/routes/accesscontrol.js b/src/routes/accesscontrol.js index a90d4e133..124026ad5 100644 --- a/src/routes/accesscontrol.js +++ b/src/routes/accesscontrol.js @@ -2,13 +2,117 @@ exports = module.exports = { scope: scope, - websocketAuth: websocketAuth + websocketAuth: websocketAuth, + initialize: initialize, + uninitialize: uninitialize }; var accesscontrol = require('../accesscontrol.js'), assert = require('assert'), + BasicStrategy = require('passport-http').BasicStrategy, + BearerStrategy = require('passport-http-bearer').Strategy, + clients = require('../clients.js'), + ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy, + ClientsError = clients.ClientsError, + DatabaseError = require('../databaseerror.js'), HttpError = require('connect-lastmile').HttpError, - passport = require('passport'); + LocalStrategy = require('passport-local').Strategy, + passport = require('passport'), + tokendb = require('../tokendb'), + users = require('../users.js'), + UsersError = users.UsersError; + +function initialize(callback) { + assert.strictEqual(typeof callback, 'function'); + + // serialize user into session + passport.serializeUser(function (user, callback) { + callback(null, user.id); + }); + + // deserialize user from session + passport.deserializeUser(function(userId, callback) { + users.get(userId, function (error, result) { + if (error) return callback(error); + + callback(null, result); + }); + }); + + // used when username/password is sent in request body. used in CLI tool login route + passport.use(new LocalStrategy(function (username, password, callback) { + if (username.indexOf('@') === -1) { + users.verifyWithUsername(username, password, function (error, result) { + if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); + if (error && error.reason === UsersError.WRONG_PASSWORD) return callback(null, false); + if (error) return callback(error); + if (!result) return callback(null, false); + callback(null, result); + }); + } else { + users.verifyWithEmail(username, password, function (error, result) { + if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); + if (error && error.reason === UsersError.WRONG_PASSWORD) return callback(null, false); + if (error) return callback(error); + if (!result) return callback(null, false); + callback(null, result); + }); + } + })); + + // Used to authenticate a OAuth2 client which uses clientId and clientSecret in the Authorization header + passport.use(new BasicStrategy(function (clientId, clientSecret, callback) { + clients.get(clientId, function (error, client) { + if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); + if (error) return callback(error); + if (client.clientSecret !== clientSecret) return callback(null, false); + callback(null, client); + }); + })); + + // Used to authenticate a OAuth2 client which uses clientId and clientSecret in the request body (client_id, client_secret) + passport.use(new ClientPasswordStrategy(function (clientId, clientSecret, callback) { + clients.get(clientId, function(error, client) { + if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); + if (error) { return callback(error); } + if (client.clientSecret !== clientSecret) { return callback(null, false); } + callback(null, client); + }); + })); + + // used for "Authorization: Bearer token" or access_token query param authentication + passport.use(new BearerStrategy(accessTokenAuth)); + + callback(null); +} + +function uninitialize(callback) { + assert.strictEqual(typeof callback, 'function'); + + callback(null); +} + +function accessTokenAuth(accessToken, callback) { + assert.strictEqual(typeof accessToken, 'string'); + assert.strictEqual(typeof callback, 'function'); + + tokendb.get(accessToken, function (error, token) { + if (error && error.reason === DatabaseError.NOT_FOUND) return callback(null, false); + if (error) return callback(error); + + users.get(token.identifier, function (error, user) { + if (error && error.reason === UsersError.NOT_FOUND) return callback(null, false); + if (error) return callback(error); + + // scopes here can define what capabilities that token carries + // passport put the 'info' object into req.authInfo, where we can further validate the scopes + var scope = accesscontrol.intersectScope(user.scope, token.scope); + var info = { scope: scope, clientId: token.clientId }; + + callback(null, user, info); + }); + }); +} // The scope middleware provides an auth middleware for routes. // @@ -41,7 +145,7 @@ function websocketAuth(requiredScopes, req, res, next) { if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized')); - accesscontrol.accessTokenAuth(req.query.access_token, function (error, user, info) { + accessTokenAuth(req.query.access_token, function (error, user, info) { if (error) return next(new HttpError(500, error.message)); if (!user) return next(new HttpError(401, 'Unauthorized')); diff --git a/src/server.js b/src/server.js index 69266ef4b..c303e1342 100644 --- a/src/server.js +++ b/src/server.js @@ -338,7 +338,7 @@ function start(callback) { gSysadminHttpServer = initializeSysadminExpressSync(); async.series([ - accesscontrol.initialize, // hooks up authentication strategies into passport + routes.accesscontrol.initialize, // hooks up authentication strategies into passport database.initialize, cloudron.initialize, setup.configureWebadmin, @@ -356,7 +356,7 @@ function stop(callback) { async.series([ cloudron.uninitialize, database.uninitialize, - accesscontrol.uninitialize, + routes.accesscontrol.uninitialize, gHttpServer.close.bind(gHttpServer), gSysadminHttpServer.close.bind(gSysadminHttpServer) ], function (error) {