Skip to content

Commit

Permalink
[Serverless] Disable Login Selector and push Basic authentication p…
Browse files Browse the repository at this point in the history
…rovidder down the authentication chain. (elastic#165810)

## Summary

Since we're planning to disable login selector in MKI soon, we should
adapt our tests for this new configuration. This PR disables Login
Selector and pushes `Basic` authentication providder down the
authentication chain in Serverless tests.
  • Loading branch information
azasypkin authored Sep 13, 2023
1 parent b35328f commit 7aa3074
Show file tree
Hide file tree
Showing 30 changed files with 300 additions and 231 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,9 @@
* 2.0.
*/

/* eslint-disable import/no-nodejs-modules */

import * as yaml from 'js-yaml';
import type { UrlObject } from 'url';
import Url from 'url';
import type { Role } from '@kbn/security-plugin/common';
import { isLocalhost } from '../../../../scripts/endpoint/common/is_localhost';
import type { LoginState } from '@kbn/security-plugin/common/login_state';
import { getWithResponseActionsRole } from '../../../../scripts/endpoint/common/roles_users/with_response_actions_role';
import { getNoResponseActionsRole } from '../../../../scripts/endpoint/common/roles_users/without_response_actions_role';
import { request } from './common';
Expand Down Expand Up @@ -90,35 +86,6 @@ const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';
const KIBANA_USERNAME = 'KIBANA_USERNAME';
const KIBANA_PASSWORD = 'KIBANA_PASSWORD';

/**
* The Kibana server endpoint used for authentication
*/
const LOGIN_API_ENDPOINT = '/internal/security/login';

/**
* cy.visit will default to the baseUrl which uses the default kibana test user
* This function will override that functionality in cy.visit by building the baseUrl
* directly from the environment variables set up in x-pack/test/security_solution_cypress/runner.ts
*
* @param role string role/user to log in with
* @param route string route to visit
*/
export const getUrlWithRoute = (role: string, route: string) => {
const url = Cypress.config().baseUrl;
const kibana = new URL(String(url));
const theUrl = `${Url.format({
auth: `${role}:changeme`,
username: role,
password: Cypress.env(ELASTICSEARCH_PASSWORD),
protocol: kibana.protocol.replace(':', ''),
hostname: kibana.hostname,
port: kibana.port,
} as UrlObject)}${route.startsWith('/') ? '' : '/'}${route}`;
cy.log(`origin: ${theUrl}`);

return theUrl;
};

export const createCustomRoleAndUser = (role: string, rolePrivileges: Omit<Role, 'name'>) => {
// post the role
request({
Expand All @@ -139,31 +106,44 @@ export const createCustomRoleAndUser = (role: string, rolePrivileges: Omit<Role,
});
};

export const loginWithRole = async (role: ROLE) => {
const loginWithUsernameAndPassword = (username: string, password: string) => {
const baseUrl = Cypress.config().baseUrl;
if (!baseUrl) {
throw Error(`Cypress config baseUrl not set!`);
}

// Programmatically authenticate without interacting with the Kibana login page.
const headers = { 'kbn-xsrf': 'cypress-creds' };
request<LoginState>({ headers, url: `${baseUrl}/internal/security/login_state` }).then(
(loginState) => {
const basicProvider = loginState.body.selector.providers.find(
(provider) => provider.type === 'basic'
);
return request({
url: `${baseUrl}/internal/security/login`,
method: 'POST',
headers,
body: {
providerType: basicProvider.type,
providerName: basicProvider.name,
currentURL: '/',
params: { username, password },
},
});
}
);
};

export const loginWithRole = (role: ROLE) => {
loginWithCustomRole(role, rolesMapping[role]);
};

export const loginWithCustomRole = async (role: string, rolePrivileges: Omit<Role, 'name'>) => {
export const loginWithCustomRole = (role: string, rolePrivileges: Omit<Role, 'name'>) => {
createCustomRoleAndUser(role, rolePrivileges);
const theUrl = new URL(String(Cypress.config().baseUrl));
theUrl.username = role;
theUrl.password = Cypress.env(ELASTICSEARCH_PASSWORD);

cy.log(`origin: ${theUrl}`);
request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: role,
password: Cypress.env(ELASTICSEARCH_PASSWORD),
},
},
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
method: 'POST',
url: getUrlWithRoute(role, LOGIN_API_ENDPOINT),
});
cy.log(`origin: ${Cypress.config().baseUrl}`);

loginWithUsernameAndPassword(role, Cypress.env(ELASTICSEARCH_PASSWORD));
};

