forked from baron/baron-sso
분리된 tenant 스코프 제어 정책 적용
This commit is contained in:
@@ -68,7 +68,36 @@ vi.mock("../../lib/i18n", () => ({
|
||||
|
||||
const roots: Root[] = [];
|
||||
|
||||
function makeClientDetail(claimKey: string): ClientDetailResponse {
|
||||
function makeClientDetail(
|
||||
claimKey: string,
|
||||
options?: {
|
||||
includeTenantScope?: boolean;
|
||||
tenantAccessRestricted?: boolean;
|
||||
tenantScopeMandatory?: boolean;
|
||||
},
|
||||
): ClientDetailResponse {
|
||||
const includeTenantScope = options?.includeTenantScope ?? false;
|
||||
const tenantAccessRestricted = options?.tenantAccessRestricted ?? false;
|
||||
const tenantScopeMandatory = options?.tenantScopeMandatory ?? false;
|
||||
const structuredScopes = [
|
||||
{
|
||||
id: "1",
|
||||
name: "openid",
|
||||
description: "",
|
||||
mandatory: true,
|
||||
},
|
||||
];
|
||||
|
||||
if (includeTenantScope) {
|
||||
structuredScopes.push({
|
||||
id: "2",
|
||||
name: "tenant",
|
||||
description: "Tenant access",
|
||||
mandatory: tenantScopeMandatory,
|
||||
locked: tenantAccessRestricted,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
client: {
|
||||
id: "client-claims",
|
||||
@@ -76,18 +105,14 @@ function makeClientDetail(claimKey: string): ClientDetailResponse {
|
||||
type: "private",
|
||||
status: "active",
|
||||
redirectUris: ["https://rp.example.com/callback"],
|
||||
scopes: ["openid", "profile"],
|
||||
scopes: includeTenantScope
|
||||
? ["openid", "tenant", "profile"]
|
||||
: ["openid", "profile"],
|
||||
tokenEndpointAuthMethod: "client_secret_basic",
|
||||
metadata: {
|
||||
description: "Claims app",
|
||||
structured_scopes: [
|
||||
{
|
||||
id: "1",
|
||||
name: "openid",
|
||||
description: "",
|
||||
mandatory: true,
|
||||
},
|
||||
],
|
||||
tenant_access_restricted: tenantAccessRestricted,
|
||||
structured_scopes: structuredScopes,
|
||||
id_token_claims: [
|
||||
{
|
||||
namespace: "rp_claims",
|
||||
@@ -304,6 +329,74 @@ describe("ClientGeneralPage RP claims", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves tenant scope mandatory state when tenant access restriction is off", async () => {
|
||||
fetchClientMock.mockResolvedValue(
|
||||
makeClientDetail("old_claim", {
|
||||
includeTenantScope: true,
|
||||
tenantAccessRestricted: false,
|
||||
tenantScopeMandatory: true,
|
||||
}),
|
||||
);
|
||||
updateClientMock.mockResolvedValue(
|
||||
makeClientDetail("old_claim", {
|
||||
includeTenantScope: true,
|
||||
tenantAccessRestricted: false,
|
||||
tenantScopeMandatory: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const { container } = await renderPage();
|
||||
|
||||
const tenantScopeRow = Array.from(
|
||||
container.querySelectorAll("tr"),
|
||||
).find((row) =>
|
||||
Array.from(row.querySelectorAll("input")).some(
|
||||
(input) => (input as HTMLInputElement).value === "tenant",
|
||||
),
|
||||
);
|
||||
|
||||
expect(tenantScopeRow).toBeDefined();
|
||||
const mandatorySwitch =
|
||||
tenantScopeRow?.querySelector<HTMLButtonElement>('[role="switch"]');
|
||||
expect(mandatorySwitch).toBeDefined();
|
||||
expect(mandatorySwitch?.getAttribute("aria-checked")).toBe("true");
|
||||
|
||||
await act(async () => {
|
||||
mandatorySwitch?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(mandatorySwitch?.getAttribute("aria-checked")).toBe("false");
|
||||
|
||||
const saveButton = Array.from(container.querySelectorAll("button")).find(
|
||||
(button) =>
|
||||
button.textContent?.includes("저장") ||
|
||||
button.textContent?.includes("Save"),
|
||||
);
|
||||
expect(saveButton).toBeDefined();
|
||||
|
||||
await act(async () => {
|
||||
saveButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
await flush();
|
||||
|
||||
expect(updateClientMock).toHaveBeenCalledWith(
|
||||
"client-claims",
|
||||
expect.objectContaining({
|
||||
metadata: expect.objectContaining({
|
||||
tenant_access_restricted: false,
|
||||
structured_scopes: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
name: "tenant",
|
||||
mandatory: false,
|
||||
locked: false,
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps nullable and default value as separate RP claim settings", async () => {
|
||||
const { container } = await renderPage();
|
||||
|
||||
|
||||
@@ -689,11 +689,18 @@ function ClientGeneralPage() {
|
||||
if (scope.name.trim() !== "tenant") {
|
||||
return scope;
|
||||
}
|
||||
if (restricted) {
|
||||
return {
|
||||
...scope,
|
||||
description: scope.description || tenantScopeDescription,
|
||||
mandatory: true,
|
||||
locked: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
...scope,
|
||||
description: scope.description || tenantScopeDescription,
|
||||
mandatory: restricted,
|
||||
locked: restricted,
|
||||
locked: false,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user