From a0dc46dc565418fef9a257508a92c788c4ec50af Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Sat, 16 May 2020 00:01:06 -0400 Subject: fixed some issues with frame animation. added comic rendering mode. added server certficate keys to get SSL to work? --- src/server/server_Initialization.ts | 39 ++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 20 deletions(-) (limited to 'src/server/server_Initialization.ts') diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 14b0dedc3..6866b01e3 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -22,6 +22,7 @@ import { publicDirectory } from '.'; import { logPort, } from './ActionUtilities'; import { blue, yellow } from 'colors'; import * as cors from "cors"; +var https = require('https'); /* RouteSetter is a wrapper around the server that prevents the server from being exposed. */ @@ -49,27 +50,25 @@ export default async function InitializeServer(routeSetter: RouteSetter) { registerRelativePath(app); const serverPort = isRelease ? Number(process.env.serverPort) : 1050; - const server = app.listen(serverPort, () => { - logPort("server", serverPort); - console.log(); - }); - // var express = require('express') - // var fs = require('fs') - // var https = require('https') - // var app = express() - - // app.get('/', function (req, res) { - // res.send('hello world') - // }) - - // https.createServer({ - // key: fs.readFileSync('server.key'), - // cert: fs.readFileSync('server.cert') - // }, app) - // .listen(3000, function () { - // console.log('Example app listening on port 3000! Go to https://localhost:3000/') - // }) + let server: any; + if (isRelease) { + server = https.createServer({ + key: fs.readFileSync(`./${process.env.serverName}.key`), + cert: fs.readFileSync(`./${process.env.serverName}.cert`) + }, app); + server.listen(serverPort, function () { + logPort("server", serverPort); + console.log(); + // console.log('Example app listening on port 3000! Go to https://localhost:3000/') + }); + } else { + server = app.listen(serverPort, () => { + logPort("server", serverPort); + console.log(); + }); + } + disconnect = async () => new Promise(resolve => server.close(resolve)); return isRelease; -- cgit v1.2.3-70-g09d2 From 3d3d6af457cc47200600ef53135ba2462b34ac4d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Fri, 15 May 2020 21:44:06 -0700 Subject: server initialization and port resolution fixes --- src/server/server_Initialization.ts | 26 +++++++++++++------------- src/server/websocket.ts | 7 ++++--- 2 files changed, 17 insertions(+), 16 deletions(-) (limited to 'src/server/server_Initialization.ts') diff --git a/src/server/server_Initialization.ts b/src/server/server_Initialization.ts index 6866b01e3..f572ec906 100644 --- a/src/server/server_Initialization.ts +++ b/src/server/server_Initialization.ts @@ -19,10 +19,11 @@ import * as fs from 'fs'; import * as request from 'request'; import RouteSubscriber from './RouteSubscriber'; import { publicDirectory } from '.'; -import { logPort, } from './ActionUtilities'; +import { logPort, pathFromRoot, } from './ActionUtilities'; import { blue, yellow } from 'colors'; import * as cors from "cors"; -var https = require('https'); +import { createServer, Server as SecureServer } from "https"; +import { Server } from "http"; /* RouteSetter is a wrapper around the server that prevents the server from being exposed. */ @@ -49,28 +50,27 @@ export default async function InitializeServer(routeSetter: RouteSetter) { routeSetter(new RouteManager(app, isRelease)); registerRelativePath(app); - const serverPort = isRelease ? Number(process.env.serverPort) : 1050; + const { serverPort } = process.env; + const resolved = isRelease && serverPort ? Number(serverPort) : 1050; - let server: any; + let server: Server | SecureServer; if (isRelease) { - server = https.createServer({ - key: fs.readFileSync(`./${process.env.serverName}.key`), - cert: fs.readFileSync(`./${process.env.serverName}.cert`) + server = createServer({ + key: fs.readFileSync(pathFromRoot(`./${process.env.serverName}.key`)), + cert: fs.readFileSync(pathFromRoot(`./${process.env.serverName}.crt`)) }, app); - server.listen(serverPort, function () { - logPort("server", serverPort); + (server as SecureServer).listen(resolved, () => { + logPort("server", resolved); console.log(); - // console.log('Example app listening on port 3000! Go to https://localhost:3000/') }); } else { - server = app.listen(serverPort, () => { - logPort("server", serverPort); + server = app.listen(resolved, () => { + logPort("server", resolved); console.log(); }); } disconnect = async () => new Promise(resolve => server.close(resolve)); - return isRelease; } diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 7278bdc32..9ab3d2611 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -122,9 +122,10 @@ export namespace WebSocket { }; }); - const socketPort = isRelease ? Number(process.env.socketPort) : 4321; - endpoint.listen(socketPort); - logPort("websocket", socketPort); + const { socketPort } = process.env; + const resolved = isRelease && socketPort ? Number(socketPort) : 4321; + endpoint.listen(resolved); + logPort("websocket", resolved); } function processGesturePoints(socket: Socket, content: GestureContent) { -- cgit v1.2.3-70-g09d2 From b24fd078149d8ee9042a279cbe7498f6c3f5c82f Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 17 May 2020 23:38:21 +0000 Subject: release server integration with ssl --- .gitignore | 4 ++ session.config.json | 4 +- src/client/DocServer.ts | 6 ++- src/client/util/SerializationHelper.ts | 2 +- src/client/views/webcam/WebCamLogic.js | 6 +-- src/debug/Viewer.tsx | 2 +- src/server/ApiManagers/DeleteManager.ts | 83 ++++++++++++++++++++--------- src/server/ApiManagers/UtilManager.ts | 3 -- src/server/RouteManager.ts | 2 +- src/server/apis/google/CredentialsLoader.ts | 18 ++++++- src/server/index.ts | 9 ++-- src/server/server_Initialization.ts | 26 ++++----- src/server/websocket.ts | 36 ++++++++----- views/admin.pug | 8 +++ 14 files changed, 137 insertions(+), 72 deletions(-) create mode 100644 views/admin.pug (limited to 'src/server/server_Initialization.ts') diff --git a/.gitignore b/.gitignore index 7a5b2ec84..6d4b98289 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ src/scraping/buxton/source/ src/server/public/files/ src/scraping/acm/package-lock.json src/server/session_manager/logs/**/*.log +*.crt +*.key +*.pfx +*.properties \ No newline at end of file diff --git a/session.config.json b/session.config.json index b2c45a0fc..5440d9bbd 100644 --- a/session.config.json +++ b/session.config.json @@ -1,7 +1,7 @@ { "showServerOutput": false, "ports": { - "server": 1050, + "server": 443, "socket": 4321 }, "polling": { @@ -9,4 +9,4 @@ "intervalSeconds": 15, "failureTolerance": 0 } -} +} \ No newline at end of file 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 = (
Document ({this.props.field[Id]}) -
+
{fields}
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 = 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(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(); 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) { diff --git a/views/admin.pug b/views/admin.pug new file mode 100644 index 000000000..25c84c607 --- /dev/null +++ b/views/admin.pug @@ -0,0 +1,8 @@ +extends ./layout + +block content + form.form-horizontal(id='admin-form', method='POST') + input.form-control(type='password', name='password', id='password', placeholder='Password', autofocus, required) + button.btn.btn-success(id='submit', type='submit') + i.fa.fa-user-plus + | Submit \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 69e411884fb0a4be2edbf0816888a5fb93b96c7d Mon Sep 17 00:00:00 2001 From: Sam Wilkins Date: Sun, 17 May 2020 17:46:30 -0700 Subject: cleanup --- src/server/ApiManagers/DeleteManager.ts | 79 ++++++++--------------------- src/server/RouteManager.ts | 13 ++++- src/server/apis/google/CredentialsLoader.ts | 30 +++++++++-- src/server/index.ts | 40 ++++++++++++++- src/server/server_Initialization.ts | 19 +++---- src/server/websocket.ts | 23 ++++----- 6 files changed, 117 insertions(+), 87 deletions(-) (limited to 'src/server/server_Initialization.ts') 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>(); @@ -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 | 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(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(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; -- cgit v1.2.3-70-g09d2 From 0b1039b75af01836082f9bb4613e66c6218a6117 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Wed, 20 May 2020 00:35:18 -0400 Subject: added link drag drop onto webBox url --- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 +- src/client/views/nodes/WebBox.tsx | 54 ++++++++++++++++------ src/server/server_Initialization.ts | 28 ++++++----- 3 files changed, 59 insertions(+), 27 deletions(-) (limited to 'src/server/server_Initialization.ts') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0751d749d..c9b3519aa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1341,7 +1341,9 @@ export class CollectionFreeFormView extends CollectionSubView e.preventDefault() } + onDragOver={e => { + e.preventDefault(); + }} onContextMenu={this.onContextMenu} style={{ pointerEvents: this.backgroundEvents ? "all" : undefined, diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 63bdbdc5c..c1a585bca 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -116,6 +116,21 @@ export class WebBox extends ViewBoxAnnotatableComponent { + e.preventDefault(); + } + @action + onUrlDrop = (e: React.DragEvent) => { + const { dataTransfer } = e; + const html = dataTransfer.getData("text/html"); + const uri = dataTransfer.getData("text/uri-list"); + const url = uri || html || this._url; + this._url = url.startsWith(window.location.origin) ? + url.replace(window.location.origin, this._url.match(/http[s]?:\/\/[^\/]*/)?.[0] || "") : url; + this.submitURL(); + e.stopPropagation(); + } + @action forward = () => { const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null); @@ -140,21 +155,25 @@ export class WebBox extends ViewBoxAnnotatableComponent { if (!this._url.startsWith("http")) this._url = "http://" + this._url; - const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null); - const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null); - const url = Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.toString(); - if (url) { - if (history === undefined) { - this.dataDoc[this.fieldKey + "-history"] = new List([url]); - } else { - history.push(url); + try { + const URLy = new URL(this._url); + const future = Cast(this.dataDoc[this.fieldKey + "-future"], listSpec("string"), null); + const history = Cast(this.dataDoc[this.fieldKey + "-history"], listSpec("string"), null); + const url = Cast(this.dataDoc[this.fieldKey], WebField, null)?.url.toString(); + if (url) { + if (history === undefined) { + this.dataDoc[this.fieldKey + "-history"] = new List([url]); + } else { + history.push(url); + } + future && (future.length = 0); } - future && (future.length = 0); + this.dataDoc[this.fieldKey] = new WebField(URLy); + } catch (e) { + console.log("Error in URL :" + this._url); } - this.dataDoc[this.fieldKey] = new WebField(new URL(this._url)); } - onValueKeyDown = async (e: React.KeyboardEvent) => { if (e.key === "Enter") { e.stopPropagation(); @@ -175,7 +194,9 @@ export class WebBox extends ViewBoxAnnotatableComponent +
-
+
@@ -198,6 +221,8 @@ export class WebBox extends ViewBoxAnnotatableComponent @@ -207,7 +232,8 @@ export class WebBox extends ViewBoxAnnotatableComponent -