/**
Expand Down Expand Up @@ -199,14 +179,6 @@ const credentialsProvidedByEnvironment = (): boolean =>
* Kibana's `/internal/security/login` endpoint, bypassing the login page (for speed).
*/
const loginViaEnvironmentCredentials = () => {
const url = Cypress.config().baseUrl;

if (!url) {
throw Error(`Cypress config baseUrl not set!`);
}

const urlObj = new URL(url);

let username: string;
let password: string;
let usernameEnvVar: string;
Expand All @@ -228,21 +200,7 @@ const loginViaEnvironmentCredentials = () => {
`Authenticating user [${username}] retrieved via environment credentials from the \`CYPRESS_${usernameEnvVar}\` and \`CYPRESS_${passwordEnvVar}\` environment variables`
);

// programmatically authenticate without interacting with the Kibana login page
request({
body: {
providerType: 'basic',
providerName: url && !isLocalhost(urlObj.hostname) ? 'cloud-basic' : 'basic',
currentURL: '/',
params: {
username,
password,
},
},
headers: { 'kbn-xsrf': 'cypress-creds-via-env' },
method: 'POST',
url: `${url}${LOGIN_API_ENDPOINT}`,
});
loginWithUsernameAndPassword(username, password);
};

/**
Expand All @@ -258,22 +216,10 @@ const loginViaConfig = () => {
// read the login details from `kibana.dev.yaml`
cy.readFile(KIBANA_DEV_YML_PATH).then((kibanaDevYml) => {
const config = yaml.safeLoad(kibanaDevYml);

// programmatically authenticate without interacting with the Kibana login page
request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: Cypress.env(ELASTICSEARCH_USERNAME),
password: config.elasticsearch.password,
},
},
headers: { 'kbn-xsrf': 'cypress-creds-via-config' },
method: 'POST',
url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
});
loginWithUsernameAndPassword(
Cypress.env(ELASTICSEARCH_USERNAME),
config.elasticsearch.password
);
});
};

Expand Down
27 changes: 23 additions & 4 deletions x-pack/test/functional/page_objects/security_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,37 @@ export class SecurityPageObject extends FtrService {
return user as AuthenticatedUser;
}

async forceLogout() {
async forceLogout(
{ waitForLoginPage }: { waitForLoginPage: boolean } = { waitForLoginPage: true }
) {
this.log.debug('SecurityPage.forceLogout');
if (await this.find.existsByDisplayedByCssSelector('.login-form', 100)) {
this.log.debug('Already on the login page, not forcing anything');
return;
}

this.log.debug('Redirecting to /logout to force the logout');
this.log.debug(`Redirecting to ${this.deployment.getHostPort()}/logout to force the logout`);
const url = this.deployment.getHostPort() + '/logout';
await this.browser.get(url);
this.log.debug('Waiting on the login form to appear');
await this.waitForLoginPage();

// After logging out, the user can be redirected to various locations depending on the context. By default, we
// expect the user to be redirected to the login page. However, if the login page is not available for some reason,
// we should simply wait until the user is redirected *elsewhere*.
if (waitForLoginPage) {
this.log.debug('Waiting on the login form to appear');
await this.waitForLoginPage();
} else {
this.log.debug('Waiting for logout to complete');
await this.retry.waitFor('Waiting for logout to complete', async () => {
// There are cases when browser/Kibana would like users to confirm that they want to navigate away from the
// current page and lose the state (e.g. unsaved changes) via native alert dialog.
const alert = await this.browser.getAlert();
if (alert?.accept) {
await alert.accept();
}
return !(await this.browser.getCurrentUrl()).includes('/logout');
});
}
}

async clickRolesSection() {
Expand Down
118 changes: 36 additions & 82 deletions x-pack/test/security_solution_cypress/cypress/tasks/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Url from 'url';

import type { ROLES } from '@kbn/security-solution-plugin/common/test';
import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '@kbn/security-solution-plugin/common/constants';
import { LoginState } from '@kbn/security-plugin/common/login_state';
import {
hostDetailsUrl,
LOGOUT_URL,
Expand Down Expand Up @@ -50,11 +51,6 @@ const ELASTICSEARCH_USERNAME = 'ELASTICSEARCH_USERNAME';
*/
const ELASTICSEARCH_PASSWORD = 'ELASTICSEARCH_PASSWORD';

