mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-23 20:42:02 +00:00
test(ui): add Okta provider form E2E test (#11600)
Co-authored-by: Pablo F.G <pablo.fernandez@prowler.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Alejandro Bailo <59607668+alejandrobailo@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
e8ffe59ce2
commit
5ecfd6ea20
@@ -77,6 +77,9 @@ jobs:
|
||||
E2E_ALIBABACLOUD_ACCESS_KEY_ID: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_ID }}
|
||||
E2E_ALIBABACLOUD_ACCESS_KEY_SECRET: ${{ secrets.E2E_ALIBABACLOUD_ACCESS_KEY_SECRET }}
|
||||
E2E_ALIBABACLOUD_ROLE_ARN: ${{ secrets.E2E_ALIBABACLOUD_ROLE_ARN }}
|
||||
E2E_OKTA_DOMAIN: ${{ secrets.E2E_OKTA_DOMAIN }}
|
||||
E2E_OKTA_CLIENT_ID: ${{ secrets.E2E_OKTA_CLIENT_ID }}
|
||||
E2E_OKTA_BASE64_PRIVATE_KEY: ${{ secrets.E2E_OKTA_BASE64_PRIVATE_KEY }}
|
||||
E2E_GOOGLEWORKSPACE_CUSTOMER_ID: ${{ secrets.E2E_GOOGLEWORKSPACE_CUSTOMER_ID }}
|
||||
E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON: ${{ secrets.E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON }}
|
||||
E2E_GOOGLEWORKSPACE_DELEGATED_USER: ${{ secrets.E2E_GOOGLEWORKSPACE_DELEGATED_USER }}
|
||||
|
||||
@@ -81,6 +81,28 @@ export interface GoogleWorkspaceProviderCredential {
|
||||
delegatedUser: string;
|
||||
}
|
||||
|
||||
// Okta provider data
|
||||
export interface OktaProviderData {
|
||||
orgDomain: string;
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
// Okta credential options
|
||||
export const OKTA_CREDENTIAL_OPTIONS = {
|
||||
OKTA_PRIVATE_KEY_JWT: "private_key_jwt",
|
||||
} as const;
|
||||
|
||||
// Okta credential type
|
||||
type OktaCredentialType =
|
||||
(typeof OKTA_CREDENTIAL_OPTIONS)[keyof typeof OKTA_CREDENTIAL_OPTIONS];
|
||||
|
||||
// Okta provider credential (OAuth 2.0 Private Key JWT)
|
||||
export interface OktaProviderCredential {
|
||||
type: OktaCredentialType;
|
||||
clientId: string;
|
||||
privateKey: string;
|
||||
}
|
||||
|
||||
// AWS credential options
|
||||
export const AWS_CREDENTIAL_OPTIONS = {
|
||||
AWS_ROLE_ARN: "role",
|
||||
@@ -267,12 +289,18 @@ export class ProvidersPage extends BasePage {
|
||||
readonly ociProviderRadio: Locator;
|
||||
readonly alibabacloudProviderRadio: Locator;
|
||||
readonly googleworkspaceProviderRadio: Locator;
|
||||
readonly oktaProviderRadio: Locator;
|
||||
|
||||
// Google Workspace provider form elements
|
||||
readonly googleworkspaceCustomerIdInput: Locator;
|
||||
readonly googleworkspaceServiceAccountJsonInput: Locator;
|
||||
readonly googleworkspaceDelegatedUserInput: Locator;
|
||||
|
||||
// Okta provider form elements
|
||||
readonly oktaOrgDomainInput: Locator;
|
||||
readonly oktaClientIdInput: Locator;
|
||||
readonly oktaPrivateKeyInput: Locator;
|
||||
|
||||
// Vercel provider form elements
|
||||
readonly vercelProviderRadio: Locator;
|
||||
readonly vercelTeamIdInput: Locator;
|
||||
@@ -522,6 +550,18 @@ export class ProvidersPage extends BasePage {
|
||||
name: /Delegated User Email/i,
|
||||
});
|
||||
|
||||
// Okta
|
||||
this.oktaProviderRadio = page.getByRole("option", {
|
||||
name: /Okta/i,
|
||||
});
|
||||
this.oktaOrgDomainInput = page.getByRole("textbox", {
|
||||
name: "Org Domain",
|
||||
});
|
||||
this.oktaClientIdInput = page.getByRole("textbox", { name: "Client ID" });
|
||||
this.oktaPrivateKeyInput = page.getByRole("textbox", {
|
||||
name: "Private Key",
|
||||
});
|
||||
|
||||
// Vercel
|
||||
this.vercelProviderRadio = page.getByRole("option", {
|
||||
name: /Vercel/i,
|
||||
@@ -1322,6 +1362,41 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(this.googleworkspaceDelegatedUserInput).toBeVisible();
|
||||
}
|
||||
|
||||
async selectOktaProvider(): Promise<void> {
|
||||
await this.selectProviderRadio(this.oktaProviderRadio);
|
||||
}
|
||||
|
||||
async fillOktaProviderDetails(data: OktaProviderData): Promise<void> {
|
||||
// Fill the Okta provider details (org domain is lowercased by the form)
|
||||
|
||||
await this.oktaOrgDomainInput.fill(data.orgDomain);
|
||||
|
||||
if (data.alias) {
|
||||
await this.aliasInput.fill(data.alias);
|
||||
}
|
||||
}
|
||||
|
||||
async fillOktaCredentials(
|
||||
credentials: OktaProviderCredential,
|
||||
): Promise<void> {
|
||||
// Fill the Okta OAuth 2.0 Private Key JWT credentials form
|
||||
|
||||
if (credentials.clientId) {
|
||||
await this.oktaClientIdInput.fill(credentials.clientId);
|
||||
}
|
||||
if (credentials.privateKey) {
|
||||
await this.oktaPrivateKeyInput.fill(credentials.privateKey);
|
||||
}
|
||||
}
|
||||
|
||||
async verifyOktaCredentialsPageLoaded(): Promise<void> {
|
||||
// Verify the Okta credentials page is loaded
|
||||
|
||||
await this.verifyPageHasProwlerTitle();
|
||||
await expect(this.oktaClientIdInput).toBeVisible();
|
||||
await expect(this.oktaPrivateKeyInput).toBeVisible();
|
||||
}
|
||||
|
||||
async selectVercelProvider(): Promise<void> {
|
||||
await this.selectProviderRadio(this.vercelProviderRadio);
|
||||
}
|
||||
@@ -1374,6 +1449,7 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(this.githubProviderRadio).toBeVisible();
|
||||
await expect(this.alibabacloudProviderRadio).toBeVisible();
|
||||
await expect(this.googleworkspaceProviderRadio).toBeVisible();
|
||||
await expect(this.oktaProviderRadio).toBeVisible();
|
||||
await expect(this.vercelProviderRadio).toBeVisible();
|
||||
}
|
||||
|
||||
|
||||
@@ -1071,3 +1071,63 @@
|
||||
- API Token is a masked (password) field; the only credential required for Vercel
|
||||
- Provider cleanup performed before each test to ensure clean state
|
||||
- Requires a valid Vercel API Token with read permissions to the resources to assess
|
||||
|
||||
---
|
||||
|
||||
## Test Case: `PROVIDER-E2E-019` - Add Okta Provider with OAuth 2.0 Private Key JWT Credentials
|
||||
|
||||
**Priority:** `critical`
|
||||
|
||||
**Tags:**
|
||||
|
||||
- type → @e2e, @serial
|
||||
- feature → @providers
|
||||
- provider → @okta
|
||||
|
||||
**Description/Objective:** Validates the complete flow of adding a new Okta provider using OAuth 2.0 Private Key JWT authentication (Client ID and PEM-encoded private key) tied to an Okta Org Domain.
|
||||
|
||||
**Preconditions:**
|
||||
|
||||
- Admin user authentication required (admin.auth.setup setup)
|
||||
- Environment variables configured: E2E_OKTA_DOMAIN, E2E_OKTA_CLIENT_ID, E2E_OKTA_BASE64_PRIVATE_KEY
|
||||
- Remove any existing provider with the same Org Domain before starting the test
|
||||
- This test must be run serially and never in parallel with other tests, as it requires the Org Domain not to be already registered beforehand.
|
||||
|
||||
### Flow Steps
|
||||
|
||||
1. Navigate to providers page
|
||||
2. Click "Add Provider" button
|
||||
3. Select Okta provider type
|
||||
4. Fill provider details (org domain and alias)
|
||||
5. Verify Okta credentials page is loaded
|
||||
6. Fill Okta credentials (client ID and PEM-encoded private key)
|
||||
7. Launch initial scan
|
||||
8. Verify redirect to Scans page
|
||||
9. Verify scheduled scan status in Scans table (provider exists and scan name is "scheduled scan")
|
||||
|
||||
### Expected Result
|
||||
|
||||
- Okta provider successfully added with OAuth 2.0 Private Key JWT credentials
|
||||
- Initial scan launched successfully
|
||||
- User redirected to Scans page
|
||||
- Scheduled scan appears in Scans table with correct provider and scan name
|
||||
|
||||
### Key verification points
|
||||
|
||||
- Provider page loads correctly
|
||||
- Connect account page displays Okta option
|
||||
- Provider details form accepts org domain (Okta-managed domain, e.g. acme.okta.com) and alias
|
||||
- Credentials page loads with Client ID input and Private Key textarea
|
||||
- Credentials are properly filled in the correct fields
|
||||
- Launch scan page appears
|
||||
- Successful redirect to Scans page after scan launch
|
||||
- Provider exists in Scans table (verified by org domain)
|
||||
- Scan name field contains "scheduled scan"
|
||||
|
||||
### Notes
|
||||
|
||||
- Test uses environment variables for Okta credentials
|
||||
- Org Domain is lowercased by the form; cleanup and scan verification use the normalized value
|
||||
- Private Key is provided as base64-encoded PEM content and decoded before use (multi-line content)
|
||||
- Provider cleanup performed before each test to ensure clean state
|
||||
- Requires a valid Okta API Services app with a registered public key (JWK) matching the provided private key
|
||||
|
||||
@@ -30,6 +30,9 @@ import {
|
||||
GoogleWorkspaceProviderData,
|
||||
GoogleWorkspaceProviderCredential,
|
||||
GOOGLEWORKSPACE_CREDENTIAL_OPTIONS,
|
||||
OktaProviderData,
|
||||
OktaProviderCredential,
|
||||
OKTA_CREDENTIAL_OPTIONS,
|
||||
VercelProviderData,
|
||||
VercelProviderCredential,
|
||||
VERCEL_CREDENTIAL_OPTIONS,
|
||||
@@ -1517,6 +1520,98 @@ test.describe("Add Provider", () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test.describe.serial("Add Okta Provider", () => {
|
||||
let providersPage: ProvidersPage;
|
||||
let scansPage: ScansPage;
|
||||
|
||||
// Test data from environment variables
|
||||
// Org Domain is lowercased by the form, so normalize here to match the
|
||||
// stored provider UID used for cleanup and scan verification.
|
||||
const orgDomain = (process.env.E2E_OKTA_DOMAIN ?? "").toLowerCase();
|
||||
const clientId = process.env.E2E_OKTA_CLIENT_ID ?? "";
|
||||
const privateKeyB64 = process.env.E2E_OKTA_BASE64_PRIVATE_KEY ?? "";
|
||||
|
||||
// Setup before each test
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.skip(
|
||||
!orgDomain || !clientId || !privateKeyB64,
|
||||
"Okta E2E env vars are not set",
|
||||
);
|
||||
providersPage = new ProvidersPage(page);
|
||||
await deleteProviderIfExists(providersPage, orgDomain!);
|
||||
});
|
||||
|
||||
// Use admin authentication for provider management
|
||||
test.use({ storageState: "playwright/.auth/admin_user.json" });
|
||||
|
||||
test(
|
||||
"should add a new Okta provider with OAuth 2.0 Private Key JWT credentials",
|
||||
{
|
||||
tag: [
|
||||
"@critical",
|
||||
"@e2e",
|
||||
"@providers",
|
||||
"@okta",
|
||||
"@serial",
|
||||
"@PROVIDER-E2E-019",
|
||||
],
|
||||
},
|
||||
async ({ page }) => {
|
||||
// The Okta app private key is PEM-encoded (multi-line), so it is passed
|
||||
// base64-encoded via the environment variable and decoded here.
|
||||
const privateKey = Buffer.from(privateKeyB64, "base64").toString(
|
||||
"utf8",
|
||||
);
|
||||
|
||||
// Prepare test data for Okta provider
|
||||
const oktaProviderData: OktaProviderData = {
|
||||
orgDomain: orgDomain,
|
||||
alias: "Test E2E Okta Account - Private Key JWT",
|
||||
};
|
||||
|
||||
// Prepare OAuth 2.0 Private Key JWT credentials
|
||||
const oktaCredentials: OktaProviderCredential = {
|
||||
type: OKTA_CREDENTIAL_OPTIONS.OKTA_PRIVATE_KEY_JWT,
|
||||
clientId: clientId,
|
||||
privateKey: privateKey,
|
||||
};
|
||||
|
||||
// Navigate to providers page
|
||||
await providersPage.goto();
|
||||
await providersPage.verifyPageLoaded();
|
||||
|
||||
// Start adding new provider
|
||||
await providersPage.clickAddProvider();
|
||||
await providersPage.verifyConnectAccountPageLoaded();
|
||||
|
||||
// Select Okta provider
|
||||
await providersPage.selectOktaProvider();
|
||||
|
||||
// Fill provider details (org domain and alias)
|
||||
await providersPage.fillOktaProviderDetails(oktaProviderData);
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Verify Okta credentials page is loaded
|
||||
await providersPage.verifyOktaCredentialsPageLoaded();
|
||||
|
||||
// Fill OAuth 2.0 Private Key JWT credentials
|
||||
await providersPage.fillOktaCredentials(oktaCredentials);
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Launch scan
|
||||
await providersPage.verifyLaunchScanPageLoaded();
|
||||
await providersPage.clickNext();
|
||||
|
||||
// Wait for redirect to scan page
|
||||
scansPage = new ScansPage(page);
|
||||
await scansPage.verifyPageLoaded();
|
||||
|
||||
// Verify scan status is "Scheduled scan"
|
||||
await scansPage.verifyScheduledScanStatus(orgDomain);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Update Provider Credentials", () => {
|
||||
|
||||
Vendored
+5
@@ -119,6 +119,11 @@ declare global {
|
||||
E2E_GOOGLEWORKSPACE_SERVICE_ACCOUNT_JSON?: string;
|
||||
E2E_GOOGLEWORKSPACE_DELEGATED_USER?: string;
|
||||
|
||||
// E2E Okta
|
||||
E2E_OKTA_DOMAIN?: string;
|
||||
E2E_OKTA_CLIENT_ID?: string;
|
||||
E2E_OKTA_BASE64_PRIVATE_KEY?: string;
|
||||
|
||||
// E2E Vercel
|
||||
E2E_VERCEL_TEAM_ID?: string;
|
||||
E2E_VERCEL_API_TOKEN?: string;
|
||||
|
||||
Reference in New Issue
Block a user