diff --git a/Parse-Dashboard/app.js b/Parse-Dashboard/app.js new file mode 100644 index 0000000000..c92f4cd851 --- /dev/null +++ b/Parse-Dashboard/app.js @@ -0,0 +1,90 @@ +'use strict'; +const express = require('express'); +const basicAuth = require('basic-auth'); +const path = require('path'); +const packageJson = require('package-json'); + +const currentVersionFeatures = require('../package.json').parseDashboardFeatures; + +var newFeaturesInLatestVersion = []; +packageJson('parse-dashboard', 'latest').then(latestPackage => { + if (latestPackage.parseDashboardFeatures instanceof Array) { + newFeaturesInLatestVersion = latestPackage.parseDashboardFeatures.filter(feature => { + return currentVersionFeatures.indexOf(feature) === -1; + }); + } +}); + +module.exports = function(config) { + var app = express(); + // Serve public files. + app.use(express.static(path.join(__dirname,'public'))); + + // Serve the configuration. + app.get('/parse-dashboard-config.json', function(req, res) { + const response = { + apps: config.apps, + newFeaturesInLatestVersion: newFeaturesInLatestVersion, + }; + const users = config.users; + + let auth = null; + //If they provide auth when their config has no users, ignore the auth + if (users) { + auth = basicAuth(req); + } + + //Based on advice from Doug Wilson here: + //https://github.com/expressjs/express/issues/2518 + const requestIsLocal = + req.connection.remoteAddress === '127.0.0.1' || + req.connection.remoteAddress === '::ffff:127.0.0.1' || + req.connection.remoteAddress === '::1'; + if (!requestIsLocal && !req.secure && !allowInsecureHTTP) { + //Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext + return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' }); + } + + if (!requestIsLocal && !users) { + //Accessing the dashboard over the internet can only be done with username and password + return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' }); + } + + const successfulAuth = + //they provided auth + auth && + //there are configured users + users && + //the provided auth matches one of the users + users.find(user => { + return user.user == auth.name && + user.pass == auth.pass + }); + if (successfulAuth) { + //They provided correct auth + return res.json(response); + } + + if (users || auth) { + //They provided incorrect auth + res.set('WWW-Authenticate', 'Basic realm=Authorization Required'); + return res.sendStatus(401); + } + + //They didn't provide auth, and have configured the dashboard to not need auth + //(ie. didn't supply usernames and passwords) + if (requestIsLocal) { + //Allow no-auth access on localhost only, if they have configured the dashboard to not need auth + return res.json(response); + } + //We shouldn't get here. Fail closed. + res.send({ success: false, error: 'Something went wrong.' }); + }); + + // For every other request, go to index.html. Let client-side handle the rest. + app.get('/*', function(req, res) { + res.sendFile(__dirname + '/index.html'); + }); + + return app; +} diff --git a/Parse-Dashboard/index.js b/Parse-Dashboard/index.js index 3b2ed4f524..6b4808b456 100644 --- a/Parse-Dashboard/index.js +++ b/Parse-Dashboard/index.js @@ -7,11 +7,10 @@ */ // Command line tool for npm start "use strict" -const packageJson = require('package-json'); -const basicAuth = require('basic-auth'); const path = require('path'); const jsonFile = require('json-file-plus'); const express = require('express'); +const parseDashboard = require('./app'); const program = require('commander'); program.option('--appId [appId]', 'the app Id of the app you would like to manage.'); @@ -24,17 +23,6 @@ program.option('--allowInsecureHTTP [allowInsecureHTTP]', 'set this flag when yo program.parse(process.argv); -const currentVersionFeatures = require('../package.json').parseDashboardFeatures; - -var newFeaturesInLatestVersion = []; -packageJson('parse-dashboard', 'latest').then(latestPackage => { - if (latestPackage.parseDashboardFeatures instanceof Array) { - newFeaturesInLatestVersion = latestPackage.parseDashboardFeatures.filter(feature => { - return currentVersionFeatures.indexOf(feature) === -1; - }); - } -}); - const port = program.port || process.env.PORT || 4040; const allowInsecureHTTP = program.allowInsecureHTTP || process.env.PARSE_DASHBOARD_ALLOW_INSECURE_HTTP; @@ -103,75 +91,7 @@ p.then(config => { const app = express(); - // Serve public files. - app.use(express.static(path.join(__dirname,'public'))); - - // Serve the configuration. - app.get('/parse-dashboard-config.json', function(req, res) { - const response = { - apps: config.data.apps, - newFeaturesInLatestVersion: newFeaturesInLatestVersion, - }; - const users = config.data.users; - - let auth = null; - //If they provide auth when their config has no users, ignore the auth - if (users) { - auth = basicAuth(req); - } - - //Based on advice from Doug Wilson here: - //https://github.com/expressjs/express/issues/2518 - const requestIsLocal = - req.connection.remoteAddress === '127.0.0.1' || - req.connection.remoteAddress === '::ffff:127.0.0.1' || - req.connection.remoteAddress === '::1'; - if (!requestIsLocal && !req.secure && !allowInsecureHTTP) { - //Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext - return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' }); - } - - if (!requestIsLocal && !users) { - //Accessing the dashboard over the internet can only be done with username and password - return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' }); - } - - const successfulAuth = - //they provided auth - auth && - //there are configured users - users && - //the provided auth matches one of the users - users.find(user => { - return user.user == auth.name && - user.pass == auth.pass - }); - if (successfulAuth) { - //They provided correct auth - return res.json(response); - } - - if (users || auth) { - //They provided incorrect auth - res.set('WWW-Authenticate', 'Basic realm=Authorization Required'); - return res.sendStatus(401); - } - - //They didn't provide auth, and have configured the dashboard to not need auth - //(ie. didn't supply usernames and passwords) - if (requestIsLocal) { - //Allow no-auth access on localhost only, if they have configured the dashboard to not need auth - return res.json(response); - } - //We shouldn't get here. Fail closed. - res.send({ success: false, error: 'Something went wrong.' }); - }); - - // For every other request, go to index.html. Let client-side handle the rest. - app.get('/*', function(req, res) { - res.sendFile(__dirname + '/index.html'); - }); - + app.use(parseDashboard(config.data)); // Start the server. app.listen(port); diff --git a/package.json b/package.json index 7f37b63456..85307eeba3 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "engines": { "node": ">=4.3" }, + "main": "Parse-Dashboard/app.js", "jest": { "testPathDirs": [ "src/lib"