test(ui): add M365 provider management E2E tests (#8954)

This commit is contained in:
StylusFrost
2025-11-04 11:22:39 +01:00
committed by GitHub
parent b9aef85aa2
commit dd85ca7c72
5 changed files with 417 additions and 3 deletions

View File

@@ -28,6 +28,11 @@ jobs:
E2E_AZURE_CLIENT_ID: ${{ secrets.E2E_AZURE_CLIENT_ID }}
E2E_AZURE_SECRET_ID: ${{ secrets.E2E_AZURE_SECRET_ID }}
E2E_AZURE_TENANT_ID: ${{ secrets.E2E_AZURE_TENANT_ID }}
E2E_M365_DOMAIN_ID: ${{ secrets.E2E_M365_DOMAIN_ID }}
E2E_M365_CLIENT_ID: ${{ secrets.E2E_M365_CLIENT_ID }}
E2E_M365_SECRET_ID: ${{ secrets.E2E_M365_SECRET_ID }}
E2E_M365_TENANT_ID: ${{ secrets.E2E_M365_TENANT_ID }}
E2E_M365_CERTIFICATE_CONTENT: ${{ secrets.E2E_M365_CERTIFICATE_CONTENT }}
E2E_NEW_PASSWORD: ${{ secrets.E2E_NEW_PASSWORD }}
steps:
- name: Checkout repository

View File

@@ -17,6 +17,12 @@ export interface AZUREProviderData {
alias?: string;
}
// M365 provider data
export interface M365ProviderData {
domainId: string;
alias?: string;
}
// AWS credential options
export const AWS_CREDENTIAL_OPTIONS = {
AWS_ROLE_ARN: "role",
@@ -51,6 +57,23 @@ export interface AZUREProviderCredential {
tenantId:string;
}
// M365 credential options
export const M365_CREDENTIAL_OPTIONS = {
M365_CREDENTIALS: "credentials",
M365_CERTIFICATE_CREDENTIALS: "certificate"
} as const;
// M365 credential type
type M365CredentialType = (typeof M365_CREDENTIAL_OPTIONS)[keyof typeof M365_CREDENTIAL_OPTIONS];
// M365 provider credential
export interface M365ProviderCredential {
type: M365CredentialType;
clientId:string;
clientSecret?:string;
tenantId:string;
certificateContent?:string;
}
// Providers page
export class ProvidersPage extends BasePage {
@@ -79,6 +102,10 @@ export class ProvidersPage extends BasePage {
readonly roleCredentialsRadio: Locator;
readonly staticCredentialsRadio: Locator;
// M365 credentials type selection
readonly m365StaticCredentialsRadio: Locator;
readonly m365CertificateCredentialsRadio: Locator;
// AWS role credentials form
readonly roleArnInput: Locator;
readonly externalIdInput: Locator;
@@ -93,6 +120,13 @@ export class ProvidersPage extends BasePage {
readonly azureClientSecretInput: Locator;
readonly azureTenantIdInput: Locator;
// M365 provider form elements
readonly m365domainIdInput: Locator;
readonly m365ClientIdInput: Locator;
readonly m365ClientSecretInput: Locator;
readonly m365TenantIdInput: Locator;
readonly m365CertificateContentInput: Locator;
// Delete button
readonly deleteProviderConfirmationButton: Locator;
@@ -131,7 +165,14 @@ export class ProvidersPage extends BasePage {
this.azureClientIdInput = page.getByRole("textbox", { name: "Client ID" });
this.azureClientSecretInput = page.getByRole("textbox", { name: "Client Secret" });
this.azureTenantIdInput = page.getByRole("textbox", { name: "Tenant ID" });
// M365 provider form inputs
this.m365domainIdInput = page.getByRole("textbox", { name: "Domain ID" });
this.m365ClientIdInput = page.getByRole("textbox", { name: "Client ID" });
this.m365ClientSecretInput = page.getByRole("textbox", { name: "Client Secret" });
this.m365TenantIdInput = page.getByRole("textbox", { name: "Tenant ID" });
this.m365CertificateContentInput = page.getByRole("textbox", { name: "Certificate Content" });
// Alias input
this.aliasInput = page.getByRole("textbox", { name: "Provider alias (optional)" });
@@ -158,6 +199,14 @@ export class ProvidersPage extends BasePage {
name: /Connect via Credentials/i,
});
// Radios for selecting M365 credentials method
this.m365StaticCredentialsRadio = page.getByRole("radio", {
name: /App Client Secret Credentials/i,
});
this.m365CertificateCredentialsRadio = page.getByRole("radio", {
name: /App Certificate Credentials/i,
});
// Inputs for IAM Role credentials
this.roleArnInput = page.getByRole("textbox", { name: "Role ARN" });
this.externalIdInput = page.getByRole("textbox", { name: "External ID" });
@@ -200,6 +249,14 @@ export class ProvidersPage extends BasePage {
await this.waitForPageLoad();
}
async selectM365Provider(): Promise<void> {
// Select the M365 provider
await this.m365ProviderRadio.click({ force: true });
await this.waitForPageLoad();
}
async fillAWSProviderDetails(data: AWSProviderData): Promise<void> {
// Fill the AWS provider details
@@ -220,6 +277,16 @@ export class ProvidersPage extends BasePage {
}
}
async fillM365ProviderDetails(data: M365ProviderData): Promise<void> {
// Fill the M365 provider details
await this.m365domainIdInput.fill(data.domainId);
if (data.alias) {
await this.aliasInput.fill(data.alias);
}
}
async clickNext(): Promise<void> {
// The wizard interface may use different labels for its primary action button on each step.
// This function determines which button to click depending on the current URL and page content.
@@ -342,6 +409,21 @@ export class ProvidersPage extends BasePage {
await this.waitForPageLoad();
}
async selectM365CredentialsType(type: M365CredentialType): Promise<void> {
// Ensure we are on the add-credentials page where the selector exists
await expect(this.page).toHaveURL(/\/providers\/add-credentials/);
if (type === M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS) {
await this.m365StaticCredentialsRadio.click({ force: true });
} else if (type === M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS) {
await this.m365CertificateCredentialsRadio.click({ force: true });
} else {
throw new Error(`Invalid M365 credential type: ${type}`);
}
// Wait for the page to load
await this.waitForPageLoad();
}
async fillRoleCredentials(credentials: AWSProviderCredential): Promise<void> {
// Fill the role credentials form
@@ -387,6 +469,34 @@ export class ProvidersPage extends BasePage {
}
}
async fillM365Credentials(credentials: M365ProviderCredential): Promise<void> {
// Fill the m365 credentials form
if (credentials.clientId) {
await this.m365ClientIdInput.fill(credentials.clientId);
}
if (credentials.clientSecret) {
await this.m365ClientSecretInput.fill(credentials.clientSecret);
}
if (credentials.tenantId) {
await this.m365TenantIdInput.fill(credentials.tenantId);
}
}
async fillM365CertificateCredentials(credentials: M365ProviderCredential): Promise<void> {
// Fill the m365 certificate credentials form
if (credentials.clientId) {
await this.m365ClientIdInput.fill(credentials.clientId);
}
if (credentials.certificateContent) {
await this.m365CertificateContentInput.fill(credentials.certificateContent);
}
if (credentials.tenantId) {
await this.m365TenantIdInput.fill(credentials.tenantId);
}
}
async verifyPageLoaded(): Promise<void> {
// Verify the providers page is loaded
@@ -409,6 +519,24 @@ export class ProvidersPage extends BasePage {
await expect(this.roleCredentialsRadio).toBeVisible();
}
async verifyM365CredentialsPageLoaded(): Promise<void> {
// Verify the M365 credentials page is loaded
await expect(this.page).toHaveTitle(/Prowler/);
await expect(this.m365ClientIdInput).toBeVisible();
await expect(this.m365ClientSecretInput).toBeVisible();
await expect(this.m365TenantIdInput).toBeVisible();
}
async verifyM365CertificateCredentialsPageLoaded(): Promise<void> {
// Verify the M365 certificate credentials page is loaded
await expect(this.page).toHaveTitle(/Prowler/);
await expect(this.m365ClientIdInput).toBeVisible();
await expect(this.m365TenantIdInput).toBeVisible();
await expect(this.m365CertificateContentInput).toBeVisible();
}
async verifyLaunchScanPageLoaded(): Promise<void> {
// Verify the launch scan page is loaded

View File

@@ -159,3 +159,109 @@
- Provider cleanup performed before each test to ensure clean state
- Requires valid Azure subscription with appropriate permissions
- Client credentials must have sufficient permissions for security scanning
---
## Test Case: `PROVIDER-E2E-004` - Add M365 Provider with Static Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @m365
**Description/Objective:** Validates the complete flow of adding a new Microsoft 365 provider using static client credentials (Client ID, Client Secret, Tenant ID) tied to a Domain ID.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, E2E_M365_SECRET_ID, E2E_M365_TENANT_ID
- Remove any existing provider with the same Domain ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Domain ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select M365 provider type
4. Fill provider details (domain ID and alias)
5. Select static credentials type
6. Fill M365 credentials (client ID, client secret, tenant ID)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- M365 provider successfully added with static credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays M365 option
- M365 credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for M365 credentials
- Provider cleanup performed before each test to ensure clean state
- Requires valid Microsoft 365 tenant with appropriate permissions
- Client credentials must have sufficient permissions for security scanning
---
## Test Case: `PROVIDER-E2E-005` - Add M365 Provider with Certificate Credentials
**Priority:** `critical`
**Tags:**
- type → @e2e, @serial
- feature → @providers
- provider → @m365
**Description/Objective:** Validates the complete flow of adding a new Microsoft 365 provider using certificate-based authentication (Client ID, Tenant ID, Certificate Content) tied to a Domain ID.
**Preconditions:**
- Admin user authentication required (admin.auth.setup setup)
- Environment variables configured: E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, E2E_M365_TENANT_ID, E2E_M365_CERTIFICATE_CONTENT
- Remove any existing provider with the same Domain ID before starting the test
- This test must be run serially and never in parallel with other tests, as it requires the Domain ID not to be already registered beforehand.
### Flow Steps:
1. Navigate to providers page
2. Click "Add Provider" button
3. Select M365 provider type
4. Fill provider details (domain ID and alias)
5. Select certificate credentials type
6. Fill M365 certificate credentials (client ID, tenant ID, certificate content)
7. Launch initial scan
8. Verify redirect to provider management page
### Expected Result:
- M365 provider successfully added with certificate credentials
- Initial scan launched successfully
- User redirected to provider details page
### Key verification points:
- Provider page loads correctly
- Connect account page displays M365 option
- Certificate credentials form accepts all required fields
- Launch scan page appears
- Successful redirect to provider page after scan launch
### Notes:
- Test uses environment variables for M365 certificate credentials
- Provider cleanup performed before each test to ensure clean state
- Requires valid Microsoft 365 tenant with certificate-based authentication
- Certificate must be properly configured and have sufficient permissions for security scanning

View File

@@ -1,5 +1,4 @@
import { test } from "@playwright/test";
import { ScansPage } from "../scans/scans-page";
import {
ProvidersPage,
AWSProviderData,
@@ -8,7 +7,11 @@ import {
AZUREProviderData,
AZUREProviderCredential,
AZURE_CREDENTIAL_OPTIONS,
M365ProviderData,
M365ProviderCredential,
M365_CREDENTIAL_OPTIONS,
} from "./providers-page";
import { ScansPage } from "../scans/scans-page";
test.describe("Add Provider", () => {
test.describe.serial("Add AWS Provider", () => {
@@ -260,4 +263,176 @@ test.describe("Add Provider", () => {
},
);
});
test.describe.serial("Add M365 Provider", () => {
// Providers page object
let providersPage: ProvidersPage;
let scansPage: ScansPage;
// Test data from environment variables
const domainId = process.env.E2E_M365_DOMAIN_ID;
const clientId = process.env.E2E_M365_CLIENT_ID;
const tenantId = process.env.E2E_M365_TENANT_ID;
// Validate required environment variables
if (!domainId || !clientId || !tenantId) {
throw new Error(
"E2E_M365_DOMAIN_ID, E2E_M365_CLIENT_ID, and E2E_M365_TENANT_ID environment variables are not set",
);
}
// Setup before each test
test.beforeEach(async ({ page }) => {
providersPage = new ProvidersPage(page);
// Clean up existing provider to ensure clean test state
await providersPage.deleteProviderIfExists(domainId);
});
// Use admin authentication for provider management
test.use({ storageState: "playwright/.auth/admin_user.json" });
test(
"should add a new M365 provider with static credentials",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@m365",
"@serial",
"@PROVIDER-E2E-004",
],
},
async ({ page }) => {
// Validate required environment variables
const clientSecret = process.env.E2E_M365_SECRET_ID;
if (!clientSecret) {
throw new Error("E2E_M365_SECRET_ID environment variable is not set");
}
// Prepare test data for M365 provider
const m365ProviderData: M365ProviderData = {
domainId: domainId,
alias: "Test E2E M365 Account - Credentials",
};
// Prepare static credentials
const m365Credentials: M365ProviderCredential = {
type: M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS,
clientId: clientId,
clientSecret: clientSecret,
tenantId: tenantId,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select M365 provider
await providersPage.selectM365Provider();
// Fill provider details
await providersPage.fillM365ProviderDetails(m365ProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectM365CredentialsType(
M365_CREDENTIAL_OPTIONS.M365_CREDENTIALS,
);
// Verify M365 credentials page is loaded
await providersPage.verifyM365CredentialsPageLoaded();
// Fill static credentials details
await providersPage.fillM365Credentials(m365Credentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
test(
"should add a new M365 provider with certificate",
{
tag: [
"@critical",
"@e2e",
"@providers",
"@m365",
"@serial",
"@PROVIDER-E2E-005",
],
},
async ({ page }) => {
// Validate required environment variables
const certificateContent = process.env.E2E_M365_CERTIFICATE_CONTENT;
if (!certificateContent) {
throw new Error(
"E2E_M365_CERTIFICATE_CONTENT environment variable is not set",
);
}
// Prepare test data for M365 provider
const m365ProviderData: M365ProviderData = {
domainId: domainId,
alias: "Test E2E M365 Account - Certificate",
};
// Prepare static credentials
const m365Credentials: M365ProviderCredential = {
type: M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS,
clientId: clientId,
tenantId: tenantId,
certificateContent: certificateContent,
};
// Navigate to providers page
await providersPage.goto();
await providersPage.verifyPageLoaded();
// Start adding new provider
await providersPage.clickAddProvider();
await providersPage.verifyConnectAccountPageLoaded();
// Select M365 provider
await providersPage.selectM365Provider();
// Fill provider details
await providersPage.fillM365ProviderDetails(m365ProviderData);
await providersPage.clickNext();
// Select static credentials type
await providersPage.selectM365CredentialsType(
M365_CREDENTIAL_OPTIONS.M365_CERTIFICATE_CREDENTIALS,
);
// Verify M365 certificate credentials page is loaded
await providersPage.verifyM365CertificateCredentialsPageLoaded();
// Fill static credentials details
await providersPage.fillM365CertificateCredentials(m365Credentials);
await providersPage.clickNext();
// Launch scan
await providersPage.verifyLaunchScanPageLoaded();
await providersPage.clickNext();
// Wait for redirect to scan page
scansPage = new ScansPage(page);
await scansPage.verifyPageLoaded();
},
);
});
});

View File

@@ -6,7 +6,7 @@ const manageCloudProvidersUserFile = 'playwright/.auth/manage_cloud_providers_us
authManageCloudProvidersSetup('authenticate as manage cloud providers e2e user', async ({ page }) => {
const cloudProvidersEmail = process.env.E2E_MANAGE_CLOUD_PROVIDERS_USER;
const cloudProvidersPassword = process.env.E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD;
if (!cloudProvidersEmail || !cloudProvidersPassword) {
throw new Error('E2E_MANAGE_CLOUD_PROVIDERS_USER and E2E_MANAGE_CLOUD_PROVIDERS_PASSWORD environment variables are required');
}