Skip to content

File shutdown enabled with allow-shutdown configuation, adding File > Exit menu option #7175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support hashed password for basic auth and match style
  • Loading branch information
gaberudy committed Jan 19, 2025
commit 6448408fc47bd4e38c9c1239774d5afb1773e396
10 changes: 8 additions & 2 deletions src/node/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ export type Options<T> = {
export const options: Options<Required<UserProvidedArgs>> = {
auth: { type: AuthType, description: "The type of authentication to use." },
"auth-user": {
type: "string",
description: "The username for http-basic authentication."
type: "string",
description: "The username for http-basic authentication.",
},
password: {
type: "string",
Expand Down Expand Up @@ -486,6 +486,7 @@ export interface DefaultedArgs extends ConfigArgs {
"proxy-domain": string[]
verbose: boolean
usingEnvPassword: boolean
usingEnvAuthUser: boolean
usingEnvHashedPassword: boolean
"extensions-dir": string
"user-data-dir": string
Expand Down Expand Up @@ -575,9 +576,13 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
if (process.env.PASSWORD) {
args.password = process.env.PASSWORD
}

const usingEnvAuthUser = !!process.env.AUTH_USER
if (process.env.AUTH_USER) {
args["auth"] = AuthType.HttpBasic
args["auth-user"] = process.env.AUTH_USER
} else if (args["auth-user"]) {
args["auth"] = AuthType.HttpBasic
}

if (process.env.CS_DISABLE_FILE_DOWNLOADS?.match(/^(1|true)$/)) {
Expand Down Expand Up @@ -631,6 +636,7 @@ export async function setDefaults(cliArgs: UserProvidedArgs, configArgs?: Config
return {
...args,
usingEnvPassword,
usingEnvAuthUser,
usingEnvHashedPassword,
} as DefaultedArgs // TODO: Technically no guarantee this is fulfilled.
}
Expand Down
39 changes: 28 additions & 11 deletions src/node/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as expressCore from "express-serve-static-core"
import * as http from "http"
import * as net from "net"
import * as qs from "qs"
import safeCompare from "safe-compare"
import { Disposable } from "../common/emitter"
import { CookieKeys, HttpCode, HttpError } from "../common/http"
import { normalize } from "../common/util"
Expand All @@ -20,6 +21,7 @@ import {
escapeHtml,
escapeJSON,
splitOnFirstEquals,
isHashMatch,
} from "./util"

/**
Expand Down Expand Up @@ -114,21 +116,31 @@ export const ensureAuthenticated = async (
/**
* Validate basic auth credentials.
*/
const validateBasicAuth = (authHeader: string | undefined, authUser: string | undefined, authPassword: string | undefined): boolean => {
if (!authHeader?.startsWith('Basic ')) {
return false;
const validateBasicAuth = async (
authHeader: string | undefined,
authUser: string | undefined,
authPassword: string | undefined,
hashedPassword: string | undefined,
): Promise<boolean> => {
if (!authHeader?.startsWith("Basic ")) {
return false
}

try {
const base64Credentials = authHeader.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');
const [username, password] = credentials.split(':');
return username === authUser && password === authPassword;
const base64Credentials = authHeader.split(" ")[1]
const credentials = Buffer.from(base64Credentials, "base64").toString("utf-8")
const [username, password] = credentials.split(":")
if (username !== authUser) return false
if (hashedPassword) {
return await isHashMatch(password, hashedPassword)
} else {
return safeCompare(password, authPassword || "")
}
} catch (error) {
logger.error('Error validating basic auth:' + error);
return false;
logger.error("Error validating basic auth:" + error)
return false
}
};
}

/**
* Return true if authenticated via cookies.
Expand All @@ -152,7 +164,12 @@ export const authenticated = async (req: express.Request): Promise<boolean> => {
return await isCookieValid(isCookieValidArgs)
}
case AuthType.HttpBasic: {
return validateBasicAuth(req.headers.authorization, req.args["auth-user"], req.args.password);
return await validateBasicAuth(
req.headers.authorization,
req.args["auth-user"],
req.args.password,
req.args["hashed-password"],
)
}
default: {
throw new Error(`Unsupported auth type ${req.args.auth}`)
Expand Down
13 changes: 8 additions & 5 deletions src/node/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const runCodeServer = async (

logger.info(`Using config file ${args.config}`)
logger.info(`${protocol.toUpperCase()} server listening on ${serverAddress.toString()}`)
if (args.auth === AuthType.Password) {
if (args.auth === AuthType.Password || args.auth === AuthType.HttpBasic) {
logger.info(" - Authentication is enabled")
if (args.usingEnvPassword) {
logger.info(" - Using password from $PASSWORD")
Expand All @@ -142,10 +142,13 @@ export const runCodeServer = async (
} else {
logger.info(` - Using password from ${args.config}`)
}
} else if (args.auth === AuthType.HttpBasic) {
logger.info(" - HTTP basic authentication is enabled")
logger.info(" - Using user from $AUTH_USER")
logger.info(" - Using password from $PASSWORD")
if (args.auth === AuthType.HttpBasic) {
if (args.usingEnvAuthUser) {
logger.info(" - Using user from $AUTH_USER")
} else {
logger.info(` - With user ${args["auth-user"]}`)
}
}
} else {
logger.info(" - Authentication is disabled")
}
Expand Down
2 changes: 1 addition & 1 deletion src/node/routes/domainProxy.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { Request, Router } from "express"
import { HttpCode, HttpError } from "../../common/http"
import { AuthType } from "../cli"
import { getHost, ensureProxyEnabled, authenticated, ensureAuthenticated, ensureOrigin, redirect, self } from "../http"
import { proxy } from "../proxy"
import { Router as WsRouter } from "../wsRouter"
import { AuthType } from "../cli"

export const router = Router()

Expand Down
6 changes: 3 additions & 3 deletions src/node/routes/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import * as http from "http"
import * as net from "net"
import * as path from "path"
import { WebsocketRequest } from "../../../typings/pluginapi"
import { HttpCode, HttpError } from "../../common/http"
import { logError } from "../../common/util"
import { AuthType, CodeArgs, toCodeArgs } from "../cli"
import { isDevMode, vsRootPath } from "../constants"
import { authenticated, ensureAuthenticated, ensureOrigin, redirect, replaceTemplates, self } from "../http"
import { SocketProxyProvider } from "../socket"
import { isFile } from "../util"
import { Router as WsRouter } from "../wsRouter"
import { HttpCode, HttpError } from "../../common/http"

export const router = express.Router()

Expand Down Expand Up @@ -121,9 +121,9 @@ router.get("/", ensureVSCodeLoaded, async (req, res, next) => {
if (!isAuthenticated) {
// If auth is HttpBasic, return a 401.
if (req.args.auth === AuthType.HttpBasic) {
res.setHeader('WWW-Authenticate', 'Basic realm="Access to the site"')
res.setHeader("WWW-Authenticate", `Basic realm="${req.args["app-name"] || "code-server"}"`)
throw new HttpError("Unauthorized", HttpCode.Unauthorized)
};
}
const to = self(req)
return redirect(req, res, "login", {
to: to !== "/" ? to : undefined,
Expand Down
Loading