fix(auth): bind current local registry and CORS
This commit is contained in:
@@ -27,7 +27,7 @@ function localConfig(url = publicUrl) {
|
||||
}
|
||||
|
||||
function usersYaml(options: { enabled?: boolean; username?: string } = {}): string {
|
||||
return [
|
||||
const users = [
|
||||
"version: 1",
|
||||
"users:",
|
||||
` - id: ${adminId}`,
|
||||
@@ -38,8 +38,19 @@ function usersYaml(options: { enabled?: boolean; username?: string } = {}): stri
|
||||
" - admin",
|
||||
` enabled: ${options.enabled ?? true}`,
|
||||
" authRevision: 1",
|
||||
"",
|
||||
].join("\n");
|
||||
];
|
||||
if (options.enabled === false) {
|
||||
users.push(
|
||||
" - id: 6ba7b811-9dad-4ed1-80b4-00c04fd430c8",
|
||||
" username: BackupAdmin",
|
||||
` passwordHash: ${passwordHash}`,
|
||||
" roles:",
|
||||
" - admin",
|
||||
" enabled: true",
|
||||
" authRevision: 1",
|
||||
);
|
||||
}
|
||||
return [...users, ""].join("\n");
|
||||
}
|
||||
|
||||
function firstSetCookie(response: { headers: Record<string, string | string[] | undefined> }): string {
|
||||
@@ -401,6 +412,58 @@ test("a verifier failure is sanitized and releases its concurrency permit", asyn
|
||||
expect(attempts).toBe(2);
|
||||
});
|
||||
|
||||
test("operational registry failures do dummy work, return 503, and never consume login-failure capacity", async () => {
|
||||
let available = false;
|
||||
let verificationCalls = 0;
|
||||
const user = {
|
||||
id: adminId, username: "Admin", normalizedUsername: "admin", displayName: "Local administrator",
|
||||
passwordHash, roles: ["admin"], enabled: true, authRevision: 1,
|
||||
};
|
||||
const { app } = await createLocalApp({
|
||||
registry: {
|
||||
findByUsername: async () => {
|
||||
if (!available) throw new Error("registry file is unavailable");
|
||||
return user;
|
||||
},
|
||||
findBySubject: async () => user,
|
||||
verify: async () => {
|
||||
verificationCalls += 1;
|
||||
return false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
for (let attempt = 0; attempt < 11; attempt += 1) {
|
||||
const response = await login(app);
|
||||
expect(response.statusCode).toBe(503);
|
||||
expect(response.json()).toEqual({ code: "auth_unavailable", error: "Authentication is unavailable" });
|
||||
}
|
||||
expect(verificationCalls).toBe(0);
|
||||
|
||||
available = true;
|
||||
for (let attempt = 0; attempt < 10; attempt += 1) expect((await login(app)).statusCode).toBe(401);
|
||||
expect((await login(app)).statusCode).toBe(429);
|
||||
});
|
||||
|
||||
test("operational config failures return 503 and never consume login-failure capacity", async () => {
|
||||
const { app, authConfigFile } = await createLocalApp();
|
||||
writeFileSync(authConfigFile, "version: 1\nmode: unsupported\n", { encoding: "utf8", mode: 0o600 });
|
||||
chmodSync(authConfigFile, 0o600);
|
||||
|
||||
for (let attempt = 0; attempt < 11; attempt += 1) {
|
||||
const response = await login(app);
|
||||
expect(response.statusCode).toBe(503);
|
||||
expect(response.json()).toEqual({ code: "auth_unavailable", error: "Authentication is unavailable" });
|
||||
}
|
||||
|
||||
writeFileSync(authConfigFile, stringify(localConfig()), { encoding: "utf8", mode: 0o600 });
|
||||
chmodSync(authConfigFile, 0o600);
|
||||
for (let attempt = 0; attempt < 10; attempt += 1) {
|
||||
expect((await login(app, { password: `${password}!` })).statusCode).toBe(401);
|
||||
}
|
||||
expect((await login(app, { password: `${password}!` })).statusCode).toBe(429);
|
||||
});
|
||||
|
||||
test("public auth configuration is safe and OIDC protocol placeholders fail closed", async () => {
|
||||
const { app } = await createLocalApp();
|
||||
const configuration = await app.inject({ method: "GET", url: "/auth/config" });
|
||||
|
||||
Reference in New Issue
Block a user