// server // app/routes.js import express from "express" import { getValidatedHost, getValidatedPort, maskSensitiveData, validateSshTerm, parseEnvVars } from "./utils.js" import handleConnection from "./connectionHandler.js" import { createNamespacedDebug } from "./logger.js" import { createAuthMiddleware } from "./middleware.js" import { ConfigError, handleError } from "./errors.js" import { HTTP } from "./constants.js" const debug = createNamespacedDebug("routes") export function createRoutes(config) { const router = express.Router() const auth = createAuthMiddleware(config) // Scenario 1: No auth required, uses websocket authentication instead router.get("/", (req, res) => { debug("router.get./: Accessed / route") handleConnection(req, res) }) /** * Handles the "/host/" route, which requires authentication and uses the * `auth` middleware function to handle HTTP Basic Authentication. * * This route validates the host and port parameters, sets the `sshCredentials` * object in the session, and calls the `handleConnection` function to handle * the connection. * * If the `config.ssh.host` is not set, it throws a `ConfigError` with the * appropriate error message. * * @param {Object} req - The Express request object * @param {Object} res - The Express response object */ router.get("/host/", auth, (req, res) => { debug(`router.get.host: /ssh/host/ route`) const envVars = parseEnvVars(req.query.env) if (envVars) { req.session.envVars = envVars debug("routes: Parsed environment variables: %O", envVars) } try { if (!config.ssh.host) { throw new ConfigError( "Host parameter required when default host not configured" ) } const { host } = config.ssh const port = getValidatedPort(req.query.port) const sshterm = validateSshTerm(req.query.sshterm) req.session.sshCredentials = req.session.sshCredentials || {} req.session.sshCredentials.host = host req.session.sshCredentials.port = port if (req.query.sshterm) { req.session.sshCredentials.term = sshterm } req.session.usedBasicAuth = true const sanitizedCredentials = maskSensitiveData( JSON.parse(JSON.stringify(req.session.sshCredentials)) ) debug("/ssh/host/ Credentials: ", sanitizedCredentials) handleConnection(req, res, { host: host }) } catch (err) { const error = new ConfigError(`Invalid configuration: ${err.message}`) handleError(error, res) } }) // Scenario 2: Auth required, uses HTTP Basic Auth router.get("/host/:host?", auth, (req, res) => { debug(`router.get.host: /ssh/host/${req.params.host} route`) const envVars = parseEnvVars(req.query.env) if (envVars) { req.session.envVars = envVars debug("routes: Parsed environment variables: %O", envVars) } try { const host = getValidatedHost(req.params.host) const port = getValidatedPort(req.query.port) // Validate and sanitize sshterm parameter if it exists const sshterm = validateSshTerm(req.query.sshterm) req.session.sshCredentials = req.session.sshCredentials || {} req.session.sshCredentials.host = host req.session.sshCredentials.port = port if (req.query.sshterm) { req.session.sshCredentials.term = sshterm } req.session.usedBasicAuth = true // Sanitize and log the sshCredentials object const sanitizedCredentials = maskSensitiveData( JSON.parse(JSON.stringify(req.session.sshCredentials)) ) debug("/ssh/host/ Credentials: ", sanitizedCredentials) handleConnection(req, res, { host: host }) } catch (err) { const error = new ConfigError(`Invalid configuration: ${err.message}`) handleError(error, res) } }) // Clear credentials route router.get("/clear-credentials", (req, res) => { req.session.sshCredentials = null res.status(HTTP.OK).send(HTTP.CREDENTIALS_CLEARED) }) router.get("/force-reconnect", (req, res) => { req.session.sshCredentials = null res.status(HTTP.UNAUTHORIZED).send(HTTP.AUTH_REQUIRED) }) return router }