Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Security Solution] [Cypress] SAML for Serverless login and role testing #172655

Merged
merged 18 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 17 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 @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We recently implemented a change where we're accessing vault through a variable path. I think for this to be future-proof to some degree, we should also care for that branching in here. However, the utility function that we use cannot be parameterized with extra args in its current state.

If this is urgent, we can go with this.
If you have some time to chisel the edges, I'll follow up with the details, so you can add it to this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dkirchan can you please take a look at @delanni comment and estimate how long is going to take to make the changes? Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@delanni as discussed private, lets unblock this PR and I will add these changes as part of my PR that I am preparing: #173005


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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cant we use only one of these two flags in lines 573 and 574? If this is not a big rework.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These flags represent two different things.

  • IS_SERVERLESS is used for both FTR and cloud environments.
  • CLOUD_SERVERLESS is used for just deployed projects on the cloud, this cannot be used for the FTR serverless environment.

};

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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should list of these role match ROLES enum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I want to double check with @maximpn when he is back. The listed roles are the ones available on serverless and are the ones we can retrieve cookies from so I've the feeling we might be missing roles on that enum.

- 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'] }, () => {
stephmilovic marked this conversation as resolved.
Show resolved Hide resolved
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
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",
]
}