Skip to content

Implement basic auth #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions Parse-Dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
var basicAuth = require('basic-auth');
var jsonFile = require('json-file-plus');
var express = require('express');
var app = express();
Expand All @@ -14,16 +15,68 @@ app.use(express.static('Parse-Dashboard/public'));

app.get('/parse-dashboard-config.json', function(req, res) {
jsonFile(__dirname + '/parse-dashboard-config.json')
.then(config => res.json(config.data))
.catch(error => {
.then(config => {
var response = {apps: config.data.apps};
var users = config.data.users;
//If they provide auth when their config has no users, ignore the auth
if (users) {
var auth = basicAuth(req);
}
//Based on advice from Doug Wilson here:
//https://github.com/expressjs/express/issues/2518
var requestIsLocal =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nits: ===

req.connection.remoteAddress === '127.0.0.1' ||
req.connection.remoteAddress === '::ffff:127.0.0.1' ||
req.connection.remoteAddress === '::1';
if (!requestIsLocal && !req.secure) {
//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' });
}

var 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.send(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.' });
}, error => {
if (error instanceof SyntaxError) {
res.send({ success: false, error: 'Your parse-dashboard-config.json file contains invalid JSON.' });
} else if (error.code === 'ENOENT') {
res.send({ success: false, error: 'Your parse-dashboard-config.json file is missing.' });
} else {
res.send({ success: false, error: 'There was a problem with your parse-dashboard-config.json file.' });
}
});
})
.catch(error => res.send({ success: false, error: 'There was a problem loading the dashboard.' }));
});

// For every other request, go to index.html. Let client-side handle the rest.
Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,24 @@ Then execute `npm run dashboard` and visit http://localhost:4040 and you will be

## Other options

You can also set `appNameForURL` for each app to control the url of your app within the dashboard.
You can set `appNameForURL` for each app to control the url of your app within the dashboard.

If you want to require a username and password to access the dashboard, you can do so by adding usernames and passwords for HTTP Basic Auth to your configuration file:

```
{
"apps": [...],
"users": [
{
"user":"user1",
"pass":"pass"
},
{
"user":"user2",
"pass":"pass"
}
]
}
```

HTTPS and Basic Auth are mandatory if you are deploying the dashboard to the internet instead of accessing it from `localhost`.
8 changes: 0 additions & 8 deletions lib/AJAX.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,9 @@ import * as CSRFManager from 'lib/CSRFManager';
import encodeFormData from 'lib/encodeFormData';
import { Promise } from 'parse';

function getHost() {
let host = location.host.split('.');
return location.protocol + '//' + host.slice(host.length - 2).join('.');
}

// abortable flag used to pass xhr reference so user can abort accordingly
export function request(method, url, body, abortable = false, withCredentials = true, useRequestedWith = true) {
let xhr = new XMLHttpRequest();
if (url.indexOf('://') < 0) {
url = getHost() + url;
}
xhr.open(method, url, true);
if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
xhr.setRequestHeader('X-CSRF-Token', CSRFManager.getToken());
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"private": true,
"dependencies": {
"babel-runtime": "~5.8.25",
"basic-auth": "^1.0.3",
"express": "^4.13.4",
"history": "~1.9.1",
"immutable": "~3.7.5",
Expand Down