2021-07-28 01:52:57 +02:00
|
|
|
import { RateLimiter } from "../../../../src/node/routes/login"
|
2021-12-17 20:06:52 +01:00
|
|
|
import { mockLogger } from "../../../utils/helpers"
|
2021-07-28 01:52:57 +02:00
|
|
|
import * as httpserver from "../../../utils/httpserver"
|
|
|
|
import * as integration from "../../../utils/integration"
|
2021-06-30 00:28:44 +02:00
|
|
|
|
2021-04-16 00:12:07 +02:00
|
|
|
describe("login", () => {
|
2021-12-17 20:06:52 +01:00
|
|
|
beforeAll(() => {
|
|
|
|
mockLogger()
|
|
|
|
})
|
|
|
|
|
2021-04-16 00:12:07 +02:00
|
|
|
describe("RateLimiter", () => {
|
|
|
|
it("should allow one try ", () => {
|
|
|
|
const limiter = new RateLimiter()
|
2021-04-19 19:57:50 +02:00
|
|
|
expect(limiter.removeToken()).toBe(true)
|
2021-04-16 00:12:07 +02:00
|
|
|
})
|
|
|
|
|
2021-04-16 23:23:46 +02:00
|
|
|
it("should pull tokens from both limiters (minute & hour)", () => {
|
|
|
|
const limiter = new RateLimiter()
|
|
|
|
|
|
|
|
// Try twice, which pulls two from the minute bucket
|
2021-04-19 19:57:50 +02:00
|
|
|
limiter.removeToken()
|
|
|
|
limiter.removeToken()
|
2021-04-16 23:23:46 +02:00
|
|
|
|
|
|
|
// Check that we can still try
|
|
|
|
// which should be true since there are 12 remaining in the hour bucket
|
|
|
|
expect(limiter.canTry()).toBe(true)
|
2021-04-19 19:57:50 +02:00
|
|
|
expect(limiter.removeToken()).toBe(true)
|
2021-04-16 23:23:46 +02:00
|
|
|
})
|
|
|
|
|
2021-04-16 00:12:07 +02:00
|
|
|
it("should not allow more than 14 tries in less than an hour", () => {
|
|
|
|
const limiter = new RateLimiter()
|
|
|
|
|
|
|
|
// The limiter allows 2 tries per minute plus 12 per hour
|
|
|
|
// so if we run it 15 times, 14 should return true and the last
|
|
|
|
// should return false
|
|
|
|
for (let i = 1; i <= 14; i++) {
|
2021-04-19 19:57:50 +02:00
|
|
|
expect(limiter.removeToken()).toBe(true)
|
2021-04-16 00:12:07 +02:00
|
|
|
}
|
|
|
|
|
2021-04-19 19:57:50 +02:00
|
|
|
expect(limiter.canTry()).toBe(false)
|
|
|
|
expect(limiter.removeToken()).toBe(false)
|
2021-04-16 00:12:07 +02:00
|
|
|
})
|
|
|
|
})
|
2021-06-30 00:28:44 +02:00
|
|
|
describe("/login", () => {
|
|
|
|
let _codeServer: httpserver.HttpServer | undefined
|
|
|
|
function codeServer(): httpserver.HttpServer {
|
|
|
|
if (!_codeServer) {
|
|
|
|
throw new Error("tried to use code-server before setting it up")
|
|
|
|
}
|
|
|
|
return _codeServer
|
|
|
|
}
|
|
|
|
|
|
|
|
// Store whatever might be in here so we can restore it afterward.
|
|
|
|
// TODO: We should probably pass this as an argument somehow instead of
|
|
|
|
// manipulating the environment.
|
|
|
|
const previousEnvPassword = process.env.PASSWORD
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
process.env.PASSWORD = "test"
|
|
|
|
_codeServer = await integration.setup(["--auth=password"], "")
|
|
|
|
})
|
|
|
|
|
2021-06-30 19:39:48 +02:00
|
|
|
afterEach(async () => {
|
2021-06-30 00:28:44 +02:00
|
|
|
process.env.PASSWORD = previousEnvPassword
|
2021-06-30 19:39:48 +02:00
|
|
|
if (_codeServer) {
|
2021-10-28 22:27:17 +02:00
|
|
|
await _codeServer.dispose()
|
2021-06-30 19:39:48 +02:00
|
|
|
_codeServer = undefined
|
|
|
|
}
|
2021-06-30 00:28:44 +02:00
|
|
|
})
|
|
|
|
|
2024-04-16 21:13:36 +02:00
|
|
|
it("should return 'Missing password' without body", async () => {
|
2021-06-30 00:28:44 +02:00
|
|
|
const resp = await codeServer().fetch("/login", { method: "POST" })
|
|
|
|
const htmlContent = await resp.text()
|
2024-04-16 21:13:36 +02:00
|
|
|
expect(resp.status).toBe(200)
|
2021-06-30 19:37:08 +02:00
|
|
|
expect(htmlContent).toContain("Missing password")
|
2021-06-30 00:28:44 +02:00
|
|
|
})
|
2021-10-28 22:27:17 +02:00
|
|
|
|
|
|
|
it("should return HTML with 'Incorrect password' message", async () => {
|
|
|
|
const params = new URLSearchParams()
|
|
|
|
params.append("password", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
|
|
|
const resp = await codeServer().fetch("/login", {
|
|
|
|
method: "POST",
|
|
|
|
body: params,
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
|
|
|
|
expect(htmlContent).toContain("Incorrect password")
|
|
|
|
})
|
2022-10-14 00:32:20 +02:00
|
|
|
|
|
|
|
it("should return correct app-name", async () => {
|
|
|
|
process.env.PASSWORD = previousEnvPassword
|
|
|
|
const appName = "testnäme"
|
|
|
|
const codeServer = await integration.setup([`--app-name=${appName}`], "")
|
|
|
|
const resp = await codeServer.fetch("/login", { method: "GET" })
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
expect(htmlContent).toContain(`${appName}</h1>`)
|
|
|
|
expect(htmlContent).toContain(`<title>${appName} login</title>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("should return correct app-name when unset", async () => {
|
|
|
|
process.env.PASSWORD = previousEnvPassword
|
|
|
|
const appName = "code-server"
|
|
|
|
const codeServer = await integration.setup([], "")
|
|
|
|
const resp = await codeServer.fetch("/login", { method: "GET" })
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
expect(htmlContent).toContain(`${appName}</h1>`)
|
|
|
|
expect(htmlContent).toContain(`<title>${appName} login</title>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("should return correct welcome text", async () => {
|
|
|
|
process.env.PASSWORD = previousEnvPassword
|
|
|
|
const welcomeText = "Welcome to your code workspace! öäü🔐"
|
|
|
|
const codeServer = await integration.setup([`--welcome-text=${welcomeText}`], "")
|
|
|
|
const resp = await codeServer.fetch("/login", { method: "GET" })
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
expect(htmlContent).toContain(welcomeText)
|
|
|
|
})
|
|
|
|
|
|
|
|
it("should return correct welcome text when none is set but app-name is", async () => {
|
|
|
|
process.env.PASSWORD = previousEnvPassword
|
|
|
|
const appName = "testnäme"
|
|
|
|
const codeServer = await integration.setup([`--app-name=${appName}`], "")
|
|
|
|
const resp = await codeServer.fetch("/login", { method: "GET" })
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
expect(htmlContent).toContain(`Welcome to ${appName}`)
|
|
|
|
})
|
2023-01-13 18:42:49 +01:00
|
|
|
|
|
|
|
it("should return correct welcome text when locale is set to non-English", async () => {
|
|
|
|
process.env.PASSWORD = previousEnvPassword
|
|
|
|
const locale = "zh-cn"
|
|
|
|
const codeServer = await integration.setup([`--locale=${locale}`], "")
|
|
|
|
const resp = await codeServer.fetch("/login", { method: "GET" })
|
|
|
|
|
|
|
|
const htmlContent = await resp.text()
|
|
|
|
expect(resp.status).toBe(200)
|
|
|
|
expect(htmlContent).toContain(`欢迎来到 code-server`)
|
|
|
|
})
|
2021-06-30 00:28:44 +02:00
|
|
|
})
|
2021-04-16 00:12:07 +02:00
|
|
|
})
|