-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
[FTR] allow to call roleScopedSupertest service with Cookie header #192727
Changes from 32 commits
95e7451
2bcec5c
5d5e449
4aa29ce
21a8fa0
1fd3ead
2ed4929
bbfef2f
a7874f6
45988fb
ae1c516
3dddbb7
166eacf
733503d
d353784
d89d468
267168a
ad9d618
fe64f44
e7d6c9f
32a9892
91048df
51b7339
7f29c2e
16225eb
bfcc489
5e18cd9
e592be9
f6d935e
192fea2
e031d9c
8040bdf
8c25dc1
27153d3
a762679
16cf8e7
4966103
bf921ee
2899971
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,51 +7,66 @@ | |
|
||
import { | ||
RoleCredentials, | ||
CookieCredentials, | ||
SupertestWithoutAuthProviderType, | ||
SamlAuthProviderType, | ||
} from '@kbn/ftr-common-functional-services'; | ||
import { Test } from 'supertest'; | ||
import { DeploymentAgnosticFtrProviderContext } from '../ftr_provider_context'; | ||
|
||
export interface RequestHeadersOptions { | ||
useCookieHeader?: boolean; | ||
withInternalHeaders?: boolean; | ||
withCommonHeaders?: boolean; | ||
withCustomHeaders?: Record<string, string>; | ||
} | ||
|
||
export class SupertestWithRoleScope { | ||
private roleAuthc: RoleCredentials | null; | ||
private authValue: RoleCredentials | CookieCredentials | null; | ||
private readonly supertestWithoutAuth: SupertestWithoutAuthProviderType; | ||
private samlAuth: SamlAuthProviderType; | ||
private readonly options: RequestHeadersOptions; | ||
|
||
constructor( | ||
roleAuthc: RoleCredentials, | ||
authValue: RoleCredentials | CookieCredentials | null, | ||
supertestWithoutAuth: SupertestWithoutAuthProviderType, | ||
samlAuth: SamlAuthProviderType, | ||
options: RequestHeadersOptions | ||
) { | ||
this.roleAuthc = roleAuthc; | ||
this.authValue = authValue; | ||
this.supertestWithoutAuth = supertestWithoutAuth; | ||
this.samlAuth = samlAuth; | ||
this.options = options; | ||
} | ||
|
||
private isRoleCredentials(value: any): value is RoleCredentials { | ||
return value && typeof value === 'object' && 'apiKey' in value && 'apiKeyHeader' in value; | ||
} | ||
|
||
async destroy() { | ||
if (this.roleAuthc) { | ||
await this.samlAuth.invalidateM2mApiKeyWithRoleScope(this.roleAuthc); | ||
this.roleAuthc = null; | ||
if (this.isRoleCredentials(this.authValue)) { | ||
await this.samlAuth.invalidateM2mApiKeyWithRoleScope(this.authValue); | ||
this.authValue = null; | ||
Comment on lines
-41
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We only delete
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could return the destroy fn from the create functions to make it obvious that it needs to be called. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about it initially and decided to not change the existing interfaces. In the end, leaving API key unvalidated shouldn't lead to any failures. |
||
} | ||
} | ||
|
||
private addHeaders(agent: Test): Test { | ||
const { withInternalHeaders, withCommonHeaders, withCustomHeaders } = this.options; | ||
const { useCookieHeader, withInternalHeaders, withCommonHeaders, withCustomHeaders } = | ||
this.options; | ||
|
||
if (!this.roleAuthc) { | ||
throw new Error('The instance has already been destroyed.'); | ||
if (useCookieHeader) { | ||
if (!this.authValue || !('Cookie' in this.authValue)) { | ||
throw new Error('The instance has already been destroyed or cookieHeader is missing.'); | ||
} | ||
// set cookie header | ||
void agent.set(this.authValue); | ||
} else { | ||
if (!this.authValue || !this.isRoleCredentials(this.authValue)) { | ||
throw new Error('The instance has already been destroyed or roleAuthc is missing.'); | ||
} | ||
// set API key header | ||
void agent.set(this.authValue.apiKeyHeader); | ||
} | ||
// set role-based API key by default | ||
void agent.set(this.roleAuthc.apiKeyHeader); | ||
|
||
if (withInternalHeaders) { | ||
void agent.set(this.samlAuth.getInternalRequestHeader()); | ||
|
@@ -69,7 +84,7 @@ export class SupertestWithRoleScope { | |
} | ||
|
||
private request(method: 'post' | 'get' | 'put' | 'delete', url: string): Test { | ||
if (!this.roleAuthc) { | ||
if (!this.authValue) { | ||
throw new Error('Instance has been destroyed and cannot be used for making requests.'); | ||
} | ||
const agent = this.supertestWithoutAuth[method](url); | ||
|
@@ -101,6 +116,9 @@ export class SupertestWithRoleScope { | |
* | ||
* Use this service to easily test API endpoints with role-specific authorization and | ||
* custom headers, both in serverless and stateful environments. | ||
* | ||
* Pass '{ useCookieHeader: true }' to use Cookie header for authentication instead of API key. | ||
* It is the correct way to perform HTTP requests for internal end-points. | ||
*/ | ||
export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFtrProviderContext) { | ||
const supertestWithoutAuth = getService('supertestWithoutAuth'); | ||
|
@@ -110,10 +128,18 @@ export function RoleScopedSupertestProvider({ getService }: DeploymentAgnosticFt | |
async getSupertestWithRoleScope( | ||
role: string, | ||
options: RequestHeadersOptions = { | ||
useCookieHeader: false, | ||
withCommonHeaders: false, | ||
withInternalHeaders: false, | ||
} | ||
) { | ||
// if 'useCookieHeader' set to 'true', HTTP requests will be called with cookie Header (like in browser) | ||
if (options.useCookieHeader) { | ||
const cookieHeader = await samlAuth.getM2MApiCookieCredentialsWithRoleScope(role); | ||
return new SupertestWithRoleScope(cookieHeader, supertestWithoutAuth, samlAuth, options); | ||
} | ||
|
||
// HTTP requests will be called with API key in header by default | ||
const roleAuthc = await samlAuth.createM2mApiKeyWithRoleScope(role); | ||
return new SupertestWithRoleScope(roleAuthc, supertestWithoutAuth, samlAuth, options); | ||
}, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid misuse: this function returns only API key + header with role-scoped privileges. To get
Cookie
header for specific role, use thegetM2MApiCredentialsWithRoleScope
function