aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/ApiManagers/DeleteManager.ts79
-rw-r--r--src/server/RouteManager.ts13
-rw-r--r--src/server/apis/google/CredentialsLoader.ts30
-rw-r--r--src/server/index.ts40
-rw-r--r--src/server/server_Initialization.ts19
-rw-r--r--src/server/websocket.ts23
6 files changed, 117 insertions, 87 deletions
diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts
index dcb21c30d..46c0d8a8a 100644
--- a/src/server/ApiManagers/DeleteManager.ts
+++ b/src/server/ApiManagers/DeleteManager.ts
@@ -3,7 +3,7 @@ import { Method, _permission_denied } from "../RouteManager";
import { WebSocket } from "../websocket";
import { Database } from "../database";
import rimraf = require("rimraf");
-import { filesDirectory, AdminPriviliges } from "..";
+import { filesDirectory } from "..";
import { DashUploadUtils } from "../DashUploadUtils";
import { mkdirSync } from "fs";
import RouteSubscriber from "../RouteSubscriber";
@@ -14,72 +14,35 @@ export default class DeleteManager extends ApiManager {
register({
method: Method.GET,
+ requireAdminInRelease: true,
subscription: new RouteSubscriber("delete").add("target?"),
- secureHandler: async ({ req, res, isRelease, user: { id } }) => {
+ secureHandler: async ({ req, res }) => {
const { target } = req.params;
- if (isRelease && process.env.PASSWORD) {
- if (AdminPriviliges.get(id)) {
- AdminPriviliges.delete(id);
- } else {
- return res.redirect(`/admin/delete${target ? `:${target}` : ``}`);
+
+ if (!target) {
+ await WebSocket.doDelete();
+ } else {
+ let all = false;
+ switch (target) {
+ case "all":
+ all = true;
+ case "database":
+ await WebSocket.doDelete(false);
+ if (!all) break;
+ case "files":
+ rimraf.sync(filesDirectory);
+ mkdirSync(filesDirectory);
+ await DashUploadUtils.buildFileDirectories();
+ break;
+ default:
+ await Database.Instance.dropSchema(target);
}
}
- this.doDelete(target);
res.redirect("/home");
}
});
- register({
- method: Method.GET,
- subscription: new RouteSubscriber("admin").add("previous_target"),
- secureHandler: ({ res }) => res.render("admin.pug", { title: "Enter Administrator Password" })
- })
-
- register({
- method: Method.POST,
- subscription: new RouteSubscriber("admin").add("previous_target"),
- secureHandler: async ({ req, res, isRelease, user: { id } }) => {
- const { PASSWORD } = process.env;
- if (!(isRelease && PASSWORD)) {
- return res.redirect("/home");
- }
- const { password } = req.body;
- const { previous_target } = req.params;
- let redirect: string;
- if (password === PASSWORD) {
- AdminPriviliges.set(id, true);
- redirect = `/${previous_target.replace(":", "/")}`;
- } else {
- redirect = `/admin/${previous_target}`;
- }
- res.redirect(redirect);
- }
- })
-
- }
-
-
- private doDelete = async (target?: string) => {
- if (!target) {
- await WebSocket.doDelete();
- } else {
- let all = false;
- switch (target) {
- case "all":
- all = true;
- case "database":
- await WebSocket.doDelete(false);
- if (!all) break;
- case "files":
- rimraf.sync(filesDirectory);
- mkdirSync(filesDirectory);
- await DashUploadUtils.buildFileDirectories();
- break;
- default:
- await Database.Instance.dropSchema(target);
- }
- }
}
} \ No newline at end of file
diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts
index 9a87ada6f..350c02f06 100644
--- a/src/server/RouteManager.ts
+++ b/src/server/RouteManager.ts
@@ -2,6 +2,7 @@ import RouteSubscriber from "./RouteSubscriber";
import { DashUserModel } from "./authentication/DashUserModel";
import { Request, Response, Express } from 'express';
import { cyan, red, green } from 'colors';
+import { AdminPriviliges } from ".";
export enum Method {
GET,
@@ -25,6 +26,7 @@ export interface RouteInitializer {
secureHandler: SecureHandler;
publicHandler?: PublicHandler;
errorHandler?: ErrorHandler;
+ requireAdminInRelease?: boolean;
}
const registered = new Map<string, Set<Method>>();
@@ -84,7 +86,7 @@ export default class RouteManager {
* @param initializer
*/
addSupervisedRoute = (initializer: RouteInitializer): void => {
- const { method, subscription, secureHandler, publicHandler, errorHandler } = initializer;
+ const { method, subscription, secureHandler, publicHandler, errorHandler, requireAdminInRelease: requireAdmin } = initializer;
typeof (initializer.subscription) === "string" && RouteManager.routes.push(initializer.subscription);
initializer.subscription instanceof RouteSubscriber && RouteManager.routes.push(initializer.subscription.root);
@@ -94,7 +96,7 @@ export default class RouteManager {
});
const isRelease = this._isRelease;
const supervised = async (req: Request, res: Response) => {
- let { user } = req;
+ let user = req.user as Partial<DashUserModel> | undefined;
const { originalUrl: target } = req;
if (process.env.DB === "MEM" && !user) {
user = { id: "guest", email: "", userDocumentId: "guestDocId" };
@@ -113,6 +115,13 @@ export default class RouteManager {
}
};
if (user) {
+ if (requireAdmin && isRelease && process.env.PASSWORD) {
+ if (AdminPriviliges.get(user.id)) {
+ AdminPriviliges.delete(user.id);
+ } else {
+ return res.redirect(`/admin/${req.originalUrl.substring(1).replace("/", ":")}`);
+ }
+ }
await tryExecute(secureHandler, { ...core, user });
} else {
req.session!.target = target;
diff --git a/src/server/apis/google/CredentialsLoader.ts b/src/server/apis/google/CredentialsLoader.ts
index 09db4fc60..ef1f9a91e 100644
--- a/src/server/apis/google/CredentialsLoader.ts
+++ b/src/server/apis/google/CredentialsLoader.ts
@@ -1,6 +1,7 @@
import { readFile, readFileSync } from "fs";
import { pathFromRoot } from "../../ActionUtilities";
import { SecureContextOptions } from "tls";
+import { blue, red } from "colors";
export namespace GoogleCredentialsLoader {
@@ -30,16 +31,37 @@ export namespace GoogleCredentialsLoader {
}
-export namespace SSLCredentialsLoader {
+export namespace SSL {
export let Credentials: SecureContextOptions = {};
+ export let Loaded = false;
+
+ const suffixes = {
+ privateKey: ".key",
+ certificate: ".crt",
+ caBundle: "-ca.crt"
+ };
export async function loadCredentials() {
const { serverName } = process.env;
const cert = (suffix: string) => readFileSync(pathFromRoot(`./${serverName}${suffix}`)).toString();
- Credentials.key = cert(".key");
- Credentials.cert = cert(".crt");
- Credentials.ca = cert("-ca.crt");
+ try {
+ Credentials.key = cert(suffixes.privateKey);
+ Credentials.cert = cert(suffixes.certificate);
+ Credentials.ca = cert(suffixes.caBundle);
+ Loaded = true;
+ } catch (e) {
+ Credentials = {};
+ Loaded = false;
+ }
+ }
+
+ export function exit() {
+ console.log(red("Running this server in release mode requires the following SSL credentials in the project root:"));
+ const serverName = process.env.serverName ? process.env.serverName : "{process.env.serverName}";
+ Object.values(suffixes).forEach(suffix => console.log(blue(`${serverName}${suffix}`)));
+ console.log(red("Please ensure these files exist and restart, or run this in development mode."));
+ process.exit(0);
}
}
diff --git a/src/server/index.ts b/src/server/index.ts
index 65a9f10a7..ff74cfb33 100644
--- a/src/server/index.ts
+++ b/src/server/index.ts
@@ -12,7 +12,7 @@ import UtilManager from './ApiManagers/UtilManager';
import { SearchManager } from './ApiManagers/SearchManager';
import UserManager from './ApiManagers/UserManager';
import DownloadManager from './ApiManagers/DownloadManager';
-import { GoogleCredentialsLoader, SSLCredentialsLoader } from './apis/google/CredentialsLoader';
+import { GoogleCredentialsLoader, SSL } from './apis/google/CredentialsLoader';
import DeleteManager from "./ApiManagers/DeleteManager";
import PDFManager from "./ApiManagers/PDFManager";
import UploadManager from "./ApiManagers/UploadManager";
@@ -41,7 +41,7 @@ async function preliminaryFunctions() {
await DashUploadUtils.buildFileDirectories();
await Logger.initialize();
await GoogleCredentialsLoader.loadCredentials();
- SSLCredentialsLoader.loadCredentials();
+ SSL.loadCredentials();
GoogleApiServerUtils.processProjectCredentials();
if (process.env.DB !== "MEM") {
await log_execution({
@@ -102,6 +102,42 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }:
res.sendFile(path.join(__dirname, '../../deploy/' + filename));
};
+ /**
+ * Serves a simple password input box for any
+ */
+ addSupervisedRoute({
+ method: Method.GET,
+ subscription: new RouteSubscriber("admin").add("previous_target"),
+ secureHandler: ({ res, isRelease }) => {
+ const { PASSWORD } = process.env;
+ if (!(isRelease && PASSWORD)) {
+ return res.redirect("/home");
+ }
+ res.render("admin.pug", { title: "Enter Administrator Password" });
+ }
+ });
+
+ addSupervisedRoute({
+ method: Method.POST,
+ subscription: new RouteSubscriber("admin").add("previous_target"),
+ secureHandler: async ({ req, res, isRelease, user: { id } }) => {
+ const { PASSWORD } = process.env;
+ if (!(isRelease && PASSWORD)) {
+ return res.redirect("/home");
+ }
+ const { password } = req.body;
+ const { previous_target } = req.params;
+ let redirect: string;
+ if (password === PASSWORD) {
+ AdminPriviliges.set(id, true);
+ redirect = `/${previous_target.replace(":", "/")}`;
+ } else {
+ redirect = `/admin/${previous_target}`;
+ }
+ res.redirect(redirect);
+ }
+ });
+
addSupervisedRoute({
method: Method.GET,
subscription: ["/home", new RouteSubscriber("doc").add("docId")],
diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts
index ab91721b2..14b8776d8 100644
--- a/src/server/server_Initialization.ts
+++ b/src/server/server_Initialization.ts
@@ -21,11 +21,11 @@ import * as request from 'request';
import RouteSubscriber from './RouteSubscriber';
import { publicDirectory } from '.';
import { logPort, pathFromRoot, } from './ActionUtilities';
-import { blue, yellow } from 'colors';
+import { blue, yellow, red } from 'colors';
import * as cors from "cors";
import { createServer, Server as HttpsServer } from "https";
import { Server as HttpServer } from "http";
-import { SSLCredentialsLoader } from './apis/google/CredentialsLoader';
+import { SSL } from './apis/google/CredentialsLoader';
/* RouteSetter is a wrapper around the server that prevents the server
from being exposed. */
@@ -49,18 +49,19 @@ export default async function InitializeServer(routeSetter: RouteSetter) {
const isRelease = determineEnvironment();
+ isRelease && !SSL.Loaded && SSL.exit();
+
routeSetter(new RouteManager(app, isRelease));
registerRelativePath(app);
+ let server: HttpServer | HttpsServer;
const { serverPort } = process.env;
const resolved = isRelease && serverPort ? Number(serverPort) : 1050;
-
- let server: HttpServer | HttpsServer;
- if (isRelease) {
- server = createServer(SSLCredentialsLoader.Credentials, app).listen(resolved, () => logPort("server", resolved));
- } else {
- server = app.listen(resolved, () => logPort("server", resolved));
- }
+ await new Promise<void>(resolve => server = isRelease ?
+ createServer(SSL.Credentials, app).listen(resolved, resolve) :
+ app.listen(resolved, resolve)
+ );
+ logPort("server", resolved);
// initialize the web socket (bidirectional communication: if a user changes
// a field on one client, that change must be broadcast to all other clients)
diff --git a/src/server/websocket.ts b/src/server/websocket.ts
index 1abfe2327..21e772e83 100644
--- a/src/server/websocket.ts
+++ b/src/server/websocket.ts
@@ -8,13 +8,13 @@ import { Database } from "./database";
import { Search } from "./Search";
import * as sio from 'socket.io';
import YoutubeApi from "./apis/youtube/youtubeApiSample";
-import { GoogleCredentialsLoader, SSLCredentialsLoader } from "./apis/google/CredentialsLoader";
+import { GoogleCredentialsLoader, SSL } from "./apis/google/CredentialsLoader";
import { timeMap } from "./ApiManagers/UserManager";
import { green } from "colors";
import { networkInterfaces } from "os";
import executeImport from "../scraping/buxton/final/BuxtonImporter";
import { DocumentsCollection } from "./IDatabase";
-import { createServer } from "https";
+import { createServer, Server } from "https";
import * as express from "express";
export namespace WebSocket {
@@ -25,21 +25,20 @@ export namespace WebSocket {
export let disconnect: Function;
const defaultPort = 4321;
- export function initialize(isRelease: boolean, app: express.Express) {
+ export async function initialize(isRelease: boolean, app: express.Express) {
let io: sio.Server;
- const log = (port: number) => {
- logPort("websocket", port);
- console.log();
- }
+ let resolved: number;
if (isRelease) {
const { socketPort } = process.env;
- const resolved = socketPort ? Number(socketPort) : defaultPort;
- const socketEndpoint = createServer(SSLCredentialsLoader.Credentials, app).listen(resolved, () => log(resolved));
- io = sio(socketEndpoint, SSLCredentialsLoader.Credentials as any);
+ resolved = socketPort ? Number(socketPort) : defaultPort;
+ let socketEndpoint: Server;
+ await new Promise<void>(resolve => socketEndpoint = createServer(SSL.Credentials, app).listen(resolved, resolve));
+ io = sio(socketEndpoint!, SSL.Credentials as any);
} else {
- io = sio().listen(defaultPort);
- log(defaultPort);
+ io = sio().listen(resolved = defaultPort);
}
+ logPort("websocket", resolved);
+ console.log();
io.on("connection", function (socket: Socket) {
_socket = socket;