Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,45 +127,22 @@ test.describe.serial("Orchestrator Entity-Workflow RBAC", () => {
expect(policyPostResponse.ok()).toBeTruthy();
});

test("Navigate to Catalog and find orchestrator-tagged template", async () => {
await uiHelper.openSidebar("Catalog");
await uiHelper.verifyHeading(/Catalog|All/);
await uiHelper.selectMuiBox("Kind", "Template");

// Find the "Greeting Test Picker" template (greeting_w_component.yaml)
await page
.getByRole("textbox", { name: "Search" })
.fill("Greeting Test Picker");
test("Verify Greeting Test Picker template exists in Catalog", async () => {
await page.goto("/catalog?filters[kind]=template");
const templateLink = page.getByRole("link", {
name: /Greeting Test Picker/i,
});

await expect(templateLink).toBeVisible({ timeout: 30000 });
await templateLink.click();

// Wait for entity page to load
await page.waitForLoadState("domcontentloaded");
await expect(page.getByRole("heading").first()).toBeVisible();
});

test("Launch template and attempt to run workflow - verify unauthorized", async () => {
// Navigate to Self-service page via global header link
await uiHelper.clickLink({ ariaLabel: "Self-service" });
await uiHelper.verifyHeading("Self-service");

// Wait for templates to load and click "Greeting Test Picker" template
await page.waitForLoadState("domcontentloaded");
// Navigate to Self-service and select the template
await page.goto("/create");
await uiHelper.clickBtnInCard("Greeting Test Picker", "Choose");

// Wait for template form to load
await uiHelper.verifyHeading(/Greeting Test Picker/i, 30000);

// The "Greeting Test Picker" template has NO input fields - it goes straight to Review
// with just a Create button. It auto-generates a component name and runs the workflow.

// Click Create to execute (we're already on the Review step)
const createButton = page.getByRole("button", { name: /Create/i });
await expect(createButton).toBeVisible({ timeout: 10000 });
// This template has no input fields — it goes straight to Review with a Create button
const createButton = page.getByRole("button", { name: "Create" });
await expect(createButton).toBeVisible({ timeout: 30000 });
await createButton.click();

// Template execution should succeed, but workflow execution should be denied
Expand Down Expand Up @@ -324,56 +301,42 @@ test.describe.serial("Orchestrator Entity-Workflow RBAC", () => {
expect(policyPostResponse.ok()).toBeTruthy();
});

test("Navigate to Catalog and find orchestrator-tagged template", async () => {
await uiHelper.openSidebar("Catalog");
await uiHelper.verifyHeading(/Catalog|All/);
await uiHelper.selectMuiBox("Kind", "Template");

// Find the "Greeting Test Picker" template (greeting_w_component.yaml)
await page
.getByRole("textbox", { name: "Search" })
.fill("Greeting Test Picker");
test("Verify Greeting Test Picker template exists in Catalog", async () => {
await page.goto("/catalog?filters[kind]=template");
const templateLink = page.getByRole("link", {
name: /Greeting Test Picker/i,
});

await expect(templateLink).toBeVisible({ timeout: 30000 });
await templateLink.click();

// Wait for entity page to load
await page.waitForLoadState("domcontentloaded");
await expect(page.getByRole("heading").first()).toBeVisible();
});

test("Launch template and run workflow - verify success", async () => {
// Navigate to Self-service page via global header link
await uiHelper.clickLink({ ariaLabel: "Self-service" });
await uiHelper.verifyHeading("Self-service");

// Wait for templates to load
await page.waitForLoadState("domcontentloaded");

// Click "Greeting Test Picker" template
// Navigate to Self-service and select the template
await page.goto("/create");
await uiHelper.clickBtnInCard("Greeting Test Picker", "Choose");

// Wait for template form to load
await uiHelper.verifyHeading(/Greeting Test Picker/i, 30000);

// The "Greeting Test Picker" template has NO input fields - it goes straight to Review
// with just a Create button. It auto-generates a component name and runs the workflow.

// Click Create to execute (we're already on the Review step)
const createButton = page.getByRole("button", { name: /Create/i });
await expect(createButton).toBeVisible({ timeout: 10000 });
// This template has no input fields — it goes straight to Review with a Create button
const createButton = page.getByRole("button", { name: "Create" });
await expect(createButton).toBeVisible({ timeout: 30000 });
await createButton.click();

// Wait for task to finish — either success or 409 Conflict (catalog entity already registered
// from a prior run). Both are acceptable.
const completed = page.getByText(/Completed|succeeded|finished/i);
const conflictError = page.getByText(/409 Conflict/i);
// Ensure we navigated to the scaffolder task page
await page.waitForURL("**/create/tasks/**", { timeout: 30000 });

// Wait for the scaffolder task to complete.
const viewInCatalog = page.getByRole("link", {
name: "View in catalog",
});
const openWorkflowRun = page.getByRole("link", {
name: "Open workflow run",
});
const startOver = page.getByRole("button", { name: "Start Over" });
// A 409 Conflict is acceptable — the catalog entity may already exist
// from a prior run.
const conflictError = page.getByText(/409 Conflict/i);

await expect(completed.or(conflictError).or(startOver)).toBeVisible({
await expect(
viewInCatalog.or(openWorkflowRun).or(conflictError).or(startOver),
).toBeVisible({
timeout: 120000,
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
test.describe.serial("Test Orchestrator RBAC: Global Workflow Access", () => {
test.describe.configure({ retries: 0 });
let common: Common;
let uiHelper: UIhelper;

Check failure on line 26 in e2e-tests/playwright/e2e/plugins/orchestrator/orchestrator-rbac.spec.ts

View workflow job for this annotation

GitHub Actions / TSC, ESLint and Prettier

'uiHelper' is assigned a value but never used
let page: Page;
let apiToken: string;

Expand Down Expand Up @@ -115,11 +115,17 @@
});

test("Test global orchestrator workflow access is allowed", async () => {
await uiHelper.goToPageUrl("/orchestrator");
await uiHelper.verifyHeading("Workflows");

const orchestrator = new Orchestrator(page);
await orchestrator.selectGreetingWorkflowItem();
// Retry with reload to handle RBAC policy propagation delay.
// After role creation, the Orchestrator page may render workflow
// names as plain text (not links) until policies propagate.
const greetingLink = page.getByRole("link", {
name: "Greeting workflow",
});
await expect(async () => {
await page.goto("/orchestrator");
await expect(greetingLink).toBeVisible({ timeout: 5000 });
}).toPass({ timeout: 60000 });
await greetingLink.click();

// Verify we're on the greeting workflow page
await expect(
Expand Down Expand Up @@ -186,11 +192,18 @@
console.log(
`beforeEach: Attempting setup for ${testInfo.title}, retry: ${testInfo.retry}`,
);
await page.context().clearCookies();
await page.goto("/");
await page.waitForLoadState("load");
await common.loginAsKeycloakUser(
process.env.GH_USER2_ID,
process.env.GH_USER2_PASS,
);
});

test("Create role with global orchestrator.workflow read-only permissions", async () => {
const rbacApi = await RhdhRbacApi.build(apiToken);
const members = ["user:default/rhdh-qe"];
const members = ["user:default/rhdh-qe-2"];

const orchestratorReadonlyRole = {
memberReferences: members,
Expand Down Expand Up @@ -235,7 +248,9 @@
role.name === "role:default/workflowReadonly",
);
expect(workflowRole).toBeDefined();
expect(workflowRole?.memberReferences).toContain("user:default/rhdh-qe");
expect(workflowRole?.memberReferences).toContain(
"user:default/rhdh-qe-2",
);

const policiesResponse = await rbacApi.getPoliciesByRole(
"default/workflowReadonly",
Expand Down Expand Up @@ -342,11 +357,18 @@
console.log(
`beforeEach: Attempting setup for ${testInfo.title}, retry: ${testInfo.retry}`,
);
await page.context().clearCookies();
await page.goto("/");
await page.waitForLoadState("load");
await common.loginAsKeycloakUser(
process.env.GH_USER2_ID,
process.env.GH_USER2_PASS,
);
});

test("Create role with global orchestrator.workflow denied permissions", async () => {
const rbacApi = await RhdhRbacApi.build(apiToken);
const members = ["user:default/rhdh-qe"];
const members = ["user:default/rhdh-qe-2"];

const orchestratorDeniedRole = {
memberReferences: members,
Expand Down Expand Up @@ -391,7 +413,9 @@
role.name === "role:default/workflowDenied",
);
expect(workflowRole).toBeDefined();
expect(workflowRole?.memberReferences).toContain("user:default/rhdh-qe");
expect(workflowRole?.memberReferences).toContain(
"user:default/rhdh-qe-2",
);

const policiesResponse = await rbacApi.getPoliciesByRole(
"default/workflowDenied",
Expand Down Expand Up @@ -481,11 +505,18 @@
console.log(
`beforeEach: Attempting setup for ${testInfo.title}, retry: ${testInfo.retry}`,
);
await page.context().clearCookies();
await page.goto("/");
await page.waitForLoadState("load");
await common.loginAsKeycloakUser(
process.env.GH_USER2_ID,
process.env.GH_USER2_PASS,
);
});

test("Create role with greeting workflow denied permissions", async () => {
const rbacApi = await RhdhRbacApi.build(apiToken);
const members = ["user:default/rhdh-qe"];
const members = ["user:default/rhdh-qe-2"];

const greetingDeniedRole = {
memberReferences: members,
Expand Down Expand Up @@ -528,7 +559,9 @@
role.name === "role:default/workflowGreetingDenied",
);
expect(workflowRole).toBeDefined();
expect(workflowRole?.memberReferences).toContain("user:default/rhdh-qe");
expect(workflowRole?.memberReferences).toContain(
"user:default/rhdh-qe-2",
);

const policiesResponse = await rbacApi.getPoliciesByRole(
"default/workflowGreetingDenied",
Expand Down Expand Up @@ -626,11 +659,18 @@
console.log(
`beforeEach: Attempting setup for ${testInfo.title}, retry: ${testInfo.retry}`,
);
await page.context().clearCookies();
await page.goto("/");
await page.waitForLoadState("load");
await common.loginAsKeycloakUser(
process.env.GH_USER2_ID,
process.env.GH_USER2_PASS,
);
});

test("Create role with greeting workflow read-write permissions", async () => {
const rbacApi = await RhdhRbacApi.build(apiToken);
const members = ["user:default/rhdh-qe"];
const members = ["user:default/rhdh-qe-2"];

const greetingReadwriteRole = {
memberReferences: members,
Expand Down Expand Up @@ -673,7 +713,9 @@
role.name === "role:default/workflowGreetingReadwrite",
);
expect(workflowRole).toBeDefined();
expect(workflowRole?.memberReferences).toContain("user:default/rhdh-qe");
expect(workflowRole?.memberReferences).toContain(
"user:default/rhdh-qe-2",
);

const policiesResponse = await rbacApi.getPoliciesByRole(
"default/workflowGreetingReadwrite",
Expand Down Expand Up @@ -779,11 +821,18 @@
console.log(
`beforeEach: Attempting setup for ${testInfo.title}, retry: ${testInfo.retry}`,
);
await page.context().clearCookies();
await page.goto("/");
await page.waitForLoadState("load");
await common.loginAsKeycloakUser(
process.env.GH_USER2_ID,
process.env.GH_USER2_PASS,
);
});

test("Create role with greeting workflow read-only permissions", async () => {
const rbacApi = await RhdhRbacApi.build(apiToken);
const members = ["user:default/rhdh-qe"];
const members = ["user:default/rhdh-qe-2"];

const greetingReadonlyRole = {
memberReferences: members,
Expand Down Expand Up @@ -826,7 +875,9 @@
role.name === "role:default/workflowGreetingReadonly",
);
expect(workflowRole).toBeDefined();
expect(workflowRole?.memberReferences).toContain("user:default/rhdh-qe");
expect(workflowRole?.memberReferences).toContain(
"user:default/rhdh-qe-2",
);

const policiesResponse = await rbacApi.getPoliciesByRole(
"default/workflowGreetingReadonly",
Expand Down
Loading