feat(auth): separate anonymous entry from expired session recovery
This commit is contained in:
@@ -4,7 +4,9 @@
|
||||
import { extname, join, resolve } from "@std/path";
|
||||
import { setupProjectionResponse } from "./setup-projection.ts";
|
||||
const [port, build] = Deno.args;
|
||||
if (!port || !build) throw new Error("usage: fixture-server.ts <port> <build-root>");
|
||||
if (!port || !build) {
|
||||
throw new Error("usage: fixture-server.ts <port> <build-root>");
|
||||
}
|
||||
const buildRoot = resolve(build);
|
||||
const mime: Record<string, string> = {
|
||||
".html": "text/html",
|
||||
@@ -23,9 +25,15 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
const url = new URL(request.url);
|
||||
if (url.pathname === "/health") return new Response("ok");
|
||||
const selected = url.searchParams.get("fixture");
|
||||
if (url.pathname === "/hosting/setup" && selected?.startsWith("setup-")) setupMode = selected;
|
||||
const mode = selected ?? request.headers.get("cookie")?.match(/ui-fixture=([\w-]+)/)?.[1] ??
|
||||
(url.pathname.startsWith("/api/auth/passkeys/addition/") ? setupMode : null) ?? "admin-on";
|
||||
if (url.pathname === "/hosting/setup" && selected?.startsWith("setup-")) {
|
||||
setupMode = selected;
|
||||
}
|
||||
const mode = selected ??
|
||||
request.headers.get("cookie")?.match(/ui-fixture=([\w-]+)/)?.[1] ??
|
||||
(url.pathname.startsWith("/api/auth/passkeys/addition/")
|
||||
? setupMode
|
||||
: null) ??
|
||||
"admin-on";
|
||||
if (selected) policies.delete(mode);
|
||||
const setup = setupProjectionResponse(url.pathname, mode, selected !== null);
|
||||
if (setup) return setup;
|
||||
@@ -33,6 +41,9 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
const ordinary = mode.startsWith("ordinary");
|
||||
if (url.pathname === "/api/auth/whoami") {
|
||||
return Response.json({
|
||||
session: !anonymous
|
||||
? { kind: "authenticated", expires_at: "2099-01-01T00:00:00Z" }
|
||||
: { kind: "anonymous" },
|
||||
actor: anonymous ? null : {
|
||||
user_id: "fixture-user",
|
||||
account_id: "fixture-account",
|
||||
@@ -46,7 +57,9 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
if (url.pathname === "/api/hosting/access") {
|
||||
return Response.json({ is_admin: !ordinary && !anonymous });
|
||||
}
|
||||
const policy = () => ({ self_registration_enabled: policies.get(mode) ?? !mode.includes("off") });
|
||||
const policy = () => ({
|
||||
self_registration_enabled: policies.get(mode) ?? !mode.includes("off"),
|
||||
});
|
||||
if (url.pathname === "/api/auth/registration-policy") {
|
||||
if (mode === "account-error") {
|
||||
return new Response("synthetic availability failure", { status: 503 });
|
||||
@@ -56,12 +69,17 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
}
|
||||
if (url.pathname === "/api/hosting/settings") {
|
||||
if (anonymous || ordinary) {
|
||||
return new Response("synthetic denied", { status: anonymous ? 401 : 403 });
|
||||
return new Response("synthetic denied", {
|
||||
status: anonymous ? 401 : 403,
|
||||
});
|
||||
}
|
||||
if (mode === "admin-load-error") {
|
||||
return new Response("synthetic settings failure", { status: 503 });
|
||||
}
|
||||
if (mode === "admin-loading" || (mode === "admin-saving" && request.method === "PUT")) {
|
||||
if (
|
||||
mode === "admin-loading" ||
|
||||
(mode === "admin-saving" && request.method === "PUT")
|
||||
) {
|
||||
return new Promise<Response>(() => {});
|
||||
}
|
||||
if (request.method === "PUT") {
|
||||
@@ -80,13 +98,18 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
return new Response("unexpected fixture API", { status: 404 });
|
||||
}
|
||||
const path = join(buildRoot, decodeURIComponent(url.pathname));
|
||||
if (!path.startsWith(buildRoot + "/")) return new Response("invalid path", { status: 400 });
|
||||
if (!path.startsWith(buildRoot + "/")) {
|
||||
return new Response("invalid path", { status: 400 });
|
||||
}
|
||||
let bytes: Uint8Array;
|
||||
let extension = extname(path);
|
||||
try {
|
||||
bytes = await Deno.readFile(path);
|
||||
} catch (error) {
|
||||
if (!(error instanceof Deno.errors.NotFound) && !(error instanceof Deno.errors.IsADirectory)) {
|
||||
if (
|
||||
!(error instanceof Deno.errors.NotFound) &&
|
||||
!(error instanceof Deno.errors.IsADirectory)
|
||||
) {
|
||||
throw error;
|
||||
}
|
||||
bytes = await Deno.readFile(join(buildRoot, "index.html"));
|
||||
@@ -106,6 +129,11 @@ Deno.serve({ hostname: "127.0.0.1", port: Number(port) }, async (request) => {
|
||||
"content-type": mime[extension] ?? "application/octet-stream",
|
||||
"cache-control": "no-store",
|
||||
});
|
||||
if (selected) headers.set("set-cookie", `ui-fixture=${selected}; Path=/; SameSite=Strict`);
|
||||
if (selected) {
|
||||
headers.set(
|
||||
"set-cookie",
|
||||
`ui-fixture=${selected}; Path=/; SameSite=Strict`,
|
||||
);
|
||||
}
|
||||
return new Response(bytes as Uint8Array<ArrayBuffer>, { headers });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user