| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- import { NodeHttpServer } from "@effect/platform-node"
- import { describe, expect } from "bun:test"
- import { Effect, Layer, Option, Schema } from "effect"
- import { HttpClient, HttpClientRequest, HttpRouter } from "effect/unstable/http"
- import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi"
- import { ServerAuth } from "../../src/server/auth"
- import { Authorization, authorizationLayer } from "../../src/server/routes/instance/httpapi/middleware/authorization"
- import { testEffect } from "../lib/effect"
- const Api = HttpApi.make("test-authorization").add(
- HttpApiGroup.make("test")
- .add(
- HttpApiEndpoint.get("probe", "/probe", {
- success: Schema.String,
- }),
- )
- .middleware(Authorization),
- )
- const handlers = HttpApiBuilder.group(Api, "test", (handlers) => handlers.handle("probe", () => Effect.succeed("ok")))
- const apiLayer = HttpRouter.serve(
- HttpApiBuilder.layer(Api).pipe(Layer.provide(handlers), Layer.provide(authorizationLayer)),
- { disableListenLog: true, disableLogger: true },
- ).pipe(Layer.provideMerge(NodeHttpServer.layerTest))
- const noAuthLayer = ServerAuth.Config.layer({ password: Option.none(), username: "opencode" })
- const secretLayer = ServerAuth.Config.layer({ password: Option.some("secret"), username: "opencode" })
- const kitSecretLayer = ServerAuth.Config.layer({ password: Option.some("secret"), username: "kit" })
- const it = testEffect(apiLayer.pipe(Layer.provide(noAuthLayer)))
- const itSecret = testEffect(apiLayer.pipe(Layer.provide(secretLayer)))
- const itKitSecret = testEffect(apiLayer.pipe(Layer.provide(kitSecretLayer)))
- const basic = (username: string, password: string) =>
- `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`
- const token = (username: string, password: string) => Buffer.from(`${username}:${password}`).toString("base64")
- const getProbe = (headers?: Record<string, string>) =>
- HttpClientRequest.get("/probe").pipe(
- headers ? HttpClientRequest.setHeaders(headers) : (request) => request,
- HttpClient.execute,
- )
- describe("HttpApi authorization middleware", () => {
- it.live("allows requests when server password is not configured", () =>
- Effect.gen(function* () {
- const response = yield* getProbe()
- expect(response.status).toBe(200)
- expect(yield* response.json).toBe("ok")
- }),
- )
- itSecret.live("requires configured password for basic auth", () =>
- Effect.gen(function* () {
- const [missing, badPassword, good] = yield* Effect.all(
- [
- getProbe(),
- getProbe({ authorization: basic("opencode", "wrong") }),
- getProbe({ authorization: basic("opencode", "secret") }),
- ],
- { concurrency: "unbounded" },
- )
- expect(missing.status).toBe(401)
- expect(badPassword.status).toBe(401)
- expect(good.status).toBe(200)
- }),
- )
- itKitSecret.live("respects configured basic auth username", () =>
- Effect.gen(function* () {
- const [defaultUser, configuredUser] = yield* Effect.all(
- [getProbe({ authorization: basic("opencode", "secret") }), getProbe({ authorization: basic("kit", "secret") })],
- { concurrency: "unbounded" },
- )
- expect(defaultUser.status).toBe(401)
- expect(configuredUser.status).toBe(200)
- }),
- )
- itSecret.live("accepts auth token query credentials", () =>
- Effect.gen(function* () {
- const response = yield* HttpClient.get(`/probe?auth_token=${encodeURIComponent(token("opencode", "secret"))}`)
- expect(response.status).toBe(200)
- }),
- )
- itSecret.live("rejects malformed auth token query credentials", () =>
- Effect.gen(function* () {
- const response = yield* HttpClient.get("/probe?auth_token=not-base64")
- expect(response.status).toBe(401)
- }),
- )
- })
|