2015-07-20 00:09:47 -07:00
|
|
|
'use strict';
|
|
|
|
|
|
2015-09-14 11:57:33 -07:00
|
|
|
exports = module.exports = {
|
|
|
|
|
start: start,
|
|
|
|
|
stop: stop
|
|
|
|
|
};
|
|
|
|
|
|
2015-09-14 13:00:03 -07:00
|
|
|
var appdb = require('./appdb.js'),
|
2015-07-20 00:09:47 -07:00
|
|
|
assert = require('assert'),
|
2015-09-14 13:00:03 -07:00
|
|
|
clientdb = require('./clientdb.js'),
|
|
|
|
|
config = require('./config.js'),
|
2015-10-21 16:05:11 +02:00
|
|
|
DatabaseError = require('./databaseerror.js'),
|
2015-07-20 00:09:47 -07:00
|
|
|
debug = require('debug')('box:proxy'),
|
2015-09-14 11:28:49 -07:00
|
|
|
express = require('express'),
|
|
|
|
|
http = require('http'),
|
2015-07-20 00:09:47 -07:00
|
|
|
proxy = require('proxy-middleware'),
|
|
|
|
|
session = require('cookie-session'),
|
2015-09-14 11:28:49 -07:00
|
|
|
superagent = require('superagent'),
|
2015-10-21 16:05:11 +02:00
|
|
|
tokendb = require('./tokendb.js'),
|
2015-09-14 11:28:49 -07:00
|
|
|
url = require('url'),
|
|
|
|
|
uuid = require('node-uuid');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
|
|
|
|
var gSessions = {};
|
|
|
|
|
var gProxyMiddlewareCache = {};
|
2015-09-14 11:28:49 -07:00
|
|
|
var gHttpServer = null;
|
2015-07-20 00:09:47 -07:00
|
|
|
|
|
|
|
|
var CALLBACK_URI = '/callback';
|
|
|
|
|
|
2015-10-21 16:05:11 +02:00
|
|
|
function clearSession(req) {
|
|
|
|
|
delete gSessions[req.session.id];
|
|
|
|
|
|
|
|
|
|
req.session.id = uuid.v4();
|
|
|
|
|
gSessions[req.session.id] = {};
|
|
|
|
|
|
|
|
|
|
req.sessionData = gSessions[req.session.id];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
function attachSessionData(req, res, next) {
|
|
|
|
|
assert.strictEqual(typeof req.session, 'object');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-10-21 16:05:11 +02:00
|
|
|
if (!req.session.id || !gSessions[req.session.id]) clearSession(req);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
// attach the session data to the requeset
|
|
|
|
|
req.sessionData = gSessions[req.session.id];
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
next();
|
|
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
function verifySession(req, res, next) {
|
|
|
|
|
assert.strictEqual(typeof req.sessionData, 'object');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
if (!req.sessionData.accessToken) {
|
|
|
|
|
req.authenticated = false;
|
|
|
|
|
return next();
|
|
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-10-21 16:05:11 +02:00
|
|
|
tokendb.get(req.sessionData.accessToken, function (error, token) {
|
|
|
|
|
if (error) {
|
|
|
|
|
if (error.reason !== DatabaseError.NOT_FOUND) console.error(error);
|
|
|
|
|
clearSession(req);
|
2015-07-20 00:09:47 -07:00
|
|
|
req.authenticated = false;
|
2015-09-14 11:22:28 -07:00
|
|
|
} else {
|
|
|
|
|
req.authenticated = true;
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
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
|
|
|
|
|
};
|
|
|
|
|
|
2015-09-16 10:13:11 -07:00
|
|
|
// use http admin origin so that it works with self-signed certs
|
|
|
|
|
superagent
|
|
|
|
|
.post(config.internalAdminOrigin() + '/api/v1/oauth/token')
|
|
|
|
|
.query(query).send(data)
|
|
|
|
|
.end(function (error, result) {
|
2015-12-15 09:12:52 -08:00
|
|
|
if (error && !error.response) {
|
2015-07-20 00:09:47 -07:00
|
|
|
console.error(error);
|
2015-09-14 11:22:28 -07:00
|
|
|
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.');
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
req.sessionData.accessToken = result.body.access_token;
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
debug('user verified.');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
// now redirect to the actual initially requested URL
|
|
|
|
|
res.redirect(req.sessionData.returnTo);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
var port = parseInt(req.headers['x-cloudron-proxy-port'], 10);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
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.');
|
|
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
debug('begin verifying user for app on port %s.', port);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
appdb.getByHttpPort(port, function (error, result) {
|
|
|
|
|
if (error) {
|
|
|
|
|
console.error('Unknown app.', error);
|
|
|
|
|
return res.send(500, 'Unknown app.');
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
2015-10-19 08:57:41 -07:00
|
|
|
clientdb.getByAppIdAndType(result.id, clientdb.TYPE_PROXY, function (error, result) {
|
2015-07-20 00:09:47 -07:00
|
|
|
if (error) {
|
2016-02-09 18:53:14 -08:00
|
|
|
console.error('Unknown OAuth client.', error);
|
2015-09-14 11:22:28 -07:00
|
|
|
return res.send(500, 'Unknown OAuth client.');
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
req.sessionData.port = port;
|
|
|
|
|
req.sessionData.returnTo = result.redirectURI + req.path;
|
|
|
|
|
req.sessionData.clientId = result.id;
|
|
|
|
|
req.sessionData.clientSecret = result.clientSecret;
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
var callbackUrl = result.redirectURI + CALLBACK_URI;
|
2015-10-15 12:50:48 +02:00
|
|
|
var scope = 'profile';
|
2015-09-14 11:22:28 -07:00
|
|
|
var oauthLogin = config.adminOrigin() + '/api/v1/oauth/dialog/authorize?response_type=code&client_id=' + result.id + '&redirect_uri=' + callbackUrl + '&scope=' + scope;
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
debug('begin OAuth flow for client %s.', result.name);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
// begin the OAuth flow
|
|
|
|
|
res.redirect(oauthLogin);
|
2015-07-20 00:09:47 -07:00
|
|
|
});
|
2015-09-14 11:22:28 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
function forwardRequestToApp(req, res, next) {
|
|
|
|
|
var port = req.sessionData.port;
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
debug('proxy request for port %s with path %s.', port, req.path);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
var proxyMiddleware = gProxyMiddlewareCache[port];
|
|
|
|
|
if (!proxyMiddleware) {
|
|
|
|
|
console.log('Adding proxy middleware for port %d', port);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
proxyMiddleware = proxy(url.parse('http://127.0.0.1:' + port));
|
|
|
|
|
gProxyMiddlewareCache[port] = proxyMiddleware;
|
|
|
|
|
}
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:22:28 -07:00
|
|
|
proxyMiddleware(req, res, next);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:28:49 -07:00
|
|
|
function initializeServer() {
|
|
|
|
|
var app = express();
|
|
|
|
|
var httpServer = http.createServer(app);
|
2015-09-14 11:22:28 -07:00
|
|
|
|
2015-09-14 11:28:49 -07:00
|
|
|
httpServer.on('error', console.error);
|
2015-09-14 11:22:28 -07:00
|
|
|
|
2015-09-14 11:28:49 -07:00
|
|
|
app
|
2015-09-14 11:22:28 -07:00
|
|
|
.use(session({ keys: ['blue', 'cheese', 'is', 'something'] }))
|
|
|
|
|
.use(attachSessionData)
|
|
|
|
|
.use(verifySession)
|
|
|
|
|
.use(authenticate)
|
|
|
|
|
.use(forwardRequestToApp);
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:28:49 -07:00
|
|
|
return httpServer;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-16 10:01:31 -07:00
|
|
|
function start(callback) {
|
2015-09-14 11:28:49 -07:00
|
|
|
assert.strictEqual(typeof callback, 'function');
|
|
|
|
|
|
|
|
|
|
gHttpServer = initializeServer();
|
|
|
|
|
|
2015-09-16 10:01:31 -07:00
|
|
|
gHttpServer.listen(config.get('oauthProxyPort'), callback);
|
2015-07-20 00:09:47 -07:00
|
|
|
}
|
|
|
|
|
|
2015-09-14 11:57:33 -07:00
|
|
|
function stop(callback) {
|
|
|
|
|
assert.strictEqual(typeof callback, 'function');
|
2015-07-20 00:09:47 -07:00
|
|
|
|
2015-09-14 11:57:33 -07:00
|
|
|
gHttpServer.close(callback);
|
|
|
|
|
}
|