diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/ApiManagers/SessionManager.ts | 57 | ||||
-rw-r--r-- | src/server/DashSession.ts | 83 | ||||
-rw-r--r-- | src/server/RouteManager.ts | 3 | ||||
-rw-r--r-- | src/server/index.ts | 17 |
4 files changed, 105 insertions, 55 deletions
diff --git a/src/server/ApiManagers/SessionManager.ts b/src/server/ApiManagers/SessionManager.ts new file mode 100644 index 000000000..eb17ff567 --- /dev/null +++ b/src/server/ApiManagers/SessionManager.ts @@ -0,0 +1,57 @@ +import ApiManager, { Registration } from "./ApiManager"; +import { Method, _permission_denied, AuthorizedCore, SecureHandler } from "../RouteManager"; +import RouteSubscriber from "../RouteSubscriber"; +import { sessionAgent } from ".."; + +const permissionError = "You are not authorized!"; + +export default class SessionManager extends ApiManager { + + private secureSubscriber = (root: string, ...params: string[]) => new RouteSubscriber(root).add("password", ...params); + + private authorizedAction = (handler: SecureHandler) => { + return (core: AuthorizedCore) => { + const { req, res, isRelease } = core; + const { password } = req.params; + if (!isRelease) { + return res.send("This can be run only on the release server."); + } + if (password !== process.env.session_key) { + return _permission_denied(res, permissionError); + } + handler(core); + res.redirect("/home"); + }; + } + + protected initialize(register: Registration): void { + + register({ + method: Method.GET, + subscription: this.secureSubscriber("debug", "mode", "recipient"), + secureHandler: this.authorizedAction(({ req }) => { + const { mode, recipient } = req.params; + if (["passive", "active"].includes(mode)) { + sessionAgent.serverWorker.sendMonitorAction("debug", { mode, recipient }); + } + }) + }); + + register({ + method: Method.GET, + subscription: this.secureSubscriber("backup"), + secureHandler: this.authorizedAction(() => sessionAgent.serverWorker.sendMonitorAction("backup")) + }); + + register({ + method: Method.GET, + subscription: this.secureSubscriber("kill"), + secureHandler: this.authorizedAction(({ res }) => { + res.send("<img src='https://media.giphy.com/media/NGIfqtcS81qi4/giphy.gif' style='width:100%;height:100%;'/>"); + sessionAgent.killSession("an authorized user has manually ended the server session via the /kill route", true); + }) + }); + + } + +}
\ No newline at end of file diff --git a/src/server/DashSession.ts b/src/server/DashSession.ts index e03487a61..c893f4b64 100644 --- a/src/server/DashSession.ts +++ b/src/server/DashSession.ts @@ -19,7 +19,7 @@ export class DashSessionAgent extends Session.AppliedSessionAgent { private readonly notificationRecipients = ["samuel_wilkins@brown.edu"]; private readonly signature = "-Dash Server Session Manager"; - + private readonly releaseDesktop = pathFromRoot("../../Desktop"); protected async launchMonitor() { const monitor = Session.Monitor.Create({ @@ -67,44 +67,10 @@ export class DashSessionAgent extends Session.AppliedSessionAgent { } } }); - const releaseDesktop = pathFromRoot("../../Desktop"); - const backup = () => monitor.exec("backup.bat", { cwd: releaseDesktop }); - monitor.addReplCommand("backup", [], backup); - monitor.addReplCommand("debug", [/active|passive/, /\S+\@\S+/], async args => { - const [mode, recipient] = args; - if (mode === "active") { - await backup(); - } - monitor.mainLog("backup complete"); - const backupsDirectory = `${releaseDesktop}/backups`; - const compressedDirectory = `${releaseDesktop}/compressed`; - if (!existsSync(compressedDirectory)) { - mkdirSync(compressedDirectory); - } - const target = readdirSync(backupsDirectory).map(filename => ({ - modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs, - filename - })).sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename; - monitor.mainLog(`targeting ${target}...`); - const zipName = `${target}.zip`; - const zipPath = `${compressedDirectory}/${zipName}`; - const output = createWriteStream(zipPath); - const zip = Archiver('zip'); - zip.pipe(output); - zip.directory(`${backupsDirectory}/${target}/Dash`, false); - await zip.finalize(); - monitor.mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`); - let instructions = readFileSync(resolve(__dirname, "./remote_debug_instructions.txt"), { encoding: "utf8" }); - instructions = instructions.replace(/__zipname__/, zipName).replace(/__target__/, target).replace(/__signature__/, this.signature); - const error = await Email.dispatch(recipient, `Compressed backup of ${target}...`, instructions, [ - { - filename: zipName, - path: zipPath - } - ]); - monitor.mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(recipient)}`); - error && monitor.mainLog(red(error.message)); - }); + monitor.addReplCommand("backup", [], this.backup); + monitor.addReplCommand("debug", [/active|passive/, /\S+\@\S+/], async ([mode, recipient]) => this.dispatchZippedDebugBackup(mode, recipient)); + monitor.addServerMessageListener("backup", this.backup); + monitor.addServerMessageListener("debug", ({ args: { mode, recipient } }) => this.dispatchZippedDebugBackup(mode, recipient)); return monitor; } @@ -120,4 +86,43 @@ export class DashSessionAgent extends Session.AppliedSessionAgent { return worker; } + public async backup() { + return this.sessionMonitor.exec("backup.bat", { cwd: this.releaseDesktop }); + } + + public async dispatchZippedDebugBackup(mode: string, recipient: string) { + if (mode === "active") { + await this.backup(); + } + this.sessionMonitor.mainLog("backup complete"); + const backupsDirectory = `${this.releaseDesktop}/backups`; + const compressedDirectory = `${this.releaseDesktop}/compressed`; + if (!existsSync(compressedDirectory)) { + mkdirSync(compressedDirectory); + } + const target = readdirSync(backupsDirectory).map(filename => ({ + modifiedTime: statSync(`${backupsDirectory}/${filename}`).mtimeMs, + filename + })).sort((a, b) => b.modifiedTime - a.modifiedTime)[0].filename; + this.sessionMonitor.mainLog(`targeting ${target}...`); + const zipName = `${target}.zip`; + const zipPath = `${compressedDirectory}/${zipName}`; + const output = createWriteStream(zipPath); + const zip = Archiver('zip'); + zip.pipe(output); + zip.directory(`${backupsDirectory}/${target}/Dash`, false); + await zip.finalize(); + this.sessionMonitor.mainLog(`zip finalized with size ${statSync(zipPath).size} bytes, saved to ${zipPath}`); + let instructions = readFileSync(resolve(__dirname, "./remote_debug_instructions.txt"), { encoding: "utf8" }); + instructions = instructions.replace(/__zipname__/, zipName).replace(/__target__/, target).replace(/__signature__/, this.signature); + const error = await Email.dispatch(recipient, `Compressed backup of ${target}...`, instructions, [ + { + filename: zipName, + path: zipPath + } + ]); + this.sessionMonitor.mainLog(`${error === null ? green("successfully dispatched") : red("failed to dispatch")} ${zipName} to ${cyan(recipient)}`); + error && this.sessionMonitor.mainLog(red(error.message)); + } + }
\ No newline at end of file diff --git a/src/server/RouteManager.ts b/src/server/RouteManager.ts index 25259bd88..a8ad81bf7 100644 --- a/src/server/RouteManager.ts +++ b/src/server/RouteManager.ts @@ -14,7 +14,8 @@ export interface CoreArguments { isRelease: boolean; } -export type SecureHandler = (core: CoreArguments & { user: DashUserModel }) => any | Promise<any>; +export type AuthorizedCore = CoreArguments & { user: DashUserModel }; +export type SecureHandler = (core: AuthorizedCore) => any | Promise<any>; export type PublicHandler = (core: CoreArguments) => any | Promise<any>; export type ErrorHandler = (core: CoreArguments & { error: any }) => any | Promise<any>; diff --git a/src/server/index.ts b/src/server/index.ts index 6b3dfd614..d73e48d8a 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -24,6 +24,7 @@ import { Logger } from "./ProcessFactory"; import { yellow, red } from "colors"; import { Session } from "./Session/session"; import { DashSessionAgent } from "./DashSession"; +import SessionManager from "./ApiManagers/SessionManager"; export const onWindows = process.platform === "win32"; export let sessionAgent: Session.AppliedSessionAgent; @@ -58,6 +59,7 @@ async function preliminaryFunctions() { */ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: RouteManager) { const managers = [ + new SessionManager(), new UserManager(), new UploadManager(), new DownloadManager(), @@ -88,21 +90,6 @@ function routeSetter({ isRelease, addSupervisedRoute, logRegistrationOutcome }: secureHandler: ({ res }) => res.send(true) }); - addSupervisedRoute({ - method: Method.GET, - subscription: new RouteSubscriber("kill").add("key"), - secureHandler: ({ req, res }) => { - if (req.params.key === process.env.session_key) { - res.send("<img src='https://media.giphy.com/media/NGIfqtcS81qi4/giphy.gif' style='width:100%;height:100%;'/>"); - setTimeout(() => { - sessionAgent.killSession("an authorized user has manually ended the server session via the /kill route", false); - }, 5000); - } else { - res.redirect("/home"); - } - } - }); - const serve: PublicHandler = ({ req, res }) => { const detector = new mobileDetect(req.headers['user-agent'] || ""); const filename = detector.mobile() !== null ? 'mobile/image.html' : 'index.html'; |