Skip to content

Commit

Permalink
[kbn-test] support new cloud login endpoint (elastic#175035)
Browse files Browse the repository at this point in the history
## Summary

Recently the cloud api we use for saml auth in our FTR tests was removed
in [cloud#123010](elastic/cloud#123010)
Since this change is still not released to all environments, this PR
adds temporary support for both endpoints.

It also changes the way of getting user full name by making API call to
kibana server
  • Loading branch information
dmlemeshko authored Jan 17, 2024
1 parent 4f8d071 commit c79a231
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 17 deletions.
79 changes: 62 additions & 17 deletions packages/kbn-test/src/auth/saml_auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import axios, { AxiosResponse } from 'axios';
import * as cheerio from 'cheerio';
import { Cookie, parse as parseCookie } from 'tough-cookie';
import Url from 'url';
import { CloudSamlSessionParams, CreateSamlSessionParams, LocalSamlSessionParams } from './types';
import {
CloudSamlSessionParams,
CreateSamlSessionParams,
LocalSamlSessionParams,
UserProfile,
} from './types';

export class Session {
readonly cookie;
Expand Down Expand Up @@ -68,11 +73,13 @@ const getCloudUrl = (hostname: string, pathname: string) => {

const createCloudSession = async (params: CreateSamlSessionParams) => {
const { hostname, email, password, log } = params;
const cloudLoginUrl = getCloudUrl(hostname, '/api/v1/users/_login');
let sessionResponse: AxiosResponse;
try {
sessionResponse = await axios.request({
url: cloudLoginUrl,
// remove after ms-104 is released
const deprecatedLoginUrl = getCloudUrl(hostname, '/api/v1/users/_login');
const cloudLoginUrl = getCloudUrl(hostname, '/api/v1/saas/auth/_login');
let sessionResponse: AxiosResponse | undefined;
const requestConfig = (cloudUrl: string) => {
return {
url: cloudUrl,
method: 'post',
data: {
email,
Expand All @@ -84,17 +91,26 @@ const createCloudSession = async (params: CreateSamlSessionParams) => {
},
validateStatus: () => true,
maxRedirects: 0,
});
};
};
try {
sessionResponse = await axios.request(requestConfig(deprecatedLoginUrl));
} catch (ex) {
log.error('Failed to create the new cloud session');
cleanException(cloudLoginUrl, ex);
throw ex;
log.error(`Failed to create the new cloud session with 'POST ${deprecatedLoginUrl}'`);
// no error on purpose
}

if (!sessionResponse || sessionResponse?.status === 404) {
// Retrying with new cloud login endpoint
try {
sessionResponse = await axios.request(requestConfig(cloudLoginUrl));
} catch (ex) {
log.error(`Failed to create the new cloud session with 'POST ${cloudLoginUrl}'`);
cleanException(cloudLoginUrl, ex);
throw ex;
}
}

const firstName = sessionResponse?.data?.user?.data?.first_name ?? '';
const lastName = sessionResponse?.data?.user?.data?.last_name ?? '';
const firstLastNames = `${firstName} ${lastName}`.trim();
const fullname = firstLastNames.length > 0 ? firstLastNames : email;
const token = sessionResponse?.data?.token as string;
if (!token) {
log.error(
Expand All @@ -104,7 +120,7 @@ const createCloudSession = async (params: CreateSamlSessionParams) => {
);
throw new Error(`Unable to create Cloud session, token is missing.`);
}
return { token, fullname };
return token;
};

const createSAMLRequest = async (kbnUrl: string, kbnVersion: string, log: ToolingLog) => {
Expand Down Expand Up @@ -202,14 +218,43 @@ const finishSAMLHandshake = async ({
return cookie;
};

const getSecurityProfile = async ({
kbnHost,
cookie,
log,
}: {
kbnHost: string;
cookie: Cookie;
log: ToolingLog;
}) => {
let meResponse: AxiosResponse<UserProfile>;
const url = kbnHost + '/internal/security/me';
try {
meResponse = (await axios.get(url, {
headers: {
Cookie: cookie.cookieString(),
'x-elastic-internal-origin': 'Kibana',
'content-type': 'application/json',
},
})) as AxiosResponse<UserProfile>;
} catch (ex) {
log.error('Failed to fetch user profile data');
cleanException(url, ex);
throw ex;
}

return meResponse.data;
};

export const createCloudSAMLSession = async (params: CloudSamlSessionParams) => {
const { email, password, kbnHost, kbnVersion, log } = params;
const hostname = getCloudHostName();
const { token, fullname } = await createCloudSession({ hostname, email, password, log });
const token = await createCloudSession({ hostname, email, password, log });
const { location, sid } = await createSAMLRequest(kbnHost, kbnVersion, log);
const samlResponse = await createSAMLResponse(location, token);
const cookie = await finishSAMLHandshake({ kbnHost, samlResponse, sid, log });
return new Session(cookie, email, fullname);
const userProfile = await getSecurityProfile({ kbnHost, cookie, log });
return new Session(cookie, email, userProfile.full_name);
};

export const createLocalSAMLSession = async (params: LocalSamlSessionParams) => {
Expand Down
9 changes: 9 additions & 0 deletions packages/kbn-test/src/auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,12 @@ export interface User {
}

export type Role = string;

export interface UserProfile {
username: string;
roles: string[];
full_name: string;
email: string;
enabled: boolean;
elastic_cloud_user: boolean;
}

0 comments on commit c79a231

Please sign in to comment.