Skip to content

Commit 7501462

Browse files
committed
Merge pull request #27 from ParsePlatform/basic-auth
Implement basic auth
2 parents 33d0d81 + 4ab179c commit 7501462

File tree

4 files changed

+78
-12
lines changed

4 files changed

+78
-12
lines changed

Parse-Dashboard/index.js

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* This source code is licensed under the license found in the LICENSE file in
66
* the root directory of this source tree.
77
*/
8+
var basicAuth = require('basic-auth');
89
var jsonFile = require('json-file-plus');
910
var express = require('express');
1011
var app = express();
@@ -14,16 +15,68 @@ app.use(express.static('Parse-Dashboard/public'));
1415

1516
app.get('/parse-dashboard-config.json', function(req, res) {
1617
jsonFile(__dirname + '/parse-dashboard-config.json')
17-
.then(config => res.json(config.data))
18-
.catch(error => {
18+
.then(config => {
19+
var response = {apps: config.data.apps};
20+
var users = config.data.users;
21+
//If they provide auth when their config has no users, ignore the auth
22+
if (users) {
23+
var auth = basicAuth(req);
24+
}
25+
//Based on advice from Doug Wilson here:
26+
//https://github.com/expressjs/express/issues/2518
27+
var requestIsLocal =
28+
req.connection.remoteAddress === '127.0.0.1' ||
29+
req.connection.remoteAddress === '::ffff:127.0.0.1' ||
30+
req.connection.remoteAddress === '::1';
31+
if (!requestIsLocal && !req.secure) {
32+
//Disallow HTTP requests except on localhost, to prevent the master key from being transmitted in cleartext
33+
return res.send({ success: false, error: 'Parse Dashboard can only be remotely accessed via HTTPS' });
34+
}
35+
36+
if (!requestIsLocal && !users) {
37+
//Accessing the dashboard over the internet can only be done with username and password
38+
return res.send({ success: false, error: 'Configure a user to access Parse Dashboard remotely' });
39+
}
40+
41+
var successfulAuth =
42+
//they provided auth
43+
auth &&
44+
//there are configured users
45+
users &&
46+
//the provided auth matches one of the users
47+
users.find(user => {
48+
return user.user == auth.name &&
49+
user.pass == auth.pass
50+
});
51+
if (successfulAuth) {
52+
//They provided correct auth
53+
return res.send(response);
54+
}
55+
56+
if (users || auth) {
57+
//They provided incorrect auth
58+
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
59+
return res.sendStatus(401);
60+
}
61+
62+
//They didn't provide auth, and have configured the dashboard to not need auth
63+
//(ie. didn't supply usernames and passwords)
64+
if (requestIsLocal) {
65+
//Allow no-auth access on localhost only, if they have configured the dashboard to not need auth
66+
return res.json(response);
67+
}
68+
//We shouldn't get here. Fail closed.
69+
res.send({ success: false, error: 'Something went wrong.' });
70+
}, error => {
1971
if (error instanceof SyntaxError) {
2072
res.send({ success: false, error: 'Your parse-dashboard-config.json file contains invalid JSON.' });
2173
} else if (error.code === 'ENOENT') {
2274
res.send({ success: false, error: 'Your parse-dashboard-config.json file is missing.' });
2375
} else {
2476
res.send({ success: false, error: 'There was a problem with your parse-dashboard-config.json file.' });
2577
}
26-
});
78+
})
79+
.catch(error => res.send({ success: false, error: 'There was a problem loading the dashboard.' }));
2780
});
2881

2982
// For every other request, go to index.html. Let client-side handle the rest.

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,24 @@ Then execute `npm run dashboard` and visit http://localhost:4040 and you will be
5353

5454
## Other options
5555

56-
You can also set `appNameForURL` for each app to control the url of your app within the dashboard.
56+
You can set `appNameForURL` for each app to control the url of your app within the dashboard.
57+
58+
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:
59+
60+
```
61+
{
62+
"apps": [...],
63+
"users": [
64+
{
65+
"user":"user1",
66+
"pass":"pass"
67+
},
68+
{
69+
"user":"user2",
70+
"pass":"pass"
71+
}
72+
]
73+
}
74+
```
75+
76+
HTTPS and Basic Auth are mandatory if you are deploying the dashboard to the internet instead of accessing it from `localhost`.

lib/AJAX.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,9 @@ import * as CSRFManager from 'lib/CSRFManager';
99
import encodeFormData from 'lib/encodeFormData';
1010
import { Promise } from 'parse';
1111

12-
function getHost() {
13-
let host = location.host.split('.');
14-
return location.protocol + '//' + host.slice(host.length - 2).join('.');
15-
}
16-
1712
// abortable flag used to pass xhr reference so user can abort accordingly
1813
export function request(method, url, body, abortable = false, withCredentials = true, useRequestedWith = true) {
1914
let xhr = new XMLHttpRequest();
20-
if (url.indexOf('://') < 0) {
21-
url = getHost() + url;
22-
}
2315
xhr.open(method, url, true);
2416
if (method === 'POST' || method === 'PUT' || method === 'DELETE') {
2517
xhr.setRequestHeader('X-CSRF-Token', CSRFManager.getToken());

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"private": true,
33
"dependencies": {
44
"babel-runtime": "~5.8.25",
5+
"basic-auth": "^1.0.3",
56
"express": "^4.13.4",
67
"history": "~1.9.1",
78
"immutable": "~3.7.5",

0 commit comments

Comments
 (0)