diff options
author | bobzel <zzzman@gmail.com> | 2020-05-17 23:38:21 +0000 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2020-05-17 23:38:21 +0000 |
commit | b24fd078149d8ee9042a279cbe7498f6c3f5c82f (patch) | |
tree | c3a74a386f0f6747ca12229d2d6dcc3e5895bfa2 /src | |
parent | 9c3d461a9642a4596c55f642af2b55e186b0fe20 (diff) |
release server integration with ssl
Diffstat (limited to 'src')
-rw-r--r-- | src/client/DocServer.ts | 6 | ||||
-rw-r--r-- | src/client/util/SerializationHelper.ts | 2 | ||||
-rw-r--r-- | src/client/views/webcam/WebCamLogic.js | 6 | ||||
-rw-r--r-- | src/debug/Viewer.tsx | 2 | ||||
-rw-r--r-- | src/server/ApiManagers/DeleteManager.ts | 83 | ||||
-rw-r--r-- | src/server/ApiManagers/UtilManager.ts | 3 | ||||
-rw-r--r-- | src/server/RouteManager.ts | 2 | ||||
-rw-r--r-- | src/server/apis/google/CredentialsLoader.ts | 18 | ||||
-rw-r--r-- | src/server/index.ts | 9 | ||||
-rw-r--r-- | src/server/server_Initialization.ts | 26 | ||||
-rw-r--r-- | src/server/websocket.ts | 36 |
11 files changed, 123 insertions, 70 deletions
diff --git a/src/client/DocServer.ts b/src/client/DocServer.ts index ac5b7a218..c6b3fa61f 100644 --- a/src/client/DocServer.ts +++ b/src/client/DocServer.ts @@ -1,4 +1,4 @@ -import * as OpenSocket from 'socket.io-client'; +import * as io from 'socket.io-client'; import { MessageStore, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent } from "./../server/Message"; import { Opt, Doc } from '../fields/Doc'; import { Utils, emptyFunction } from '../Utils'; @@ -108,7 +108,9 @@ export namespace DocServer { export function init(protocol: string, hostname: string, port: number, identifier: string) { _cache = {}; GUID = identifier; - _socket = OpenSocket(`${protocol}//${hostname}:${port}`);// OpenSocket(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket + protocol = protocol.startsWith("https") ? "wss" : "ws"; + _socket = io.connect(`${protocol}://${hostname}:${port}`); + // io.connect(`https://7f079dda.ngrok.io`);// if using ngrok, create a special address for the websocket _GetCachedRefField = _GetCachedRefFieldImpl; _GetRefField = _GetRefFieldImpl; diff --git a/src/client/util/SerializationHelper.ts b/src/client/util/SerializationHelper.ts index ad0309fa7..19b217726 100644 --- a/src/client/util/SerializationHelper.ts +++ b/src/client/util/SerializationHelper.ts @@ -91,7 +91,7 @@ export function Deserializable(constructor: { new(...args: any[]): any } | strin if (typeof constructor === "string") { return Object.assign((ctor: { new(...args: any[]): any }) => { addToMap(constructor, ctor); - }, { withFields: (fields: string[]) => Deserializable.withFields(fields, name, afterDeserialize) }); + }, { withFields: (fields: string[]) => Deserializable.withFields(fields, constructor, afterDeserialize) }); } addToMap(constructor.name, constructor); } diff --git a/src/client/views/webcam/WebCamLogic.js b/src/client/views/webcam/WebCamLogic.js index f542fb983..c847b8656 100644 --- a/src/client/views/webcam/WebCamLogic.js +++ b/src/client/views/webcam/WebCamLogic.js @@ -104,9 +104,9 @@ export function initialize(roomName, handlerUI) { navigator.mediaDevices.getUserMedia({ - audio: true, - video: true - }) + audio: true, + video: true + }) .then(gotStream) .catch(function (e) { alert('getUserMedia() error: ' + e.name); diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index 6ce39d533..ddddee3be 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -96,7 +96,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { content = ( <div> Document ({this.props.field[Id]}) - <div style={{ paddingLeft: "25px" }}> + <div style={{ paddingLeft: "25px" }}> {fields} </div> </div> diff --git a/src/server/ApiManagers/DeleteManager.ts b/src/server/ApiManagers/DeleteManager.ts index 7fbb37658..dcb21c30d 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 } from ".."; +import { filesDirectory, AdminPriviliges } from ".."; import { DashUploadUtils } from "../DashUploadUtils"; import { mkdirSync } from "fs"; import RouteSubscriber from "../RouteSubscriber"; @@ -15,38 +15,71 @@ export default class DeleteManager extends ApiManager { register({ method: Method.GET, subscription: new RouteSubscriber("delete").add("target?"), - secureHandler: async ({ req, res, isRelease }) => { - if (isRelease) { - return _permission_denied(res, "Cannot perform a delete operation outside of the development environment!"); - } - + secureHandler: async ({ req, res, isRelease, user: { id } }) => { const { target } = req.params; - const { doDelete } = WebSocket; - - if (!target) { - await doDelete(); - } else { - let all = false; - switch (target) { - case "all": - all = true; - case "database": - await doDelete(false); - if (!all) break; - case "files": - rimraf.sync(filesDirectory); - mkdirSync(filesDirectory); - await DashUploadUtils.buildFileDirectories(); - break; - default: - await Database.Instance.dropSchema(target); + if (isRelease && process.env.PASSWORD) { + if (AdminPriviliges.get(id)) { + AdminPriviliges.delete(id); + } else { + return res.redirect(`/admin/delete${target ? `:${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/ApiManagers/UtilManager.ts b/src/server/ApiManagers/UtilManager.ts index aec523cd0..e2cd88726 100644 --- a/src/server/ApiManagers/UtilManager.ts +++ b/src/server/ApiManagers/UtilManager.ts @@ -1,8 +1,6 @@ import ApiManager, { Registration } from "./ApiManager"; import { Method } from "../RouteManager"; import { exec } from 'child_process'; -import RouteSubscriber from "../RouteSubscriber"; -import { red } from "colors"; // import { IBM_Recommender } from "../../client/apis/IBM_Recommender"; // import { Recommender } from "../Recommender"; @@ -34,7 +32,6 @@ export default class UtilManager extends ApiManager { // } // }); - register({ method: Method.GET, subscription: "/pull", diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts index b23215996..9a87ada6f 100644 --- a/src/server/RouteManager.ts +++ b/src/server/RouteManager.ts @@ -205,5 +205,5 @@ export function _permission_denied(res: Response, message?: string) { if (message) { res.statusMessage = message; } - res.status(STATUS.PERMISSION_DENIED).send("Permission Denied!"); + res.status(STATUS.PERMISSION_DENIED).send(`Permission Denied! ${message}`); } diff --git a/src/server/apis/google/CredentialsLoader.ts b/src/server/apis/google/CredentialsLoader.ts index e3f4d167b..09db4fc60 100644 --- a/src/server/apis/google/CredentialsLoader.ts +++ b/src/server/apis/google/CredentialsLoader.ts @@ -1,4 +1,6 @@ -import { readFile } from "fs"; +import { readFile, readFileSync } from "fs"; +import { pathFromRoot } from "../../ActionUtilities"; +import { SecureContextOptions } from "tls"; export namespace GoogleCredentialsLoader { @@ -27,3 +29,17 @@ export namespace GoogleCredentialsLoader { } } + +export namespace SSLCredentialsLoader { + + export let Credentials: SecureContextOptions = {}; + + 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"); + } + +} diff --git a/src/server/index.ts b/src/server/index.ts index 7ac032ed3..65a9f10a7 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -11,9 +11,8 @@ import * as qs from 'query-string'; import UtilManager from './ApiManagers/UtilManager'; import { SearchManager } from './ApiManagers/SearchManager'; import UserManager from './ApiManagers/UserManager'; -import { WebSocket } from './websocket'; import DownloadManager from './ApiManagers/DownloadManager'; -import { GoogleCredentialsLoader } from './apis/google/CredentialsLoader'; +import { GoogleCredentialsLoader, SSLCredentialsLoader } from './apis/google/CredentialsLoader'; import DeleteManager from "./ApiManagers/DeleteManager"; import PDFManager from "./ApiManagers/PDFManager"; import UploadManager from "./ApiManagers/UploadManager"; @@ -26,6 +25,7 @@ import { DashSessionAgent } from "./DashSession/DashSessionAgent"; import SessionManager from "./ApiManagers/SessionManager"; import { AppliedSessionAgent } from "./DashSession/Session/agents/applied_session_agent"; +export const AdminPriviliges: Map<string, boolean> = new Map(); export const onWindows = process.platform === "win32"; export let sessionAgent: AppliedSessionAgent; export const publicDirectory = path.resolve(__dirname, "public"); @@ -41,6 +41,7 @@ async function preliminaryFunctions() { await DashUploadUtils.buildFileDirectories(); await Logger.initialize(); await GoogleCredentialsLoader.loadCredentials(); + SSLCredentialsLoader.loadCredentials(); GoogleApiServerUtils.processProjectCredentials(); if (process.env.DB !== "MEM") { await log_execution({ @@ -121,10 +122,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: }); logRegistrationOutcome(); - - // initialize the web socket (bidirectional communication: if a user changes - // a field on one client, that change must be broadcast to all other clients) - WebSocket.initialize(isRelease); } diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index f572ec906..ab91721b2 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -10,6 +10,7 @@ import { Database } from './database'; import { getForgot, getLogin, getLogout, getReset, getSignup, postForgot, postLogin, postReset, postSignup } from './authentication/AuthenticationManager'; const MongoStore = require('connect-mongo')(session); import RouteManager from './RouteManager'; +import { WebSocket } from './websocket'; import * as webpack from 'webpack'; const config = require('../../webpack.config'); const compiler = webpack(config); @@ -22,8 +23,9 @@ import { publicDirectory } from '.'; import { logPort, pathFromRoot, } from './ActionUtilities'; import { blue, yellow } from 'colors'; import * as cors from "cors"; -import { createServer, Server as SecureServer } from "https"; -import { Server } from "http"; +import { createServer, Server as HttpsServer } from "https"; +import { Server as HttpServer } from "http"; +import { SSLCredentialsLoader } from './apis/google/CredentialsLoader'; /* RouteSetter is a wrapper around the server that prevents the server from being exposed. */ @@ -53,23 +55,17 @@ export default async function InitializeServer(routeSetter: RouteSetter) { const { serverPort } = process.env; const resolved = isRelease && serverPort ? Number(serverPort) : 1050; - let server: Server | SecureServer; + let server: HttpServer | HttpsServer; if (isRelease) { - server = createServer({ - key: fs.readFileSync(pathFromRoot(`./${process.env.serverName}.key`)), - cert: fs.readFileSync(pathFromRoot(`./${process.env.serverName}.crt`)) - }, app); - (server as SecureServer).listen(resolved, () => { - logPort("server", resolved); - console.log(); - }); + server = createServer(SSLCredentialsLoader.Credentials, app).listen(resolved, () => logPort("server", resolved)); } else { - server = app.listen(resolved, () => { - logPort("server", resolved); - console.log(); - }); + server = app.listen(resolved, () => 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) + WebSocket.initialize(isRelease, app); + disconnect = async () => new Promise<Error>(resolve => server.close(resolve)); return isRelease; } diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 9ab3d2611..1abfe2327 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -1,18 +1,21 @@ +import * as fs from 'fs'; +import { logPort } from './ActionUtilities'; import { Utils } from "../Utils"; import { MessageStore, Transferable, Types, Diff, YoutubeQueryInput, YoutubeQueryTypes, GestureContent, MobileInkOverlayContent, UpdateMobileInkOverlayPositionContent, MobileDocumentUploadContent, RoomMessage } from "./Message"; import { Client } from "./Client"; import { Socket } from "socket.io"; import { Database } from "./database"; import { Search } from "./Search"; -import * as io from 'socket.io'; +import * as sio from 'socket.io'; import YoutubeApi from "./apis/youtube/youtubeApiSample"; -import { GoogleCredentialsLoader } from "./apis/google/CredentialsLoader"; -import { logPort } from "./ActionUtilities"; +import { GoogleCredentialsLoader, SSLCredentialsLoader } 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 * as express from "express"; export namespace WebSocket { @@ -20,12 +23,26 @@ export namespace WebSocket { const clients: { [key: string]: Client } = {}; export const socketMap = new Map<SocketIO.Socket, string>(); export let disconnect: Function; + const defaultPort = 4321; - export function initialize(isRelease: boolean) { - const endpoint = io(); - endpoint.on("connection", function (socket: Socket) { - _socket = socket; + export function initialize(isRelease: boolean, app: express.Express) { + let io: sio.Server; + const log = (port: number) => { + logPort("websocket", port); + console.log(); + } + 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); + } else { + io = sio().listen(defaultPort); + log(defaultPort); + } + io.on("connection", function (socket: Socket) { + _socket = socket; socket.use((_packet, next) => { const userEmail = socketMap.get(socket); if (userEmail) { @@ -121,11 +138,6 @@ export namespace WebSocket { socket.disconnect(true); }; }); - - const { socketPort } = process.env; - const resolved = isRelease && socketPort ? Number(socketPort) : 4321; - endpoint.listen(resolved); - logPort("websocket", resolved); } function processGesturePoints(socket: Socket, content: GestureContent) { |