첫 커밋: 로컬 프로젝트 업로드
This commit is contained in:
222
baron-sso/devfront/tests/devfront-security.spec.ts
Normal file
222
baron-sso/devfront/tests/devfront-security.spec.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
type Consent,
|
||||
installDevApiMock,
|
||||
makeClient,
|
||||
seedAuth,
|
||||
} from "./helpers/devfront-fixtures";
|
||||
import { captureEvidence } from "./helpers/evidence";
|
||||
|
||||
test.describe("DevFront security and isolation", () => {
|
||||
test.afterEach(async ({ page }, testInfo) => {
|
||||
if (testInfo.status === "passed") {
|
||||
await captureEvidence(page, testInfo, testInfo.title);
|
||||
}
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
page.on("dialog", async (dialog) => {
|
||||
await dialog.accept();
|
||||
});
|
||||
await seedAuth(page);
|
||||
});
|
||||
|
||||
test("tenant isolation: forbidden client shows blocked error", async ({
|
||||
page,
|
||||
}) => {
|
||||
const state = {
|
||||
clients: [makeClient("tenant-a-client", { name: "Tenant A app" })],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.goto("/clients/tenant-b-client");
|
||||
await expect(
|
||||
page.getByText(
|
||||
/Error loading (app|client)|앱 정보를 불러오지 못했습니다|클라이언트 정보를 불러오지 못했습니다/i,
|
||||
),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("RBAC: user without manage_all permission should not see private apps", async ({
|
||||
page,
|
||||
}) => {
|
||||
const state = {
|
||||
clients: [
|
||||
makeClient("pkce-client", {
|
||||
name: "PKCE only app",
|
||||
type: "pkce",
|
||||
}),
|
||||
],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.goto("/clients");
|
||||
await expect(page.getByText("PKCE only app")).toBeVisible();
|
||||
await expect(page.getByText("Server side App")).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("tenant_member user can enter DevFront and sees empty RP list", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seedAuth(page, "tenant_member");
|
||||
const state = {
|
||||
clients: [] as ReturnType<typeof makeClient>[],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.goto("/clients");
|
||||
await expect(page).toHaveURL(/\/clients$/);
|
||||
await expect(
|
||||
page.getByText(/조회 가능한 RP가 없습니다|No RPs are available/i),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /연동 앱 추가|새 클라이언트|Create/i }),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("rp_admin receives 403 on clients list and sees ForbiddenMessage", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seedAuth(page, "rp_admin");
|
||||
|
||||
const state = {
|
||||
clients: [] as ReturnType<typeof makeClient>[],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.route("**/api/v1/dev/clients", async (route) => {
|
||||
if (route.request().method() === "GET") {
|
||||
return route.fulfill({
|
||||
status: 403,
|
||||
contentType: "application/json",
|
||||
body: '{"error": "forbidden"}',
|
||||
});
|
||||
}
|
||||
return route.fallback();
|
||||
});
|
||||
|
||||
await page.goto("/clients");
|
||||
await expect(
|
||||
page.getByText(/RP 관리자는|RP administrators can only access/i),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("tenant_admin receives 403 on audit logs and sees ForbiddenMessage", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seedAuth(page, "tenant_admin");
|
||||
|
||||
const state = {
|
||||
clients: [] as ReturnType<typeof makeClient>[],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.route("**/api/v1/dev/audit-logs*", async (route) => {
|
||||
if (route.request().method() === "GET") {
|
||||
return route.fulfill({
|
||||
status: 403,
|
||||
contentType: "application/json",
|
||||
body: '{"error": "forbidden"}',
|
||||
});
|
||||
}
|
||||
return route.fallback();
|
||||
});
|
||||
|
||||
await page.goto("/audit-logs");
|
||||
await expect(
|
||||
page.getByText(/테넌트 관리자 권한|Tenant administrator permissions/i),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test("user sees audit log access CTA when access is blocked", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await seedAuth(page, "user");
|
||||
|
||||
const state = {
|
||||
clients: [] as ReturnType<typeof makeClient>[],
|
||||
consents: [] as Consent[],
|
||||
auditLogsByCursor: undefined,
|
||||
developerRequests: [],
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.goto("/audit-logs");
|
||||
await expect(
|
||||
page.getByRole("heading", { name: /감사 로그|Audit Logs/ }),
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
page.getByText(
|
||||
/감사 로그는 개발자 권한이 있어야 볼 수 있습니다|Audit logs are available only to users with developer access/i,
|
||||
),
|
||||
).toBeVisible();
|
||||
const requestBtn = page.getByRole("button", {
|
||||
name: /개발자 권한 신청/,
|
||||
});
|
||||
await expect(requestBtn).toBeVisible();
|
||||
await requestBtn.click();
|
||||
await expect(page).toHaveURL(/\/developer-requests$/);
|
||||
await captureEvidence(page, testInfo, "security-user-audit-request-entry");
|
||||
});
|
||||
|
||||
test("user with approved developer request can enter audit logs without CTA", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
await seedAuth(page, "user");
|
||||
|
||||
const state = {
|
||||
clients: [] as ReturnType<typeof makeClient>[],
|
||||
consents: [] as Consent[],
|
||||
auditLogs: [
|
||||
{
|
||||
event_id: "evt-audit-1",
|
||||
timestamp: "2026-05-29T00:00:00.000Z",
|
||||
user_id: "playwright-user",
|
||||
event_type: "CLIENT_UPDATE",
|
||||
status: "success" as const,
|
||||
ip_address: "127.0.0.1",
|
||||
user_agent: "playwright",
|
||||
details: JSON.stringify({
|
||||
action: "UPDATE_CLIENT",
|
||||
target_id: "tenant-a-client",
|
||||
tenant_id: "tenant-a",
|
||||
}),
|
||||
},
|
||||
],
|
||||
auditLogsByCursor: undefined,
|
||||
developerRequests: [
|
||||
{
|
||||
id: "req-approved",
|
||||
userId: "playwright-user",
|
||||
userName: "Playwright User",
|
||||
name: "Playwright User",
|
||||
userEmail: "playwright@example.com",
|
||||
organization: "Tenant A",
|
||||
reason: "Need access",
|
||||
status: "approved",
|
||||
createdAt: "2026-05-29T00:00:00.000Z",
|
||||
updatedAt: "2026-05-29T00:10:00.000Z",
|
||||
approvedAt: "2026-05-29T00:10:00.000Z",
|
||||
},
|
||||
],
|
||||
};
|
||||
await installDevApiMock(page, state);
|
||||
|
||||
await page.goto("/audit-logs");
|
||||
await expect(page.getByText("UPDATE_CLIENT")).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole("button", { name: /개발자 권한 신청/ }),
|
||||
).toHaveCount(0);
|
||||
await captureEvidence(page, testInfo, "security-user-audit-approved");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user