diff --git a/box.js b/box.js index 210a7adbe..ef8838b1f 100755 --- a/box.js +++ b/box.js @@ -14,7 +14,6 @@ var appHealthMonitor = require('./src/apphealthmonitor.js'), async = require('async'), config = require('./src/config.js'), ldap = require('./src/ldap.js'), - oauthproxy = require('./src/oauthproxy.js'), server = require('./src/server.js'), simpleauth = require('./src/simpleauth.js'); @@ -37,7 +36,6 @@ async.series([ ldap.start, simpleauth.start, appHealthMonitor.start, - oauthproxy.start ], function (error) { if (error) { console.error('Error starting server', error); @@ -52,7 +50,6 @@ process.on('SIGINT', function () { server.stop(NOOP_CALLBACK); ldap.stop(NOOP_CALLBACK); simpleauth.stop(NOOP_CALLBACK); - oauthproxy.stop(NOOP_CALLBACK); setTimeout(process.exit.bind(process), 3000); }); @@ -60,6 +57,5 @@ process.on('SIGTERM', function () { server.stop(NOOP_CALLBACK); ldap.stop(NOOP_CALLBACK); simpleauth.stop(NOOP_CALLBACK); - oauthproxy.stop(NOOP_CALLBACK); setTimeout(process.exit.bind(process), 3000); }); diff --git a/migrations/20161113014146-appdb-drop-oauthProxy.js b/migrations/20161113014146-appdb-drop-oauthProxy.js new file mode 100644 index 000000000..70bc18ade --- /dev/null +++ b/migrations/20161113014146-appdb-drop-oauthProxy.js @@ -0,0 +1,16 @@ +var dbm = global.dbm || require('db-migrate'); +var type = dbm.dataType; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE apps DROP COLUMN oauthProxy', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE apps ADD COLUMN oauthProxy BOOLEAN DEFAULT 0', function (error) { + if (error) console.error(error); + callback(error); + }); +}; diff --git a/migrations/schema.sql b/migrations/schema.sql index a7226fb70..26968989a 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -63,7 +63,6 @@ CREATE TABLE IF NOT EXISTS apps( location VARCHAR(128) NOT NULL UNIQUE, dnsRecordId VARCHAR(512), accessRestrictionJson TEXT, // { users: [ ], groups: [ ] } - oauthProxy BOOLEAN DEFAULT 0, createdAt TIMESTAMP(2) NOT NULL DEFAULT CURRENT_TIMESTAMP, memoryLimit BIGINT DEFAULT 0, altDomain VARCHAR(256), diff --git a/setup/start/nginx/appconfig.ejs b/setup/start/nginx/appconfig.ejs index ff961470b..d66b5d844 100644 --- a/setup/start/nginx/appconfig.ejs +++ b/setup/start/nginx/appconfig.ejs @@ -80,9 +80,6 @@ server { index index.html index.htm; } -<% } else if ( endpoint === 'oauthproxy' ) { %> - proxy_pass http://127.0.0.1:3003; - proxy_set_header X-Cloudron-Proxy-Port <%= port %>; <% } else if ( endpoint === 'app' ) { %> proxy_pass http://127.0.0.1:<%= port %>; <% } else if ( endpoint === 'splash' ) { %> diff --git a/src/appdb.js b/src/appdb.js index 4d0fce71f..c85649576 100644 --- a/src/appdb.js +++ b/src/appdb.js @@ -60,7 +60,7 @@ var assert = require('assert'), var APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationState', 'apps.installationProgress', 'apps.runState', 'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.httpPort', 'apps.location', 'apps.dnsRecordId', 'apps.accessRestrictionJson', 'apps.lastBackupId', 'apps.oldConfigJson', 'apps.memoryLimit', 'apps.altDomain', - 'apps.xFrameOptions', 'apps.oauthProxy', 'apps.sso' ].join(','); + 'apps.xFrameOptions', 'apps.sso' ].join(','); var PORT_BINDINGS_FIELDS = [ 'hostPort', 'environmentVariable', 'appId' ].join(','); @@ -97,7 +97,6 @@ function postProcess(result) { // TODO remove later once all apps have this attribute result.xFrameOptions = result.xFrameOptions || 'SAMEORIGIN'; - result.oauthProxy = !!result.oauthProxy; // make it bool result.sso = !!result.sso; // make it bool } @@ -185,12 +184,11 @@ function add(id, appStoreId, manifest, location, portBindings, data, callback) { var xFrameOptions = data.xFrameOptions || ''; var installationState = data.installationState || exports.ISTATE_PENDING_INSTALL; var lastBackupId = data.lastBackupId || null; // used when cloning - var oauthProxy = data.oauthProxy || false; var queries = [ ]; queries.push({ - query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, oauthProxy) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', - args: [ id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId, oauthProxy ] + query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', + args: [ id, appStoreId, manifestJson, installationState, location, accessRestrictionJson, memoryLimit, altDomain, xFrameOptions, lastBackupId ] }); Object.keys(portBindings).forEach(function (env) { diff --git a/src/apps.js b/src/apps.js index 1144b82b4..2e0224bd1 100644 --- a/src/apps.js +++ b/src/apps.js @@ -149,7 +149,6 @@ function validatePortBindings(portBindings, tcpPorts) { config.get('sysadminPort'), /* sysadmin app server (lo) */ config.get('smtpPort'), /* internal smtp port (lo) */ config.get('ldapPort'), /* ldap server (lo) */ - config.get('oauthProxyPort'), /* oauth proxy server (lo) */ config.get('simpleAuthPort'), /* simple auth server (lo) */ 3306, /* mysql (lo) */ 4190, /* managesieve */ @@ -476,7 +475,6 @@ function install(data, auditSource, callback) { memoryLimit = data.memoryLimit || 0, altDomain = data.altDomain || null, xFrameOptions = data.xFrameOptions || 'SAMEORIGIN', - oauthProxy = data.oauthProxy === true, sso = 'sso' in data ? data.sso : null; assert(data.appStoreId || data.manifest); // atleast one of them is required @@ -532,7 +530,6 @@ function install(data, auditSource, callback) { memoryLimit: memoryLimit, altDomain: altDomain, xFrameOptions: xFrameOptions, - oauthProxy: oauthProxy, sso: sso }; @@ -612,10 +609,6 @@ function configure(appId, data, auditSource, callback) { if (error) return callback(error); } - if ('oauthProxy' in data) { - values.oauthProxy = data.oauthProxy; - } - // save cert to data/box/certs. TODO: move this to apptask when we have a real task queue if ('cert' in data && 'key' in data) { if (data.cert && data.key) { diff --git a/src/apptask.js b/src/apptask.js index e81825886..55e7299d3 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -12,8 +12,6 @@ exports = module.exports = { _unconfigureNginx: unconfigureNginx, _createVolume: createVolume, _deleteVolume: deleteVolume, - _allocateOAuthProxyCredentials: allocateOAuthProxyCredentials, - _removeOAuthProxyCredentials: removeOAuthProxyCredentials, _verifyManifest: verifyManifest, _registerSubdomain: registerSubdomain, _unregisterSubdomain: unregisterSubdomain, @@ -155,34 +153,6 @@ function deleteVolume(app, callback) { shell.sudo('deleteVolume', [ RMAPPDIR_CMD, app.id ], callback); } -function allocateOAuthProxyCredentials(app, callback) { - assert.strictEqual(typeof app, 'object'); - assert.strictEqual(typeof callback, 'function'); - - if (!app.oauthProxy) return callback(null); - - debugApp(app, 'Creating oauth proxy credentials'); - - var redirectURI = 'https://' + config.appFqdn(app.location); - var scope = 'profile'; - - clients.add(app.id, clients.TYPE_PROXY, redirectURI, scope, callback); -} - -function removeOAuthProxyCredentials(app, callback) { - assert.strictEqual(typeof app, 'object'); - assert.strictEqual(typeof callback, 'function'); - - clients.delByAppIdAndType(app.id, clients.TYPE_PROXY, function (error) { - if (error && error.reason !== ClientsError.NOT_FOUND) { - debugApp(app, 'Error removing OAuth client id', error); - return callback(error); - } - - callback(null); - }); -} - function addCollectdProfile(app, callback) { assert.strictEqual(typeof app, 'object'); assert.strictEqual(typeof callback, 'function'); @@ -393,7 +363,6 @@ function install(app, callback) { addons.teardownAddons.bind(null, app, app.manifest.addons), deleteVolume.bind(null, app), unregisterSubdomain.bind(null, app, app.location), - removeOAuthProxyCredentials.bind(null, app), // removeIcon.bind(null, app), // do not remove icon for non-appstore installs reserveHttpPort.bind(null, app), @@ -401,9 +370,6 @@ function install(app, callback) { updateApp.bind(null, app, { installationProgress: '20, Downloading icon' }), downloadIcon.bind(null, app), - updateApp.bind(null, app, { installationProgress: '25, Creating OAuth proxy credentials' }), - allocateOAuthProxyCredentials.bind(null, app), - updateApp.bind(null, app, { installationProgress: '30, Registering subdomain' }), registerSubdomain.bind(null, app), @@ -497,7 +463,6 @@ function restore(app, callback) { docker.deleteImage(app.oldConfig.manifest, done); }, - removeOAuthProxyCredentials.bind(null, app), removeIcon.bind(null, app), reserveHttpPort.bind(null, app), @@ -505,9 +470,6 @@ function restore(app, callback) { updateApp.bind(null, app, { installationProgress: '40, Downloading icon' }), downloadIcon.bind(null, app), - updateApp.bind(null, app, { installationProgress: '50, Create OAuth proxy credentials' }), - allocateOAuthProxyCredentials.bind(null, app), - updateApp.bind(null, app, { installationProgress: '55, Registering subdomain' }), // ip might change during upgrades registerSubdomain.bind(null, app), @@ -568,13 +530,9 @@ function configure(app, callback) { if (!app.oldConfig || app.oldConfig.location === app.location) return next(); unregisterSubdomain(app, app.oldConfig.location, next); }, - removeOAuthProxyCredentials.bind(null, app), reserveHttpPort.bind(null, app), - updateApp.bind(null, app, { installationProgress: '30, Create OAuth proxy credentials' }), - allocateOAuthProxyCredentials.bind(null, app), - updateApp.bind(null, app, { installationProgress: '35, Registering subdomain' }), registerSubdomain.bind(null, app), @@ -714,9 +672,6 @@ function uninstall(app, callback) { updateApp.bind(null, app, { installationProgress: '60, Unregistering subdomain' }), unregisterSubdomain.bind(null, app, app.location), - updateApp.bind(null, app, { installationProgress: '70, Remove OAuth credentials' }), - removeOAuthProxyCredentials.bind(null, app), - updateApp.bind(null, app, { installationProgress: '80, Cleanup icon' }), removeIcon.bind(null, app), diff --git a/src/config.js b/src/config.js index 13148ebc9..027e53603 100644 --- a/src/config.js +++ b/src/config.js @@ -81,7 +81,6 @@ function initConfig() { data.smtpPort = 2525; // // this value comes from mail container data.sysadminPort = 3001; data.ldapPort = 3002; - data.oauthProxyPort = 3003; data.simpleAuthPort = 3004; data.provider = 'caas'; data.appBundle = [ ]; diff --git a/src/nginx.js b/src/nginx.js index 0aa7cbdd9..1303a7787 100644 --- a/src/nginx.js +++ b/src/nginx.js @@ -49,8 +49,7 @@ function configureApp(app, certFilePath, keyFilePath, callback) { assert.strictEqual(typeof callback, 'function'); var sourceDir = path.resolve(__dirname, '..'); - var oauthProxy = app.oauthProxy; - var endpoint = oauthProxy ? 'oauthproxy' : 'app'; + var endpoint = 'app'; var vhost = app.altDomain || config.appFqdn(app.location); var data = { diff --git a/src/oauthproxy.js b/src/oauthproxy.js deleted file mode 100644 index 5cfce3ce7..000000000 --- a/src/oauthproxy.js +++ /dev/null @@ -1,196 +0,0 @@ -'use strict'; - -exports = module.exports = { - start: start, - stop: stop -}; - -var appdb = require('./appdb.js'), - assert = require('assert'), - clients = require('./clients.js'), - config = require('./config.js'), - DatabaseError = require('./databaseerror.js'), - debug = require('debug')('box:oauthproxy'), - express = require('express'), - http = require('http'), - proxy = require('proxy-middleware'), - session = require('cookie-session'), - superagent = require('superagent'), - tokendb = require('./tokendb.js'), - url = require('url'), - uuid = require('node-uuid'); - -var gSessions = {}; -var gProxyMiddlewareCache = {}; -var gHttpServer = null; - -var CALLBACK_URI = '/callback'; - -function clearSession(req) { - delete gSessions[req.session.id]; - - req.session.id = uuid.v4(); - gSessions[req.session.id] = {}; - - req.sessionData = gSessions[req.session.id]; - -} - -function attachSessionData(req, res, next) { - assert.strictEqual(typeof req.session, 'object'); - - if (!req.session.id || !gSessions[req.session.id]) clearSession(req); - - // attach the session data to the requeset - req.sessionData = gSessions[req.session.id]; - - next(); -} - -function verifySession(req, res, next) { - assert.strictEqual(typeof req.sessionData, 'object'); - - if (!req.sessionData.accessToken) { - req.authenticated = false; - return next(); - } - - tokendb.get(req.sessionData.accessToken, function (error, token) { - if (error) { - if (error.reason !== DatabaseError.NOT_FOUND) console.error(error); - clearSession(req); - req.authenticated = false; - } else { - req.authenticated = true; - } - - next(); - }); -} - -function authenticate(req, res, next) { - // proceed if we are authenticated - if (req.authenticated) return next(); - - if (req.path === CALLBACK_URI && req.sessionData.returnTo) { - // exchange auth code for an access token - var query = { - response_type: 'token', - client_id: req.sessionData.clientId - }; - - var data = { - grant_type: 'authorization_code', - code: req.query.code, - redirect_uri: req.sessionData.returnTo, - client_id: req.sessionData.clientId, - client_secret: req.sessionData.clientSecret - }; - - // use http admin origin so that it works with self-signed certs - superagent - .post(config.internalAdminOrigin() + '/api/v1/oauth/token') - .query(query).send(data) - .timeout(30 * 1000) - .end(function (error, result) { - if (error && !error.response) { - console.error(error); - return res.send(500, 'Unable to contact the oauth server.'); - } - if (result.statusCode !== 200) { - console.error('Failed to exchange auth code for a token.', result.statusCode, result.body); - return res.send(500, 'Failed to exchange auth code for a token.'); - } - - req.sessionData.accessToken = result.body.access_token; - - debug('user verified.'); - - // now redirect to the actual initially requested URL - res.redirect(req.sessionData.returnTo); - }); - } else { - var port = parseInt(req.headers['x-cloudron-proxy-port'], 10); - - if (!Number.isFinite(port)) { - console.error('Failed to parse nginx proxy header to get app port.'); - return res.send(500, 'Routing error. No forwarded port.'); - } - - debug('begin verifying user for app on port %s.', port); - - appdb.getByHttpPort(port, function (error, result) { - if (error) { - console.error('Unknown app.', error); - return res.send(500, 'Unknown app.'); - } - - clients.getByAppIdAndType(result.id, clients.TYPE_PROXY, function (error, result) { - if (error) { - console.error('Unknown OAuth client.', error); - return res.send(500, 'Unknown OAuth client.'); - } - - req.sessionData.port = port; - req.sessionData.returnTo = result.redirectURI + req.path; - req.sessionData.clientId = result.id; - req.sessionData.clientSecret = result.clientSecret; - - var callbackUrl = result.redirectURI + CALLBACK_URI; - var scope = 'profile'; - var oauthLogin = config.adminOrigin() + '/api/v1/oauth/dialog/authorize?response_type=code&client_id=' + result.id + '&redirect_uri=' + callbackUrl + '&scope=' + scope; - - debug('begin OAuth flow for client %s.', result.id); - - // begin the OAuth flow - res.redirect(oauthLogin); - }); - }); - } -} - -function forwardRequestToApp(req, res, next) { - var port = req.sessionData.port; - - debug('proxy request for port %s with path %s.', port, req.path); - - var proxyMiddleware = gProxyMiddlewareCache[port]; - if (!proxyMiddleware) { - console.log('Adding proxy middleware for port %d', port); - - proxyMiddleware = proxy(url.parse('http://127.0.0.1:' + port)); - gProxyMiddlewareCache[port] = proxyMiddleware; - } - - proxyMiddleware(req, res, next); -} - -function initializeServer() { - var app = express(); - var httpServer = http.createServer(app); - - httpServer.on('error', console.error); - - app - .use(session({ keys: ['blue', 'cheese', 'is', 'something'] })) - .use(attachSessionData) - .use(verifySession) - .use(authenticate) - .use(forwardRequestToApp); - - return httpServer; -} - -function start(callback) { - assert.strictEqual(typeof callback, 'function'); - - gHttpServer = initializeServer(); - - gHttpServer.listen(config.get('oauthProxyPort'), callback); -} - -function stop(callback) { - assert.strictEqual(typeof callback, 'function'); - - if (gHttpServer) gHttpServer.close(callback); -} diff --git a/src/routes/apps.js b/src/routes/apps.js index 8bc644d98..9e47f235b 100644 --- a/src/routes/apps.js +++ b/src/routes/apps.js @@ -54,8 +54,7 @@ function removeInternalAppFields(app) { fqdn: app.fqdn, memoryLimit: app.memoryLimit, altDomain: app.altDomain, - xFrameOptions: app.xFrameOptions, - oauthProxy: app.oauthProxy + xFrameOptions: app.xFrameOptions }; } @@ -124,7 +123,6 @@ function installApp(req, res, next) { if (data.xFrameOptions && typeof data.xFrameOptions !== 'string') return next(new HttpError(400, 'xFrameOptions must be a string')); - if ('oauthProxy' in data && typeof data.oauthProxy !== 'boolean') return next(new HttpError(400, 'oauthProxy must be a boolean')); if ('sso' in data && typeof data.sso !== 'boolean') return next(new HttpError(400, 'sso must be a boolean')); debug('Installing app :%j', data); @@ -163,8 +161,6 @@ function configureApp(req, res, next) { if (data.altDomain && typeof data.altDomain !== 'string') return next(new HttpError(400, 'altDomain must be a string')); if (data.xFrameOptions && typeof data.xFrameOptions !== 'string') return next(new HttpError(400, 'xFrameOptions must be a string')); - if ('oauthProxy' in data && typeof data.oauthProxy !== 'boolean') return next(new HttpError(400, 'oauthProxy must be a boolean')); - debug('Configuring app id:%s data:%j', req.params.id, data); apps.configure(req.params.id, data, auditSource(req), function (error) { diff --git a/src/test/database-test.js b/src/test/database-test.js index 97a52126a..f7ccac070 100644 --- a/src/test/database-test.js +++ b/src/test/database-test.js @@ -544,7 +544,6 @@ describe('database', function () { memoryLimit: 4294967296, altDomain: null, xFrameOptions: 'DENY', - oauthProxy: false, sso: true }; var APP_1 = { @@ -566,7 +565,6 @@ describe('database', function () { memoryLimit: 0, altDomain: null, xFrameOptions: 'SAMEORIGIN', - oauthProxy: false, sso: true };