remove oauth proxy backend logic
This commit is contained in:
+3
-5
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
@@ -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 = [ ];
|
||||
|
||||
+1
-2
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
+1
-5
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user