143 lines
6.8 KiB
JavaScript
143 lines
6.8 KiB
JavaScript
// Vault settings: explicit DN order, mandatory DN and template application.
|
|
const { test, expect } = require("@playwright/test");
|
|
|
|
function unique(prefix) {
|
|
return `${prefix}-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
|
|
}
|
|
|
|
async function field(page, label) {
|
|
return page.locator("label").filter({ hasText: label }).first().locator("..").locator("input, select, textarea").first();
|
|
}
|
|
|
|
async function login(page, username) {
|
|
await page.goto("/login");
|
|
await page.getByRole("link", { name: "Sign in with default" }).click();
|
|
await page.getByPlaceholder("Enter any user/subject").fill(username);
|
|
await page.getByRole("button", { name: "Sign-in" }).click();
|
|
await expect(page.getByRole("heading", { name: "Vaults" })).toBeVisible();
|
|
}
|
|
|
|
async function api(page, method, url, body) {
|
|
return page.evaluate(
|
|
async ({ method, url, body }) => {
|
|
const response = await fetch(url, {
|
|
method,
|
|
headers: { "content-type": "application/json" },
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
const text = await response.text();
|
|
return { status: response.status, json: text ? JSON.parse(text) : null };
|
|
},
|
|
{ method, url, body }
|
|
);
|
|
}
|
|
|
|
test("explicit DN order, mandatory DN and template apply", async ({ page }) => {
|
|
const user = unique("settings");
|
|
const vaultName = unique("Settings Vault");
|
|
const certName = unique("ordered.example");
|
|
|
|
await login(page, user);
|
|
await page.getByRole("button", { name: "New vault" }).click();
|
|
await page.getByPlaceholder("Production PKI").fill(vaultName);
|
|
await page.getByRole("button", { name: "Create vault" }).click();
|
|
await page.getByRole("link", { name: vaultName }).click();
|
|
const vaultUrl = page.url();
|
|
const vaultId = vaultUrl.split("/").pop();
|
|
|
|
const settings = await api(page, "GET", `/api/vaults/${vaultId}/settings`);
|
|
const updated = await api(page, "PATCH", `/api/vaults/${vaultId}/settings`, {
|
|
version: settings.json.version,
|
|
settings: { explicit_dn: ["CN", "O"], mandatory_dn: ["CN"] },
|
|
});
|
|
expect(updated.status).toBe(200);
|
|
|
|
await page.goto(`${vaultUrl}/certificates/new`);
|
|
await expect(page.getByText("Additional subject attributes")).toBeVisible();
|
|
await expect(page.locator("label").filter({ hasText: "Country (C)" })).toHaveCount(0);
|
|
const labels = await page.locator("label").allTextContents();
|
|
const cnIndex = labels.findIndex((label) => label.trim() === "Common name (CN)");
|
|
const oIndex = labels.findIndex((label) => label.trim() === "Organization (O)");
|
|
expect(cnIndex).toBeGreaterThan(-1);
|
|
expect(oIndex).toBeGreaterThan(cnIndex);
|
|
|
|
// Applying a template uses the server merge endpoint and updates typed fields.
|
|
await page.locator("select").nth(2).selectOption("builtin:tls-server");
|
|
await page.locator("select").nth(3).selectOption("extensions");
|
|
await page.getByRole("button", { name: "Apply", exact: true }).click();
|
|
await expect(page.locator("label").filter({ hasText: "TLS server" }).locator("..").locator("input[type=checkbox]")).toBeChecked();
|
|
|
|
await (await field(page, "Internal name")).fill(certName);
|
|
await (await field(page, "Organization (O)")).fill("KCA E2E");
|
|
await page.locator("#new-key").check();
|
|
await page.getByRole("button", { name: "Create certificate" }).click();
|
|
await expect(page.getByText("missing mandatory subject entries: CN")).toBeVisible();
|
|
|
|
await (await field(page, "Common name (CN)")).fill(certName);
|
|
await page.locator("label").filter({ hasText: "No well-defined expiration" }).locator("..").locator("input[type=checkbox]").check();
|
|
await page.getByRole("button", { name: "Create certificate" }).click();
|
|
await expect(page.getByRole("heading", { name: certName })).toBeVisible();
|
|
await expect(page.getByText("No expiration", { exact: true })).toBeVisible();
|
|
const cert = await api(page, "GET", `/api/vaults/${vaultId}/certificates`);
|
|
const created = cert.json.results.find((item) => item.name === certName);
|
|
// RFC 4514 renders the most specific RDN first, so CN follows O.
|
|
expect(created.subject).toBe(`O=KCA E2E,CN=${certName}`);
|
|
});
|
|
|
|
test("additional subject attributes use an XCA-style editor", async ({ page }) => {
|
|
const user = unique("subject-editor");
|
|
const vaultName = unique("Subject Editor Vault");
|
|
const certName = unique("attributes.example");
|
|
|
|
await login(page, user);
|
|
await page.getByRole("button", { name: "New vault" }).click();
|
|
await page.getByPlaceholder("Production PKI").fill(vaultName);
|
|
await page.getByRole("button", { name: "Create vault" }).click();
|
|
await page.getByRole("link", { name: vaultName }).click();
|
|
const vaultUrl = page.url();
|
|
const vaultId = vaultUrl.split("/").pop();
|
|
|
|
await page.goto(`${vaultUrl}/certificates/new`);
|
|
const editor = page.locator(".additional-subject-attributes");
|
|
const add = editor.getByRole("button", { name: "Add", exact: true });
|
|
const rows = editor.locator(".subject-extra-row");
|
|
await expect(add).toBeEnabled();
|
|
await expect(editor.locator("textarea")).toHaveCount(0);
|
|
|
|
await add.click();
|
|
await expect(rows).toHaveCount(1);
|
|
await rows.nth(0).getByRole("combobox").selectOption("serialNumber");
|
|
await rows.nth(0).getByRole("textbox").fill("1234");
|
|
|
|
// A second row must not disturb the first row's nested input signals.
|
|
await add.click();
|
|
await expect(rows).toHaveCount(2);
|
|
await rows.nth(1).getByRole("combobox").selectOption("title");
|
|
await rows.nth(1).getByRole("textbox").fill("delete me");
|
|
await expect(rows.nth(0).getByRole("textbox")).toHaveValue("1234");
|
|
await rows.nth(1).getByRole("button", { name: "Delete", exact: true }).click();
|
|
await expect(rows).toHaveCount(1);
|
|
await expect(rows.nth(0).getByRole("textbox")).toHaveValue("1234");
|
|
|
|
await (await field(page, "Internal name")).fill(certName);
|
|
await (await field(page, "Common name (CN)")).fill(certName);
|
|
await page.locator("#new-key").check();
|
|
await page.getByRole("button", { name: "Create certificate" }).click();
|
|
await expect(page.getByRole("heading", { name: certName })).toBeVisible();
|
|
|
|
const certId = page.url().split("/").pop();
|
|
const detail = await api(page, "GET", `/api/vaults/${vaultId}/certificates/${certId}`);
|
|
expect(detail.status).toBe(200);
|
|
const entries = detail.json.subject_entries;
|
|
expect(entries.find((entry) => entry.name === "serialNumber")?.value).toBe("1234");
|
|
expect(entries.some((entry) => entry.name === "title")).toBe(false);
|
|
|
|
// Copying a certificate populates the structured editor with the extra row.
|
|
await page.goto(`${vaultUrl}/certificates/${certId}/similar`);
|
|
const copiedEditor = page.locator(".additional-subject-attributes");
|
|
const copiedRows = copiedEditor.locator(".subject-extra-row");
|
|
await expect(copiedRows).toHaveCount(1);
|
|
await expect(copiedRows.nth(0).getByRole("combobox")).toHaveValue("serialNumber");
|
|
await expect(copiedRows.nth(0).getByRole("textbox")).toHaveValue("1234");
|
|
});
|