mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-01-25 02:08:11 +00:00
test(ui): add M365 provider management E2E tests (#8954)
This commit is contained in:
5
.github/workflows/ui-e2e-tests.yml
vendored
5
.github/workflows/ui-e2e-tests.yml
vendored
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user