Initial commit

Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com>
Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com>
Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com>
Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com>
Co-Authored-By: Johan  <4934465+joolsus@users.noreply.github.com>
Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com>
Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
This commit is contained in:
Michael Cartner
2024-01-26 11:52:55 +01:00
commit b9c60b07dc
2789 changed files with 868795 additions and 0 deletions

View File

@@ -0,0 +1,208 @@
'use strict';
const express = require('express');
const path = require('path');
const Authentication = require('./Authentication.js');
var fs = require('fs');
var newFeaturesInLatestVersion = [];
function getMount(mountPath) {
mountPath = mountPath || '';
if (!mountPath.endsWith('/')) {
mountPath += '/';
}
return mountPath;
}
function checkIfIconsExistForApps(apps, iconsFolder) {
for (var i in apps) {
var currentApp = apps[i];
var iconName = currentApp.iconName;
var path = iconsFolder + '/' + iconName;
fs.stat(path, function(err) {
if (err) {
if ('ENOENT' == err.code) {// file does not exist
console.warn('Icon with file name: ' + iconName +' couldn\'t be found in icons folder!');
} else {
console.log(
'An error occurd while checking for icons, please check permission!');
}
} else {
//every thing was ok so for example you can read it and send it to client
}
} );
}
}
module.exports = function(config, options) {
options = options || {};
var app = express();
// Serve public files.
app.use(express.static(path.join(__dirname,'parse-dashboard-public')));
// Allow setting via middleware
if (config.trustProxy && app.disabled('trust proxy')) {
app.enable('trust proxy');
}
// wait for app to mount in order to get mountpath
app.on('mount', function() {
const mountPath = getMount(app.mountpath);
const users = config.users;
const useEncryptedPasswords = config.useEncryptedPasswords ? true : false;
const authInstance = new Authentication(users, useEncryptedPasswords, mountPath);
authInstance.initialize(app, { cookieSessionSecret: options.cookieSessionSecret });
// Serve the configuration.
app.get('/parse-dashboard-config.json', function(req, res) {
let apps = config.apps.map((app) => Object.assign({}, app)); // make a copy
let response = {
apps: apps,
newFeaturesInLatestVersion: newFeaturesInLatestVersion,
};
//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 (!options.dev && !requestIsLocal) {
if (!req.secure && !options.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 (!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 authentication = req.user;
const successfulAuth = authentication && authentication.isAuthenticated;
const appsUserHasAccess = authentication && authentication.appsUserHasAccessTo;
const isReadOnly = authentication && authentication.isReadOnly;
// User is full read-only, replace the masterKey by the read-only one
if (isReadOnly) {
response.apps = response.apps.map((app) => {
app.masterKey = app.readOnlyMasterKey;
if (!app.masterKey) {
throw new Error('You need to provide a readOnlyMasterKey to use read-only features.');
}
return app;
});
}
if (successfulAuth) {
if (appsUserHasAccess) {
// Restric access to apps defined in user dictionary
// If they didn't supply any app id, user will access all apps
response.apps = response.apps.filter(function (app) {
return appsUserHasAccess.find(appUserHasAccess => {
const isSame = app.appId === appUserHasAccess.appId;
if (isSame && appUserHasAccess.readOnly) {
app.masterKey = app.readOnlyMasterKey;
}
return isSame;
})
});
}
// They provided correct auth
return res.json(response);
}
if (users) {
//They provided incorrect auth
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 || options.dev) {
//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.' });
});
// Serve the app icons. Uses the optional `iconsFolder` parameter as
// directory name, that was setup in the config file.
// We are explicitly not using `__dirpath` here because one may be
// running parse-dashboard from globally installed npm.
if (config.iconsFolder) {
try {
var stat = fs.statSync(config.iconsFolder);
if (stat.isDirectory()) {
app.use('/appicons', express.static(config.iconsFolder));
//Check also if the icons really exist
checkIfIconsExistForApps(config.apps, config.iconsFolder);
}
} catch (e) {
// Directory doesn't exist or something.
console.warn('Iconsfolder at path: ' + config.iconsFolder +
' not found!');
}
}
app.get('/login', function(req, res) {
if (!users || (req.user && req.user.isAuthenticated)) {
return res.redirect(`${mountPath}apps`);
}
let errors = req.flash('error');
if (errors && errors.length) {
errors = `<div id="login_errors" style="display: none;">
${errors.join(' ')}
</div>`
}
res.send(`<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" type="image/x-icon" href="${mountPath}favicon.ico" />
<base href="${mountPath}"/>
<script>
PARSE_DASHBOARD_PATH = "${mountPath}";
</script>
<title>Parse Dashboard</title>
</head>
<body>
<div id="login_mount"></div>
${errors}
<script src="${mountPath}bundles/login.bundle.js"></script>
</body>
</html>
`);
});
// For every other request, go to index.html. Let client-side handle the rest.
app.get('/*', function(req, res) {
if (users && (!req.user || !req.user.isAuthenticated)) {
return res.redirect(`${mountPath}login`);
}
if (users && req.user && req.user.matchingUsername ) {
res.append('username', req.user.matchingUsername);
}
res.send(`<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" type="image/x-icon" href="${mountPath}favicon.ico" />
<base href="${mountPath}"/>
<script>
PARSE_DASHBOARD_PATH = "${mountPath}";
</script>
<title>Parse Dashboard</title>
</head>
<body>
<div id="browser_mount"></div>
<script src="${mountPath}bundles/dashboard.bundle.js"></script>
</body>
</html>
`);
});
});
return app;
}