mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
Add authkey header for requests to the backend (#256)
With this PR, Electron will generate a new authorization key that the Go backend will look for in any incoming requests. The Electron backend will inject this header with all requests to the backend to ensure no additional work is required on the frontend. This also adds a `fetchutil` abstraction that will use the Electron `net` module when calls are made from the Electron backend to the Go backend. When using the `node:fetch` module, Electron can't inject headers to requests. The Electron `net` module is also faster than the Node module. This also breaks out platform functions in emain into their own file so other emain modules can import them.
This commit is contained in:
parent
23261a7a98
commit
e527e2ab77
@ -17,6 +17,7 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/authkey"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/service"
|
"github.com/wavetermdev/thenextwave/pkg/service"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
||||||
@ -165,7 +166,13 @@ func main() {
|
|||||||
wavebase.WaveVersion = WaveVersion
|
wavebase.WaveVersion = WaveVersion
|
||||||
wavebase.BuildTime = BuildTime
|
wavebase.BuildTime = BuildTime
|
||||||
|
|
||||||
err := service.ValidateServiceMap()
|
err := authkey.SetAuthKeyFromEnv()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("error setting auth key: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = service.ValidateServiceMap()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error validating service map: %v\n", err)
|
log.Printf("error validating service map: %v\n", err)
|
||||||
return
|
return
|
||||||
|
25
emain/authkey.ts
Normal file
25
emain/authkey.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { getWebServerEndpoint, getWSServerEndpoint } from "@/util/endpoints";
|
||||||
|
import { ipcMain } from "electron";
|
||||||
|
|
||||||
|
const AuthKeyHeader = "X-AuthKey";
|
||||||
|
export const AuthKeyEnv = "AUTH_KEY";
|
||||||
|
export const AuthKey = crypto.randomUUID();
|
||||||
|
|
||||||
|
console.log("authKey", AuthKey);
|
||||||
|
|
||||||
|
ipcMain.on("get-auth-key", (event) => {
|
||||||
|
event.returnValue = AuthKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
export function configureAuthKeyRequestInjection(session: Electron.Session) {
|
||||||
|
const filter: Electron.WebRequestFilter = {
|
||||||
|
urls: [`${getWebServerEndpoint()}/*`, `${getWSServerEndpoint()}/*`],
|
||||||
|
};
|
||||||
|
session.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
|
||||||
|
details.requestHeaders[AuthKeyHeader] = AuthKey;
|
||||||
|
callback({ requestHeaders: details.requestHeaders });
|
||||||
|
});
|
||||||
|
}
|
@ -5,7 +5,6 @@ import * as electron from "electron";
|
|||||||
import { FastAverageColor } from "fast-average-color";
|
import { FastAverageColor } from "fast-average-color";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import * as child_process from "node:child_process";
|
import * as child_process from "node:child_process";
|
||||||
import os from "os";
|
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import { PNG } from "pngjs";
|
import { PNG } from "pngjs";
|
||||||
import * as readline from "readline";
|
import * as readline from "readline";
|
||||||
@ -16,9 +15,20 @@ import winston from "winston";
|
|||||||
import { initGlobal } from "../frontend/app/store/global";
|
import { initGlobal } from "../frontend/app/store/global";
|
||||||
import * as services from "../frontend/app/store/services";
|
import * as services from "../frontend/app/store/services";
|
||||||
import { WSServerEndpointVarName, WebServerEndpointVarName, getWebServerEndpoint } from "../frontend/util/endpoints";
|
import { WSServerEndpointVarName, WebServerEndpointVarName, getWebServerEndpoint } from "../frontend/util/endpoints";
|
||||||
import { WaveDevVarName, WaveDevViteVarName } from "../frontend/util/isdev";
|
import { fetch } from "../frontend/util/fetchutil";
|
||||||
import * as keyutil from "../frontend/util/keyutil";
|
|
||||||
import { fireAndForget } from "../frontend/util/util";
|
import { fireAndForget } from "../frontend/util/util";
|
||||||
|
import { AuthKey, AuthKeyEnv, configureAuthKeyRequestInjection } from "./authkey";
|
||||||
|
import {
|
||||||
|
getElectronAppBasePath,
|
||||||
|
getGoAppBasePath,
|
||||||
|
getWaveHomeDir,
|
||||||
|
getWaveSrvCwd,
|
||||||
|
getWaveSrvPath,
|
||||||
|
isDev,
|
||||||
|
isDevVite,
|
||||||
|
unameArch,
|
||||||
|
unamePlatform,
|
||||||
|
} from "./platform";
|
||||||
import { configureAutoUpdater, updater } from "./updater";
|
import { configureAutoUpdater, updater } from "./updater";
|
||||||
|
|
||||||
const electronApp = electron.app;
|
const electronApp = electron.app;
|
||||||
@ -27,7 +37,6 @@ let WaveBuildTime = 0; // set by WAVESRV-ESTART
|
|||||||
|
|
||||||
const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
const WaveAppPathVarName = "WAVETERM_APP_PATH";
|
||||||
const WaveSrvReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID";
|
const WaveSrvReadySignalPidVarName = "WAVETERM_READY_SIGNAL_PID";
|
||||||
const AuthKeyFile = "waveterm.authkey";
|
|
||||||
electron.nativeTheme.themeSource = "dark";
|
electron.nativeTheme.themeSource = "dark";
|
||||||
|
|
||||||
type WaveBrowserWindow = Electron.BrowserWindow & { waveWindowId: string; readyPromise: Promise<void> };
|
type WaveBrowserWindow = Electron.BrowserWindow & { waveWindowId: string; readyPromise: Promise<void> };
|
||||||
@ -44,35 +53,14 @@ let globalIsRelaunching = false;
|
|||||||
let wasActive = true;
|
let wasActive = true;
|
||||||
let wasInFg = true;
|
let wasInFg = true;
|
||||||
|
|
||||||
const isDev = !electronApp.isPackaged;
|
|
||||||
const isDevVite = isDev && process.env.ELECTRON_RENDERER_URL;
|
|
||||||
if (isDev) {
|
|
||||||
process.env[WaveDevVarName] = "1";
|
|
||||||
}
|
|
||||||
if (isDevVite) {
|
|
||||||
process.env[WaveDevViteVarName] = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
|
let waveSrvProc: child_process.ChildProcessWithoutNullStreams | null = null;
|
||||||
electronApp.setName(isDev ? "TheNextWave (Dev)" : "TheNextWave");
|
|
||||||
const unamePlatform = process.platform;
|
|
||||||
let unameArch: string = process.arch;
|
|
||||||
if (unameArch == "x64") {
|
|
||||||
unameArch = "amd64";
|
|
||||||
}
|
|
||||||
keyutil.setKeyUtilPlatform(unamePlatform);
|
|
||||||
|
|
||||||
// must match golang
|
|
||||||
function getWaveHomeDir() {
|
|
||||||
return path.join(os.homedir(), isDev ? ".w2-dev" : ".w2");
|
|
||||||
}
|
|
||||||
|
|
||||||
const waveHome = getWaveHomeDir();
|
const waveHome = getWaveHomeDir();
|
||||||
|
|
||||||
const oldConsoleLog = console.log;
|
const oldConsoleLog = console.log;
|
||||||
|
|
||||||
const loggerTransports: winston.transport[] = [
|
const loggerTransports: winston.transport[] = [
|
||||||
new winston.transports.File({ filename: path.join(waveHome, "waveterm-app.log"), level: "info" }),
|
new winston.transports.File({ filename: path.join(getWaveHomeDir(), "waveterm-app.log"), level: "info" }),
|
||||||
];
|
];
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
loggerTransports.push(new winston.transports.Console());
|
loggerTransports.push(new winston.transports.Console());
|
||||||
@ -110,29 +98,6 @@ if (isDev) {
|
|||||||
|
|
||||||
initGlobal({ windowId: null, clientId: null, platform: unamePlatform, environment: "electron" });
|
initGlobal({ windowId: null, clientId: null, platform: unamePlatform, environment: "electron" });
|
||||||
|
|
||||||
function getElectronAppBasePath(): string {
|
|
||||||
return path.dirname(__dirname);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getGoAppBasePath(): string {
|
|
||||||
return getElectronAppBasePath().replace("app.asar", "app.asar.unpacked");
|
|
||||||
}
|
|
||||||
|
|
||||||
const wavesrvBinName = `wavesrv.${unameArch}`;
|
|
||||||
|
|
||||||
function getWaveSrvPath(): string {
|
|
||||||
if (process.platform === "win32") {
|
|
||||||
const winBinName = `${wavesrvBinName}.exe`;
|
|
||||||
const appPath = path.join(getGoAppBasePath(), "bin", winBinName);
|
|
||||||
return `${appPath}`;
|
|
||||||
}
|
|
||||||
return path.join(getGoAppBasePath(), "bin", wavesrvBinName);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWaveSrvCwd(): string {
|
|
||||||
return getWaveHomeDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getWindowForEvent(event: Electron.IpcMainEvent): Electron.BrowserWindow {
|
function getWindowForEvent(event: Electron.IpcMainEvent): Electron.BrowserWindow {
|
||||||
const windowId = event.sender.id;
|
const windowId = event.sender.id;
|
||||||
return electron.BrowserWindow.fromId(windowId);
|
return electron.BrowserWindow.fromId(windowId);
|
||||||
@ -148,6 +113,7 @@ function runWaveSrv(): Promise<boolean> {
|
|||||||
const envCopy = { ...process.env };
|
const envCopy = { ...process.env };
|
||||||
envCopy[WaveAppPathVarName] = getGoAppBasePath();
|
envCopy[WaveAppPathVarName] = getGoAppBasePath();
|
||||||
envCopy[WaveSrvReadySignalPidVarName] = process.pid.toString();
|
envCopy[WaveSrvReadySignalPidVarName] = process.pid.toString();
|
||||||
|
envCopy[AuthKeyEnv] = AuthKey;
|
||||||
const waveSrvCmd = getWaveSrvPath();
|
const waveSrvCmd = getWaveSrvPath();
|
||||||
console.log("trying to run local server", waveSrvCmd);
|
console.log("trying to run local server", waveSrvCmd);
|
||||||
const proc = child_process.spawn(getWaveSrvPath(), {
|
const proc = child_process.spawn(getWaveSrvPath(), {
|
||||||
@ -197,6 +163,7 @@ function runWaveSrv(): Promise<boolean> {
|
|||||||
applicationVersion: "v" + WaveVersion,
|
applicationVersion: "v" + WaveVersion,
|
||||||
version: (isDev ? "dev-" : "") + String(WaveBuildTime),
|
version: (isDev ? "dev-" : "") + String(WaveBuildTime),
|
||||||
});
|
});
|
||||||
|
configureAuthKeyRequestInjection(electron.session.defaultSession);
|
||||||
waveSrvReadyResolve(true);
|
waveSrvReadyResolve(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -465,6 +432,7 @@ function createBrowserWindow(
|
|||||||
console.log("window-open denied", url);
|
console.log("window-open denied", url);
|
||||||
return { action: "deny" };
|
return { action: "deny" };
|
||||||
});
|
});
|
||||||
|
configureAuthKeyRequestInjection(win.webContents.session);
|
||||||
return win;
|
return win;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -548,12 +516,6 @@ function ensureBoundsAreVisible(bounds: electron.Rectangle): electron.Rectangle
|
|||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
electron.ipcMain.on("getIsDev", (event) => {
|
|
||||||
event.returnValue = isDev;
|
|
||||||
});
|
|
||||||
electron.ipcMain.on("getPlatform", (event, url) => {
|
|
||||||
event.returnValue = unamePlatform;
|
|
||||||
});
|
|
||||||
// Listen for the open-external event from the renderer process
|
// Listen for the open-external event from the renderer process
|
||||||
electron.ipcMain.on("open-external", (event, url) => {
|
electron.ipcMain.on("open-external", (event, url) => {
|
||||||
if (url && typeof url === "string") {
|
if (url && typeof url === "string") {
|
||||||
@ -571,7 +533,7 @@ electron.ipcMain.on("download", (event, payload) => {
|
|||||||
window.webContents.downloadURL(streamingUrl);
|
window.webContents.downloadURL(streamingUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
electron.ipcMain.on("getCursorPoint", (event) => {
|
electron.ipcMain.on("get-cursor-point", (event) => {
|
||||||
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
||||||
const screenPoint = electron.screen.getCursorScreenPoint();
|
const screenPoint = electron.screen.getCursorScreenPoint();
|
||||||
const windowRect = window.getContentBounds();
|
const windowRect = window.getContentBounds();
|
||||||
@ -582,7 +544,7 @@ electron.ipcMain.on("getCursorPoint", (event) => {
|
|||||||
event.returnValue = retVal;
|
event.returnValue = retVal;
|
||||||
});
|
});
|
||||||
|
|
||||||
electron.ipcMain.on("getEnv", (event, varName) => {
|
electron.ipcMain.on("get-env", (event, varName) => {
|
||||||
event.returnValue = process.env[varName] ?? null;
|
event.returnValue = process.env[varName] ?? null;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -617,7 +579,7 @@ async function createNewWaveWindow() {
|
|||||||
newBrowserWindow.show();
|
newBrowserWindow.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
electron.ipcMain.on("openNewWindow", () => fireAndForget(createNewWaveWindow));
|
electron.ipcMain.on("open-new-window", () => fireAndForget(createNewWaveWindow));
|
||||||
|
|
||||||
electron.ipcMain.on("contextmenu-show", (event, menuDefArr?: ElectronContextMenuItem[]) => {
|
electron.ipcMain.on("contextmenu-show", (event, menuDefArr?: ElectronContextMenuItem[]) => {
|
||||||
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
const window = electron.BrowserWindow.fromWebContents(event.sender);
|
||||||
|
69
emain/platform.ts
Normal file
69
emain/platform.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import { WaveDevVarName, WaveDevViteVarName } from "@/util/isdev";
|
||||||
|
import { app, ipcMain } from "electron";
|
||||||
|
import os from "os";
|
||||||
|
import path from "path";
|
||||||
|
import * as keyutil from "../frontend/util/keyutil";
|
||||||
|
|
||||||
|
const isDev = !app.isPackaged;
|
||||||
|
const isDevVite = isDev && process.env.ELECTRON_RENDERER_URL;
|
||||||
|
if (isDev) {
|
||||||
|
process.env[WaveDevVarName] = "1";
|
||||||
|
}
|
||||||
|
if (isDevVite) {
|
||||||
|
process.env[WaveDevViteVarName] = "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
app.setName(isDev ? "TheNextWave (Dev)" : "TheNextWave");
|
||||||
|
const unamePlatform = process.platform;
|
||||||
|
const unameArch: string = process.arch === "x64" ? "amd64" : process.arch;
|
||||||
|
keyutil.setKeyUtilPlatform(unamePlatform);
|
||||||
|
|
||||||
|
ipcMain.on("get-is-dev", (event) => {
|
||||||
|
event.returnValue = isDev;
|
||||||
|
});
|
||||||
|
ipcMain.on("get-platform", (event, url) => {
|
||||||
|
event.returnValue = unamePlatform;
|
||||||
|
});
|
||||||
|
|
||||||
|
// must match golang
|
||||||
|
function getWaveHomeDir() {
|
||||||
|
return path.join(os.homedir(), isDev ? ".w2-dev" : ".w2");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getElectronAppBasePath(): string {
|
||||||
|
return path.dirname(__dirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGoAppBasePath(): string {
|
||||||
|
return getElectronAppBasePath().replace("app.asar", "app.asar.unpacked");
|
||||||
|
}
|
||||||
|
|
||||||
|
const wavesrvBinName = `wavesrv.${unameArch}`;
|
||||||
|
|
||||||
|
function getWaveSrvPath(): string {
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const winBinName = `${wavesrvBinName}.exe`;
|
||||||
|
const appPath = path.join(getGoAppBasePath(), "bin", winBinName);
|
||||||
|
return `${appPath}`;
|
||||||
|
}
|
||||||
|
return path.join(getGoAppBasePath(), "bin", wavesrvBinName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getWaveSrvCwd(): string {
|
||||||
|
return getWaveHomeDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
getElectronAppBasePath,
|
||||||
|
getGoAppBasePath,
|
||||||
|
getWaveHomeDir,
|
||||||
|
getWaveSrvCwd,
|
||||||
|
getWaveSrvPath,
|
||||||
|
isDev,
|
||||||
|
isDevVite,
|
||||||
|
unameArch,
|
||||||
|
unamePlatform,
|
||||||
|
};
|
@ -4,10 +4,11 @@
|
|||||||
const { contextBridge, ipcRenderer } = require("electron");
|
const { contextBridge, ipcRenderer } = require("electron");
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("api", {
|
contextBridge.exposeInMainWorld("api", {
|
||||||
getIsDev: () => ipcRenderer.sendSync("getIsDev"),
|
getAuthKey: () => ipcRenderer.sendSync("get-auth-key"),
|
||||||
getPlatform: () => ipcRenderer.sendSync("getPlatform"),
|
getIsDev: () => ipcRenderer.sendSync("get-is-dev"),
|
||||||
getCursorPoint: () => ipcRenderer.sendSync("getCursorPoint"),
|
getPlatform: () => ipcRenderer.sendSync("get-platform"),
|
||||||
openNewWindow: () => ipcRenderer.send("openNewWindow"),
|
getCursorPoint: () => ipcRenderer.sendSync("get-cursor-point"),
|
||||||
|
openNewWindow: () => ipcRenderer.send("open-new-window"),
|
||||||
showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position),
|
showContextMenu: (menu, position) => ipcRenderer.send("contextmenu-show", menu, position),
|
||||||
onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", (_event, id) => callback(id)),
|
onContextMenuClick: (callback) => ipcRenderer.on("contextmenu-click", (_event, id) => callback(id)),
|
||||||
downloadFile: (filePath) => ipcRenderer.send("download", { filePath }),
|
downloadFile: (filePath) => ipcRenderer.send("download", { filePath }),
|
||||||
@ -18,7 +19,7 @@ contextBridge.exposeInMainWorld("api", {
|
|||||||
console.error("Invalid URL passed to openExternal:", url);
|
console.error("Invalid URL passed to openExternal:", url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getEnv: (varName) => ipcRenderer.sendSync("getEnv", varName),
|
getEnv: (varName) => ipcRenderer.sendSync("get-env", varName),
|
||||||
onFullScreenChange: (callback) =>
|
onFullScreenChange: (callback) =>
|
||||||
ipcRenderer.on("fullscreen-change", (_event, isFullScreen) => callback(isFullScreen)),
|
ipcRenderer.on("fullscreen-change", (_event, isFullScreen) => callback(isFullScreen)),
|
||||||
onUpdaterStatusChange: (callback) => ipcRenderer.on("app-update-status", (_event, status) => callback(status)),
|
onUpdaterStatusChange: (callback) => ipcRenderer.on("app-update-status", (_event, status) => callback(status)),
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
newLayoutNode,
|
newLayoutNode,
|
||||||
} from "@/layout/index";
|
} from "@/layout/index";
|
||||||
import { getWebServerEndpoint, getWSServerEndpoint } from "@/util/endpoints";
|
import { getWebServerEndpoint, getWSServerEndpoint } from "@/util/endpoints";
|
||||||
|
import { fetch } from "@/util/fetchutil";
|
||||||
import { produce } from "immer";
|
import { produce } from "immer";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as rxjs from "rxjs";
|
import * as rxjs from "rxjs";
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
import { sendRpcCommand } from "@/app/store/wshrpc";
|
import { sendRpcCommand } from "@/app/store/wshrpc";
|
||||||
import { getWebServerEndpoint } from "@/util/endpoints";
|
import { getWebServerEndpoint } from "@/util/endpoints";
|
||||||
|
import { fetch } from "@/util/fetchutil";
|
||||||
import * as jotai from "jotai";
|
import * as jotai from "jotai";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { atoms, globalStore } from "./global";
|
import { atoms, globalStore } from "./global";
|
||||||
|
1
frontend/types/custom.d.ts
vendored
1
frontend/types/custom.d.ts
vendored
@ -52,6 +52,7 @@ declare global {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type ElectronApi = {
|
type ElectronApi = {
|
||||||
|
getAuthKey(): string;
|
||||||
getIsDev(): boolean;
|
getIsDev(): boolean;
|
||||||
getCursorPoint: () => Electron.Point;
|
getCursorPoint: () => Electron.Point;
|
||||||
|
|
||||||
|
20
frontend/util/fetchutil.ts
Normal file
20
frontend/util/fetchutil.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Utility to abstract the fetch function so the Electron net module can be used when available.
|
||||||
|
|
||||||
|
let net: Electron.Net;
|
||||||
|
|
||||||
|
try {
|
||||||
|
import("electron").then(({ net: electronNet }) => (net = electronNet));
|
||||||
|
} catch (e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
export function fetch(input: string | GlobalRequest | URL, init?: RequestInit): Promise<Response> {
|
||||||
|
if (net) {
|
||||||
|
return net.fetch(input.toString(), init);
|
||||||
|
} else {
|
||||||
|
return globalThis.fetch(input, init);
|
||||||
|
}
|
||||||
|
}
|
38
pkg/authkey/authkey.go
Normal file
38
pkg/authkey/authkey.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package authkey
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var authkey string
|
||||||
|
|
||||||
|
const AuthKeyEnv = "AUTH_KEY"
|
||||||
|
|
||||||
|
func SetAuthKeyFromEnv() error {
|
||||||
|
authkey = os.Getenv(AuthKeyEnv)
|
||||||
|
if authkey == "" {
|
||||||
|
return fmt.Errorf("no auth key found in environment variables")
|
||||||
|
}
|
||||||
|
os.Setenv(AuthKeyEnv, "")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAuthKey() string {
|
||||||
|
return authkey
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateIncomingRequest(r *http.Request) error {
|
||||||
|
reqAuthKey := r.Header.Get("X-AuthKey")
|
||||||
|
if reqAuthKey == "" {
|
||||||
|
return fmt.Errorf("no x-authkey header")
|
||||||
|
}
|
||||||
|
if reqAuthKey != GetAuthKey() {
|
||||||
|
return fmt.Errorf("x-authkey header is invalid")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/authkey"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
"github.com/wavetermdev/thenextwave/pkg/filestore"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/service"
|
"github.com/wavetermdev/thenextwave/pkg/service"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
"github.com/wavetermdev/thenextwave/pkg/telemetry"
|
||||||
@ -397,17 +398,13 @@ func WebFnWrap(opts WebFnOpts, fn WebFnType) WebFnType {
|
|||||||
w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache)
|
w.Header().Set(CacheControlHeaderKey, CacheControlHeaderNoCache)
|
||||||
}
|
}
|
||||||
w.Header().Set("Access-Control-Expose-Headers", "X-ZoneFileInfo")
|
w.Header().Set("Access-Control-Expose-Headers", "X-ZoneFileInfo")
|
||||||
// reqAuthKey := r.Header.Get("X-AuthKey")
|
err := authkey.ValidateIncomingRequest(r)
|
||||||
// if reqAuthKey == "" {
|
if err != nil {
|
||||||
// w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
// w.Write([]byte("no x-authkey header"))
|
w.Write([]byte(fmt.Sprintf("error validating authkey: %v", err)))
|
||||||
// return
|
log.Printf("error validating request: %v", err)
|
||||||
// }
|
return
|
||||||
// if reqAuthKey != scbase.WaveAuthKey {
|
}
|
||||||
// w.WriteHeader(http.StatusInternalServerError)
|
|
||||||
// w.Write([]byte("x-authkey header is invalid"))
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
fn(w, r)
|
fn(w, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/authkey"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/eventbus"
|
"github.com/wavetermdev/thenextwave/pkg/eventbus"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
"github.com/wavetermdev/thenextwave/pkg/web/webcmd"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
"github.com/wavetermdev/thenextwave/pkg/wshrpc"
|
||||||
@ -244,6 +245,14 @@ func HandleWsInternal(w http.ResponseWriter, r *http.Request) error {
|
|||||||
if windowId == "" {
|
if windowId == "" {
|
||||||
return fmt.Errorf("windowid is required")
|
return fmt.Errorf("windowid is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := authkey.ValidateIncomingRequest(r)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte(fmt.Sprintf("error validating authkey: %v", err)))
|
||||||
|
log.Printf("error validating request: %v", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
conn, err := WebSocketUpgrader.Upgrade(w, r, nil)
|
conn, err := WebSocketUpgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("WebSocket Upgrade Failed: %v", err)
|
return fmt.Errorf("WebSocket Upgrade Failed: %v", err)
|
||||||
|
Loading…
Reference in New Issue
Block a user