mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[ftr] add roleScopedSupertest service for deployment-agnostic tests (#190279)
## Summary Adding new service that acts as a wrapper of `supertestWithoutAuth` service authenticated with role-based API key and enriched with request headers. The proposed change streamlines test design by centralizing the management of API key and internal/common headers, eliminating the need to pass these arguments individually in every API call. This approach reduces code duplication and enhances maintainability Before: ```ts const samlAuth = getService('samlAuth'); const supertestWithoutAuth = getService('supertestWithoutAuth'); const roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin'); const internalHeaders = samlAuth.getInternalRequestHeader(); await supertestWithoutAuth .get('/api/console/api_server') .set(roleAuthc.apiKeyHeader) .set(internalHeaders) .set('kbn-xsrf', 'true') .expect(200); ``` After: ```ts const roleScopedSupertest = getService('roleScopedSupertest'); const supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', { withInternalHeaders: true, withCustomHeaders: {'kbn-xsrf': 'true'}, }); await supertestWithAdminScope .get('/api/console/api_server') .expect(200); ``` Use this service to easily test API endpoints with role-specific authorization and custom headers, both in serverless and stateful environments. closes #190228 --------- Co-authored-by: Aleh Zasypkin <aleh.zasypkin@gmail.com>
This commit is contained in:
parent
a13f8d983c
commit
96d3325524
7 changed files with 165 additions and 60 deletions
|
@ -25,4 +25,7 @@ export type SupertestWithoutAuthProviderType = ProvidedType<typeof SupertestWith
|
|||
|
||||
export type { InternalRequestHeader, RoleCredentials } from './services/saml_auth';
|
||||
|
||||
import { SamlAuthProvider } from './services/saml_auth/saml_auth_provider';
|
||||
export type SamlAuthProviderType = ProvidedType<typeof SamlAuthProvider>;
|
||||
|
||||
export type { FtrProviderContext } from './services/ftr_provider_context';
|
||||
|
|
|
@ -94,26 +94,23 @@ Add test files to `x-pack/test/<my_own_api_integration_folder>/deployment_agnost
|
|||
test example
|
||||
```ts
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const samlAuth = getService('samlAuth');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
let roleAuthc: RoleCredentials;
|
||||
let internalHeaders: InternalRequestHeader;
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
let supertestWithAdminScope: SupertestWithRoleScopeType;
|
||||
|
||||
describe('compression', () => {
|
||||
before(async () => {
|
||||
roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
|
||||
internalHeaders = samlAuth.getInternalRequestHeader();
|
||||
supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCustomHeaders: { 'accept-encoding': 'gzip' },
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc);
|
||||
// always invalidate API key for the scoped role in the end
|
||||
await supertestWithAdminScope.destroy();
|
||||
});
|
||||
describe('against an application page', () => {
|
||||
it(`uses compression when there isn't a referer`, async () => {
|
||||
const response = await supertestWithoutAuth
|
||||
.get('/app/kibana')
|
||||
.set('accept-encoding', 'gzip')
|
||||
.set(internalHeaders)
|
||||
.set(roleAuthc.apiKeyHeader);
|
||||
const response = await supertestWithAdminScope.get('/app/kibana');
|
||||
expect(response.header).to.have.property('content-encoding', 'gzip');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,30 +6,25 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services';
|
||||
import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context';
|
||||
import { SupertestWithRoleScopeType } from '../../services';
|
||||
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const samlAuth = getService('samlAuth');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
let roleAuthc: RoleCredentials;
|
||||
let internalHeaders: InternalRequestHeader;
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
let supertestWithAdminScope: SupertestWithRoleScopeType;
|
||||
|
||||
describe('GET /api/console/api_server', () => {
|
||||
before(async () => {
|
||||
roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
|
||||
internalHeaders = samlAuth.getInternalRequestHeader();
|
||||
supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCustomHeaders: { 'kbn-xsrf': 'true' },
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc);
|
||||
await supertestWithAdminScope.destroy();
|
||||
});
|
||||
it('returns autocomplete definitions', async () => {
|
||||
const { body } = await supertestWithoutAuth
|
||||
.get('/api/console/api_server')
|
||||
.set(roleAuthc.apiKeyHeader)
|
||||
.set(internalHeaders)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.expect(200);
|
||||
const { body } = await supertestWithAdminScope.get('/api/console/api_server').expect(200);
|
||||
expect(body.es).to.be.ok();
|
||||
const {
|
||||
es: { name, globals, endpoints },
|
||||
|
|
|
@ -6,40 +6,32 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services';
|
||||
import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context';
|
||||
import { SupertestWithRoleScopeType } from '../../services';
|
||||
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const samlAuth = getService('samlAuth');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
let roleAuthc: RoleCredentials;
|
||||
let internalHeaders: InternalRequestHeader;
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
let supertestWithAdminScope: SupertestWithRoleScopeType;
|
||||
|
||||
describe('compression', () => {
|
||||
before(async () => {
|
||||
roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
|
||||
internalHeaders = samlAuth.getInternalRequestHeader();
|
||||
supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withCustomHeaders: { 'accept-encoding': 'gzip' },
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc);
|
||||
await supertestWithAdminScope.destroy();
|
||||
});
|
||||
describe('against an application page', () => {
|
||||
it(`uses compression when there isn't a referer`, async () => {
|
||||
const response = await supertestWithoutAuth
|
||||
.get('/app/kibana')
|
||||
.set('accept-encoding', 'gzip')
|
||||
.set(internalHeaders)
|
||||
.set(roleAuthc.apiKeyHeader);
|
||||
const response = await supertestWithAdminScope.get('/app/kibana');
|
||||
expect(response.header).to.have.property('content-encoding', 'gzip');
|
||||
});
|
||||
|
||||
it(`uses compression when there is a whitelisted referer`, async () => {
|
||||
const response = await supertestWithoutAuth
|
||||
const response = await supertestWithAdminScope
|
||||
.get('/app/kibana')
|
||||
.set('accept-encoding', 'gzip')
|
||||
.set(internalHeaders)
|
||||
.set('referer', 'https://some-host.com')
|
||||
.set(roleAuthc.apiKeyHeader);
|
||||
.set('referer', 'https://some-host.com');
|
||||
expect(response.header).to.have.property('content-encoding', 'gzip');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,35 +6,32 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { RoleCredentials, InternalRequestHeader } from '@kbn/ftr-common-functional-services';
|
||||
import { DeploymentAgnosticFtrProviderContext } from '../../ftr_provider_context';
|
||||
import { SupertestWithRoleScopeType } from '../../services';
|
||||
|
||||
const API_BASE_PATH = '/api/painless_lab';
|
||||
|
||||
export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const samlAuth = getService('samlAuth');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
let roleAuthc: RoleCredentials;
|
||||
let internalHeaders: InternalRequestHeader;
|
||||
const roleScopedSupertest = getService('roleScopedSupertest');
|
||||
let supertestWithAdminScope: SupertestWithRoleScopeType;
|
||||
|
||||
describe('Painless Lab Routes', function () {
|
||||
before(async () => {
|
||||
roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope('admin');
|
||||
internalHeaders = samlAuth.getInternalRequestHeader();
|
||||
supertestWithAdminScope = await roleScopedSupertest.getSupertestWithRoleScope('admin', {
|
||||
withInternalHeaders: true,
|
||||
withCustomHeaders: { 'Content-Type': 'application/json;charset=UTF-8' },
|
||||
});
|
||||
});
|
||||
after(async () => {
|
||||
await samlAuth.invalidateM2mApiKeyWithRoleScope(roleAuthc);
|
||||
await supertestWithAdminScope.destroy();
|
||||
});
|
||||
describe('Execute', () => {
|
||||
it('should execute a valid painless script', async () => {
|
||||
const script =
|
||||
'"{\\n \\"script\\": {\\n \\"source\\": \\"return true;\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"';
|
||||
|
||||
const { body } = await supertestWithoutAuth
|
||||
const { body } = await supertestWithAdminScope
|
||||
.post(`${API_BASE_PATH}/execute`)
|
||||
.set(internalHeaders)
|
||||
.set(roleAuthc.apiKeyHeader)
|
||||
.set('Content-Type', 'application/json;charset=UTF-8')
|
||||
.send(script)
|
||||
.expect(200);
|
||||
|
||||
|
@ -47,11 +44,8 @@ export default function ({ getService }: DeploymentAgnosticFtrProviderContext) {
|
|||
const invalidScript =
|
||||
'"{\\n \\"script\\": {\\n \\"source\\": \\"foobar\\",\\n \\"params\\": {\\n \\"string_parameter\\": \\"string value\\",\\n \\"number_parameter\\": 1.5,\\n \\"boolean_parameter\\": true\\n}\\n }\\n}"';
|
||||
|
||||
const { body } = await supertestWithoutAuth
|
||||
const { body } = await supertestWithAdminScope
|
||||
.post(`${API_BASE_PATH}/execute`)
|
||||
.set(internalHeaders)
|
||||
.set('Content-Type', 'application/json;charset=UTF-8')
|
||||
.set(roleAuthc.apiKeyHeader)
|
||||
.send(invalidScript)
|
||||
.expect(200);
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { commonFunctionalServices } from '@kbn/ftr-common-functional-services';
|
|||
import { deploymentAgnosticServices } from './deployment_agnostic_services';
|
||||
import { DataViewApiProvider } from './data_view_api';
|
||||
import { SloApiProvider } from './slo_api';
|
||||
import { RoleScopedSupertestProvider, SupertestWithRoleScope } from './role_scoped_supertest';
|
||||
|
||||
export type {
|
||||
InternalRequestHeader,
|
||||
|
@ -22,7 +23,9 @@ export const services = {
|
|||
samlAuth: commonFunctionalServices.samlAuth,
|
||||
dataViewApi: DataViewApiProvider,
|
||||
sloApi: SloApiProvider,
|
||||
roleScopedSupertest: RoleScopedSupertestProvider,
|
||||
// create a new deployment-agnostic service and load here
|
||||
};
|
||||
|
||||
export type SupertestWithRoleScopeType = SupertestWithRoleScope;
|
||||
export type DeploymentAgnosticCommonServices = typeof services;
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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 {
|
||||
RoleCredentials,
|
||||
SupertestWithoutAuthProviderType,
|
||||
SamlAuthProviderType,
|
||||
} from '@kbn/ftr-common-functional-services';
|
||||
import { Test } from 'supertest';
|
||||
import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context';
|
||||
|
||||
export interface RequestHeadersOptions {
|
||||
withInternalHeaders?: boolean;
|
||||
withCommonHeaders?: boolean;
|
||||
withCustomHeaders?: Record<string, string>;
|
||||
}
|
||||
|
||||
export class SupertestWithRoleScope {
|
||||
private roleAuthc: RoleCredentials | null;
|
||||
private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType;
|
||||
private samlAuth: SamlAuthProviderType;
|
||||
private readonly options: RequestHeadersOptions;
|
||||
|
||||
constructor(
|
||||
roleAuthc: RoleCredentials,
|
||||
supertestWithoutAuth: SupertestWithoutAuthProviderType,
|
||||
samlAuth: SamlAuthProviderType,
|
||||
options: RequestHeadersOptions
|
||||
) {
|
||||
this.roleAuthc = roleAuthc;
|
||||
this.supertestWithoutAuth = supertestWithoutAuth;
|
||||
this.samlAuth = samlAuth;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
if (this.roleAuthc) {
|
||||
await this.samlAuth.invalidateM2mApiKeyWithRoleScope(this.roleAuthc);
|
||||
this.roleAuthc = null;
|
||||
}
|
||||
}
|
||||
|
||||
private addHeaders(agent: Test): Test {
|
||||
const { withInternalHeaders, withCommonHeaders, withCustomHeaders } = this.options;
|
||||
|
||||
if (!this.roleAuthc) {
|
||||
throw new Error('The instance has already been destroyed.');
|
||||
}
|
||||
// set role-based API key by default
|
||||
agent.set(this.roleAuthc.apiKeyHeader);
|
||||
|
||||
if (withInternalHeaders) {
|
||||
agent.set(this.samlAuth.getInternalRequestHeader());
|
||||
}
|
||||
|
||||
if (withCommonHeaders) {
|
||||
agent.set(this.samlAuth.getCommonRequestHeader());
|
||||
}
|
||||
|
||||
if (withCustomHeaders) {
|
||||
agent.set(withCustomHeaders);
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
private request(method: 'post' | 'get' | 'put' | 'delete', url: string): Test {
|
||||
if (!this.roleAuthc) {
|
||||
throw new Error('Instance has been destroyed and cannot be used for making requests.');
|
||||
}
|
||||
const agent = this.supertestWithoutAuth[method](url);
|
||||
return this.addHeaders(agent);
|
||||
}
|
||||
|
||||
post(url: string) {
|
||||
return this.request('post', url);
|
||||
}
|
||||
|
||||
get(url: string) {
|
||||
return this.request('get', url);
|
||||
}
|
||||
|
||||
put(url: string) {
|
||||
return this.request('put', url);
|
||||
}
|
||||
|
||||
delete(url: string) {
|
||||
return this.request('delete', url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a customized 'supertest' instance that is authenticated using a role-based API key
|
||||
* and enriched with the appropriate request headers. This service allows you to perform
|
||||
* HTTP requests with specific authentication and header configurations, ensuring that
|
||||
* the requests are scoped to the provided role and environment.
|
||||
*
|
||||
* Use this service to easily test API endpoints with role-specific authorization and
|
||||
* custom headers, both in serverless and stateful environments.
|
||||
*/
|
||||
export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFtrProviderContext) {
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const samlAuth = getService('samlAuth');
|
||||
|
||||
return {
|
||||
async getSupertestWithRoleScope(
|
||||
role: string,
|
||||
options: RequestHeadersOptions = {
|
||||
withCommonHeaders: false,
|
||||
withInternalHeaders: false,
|
||||
}
|
||||
) {
|
||||
const roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope(role);
|
||||
return new SupertestWithRoleScope(roleAuthc, supertestWithoutAuth, samlAuth, options);
|
||||
},
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue