Skip to content

Commit

Permalink
[Security Solution] [Cypress] SAML for Serverless login and role test…
Browse files Browse the repository at this point in the history
…ing (#172655)

Relates to:
* #166340
* #170852
* #170417
* #172678

## Summary

In this PR we are using the code implemented on
#170417 and
#172678 to allow SAML and role
testing inside Cypress.

* We are creating a Cypress task to use the above-developed code and be
able to retrieve a session cookie given a role.
* We updated the login task to know how we should perform the login
depending if we are in Serverless (MKI or serverless FTR) or ESS
* In the parallel serverless script:
* We are updating the `BASE_ENV_URL` variable to use the proper QA
environment (pending to be done in follow-up PRs, to extract this value
so it is not hardcoded cc @dkirchan )
* We are adding the `IS_SERVERLESS` environment variable needed for the
logic on the login task. This changed implied to update the
`es_archiver` file to continue work as expected.
* We have added the `TEST_CLOUD_HOST_NAME` environment variable needed
for the code we are reusing to retrieve the session cookie for MKI.
* We have updated the Security Solution quality gate script to set the
`role_users.json` file needed by the code we are reusing to get the
different session cookies on MKI
* We have adjusted the tests because the username now follows the
pattern `test <role>` (@dmlemeshko is it possible to have as username
just the role? Is this something that can impact other tests and teams?)
* We have [skipped](#173168) a
test that got unstable after the changes.

## How to test it in your machine

### Serverless FTR

1. Navigate to `x-pack/test/security_solution_cypress`
2. Execute `yarn cypress:open:qa:serverless`
3. Click on `E2E testing`
4. Click on any test to execute it


### Serverless MKI

Setup a valid Elastic Cloud API key for QA environment:

1. Navigate to QA environment.
2. Click on the `User menu button` located on the top right of the
header.
3. Click on `Organization`.
5. Click on the `API keys` tab.
6. Click on `Create API key` button.
7. Add a name, set an expiration date, assign an organization owner
role.
8. Click on `Create API key`
9. Save the value of the key

Store the saved key on `~/.elastic/cloud.json` using the following
format:

```json
{
  "api_key": {
    "qa": "<API_KEY>"
  }
}
```

Store the email and password of the account you used to login in the QA
Environment at the root directory of your Kibana project on
`.ftr/role_users.json`, using the following format:

```json
{
  "admin": {
    "email": "<email>",
    "password": "<password>"
  }
}
```

If you want to execute a test with a role different from the default
one, make sure you have created the user under your organization and is
added to the above json following the format:

```json
{
  "admin": {
    "email": "<email>",
    "password": "<password>"
  },
  "<roleName>": {
    "email": "<email>",
    "password": "<password>"
  }
}
```

1. Navigate to `x-pack/test/security_solution_cypress`
2. Execute `yarn cypress:open:qa:serverless`
3. Click on `E2E testing`
4. Click on any test to execute it

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
MadameSheema and kibanamachine authored Dec 13, 2023
1 parent a26eec4 commit c580213
Show file tree
Hide file tree
Showing 14 changed files with 131 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export JOB=kibana-security-solution-chrome

buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" "true"

mkdir .ftr
retry 5 5 vault kv get -format=json -field=data secret/kibana-issues/dev/security-quality-gate/role-users > .ftr/role_users.json

cd x-pack/test/security_solution_cypress
set +e

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const DEFAULT_CONFIGURATION: Readonly<ProductType[]> = [

const DEFAULT_REGION = 'aws-eu-west-1';
const PROJECT_NAME_PREFIX = 'kibana-cypress-security-solution-ephemeral';
const BASE_ENV_URL = 'https://global.qa.cld.elstc.co';
const BASE_ENV_URL = 'https://console.qa.cld.elstc.co';
let log: ToolingLog;
const API_HEADERS = Object.freeze({
'kbn-xsrf': 'cypress-creds',
Expand Down Expand Up @@ -571,6 +571,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
KIBANA_PASSWORD: credentials.password,

CLOUD_SERVERLESS: true,
IS_SERVERLESS: true,
};

if (process.env.DEBUG && !process.env.CI) {
Expand All @@ -582,6 +583,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)}
----------------------------------------------
`);
}
process.env.TEST_CLOUD_HOST_NAME = new URL(BASE_ENV_URL).hostname;

if (isOpen) {
await cypress.open({
Expand Down
47 changes: 45 additions & 2 deletions x-pack/test/security_solution_cypress/cypress/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,8 +304,51 @@ Store the saved key on `~/.elastic/cloud.json` using the following format:
}
```
#### Known limitations
- Currently RBAC cannot be tested.
Store the email and password of the account you used to login in the QA Environment at the root directory of your Kibana project on `.ftr/role_users.json`, using the following format:
```json
{
"admin": {
"email": "<email>",
"password": "<password>"
}
}
```
#### Testing with different roles
If you want to execute a test using Cypress on visual mode with MKI, you need to make sure you have the user created in your organization, and add it tot he `.ftr/role_users.json`:
```json
{
"admin": {
"email": "<email>",
"password": "<password>"
},
"<roleName>": {
"email": "<email>",
"password": "<password>"
}
}
```
As role names please use:
- admin
- detections_admin
- editor
- endpoint_operations_analyst
- endpoint_policy_manager
- none
- platform_engineer
- rule_author
- soc_manager
- t1_analyst
- t2_analyst
- t3_analyst
- threat_intelligence_analyst
- viewer
The above should be the same used on the automation.
#### PLIs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { defineCypressConfig } from '@kbn/cypress-config';
import { esArchiver } from './support/es_archiver';
import { samlAuthentication } from './support/saml_auth';

// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
Expand Down Expand Up @@ -39,6 +40,7 @@ export default defineCypressConfig({
specPattern: './cypress/e2e/**/*.cy.ts',
setupNodeEvents(on, config) {
esArchiver(on, config);
samlAuthentication(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { defineCypressConfig } from '@kbn/cypress-config';
import { esArchiver } from './support/es_archiver';
import { samlAuthentication } from './support/saml_auth';

// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
Expand Down Expand Up @@ -41,6 +42,7 @@ export default defineCypressConfig({
specPattern: './cypress/e2e/**/*.cy.ts',
setupNodeEvents(on, config) {
esArchiver(on, config);
samlAuthentication(on, config);
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { defineCypressConfig } from '@kbn/cypress-config';
import { esArchiver } from './support/es_archiver';
import { samlAuthentication } from './support/saml_auth';

// eslint-disable-next-line import/no-default-export
export default defineCypressConfig({
Expand All @@ -31,6 +32,7 @@ export default defineCypressConfig({
experimentalMemoryManagement: true,
setupNodeEvents(on, config) {
esArchiver(on, config);
samlAuthentication(on, config);
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('@cypress/grep/src/plugin')(config);
return config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@ describe('Cases', { tags: ['@ess', '@serverless'] }, () => {
);
cy.get(CASE_DETAILS_USERNAMES)
.eq(REPORTER)
.should('have.text', Cypress.env(ELASTICSEARCH_USERNAME));
.should('contain', Cypress.env(ELASTICSEARCH_USERNAME));
cy.get(CASE_DETAILS_USERNAMES)
.eq(PARTICIPANTS)
.should('have.text', Cypress.env(ELASTICSEARCH_USERNAME));
.should('contain', Cypress.env(ELASTICSEARCH_USERNAME));
cy.get(CASE_DETAILS_TAGS).should('have.text', expectedTags);

EXPECTED_METRICS.forEach((metric) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ describe('Overview Page', { tags: ['@ess', '@serverless'] }, () => {
});
});

describe('Favorite Timelines', () => {
// https://github.com/elastic/kibana/issues/173168
describe('Favorite Timelines', { tags: ['@brokenInServerless'] }, () => {
it('should appear on overview page', () => {
createTimeline(getTimeline())
.then((response) => response.body.data.persistTimeline.timeline.savedObjectId)
Expand Down
11 changes: 9 additions & 2 deletions x-pack/test/security_solution_cypress/cypress/screens/alerts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { IS_SERVERLESS, CLOUD_SERVERLESS } from '../env_var_names_constants';
import { getDataTestSubjectSelector } from '../helpers/common';
import { GLOBAL_FILTERS_CONTAINER } from './date_picker';

Expand Down Expand Up @@ -204,9 +205,15 @@ export const ALERT_ASSIGNEES_SELECT_PANEL =
export const ALERT_ASSIGNEES_UPDATE_BUTTON =
'[data-test-subj="securitySolutionAssigneesApplyButton"]';

export const ALERT_USER_AVATAR = (assignee: string) =>
`[data-test-subj="securitySolutionUsersAvatar-${assignee}"][title='${assignee}']`;
export const ALERT_USER_AVATAR = (assignee: string) => {
let expectedAssignee = assignee;

if (Cypress.env(IS_SERVERLESS) && !Cypress.env(CLOUD_SERVERLESS)) {
expectedAssignee = `test ${expectedAssignee}`;
}

return `[data-test-subj^="securitySolutionUsersAvatar-"][title='${expectedAssignee}']`;
};
export const ALERT_AVATARS_PANEL = '[data-test-subj="securitySolutionUsersAvatarsPanel"]';

export const ALERT_ASIGNEES_COLUMN =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const esArchiver = (
): EsArchiver => {
const log = new ToolingLog({ level: 'verbose', writeTo: process.stdout });

const isSnapshotServerless = config.env.IS_SERVERLESS;
const isServerless = config.env.IS_SERVERLESS;
const isCloudServerless = config.env.CLOUD_SERVERLESS;

const serverlessCloudUser = {
Expand All @@ -27,7 +27,7 @@ export const esArchiver = (
};

let authOverride;
if (!isSnapshotServerless) {
if (isServerless) {
authOverride = isCloudServerless ? serverlessCloudUser : systemIndicesSuperuser;
}

Expand Down
41 changes: 41 additions & 0 deletions x-pack/test/security_solution_cypress/cypress/support/saml_auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { ToolingLog } from '@kbn/tooling-log';

import { SecurityRoleName } from '@kbn/security-solution-plugin/common/test';
import { HostOptions, SamlSessionManager } from '@kbn/test';

export const samlAuthentication = async (
on: Cypress.PluginEvents,
config: Cypress.PluginConfigOptions
): Promise<void> => {
const log = new ToolingLog({ level: 'verbose', writeTo: process.stdout });

const kbnHost = config.env.KIBANA_URL || config.env.BASE_URL;

const kbnUrl = new URL(kbnHost);

const hostOptions: HostOptions = {
protocol: kbnUrl.protocol as 'http' | 'https',
hostname: kbnUrl.hostname,
port: parseInt(kbnUrl.port, 10),
username: config.env.ELASTICSEARCH_USERNAME,
password: config.env.ELASTICSEARCH_PASSWORD,
};

on('task', {
getSessionCookie: async (role: string | SecurityRoleName): Promise<string> => {
const sessionManager = new SamlSessionManager({
hostOptions,
log,
isCloud: config.env.CLOUD_SERVERLESS,
});
return sessionManager.getSessionCookieForRole(role);
},
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function createUser(username: string, password: string, roles: string[] = []): v
password,
roles,
full_name: username,
email: '',
email: `${username}@elastic.co`,
};

rootRequest({
Expand Down
18 changes: 16 additions & 2 deletions x-pack/test/security_solution_cypress/cypress/tasks/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,22 @@ export const getEnvAuth = (role: SecurityRoleName): User => {
};

export const login = (role?: SecurityRoleName): void => {
const user = role ? getEnvAuth(role) : defaultUser;
loginWithUser(user);
let testRole = '';

if (Cypress.env(IS_SERVERLESS)) {
if (!role) {
testRole = Cypress.env(CLOUD_SERVERLESS) ? 'admin' : 'system_indices_superuser';
} else {
testRole = role;
}
cy.task('getSessionCookie', testRole).then((cookie) => {
cy.setCookie('sid', cookie as string);
});
cy.visit('/');
} else {
const user = role ? getEnvAuth(role) : defaultUser;
loginWithUser(user);
}
};

export const loginWithUser = (user: User): void => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@
"@kbn/management-settings-ids",
"@kbn/es-query",
"@kbn/ml-plugin",
"@kbn/license-management-plugin"
"@kbn/license-management-plugin",
]
}

0 comments on commit c580213

Please sign in to comment.