diff --git a/src/accesscontrol.js b/src/accesscontrol.js index 1f25f18d9..5190f9d31 100644 --- a/src/accesscontrol.js +++ b/src/accesscontrol.js @@ -13,6 +13,11 @@ exports = module.exports = { SCOPE_ANY: '*', + initialize: initialize, + uninitialize: uninitialize, + + accessTokenAuth: accessTokenAuth, + validateScope: validateScope, validateRequestedScopes: validateRequestedScopes, normalizeScope: normalizeScope, @@ -20,9 +25,136 @@ 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, + crypto = require('crypto'), + 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'); + + passport.serializeUser(function (user, callback) { + callback(null, user.id); + }); + + passport.deserializeUser(function(userId, callback) { + users.get(userId, function (error, result) { + if (error) return callback(error); + + var md5 = crypto.createHash('md5').update(result.email).digest('hex'); + result.gravatar = 'https://www.gravatar.com/avatar/' + md5 + '.jpg?s=24&d=mm'; + + callback(null, result); + }); + }); + + 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); + }); + } + })); + + passport.use(new BasicStrategy(function (username, password, callback) { + if (username.indexOf('cid-') === 0) { + debug('BasicStrategy: detected client id %s instead of username:password', username); + // username is actually client id here + // password is client secret + clients.get(username, function (error, client) { + if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); + if (error) return callback(error); + if (client.clientSecret != password) return callback(null, false); + return callback(null, client); + }); + } else { + 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); + }); + } + })); + + 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); } + return callback(null, client); + }); + })); + + passport.use(new BearerStrategy(accessTokenAuth)); + + callback(null); +} + +function uninitialize(callback) { + assert.strictEqual(typeof callback, 'function'); + + callback(null); +} + +function normalizeScope(allowedScope, wantedScope) { + assert.strictEqual(typeof allowedScope, 'string'); + assert.strictEqual(typeof wantedScope, 'string'); + + const allowedScopes = allowedScope.split(','); + const wantedScopes = wantedScope.split(','); + + if (allowedScopes.indexOf(exports.SCOPE_ANY) !== -1) return wantedScope; + if (wantedScopes.indexOf(exports.SCOPE_ANY) !== -1) return allowedScope; + + 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 = normalizeScope(user.scope, token.scope); + var info = { scope: scope, clientId: token.clientId }; + + callback(null, user, info); + }); + }); +} + function validateScope(scope) { assert.strictEqual(typeof scope, 'string'); @@ -55,19 +187,6 @@ function validateRequestedScopes(authInfo, requestedScopes) { return null; } -function normalizeScope(allowedScope, wantedScope) { - assert.strictEqual(typeof allowedScope, 'string'); - assert.strictEqual(typeof wantedScope, 'string'); - - const allowedScopes = allowedScope.split(','); - const wantedScopes = wantedScope.split(','); - - if (allowedScopes.indexOf(exports.SCOPE_ANY) !== -1) return wantedScope; - if (wantedScopes.indexOf(exports.SCOPE_ANY) !== -1) return allowedScope; - - return _.intersection(allowedScopes, wantedScopes).join(','); -} - function canonicalScope(scope) { return scope.replace(exports.SCOPE_ANY, exports.VALID_SCOPES.join(',')); } diff --git a/src/auth.js b/src/auth.js deleted file mode 100644 index e8777321f..000000000 --- a/src/auth.js +++ /dev/null @@ -1,126 +0,0 @@ -'use strict'; - -exports = module.exports = { - initialize: initialize, - uninitialize: uninitialize, - - accessTokenAuth: accessTokenAuth -}; - -var accesscontrol = require('./accesscontrol.js'), - 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:auth'), - LocalStrategy = require('passport-local').Strategy, - crypto = require('crypto'), - passport = require('passport'), - tokendb = require('./tokendb'), - users = require('./users.js'), - UsersError = users.UsersError; - -function initialize(callback) { - assert.strictEqual(typeof callback, 'function'); - - passport.serializeUser(function (user, callback) { - callback(null, user.id); - }); - - passport.deserializeUser(function(userId, callback) { - users.get(userId, function (error, result) { - if (error) return callback(error); - - var md5 = crypto.createHash('md5').update(result.email).digest('hex'); - result.gravatar = 'https://www.gravatar.com/avatar/' + md5 + '.jpg?s=24&d=mm'; - - callback(null, result); - }); - }); - - 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); - }); - } - })); - - passport.use(new BasicStrategy(function (username, password, callback) { - if (username.indexOf('cid-') === 0) { - debug('BasicStrategy: detected client id %s instead of username:password', username); - // username is actually client id here - // password is client secret - clients.get(username, function (error, client) { - if (error && error.reason === ClientsError.NOT_FOUND) return callback(null, false); - if (error) return callback(error); - if (client.clientSecret != password) return callback(null, false); - return callback(null, client); - }); - } else { - 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); - }); - } - })); - - 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); } - return callback(null, client); - }); - })); - - 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.normalizeScope(user.scope, token.scope); - var info = { scope: scope, clientId: token.clientId }; - - callback(null, user, info); - }); - }); -} diff --git a/src/routes/accesscontrol.js b/src/routes/accesscontrol.js index 0702eb35f..679641e25 100644 --- a/src/routes/accesscontrol.js +++ b/src/routes/accesscontrol.js @@ -7,7 +7,6 @@ exports = module.exports = { var accesscontrol = require('../accesscontrol.js'), assert = require('assert'), - auth = require('../auth.js'), debug = require('debug')('box:routes/accesscontrol'), HttpError = require('connect-lastmile').HttpError, passport = require('passport'); @@ -44,7 +43,7 @@ function websocketAuth(requestedScopes, req, res, next) { if (typeof req.query.access_token !== 'string') return next(new HttpError(401, 'Unauthorized')); - auth.accessTokenAuth(req.query.access_token, function (error, user, info) { + accesscontrol.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 7e5dc10d3..5371cb47e 100644 --- a/src/server.js +++ b/src/server.js @@ -8,7 +8,6 @@ exports = module.exports = { var accesscontrol = require('./accesscontrol.js'), assert = require('assert'), async = require('async'), - auth = require('./auth.js'), cloudron = require('./cloudron.js'), config = require('./config.js'), database = require('./database.js'), @@ -332,13 +331,13 @@ function start(callback) { assert.strictEqual(typeof callback, 'function'); assert.strictEqual(gHttpServer, null, 'Server is already up and running.'); - routes.oauth2.initialize(); + routes.oauth2.initialize(); // init's the oauth server gHttpServer = initializeExpressSync(); gSysadminHttpServer = initializeSysadminExpressSync(); async.series([ - auth.initialize, + accesscontrol.initialize, // hooks up authentication strategies into passport database.initialize, cloudron.initialize, setup.configureWebadmin, @@ -356,7 +355,7 @@ function stop(callback) { async.series([ cloudron.uninitialize, database.uninitialize, - auth.uninitialize, + accesscontrol.uninitialize, gHttpServer.close.bind(gHttpServer), gSysadminHttpServer.close.bind(gSysadminHttpServer) ], function (error) {