/**
* The Kibana server endpoint used for authentication
*/
const LOGIN_API_ENDPOINT = '/internal/security/login';

/**
* cy.visit will default to the baseUrl which uses the default kibana test user
* This function will override that functionality in cy.visit by building the baseUrl
Expand Down Expand Up @@ -141,53 +137,46 @@ export const deleteRoleAndUser = (role: ROLES) => {
});
};

const loginWithUsernameAndPassword = (username: string, password: string) => {
const baseUrl = Cypress.config().baseUrl;
if (!baseUrl) {
throw Error(`Cypress config baseUrl not set!`);
}

const headers = { 'kbn-xsrf': 'cypress-creds', 'x-elastic-internal-origin': 'security-solution' };
// programmatically authenticate without interacting with the Kibana login page
cy.request<LoginState>({ headers, url: `${baseUrl}/internal/security/login_state` }).then(
(loginState) => {
const basicProvider = loginState.body.selector.providers.find(
(provider) => provider.type === 'basic'
);
return cy.request({
url: `${baseUrl}/internal/security/login`,
method: 'POST',
headers,
body: {
providerType: basicProvider.type,
providerName: basicProvider.name,
currentURL: '/',
params: { username, password },
},
});
}
);
};

export const loginWithUser = (user: User) => {
cy.session(user, () => {
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: user.username,
password: user.password,
},
},
headers: {
'kbn-xsrf': 'cypress-creds-via-config',
'x-elastic-internal-origin': 'security-solution',
},
method: 'POST',
url: constructUrlWithUser(user, LOGIN_API_ENDPOINT),
});
loginWithUsernameAndPassword(user.username, user.password);
});
};

const loginWithRole = async (role: ROLES) => {
const loginWithRole = (role: ROLES) => {
postRoleAndUser(role);
const theUrl = new URL(String(Cypress.config().baseUrl));
theUrl.username = role;
theUrl.password = Cypress.env(ELASTICSEARCH_PASSWORD);

cy.log(`origin: ${theUrl}`);
cy.log(`origin: ${Cypress.config().baseUrl}`);
cy.session(role, () => {
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: role,
password: 'changeme',
},
},
headers: {
'kbn-xsrf': 'cypress-creds-via-config',
'x-elastic-internal-origin': 'security-solution',
},
method: 'POST',
url: getUrlWithRoute(role, LOGIN_API_ENDPOINT),
});
loginWithUsernameAndPassword(role, 'changeme');
});
};

Expand Down Expand Up @@ -231,24 +220,7 @@ const loginViaEnvironmentCredentials = () => {
const password = Cypress.env(ELASTICSEARCH_PASSWORD);

cy.session([username, password], () => {
// programmatically authenticate without interacting with the Kibana login page
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username,
password,
},
},
headers: {
'kbn-xsrf': 'cypress-creds-via-env',
'x-elastic-internal-origin': 'security-solution',
},
method: 'POST',
url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
});
loginWithUsernameAndPassword(username, password);
});
};

Expand All @@ -264,26 +236,8 @@ const loginViaConfig = () => {

// read the login details from `kibana.dev.yaml`
cy.readFile(KIBANA_DEV_YML_PATH).then((kibanaDevYml) => {
const config = yaml.safeLoad(kibanaDevYml);

// programmatically authenticate without interacting with the Kibana login page
cy.request({
body: {
providerType: 'basic',
providerName: 'basic',
currentURL: '/',
params: {
username: config.elasticsearch.username,
password: config.elasticsearch.password,
},
},
headers: {
'kbn-xsrf': 'cypress-creds-via-config',
'x-elastic-internal-origin': 'security-solution',
},
method: 'POST',
url: `${Cypress.config().baseUrl}${LOGIN_API_ENDPOINT}`,
});
const { username, password } = yaml.safeLoad(kibanaDevYml);
loginWithUsernameAndPassword(username, password);
});
};

Expand Down
3 changes: 2 additions & 1 deletion x-pack/test/security_solution_cypress/cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@kbn/expandable-flyout",
"@kbn/config-schema",
"@kbn/lists-plugin",
"@kbn/securitysolution-list-constants"
"@kbn/securitysolution-list-constants",
"@kbn/security-plugin"
]
}
Loading

0 comments on commit 7aa3074

Please sign in to comment.