feat: add auth extension points and one ssh connect pipeline

This commit is contained in:
LukeGus committed 2026-09-23 00:44:33 -05:00
1 parent d3c209af13
commit d774df8b41
159 files changed
+43243 -7451

No files matched your search

@@ -0,0 +1,128 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
createAuthState,
type AuthState,
} from "../../auth/auth-test-helpers.js";
const h = vi.hoisted(() => ({ state: null as unknown as AuthState }));
vi.mock("../../../database/repositories/factory.js", async () => {
const helpers = await import("../../auth/auth-test-helpers.js");
const names = Object.keys(helpers.fakeFactory(helpers.createAuthState()));
return Object.fromEntries(
names.map((name) => [
name,
(...args: unknown[]) =>
(
helpers.fakeFactory(h.state) as Record<
string,
(...a: unknown[]) => unknown
>
)[name](...args),
]),
);
});
vi.mock("../../../utils/logger.js", () => ({
databaseLogger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
}));
const { runExternalIdentityMigration, parseLegacyIdentifier } =
await import("../../../utils/crypto-migration/external-identity-migration.js");
function user(id: string, extra: Record<string, unknown>) {
h.state.users.set(id, {
id,
username: id,
passwordHash: "",
isAdmin: false,
isOidc: true,
...extra,
});
}
beforeEach(() => {
h.state = createAuthState();
});
describe("parseLegacyIdentifier", () => {
it("splits the prefixed forms and keeps bare subjects on their provider", () => {
expect(parseLegacyIdentifier("ldap:4:bob", null)).toEqual({
providerId: "4",
subject: "bob",
});
expect(parseLegacyIdentifier("github:7:42", null)).toEqual({
providerId: "7",
subject: "42",
});
expect(parseLegacyIdentifier("ldap:4:cn=a:b", null)).toEqual({
providerId: "4",
subject: "cn=a:b",
});
expect(parseLegacyIdentifier("sub-123", 3)).toEqual({
providerId: "3",
subject: "sub-123",
});
expect(parseLegacyIdentifier("sub-123", null)).toEqual({
providerId: "legacy-oidc",
subject: "sub-123",
});
expect(parseLegacyIdentifier("github:null:42", null)).toEqual({
providerId: "legacy-oidc",
subject: "42",
});
});
});
describe("runExternalIdentityMigration", () => {
it("moves every identifier and TOTP enrolment without touching the old data", async () => {
user("ldap-user", { oidcIdentifier: "ldap:4:bob", ssoProviderId: 4 });
user("gh-user", { oidcIdentifier: "github:7:42", ssoProviderId: 7 });
user("oidc-user", {
oidcIdentifier: "sub-123",
ssoProviderId: 3,
totpEnabled: true,
});
user("env-user", { oidcIdentifier: "someone@example.com" });
user("local", {
isOidc: false,
passwordHash: "hash",
totpEnabled: true,
});
const result = await runExternalIdentityMigration();
expect(result).toEqual({ identities: 4, factors: 2, skipped: 0 });
expect(
h.state.identities.map(({ userId, providerId, subject }) => ({
userId,
providerId,
subject,
})),
).toEqual([
{ userId: "ldap-user", providerId: "4", subject: "bob" },
{ userId: "gh-user", providerId: "7", subject: "42" },
{ userId: "oidc-user", providerId: "3", subject: "sub-123" },
{
userId: "env-user",
providerId: "legacy-oidc",
subject: "someone@example.com",
},
]);
expect(h.state.factors).toEqual([
{ userId: "oidc-user", pluginId: "core", factorId: "totp" },
{ userId: "local", pluginId: "core", factorId: "totp" },
]);
// Lossless: the old column and flag stay.
expect(h.state.users.get("ldap-user")!.oidcIdentifier).toBe("ldap:4:bob");
expect(h.state.users.get("local")!.totpEnabled).toBe(true);
});
it("is idempotent", async () => {
user("ldap-user", { oidcIdentifier: "ldap:4:bob", totpEnabled: true });
await runExternalIdentityMigration();
const second = await runExternalIdentityMigration();
expect(second).toEqual({ identities: 0, factors: 0, skipped: 2 });
expect(h.state.identities).toHaveLength(1);
expect(h.state.factors).toHaveLength(1);
});
});