mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
fix(auth): reject unresolved legacy identity provisioning (#1550)
This commit is contained in:
1 parent
c9a1348836
commit
7b2198e18c
2 files changed
+102
No files matched your search
@@ -198,6 +198,25 @@ async function mayClaimLegacyAccount(
|
||||
const links = await createCurrentUserAuthRepository().listIdentitiesForUser(
|
||||
legacy.id,
|
||||
);
|
||||
// An unscoped legacy subject may already have been migrated under a
|
||||
// different provider. Creating another account loses access to the old
|
||||
// data; claiming it automatically could cross an identity-provider boundary.
|
||||
if (
|
||||
legacy.ssoProviderId == null &&
|
||||
identity.ssoProviderId != null &&
|
||||
identity.legacyIdentifier === identity.subject &&
|
||||
links.some(
|
||||
(link) =>
|
||||
link.subject === identity.subject &&
|
||||
link.providerId !== identity.provider,
|
||||
)
|
||||
) {
|
||||
throw new LoginMethodError(
|
||||
"A legacy SSO account has an unresolved provider mapping. Ask an administrator to verify and repair its identity mapping before signing in.",
|
||||
409,
|
||||
"legacy_identity_conflict",
|
||||
);
|
||||
}
|
||||
return links.length === 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -521,6 +521,89 @@ describe("external identities", () => {
|
||||
).rejects.toMatchObject({ code: "user_not_allowed" });
|
||||
});
|
||||
|
||||
it.each(["true", "false"])(
|
||||
"does not duplicate an unscoped migrated account with provisioning %s",
|
||||
async (enabled) => {
|
||||
addUser({
|
||||
id: "old",
|
||||
username: "Alice",
|
||||
isOidc: true,
|
||||
passwordHash: "",
|
||||
oidcIdentifier: "sub-1",
|
||||
ssoProviderId: null,
|
||||
});
|
||||
const { runExternalIdentityMigration } =
|
||||
await import("../../upgrade/external-identity-migration.js");
|
||||
await runExternalIdentityMigration();
|
||||
h.state.settings.set("oidc_auto_provision", enabled);
|
||||
await expect(
|
||||
runLogin(fakeRequest() as never, external, {
|
||||
methodId: "oidc",
|
||||
rememberMe: false,
|
||||
}),
|
||||
).rejects.toMatchObject({ code: "legacy_identity_conflict" });
|
||||
expect([...h.state.users.keys()]).toEqual(["old"]);
|
||||
expect(h.state.identities).toHaveLength(1);
|
||||
expect(h.state.identities[0]).toMatchObject({
|
||||
userId: "old",
|
||||
providerId: "legacy-oidc",
|
||||
subject: "sub-1",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps equal subjects on explicitly different providers separate", async () => {
|
||||
addUser({
|
||||
id: "other",
|
||||
username: "Other Alice",
|
||||
isOidc: true,
|
||||
passwordHash: "",
|
||||
oidcIdentifier: "sub-1",
|
||||
ssoProviderId: 9,
|
||||
});
|
||||
h.state.identities.push({
|
||||
id: 1,
|
||||
userId: "other",
|
||||
providerId: "9",
|
||||
subject: "sub-1",
|
||||
email: null,
|
||||
});
|
||||
h.state.settings.set("oidc_auto_provision", "true");
|
||||
const result = await runLogin(fakeRequest() as never, external, {
|
||||
methodId: "oidc",
|
||||
rememberMe: false,
|
||||
});
|
||||
expect(result.kind).toBe("session");
|
||||
expect(h.state.users.size).toBe(2);
|
||||
expect(
|
||||
h.state.identities.find((link) => link.providerId === "3")?.userId,
|
||||
).not.toBe("other");
|
||||
});
|
||||
|
||||
it("uses a verified provider link even when the old provider column is empty", async () => {
|
||||
addUser({
|
||||
id: "old",
|
||||
username: "Alice",
|
||||
isOidc: true,
|
||||
passwordHash: "",
|
||||
oidcIdentifier: "sub-1",
|
||||
ssoProviderId: null,
|
||||
});
|
||||
h.state.identities.push({
|
||||
id: 1,
|
||||
userId: "old",
|
||||
providerId: "3",
|
||||
subject: "sub-1",
|
||||
email: null,
|
||||
});
|
||||
const result = await runLogin(fakeRequest() as never, external, {
|
||||
methodId: "oidc",
|
||||
rememberMe: false,
|
||||
});
|
||||
expect(result.kind).toBe("session");
|
||||
expect(h.state.users.size).toBe(1);
|
||||
});
|
||||
|
||||
it("re-checks the allowed list for existing users", async () => {
|
||||
addUser({ id: "u9", username: "Alice", isOidc: true, passwordHash: "" });
|
||||
h.state.identities.push({
|
||||
|
||||
Reference in new issue
Block a user