From a530f59704ea197fb926b871cb4271b30ba7c968 Mon Sep 17 00:00:00 2001 From: Bill Church Date: Mon, 19 Aug 2024 18:51:07 +0000 Subject: [PATCH] feat: `Switch User` or `reauth` feature for Basic Auth sessions --- app/app.js | 16 ++++++++++++++++ app/config.js | 16 +++++++++++----- app/connectionHandler.js | 3 ++- app/routes.js | 8 +++++--- app/socket.js | 4 +++- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/app/app.js b/app/app.js index 3010a56..414f922 100644 --- a/app/app.js +++ b/app/app.js @@ -45,6 +45,22 @@ function createApp() { app.use(bodyParser.urlencoded({ extended: true })) app.use(bodyParser.json()) + // Add cookie-setting middleware + app.use((req, res, next) => { + if (req.session.sshCredentials) { + const cookieData = { + host: req.session.sshCredentials.host, + port: req.session.sshCredentials.port + } + res.cookie("basicauth", JSON.stringify(cookieData), { + httpOnly: false, + path: '/ssh/host/', + sameSite: 'Strict' + }) // ensure httOnly is false for the client to read the cookie + } + next() + }) + // Serve static files from the webssh2_client module with a custom prefix app.use("/ssh/assets", express.static(clientPath)) diff --git a/app/config.js b/app/config.js index 520a825..9cfa572 100644 --- a/app/config.js +++ b/app/config.js @@ -282,22 +282,28 @@ function loadConfig() { providedConfig ) + // Override the port with the PORT environment variable if it's set + if (process.env.PORT) { + mergedConfig.listen.port = parseInt(process.env.PORT, 10) + console.log(`Using PORT from environment: ${mergedConfig.listen.port}`) + } + const validatedConfig = validateConfig(mergedConfig) - console.log("Merged and validated configuration") + console.log('Merged and validated configuration') return validatedConfig } else { logError( - "\n\nERROR: Missing config.json for webssh. Using default config: " + + '\n\nERROR: Missing config.json for webssh. Using default config: ' + JSON.stringify(defaultConfig) + - "\n\n See config.json.sample for details\n\n" + '\n\n See config.json.sample for details\n\n' ) return defaultConfig } } catch (err) { logError( - "\n\nERROR: Problem loading config.json for webssh. Using default config: " + + '\n\nERROR: Problem loading config.json for webssh. Using default config: ' + JSON.stringify(defaultConfig) + - "\n\n See config.json.sample for details\n\n", + '\n\n See config.json.sample for details\n\n', err ) return defaultConfig diff --git a/app/connectionHandler.js b/app/connectionHandler.js index 1f87556..4ed791c 100644 --- a/app/connectionHandler.js +++ b/app/connectionHandler.js @@ -28,7 +28,8 @@ function handleConnection(req, res, urlParams) { } // Check if the current route is /host/:host - if (req.path.startsWith('/ssh/host/')) { + debug('handleConnection req.path:', req.path) + if (req.path.startsWith('/host/')) { tempConfig.autoConnect = true } diff --git a/app/routes.js b/app/routes.js index bec3620..9b17505 100644 --- a/app/routes.js +++ b/app/routes.js @@ -10,7 +10,7 @@ const { sanitizeObject, validateSshTerm } = require("./utils") const validator = require("validator") function auth(req, res, next) { - debug("Authenticating user with HTTP Basic Auth") + debug("auth: Basic Auth") var credentials = basicAuth(req) if (!credentials) { res.setHeader("WWW-Authenticate", 'Basic realm="WebSSH2"') @@ -21,6 +21,7 @@ function auth(req, res, next) { username: validator.escape(credentials.name), password: credentials.pass // We don't sanitize the password as it might contain special characters } + req.session.usedBasicAuth = true // Set this flag when Basic Auth is used next() } @@ -59,6 +60,7 @@ router.get("/host/:host", auth, function (req, res) { if (req.query.sshTerm) { req.session.sshCredentials.term = sshTerm } + req.session.usedBasicAuth = true // Sanitize and log the sshCredentials object const sanitizedCredentials = sanitizeObject( @@ -70,12 +72,12 @@ router.get("/host/:host", auth, function (req, res) { }) // Clear credentials route -router.post("/clear-credentials", function (req, res) { +router.get("/clear-credentials", function (req, res) { req.session.sshCredentials = null res.status(200).send("Credentials cleared.") }) -router.post("/force-reconnect", function (req, res) { +router.get("/force-reconnect", function (req, res) { req.session.sshCredentials = null res.status(401).send("Authentication required.") }) diff --git a/app/socket.js b/app/socket.js index 5c44519..8513324 100644 --- a/app/socket.js +++ b/app/socket.js @@ -371,6 +371,7 @@ module.exports = function (io, config) { socket.handshake.session.sshCredentials.username = null socket.handshake.session.sshCredentials.password = null } + socket.handshake.session.usedBasicAuth = false sessionState.authenticated = false sessionState.username = null sessionState.password = null @@ -382,7 +383,8 @@ module.exports = function (io, config) { } // Check for HTTP Basic Auth credentials - if (socket.handshake.session.sshCredentials) { + if (socket.handshake.session.usedBasicAuth && socket.handshake.session.sshCredentials) { + // if (socket.handshake.session.sshCredentials) { var creds = socket.handshake.session.sshCredentials debug( "handleConnection: " +