mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Allow creating CoreKibanaRequest
using fake raw requests (#147919)
## Summary
Fix https://github.com/elastic/kibana/issues/147853
(See issue for more context)
Given we don't want to spend much effort in user impersonation right
now, the compromise would be to make sure that our system is resilient
to creating 'stubbed' requests. The best way to do that is to offer an
official, proper way to instantiate `CoreKibanaRequest` without having a
proper `HAPI.Request` instance.
This PR is introducing the `FakeRawRequest` type, and changing the
signature of `CoreKibanaRequest.create` to accept this new type.
d2eb3e1801/packages/core/http/core-http-server/src/router/raw_request.ts (L14-L36)
The PR also adapts the existing, non-test usages of
`CoreKibanaRequest.from(someUglyStub as unknown as Request)` to use the
new `FakeRawRequest` type instead.
This commit is contained in:
parent
3de5b43fba
commit
a4674f65b6
21 changed files with 493 additions and 390 deletions
|
@ -12,282 +12,342 @@ jest.mock('uuid', () => ({
|
|||
|
||||
import { RouteOptions } from '@hapi/hapi';
|
||||
import { hapiMocks } from '@kbn/hapi-mocks';
|
||||
import type { FakeRawRequest } from '@kbn/core-http-server';
|
||||
import { CoreKibanaRequest } from './request';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
describe('CoreKibanaRequest', () => {
|
||||
describe('id property', () => {
|
||||
it('uses the request.app.requestId property if present', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
app: { requestId: 'fakeId' },
|
||||
describe('using real requests', () => {
|
||||
describe('id property', () => {
|
||||
it('uses the request.app.requestId property if present', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
app: { requestId: 'fakeId' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('fakeId');
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('fakeId');
|
||||
});
|
||||
|
||||
it('generates a new UUID if request.app property is not present', () => {
|
||||
// Undefined app property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: undefined,
|
||||
it('generates a new UUID if request.app property is not present', () => {
|
||||
// Undefined app property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: undefined,
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
|
||||
it('generates a new UUID if request.app.requestId property is not present', () => {
|
||||
// Undefined app.requestId property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: {},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
});
|
||||
|
||||
describe('uuid property', () => {
|
||||
it('uses the request.app.requestUuid property if present', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
app: { requestUuid: '123e4567-e89b-12d3-a456-426614174000' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('123e4567-e89b-12d3-a456-426614174000');
|
||||
});
|
||||
|
||||
it('generates a new UUID if request.app property is not present', () => {
|
||||
// Undefined app property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: undefined,
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
|
||||
it('generates a new UUID if request.app.requestUuid property is not present', () => {
|
||||
// Undefined app.requestUuid property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: {},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get all headers', () => {
|
||||
it('returns all headers', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.headers).toEqual({ custom: 'one', authorization: 'token' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('headers property', () => {
|
||||
it('provides a frozen copy of request headers', () => {
|
||||
const rawRequestHeaders = { custom: 'one' };
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: rawRequestHeaders,
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.headers).toEqual({ custom: 'one' });
|
||||
expect(kibanaRequest.headers).not.toBe(rawRequestHeaders);
|
||||
expect(Object.isFrozen(kibanaRequest.headers)).toBe(true);
|
||||
});
|
||||
|
||||
it.skip("doesn't expose authorization header by default", () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.headers).toEqual({
|
||||
custom: 'one',
|
||||
it('generates a new UUID if request.app.requestId property is not present', () => {
|
||||
// Undefined app.requestId property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: {},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.id).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
});
|
||||
|
||||
it('exposes authorization header if secured = false', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
describe('uuid property', () => {
|
||||
it('uses the request.app.requestUuid property if present', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
app: { requestUuid: '123e4567-e89b-12d3-a456-426614174000' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('123e4567-e89b-12d3-a456-426614174000');
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, undefined, false);
|
||||
expect(kibanaRequest.headers).toEqual({
|
||||
custom: 'one',
|
||||
authorization: 'token',
|
||||
|
||||
it('generates a new UUID if request.app property is not present', () => {
|
||||
// Undefined app property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: undefined,
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
|
||||
it('generates a new UUID if request.app.requestUuid property is not present', () => {
|
||||
// Undefined app.requestUuid property
|
||||
const request = hapiMocks.createRequest({
|
||||
app: {},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.uuid).toEqual('xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get all headers', () => {
|
||||
it('returns all headers', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.headers).toEqual({ custom: 'one', authorization: 'token' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('headers property', () => {
|
||||
it('provides a frozen copy of request headers', () => {
|
||||
const rawRequestHeaders = { custom: 'one' };
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: rawRequestHeaders,
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.headers).toEqual({ custom: 'one' });
|
||||
expect(kibanaRequest.headers).not.toBe(rawRequestHeaders);
|
||||
expect(Object.isFrozen(kibanaRequest.headers)).toBe(true);
|
||||
});
|
||||
|
||||
it.skip("doesn't expose authorization header by default", () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.headers).toEqual({
|
||||
custom: 'one',
|
||||
});
|
||||
});
|
||||
|
||||
it('exposes authorization header if secured = false', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', authorization: 'token' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, undefined, false);
|
||||
expect(kibanaRequest.headers).toEqual({
|
||||
custom: 'one',
|
||||
authorization: 'token',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSytemApi property', () => {
|
||||
it('is false when no kbn-system-request header is set', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(false);
|
||||
});
|
||||
|
||||
it('is true when kbn-system-request header is set to true', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', 'kbn-system-request': 'true' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when kbn-system-request header is set to false', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', 'kbn-system-request': 'false' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('route.options.authRequired property', () => {
|
||||
it('handles required auth: undefined', () => {
|
||||
const auth: RouteOptions['auth'] = undefined;
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth,
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(true);
|
||||
});
|
||||
it('handles required auth: false', () => {
|
||||
const auth: RouteOptions['auth'] = false;
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
// @ts-expect-error According to types/hapi__hapi, `auth` can't be a boolean, but it can according to the @hapi/hapi source (https://github.com/hapijs/hapi/blob/v18.4.2/lib/route.js#L139)
|
||||
auth,
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(false);
|
||||
});
|
||||
it('handles required auth: { mode: "required" }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'required' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(true);
|
||||
});
|
||||
|
||||
it('handles required auth: { mode: "optional" }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'optional' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe('optional');
|
||||
});
|
||||
|
||||
it('handles required auth: { mode: "try" } as "optional"', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'try' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe('optional');
|
||||
});
|
||||
|
||||
it('throws on auth: strategy name', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { strategies: ['session'] },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => CoreKibanaRequest.from(request)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"unexpected authentication options: {\\"strategies\\":[\\"session\\"]} for route: /"`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws on auth: { mode: unexpected mode }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: undefined },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => CoreKibanaRequest.from(request)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"unexpected authentication options: {} for route: /"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RouteSchema type inferring', () => {
|
||||
it('should work with config-schema', () => {
|
||||
const body = Buffer.from('body!');
|
||||
const request = {
|
||||
...hapiMocks.createRequest({
|
||||
params: { id: 'params' },
|
||||
query: { search: 'query' },
|
||||
}),
|
||||
payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
|
||||
} as any;
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, {
|
||||
params: schema.object({ id: schema.string() }),
|
||||
query: schema.object({ search: schema.string() }),
|
||||
body: schema.buffer(),
|
||||
});
|
||||
expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
|
||||
expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
|
||||
expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
|
||||
expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
|
||||
expect(kibanaRequest.body).toEqual(body);
|
||||
expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
|
||||
});
|
||||
|
||||
it('should work with ValidationFunction', () => {
|
||||
const body = Buffer.from('body!');
|
||||
const request = {
|
||||
...hapiMocks.createRequest({
|
||||
params: { id: 'params' },
|
||||
query: { search: 'query' },
|
||||
}),
|
||||
payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
|
||||
} as any;
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, {
|
||||
params: schema.object({ id: schema.string() }),
|
||||
query: schema.object({ search: schema.string() }),
|
||||
body: (data, { ok, badRequest }) => {
|
||||
if (Buffer.isBuffer(data)) {
|
||||
return ok(data);
|
||||
} else {
|
||||
return badRequest('It should be a Buffer', []);
|
||||
}
|
||||
},
|
||||
});
|
||||
expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
|
||||
expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
|
||||
expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
|
||||
expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
|
||||
expect(kibanaRequest.body).toEqual(body);
|
||||
expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFakeRequest', () => {
|
||||
it('should be false', () => {
|
||||
const request = hapiMocks.createRequest({});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isFakeRequest).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isSytemApi property', () => {
|
||||
it('is false when no kbn-system-request header is set', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one' },
|
||||
describe('using fake requests', () => {
|
||||
describe('isFakeRequest', () => {
|
||||
it('should be true', () => {
|
||||
const request: FakeRawRequest = {
|
||||
headers: {},
|
||||
path: '/',
|
||||
};
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isFakeRequest).toBe(true);
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(false);
|
||||
});
|
||||
|
||||
it('is true when kbn-system-request header is set to true', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', 'kbn-system-request': 'true' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(true);
|
||||
});
|
||||
|
||||
it('is false when kbn-system-request header is set to false', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
headers: { custom: 'one', 'kbn-system-request': 'false' },
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.isSystemRequest).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('route.options.authRequired property', () => {
|
||||
it('handles required auth: undefined', () => {
|
||||
const auth: RouteOptions['auth'] = undefined;
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth,
|
||||
describe('headers', () => {
|
||||
it('returns the correct headers', () => {
|
||||
const request: FakeRawRequest = {
|
||||
headers: {
|
||||
foo: 'bar',
|
||||
hello: 'dolly',
|
||||
},
|
||||
},
|
||||
path: '/',
|
||||
};
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
expect(kibanaRequest.headers).toEqual({
|
||||
foo: 'bar',
|
||||
hello: 'dolly',
|
||||
});
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(true);
|
||||
});
|
||||
it('handles required auth: false', () => {
|
||||
const auth: RouteOptions['auth'] = false;
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
// @ts-expect-error According to types/hapi__hapi, `auth` can't be a boolean, but it can according to the @hapi/hapi source (https://github.com/hapijs/hapi/blob/v18.4.2/lib/route.js#L139)
|
||||
auth,
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(false);
|
||||
});
|
||||
it('handles required auth: { mode: "required" }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'required' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe(true);
|
||||
});
|
||||
|
||||
it('handles required auth: { mode: "optional" }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'optional' },
|
||||
},
|
||||
},
|
||||
describe('auth', () => {
|
||||
it('returns the correct value for isAuthenticated', () => {
|
||||
expect(
|
||||
CoreKibanaRequest.from({
|
||||
headers: {},
|
||||
path: '/',
|
||||
auth: { isAuthenticated: true },
|
||||
}).auth.isAuthenticated
|
||||
).toEqual(true);
|
||||
expect(
|
||||
CoreKibanaRequest.from({
|
||||
headers: {},
|
||||
path: '/',
|
||||
auth: { isAuthenticated: false },
|
||||
}).auth.isAuthenticated
|
||||
).toEqual(false);
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe('optional');
|
||||
});
|
||||
|
||||
it('handles required auth: { mode: "try" } as "optional"', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: 'try' },
|
||||
},
|
||||
},
|
||||
});
|
||||
const kibanaRequest = CoreKibanaRequest.from(request);
|
||||
|
||||
expect(kibanaRequest.route.options.authRequired).toBe('optional');
|
||||
});
|
||||
|
||||
it('throws on auth: strategy name', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { strategies: ['session'] },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => CoreKibanaRequest.from(request)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"unexpected authentication options: {\\"strategies\\":[\\"session\\"]} for route: /"`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws on auth: { mode: unexpected mode }', () => {
|
||||
const request = hapiMocks.createRequest({
|
||||
route: {
|
||||
settings: {
|
||||
auth: { mode: undefined },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => CoreKibanaRequest.from(request)).toThrowErrorMatchingInlineSnapshot(
|
||||
`"unexpected authentication options: {} for route: /"`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RouteSchema type inferring', () => {
|
||||
it('should work with config-schema', () => {
|
||||
const body = Buffer.from('body!');
|
||||
const request = {
|
||||
...hapiMocks.createRequest({
|
||||
params: { id: 'params' },
|
||||
query: { search: 'query' },
|
||||
}),
|
||||
payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
|
||||
} as any;
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, {
|
||||
params: schema.object({ id: schema.string() }),
|
||||
query: schema.object({ search: schema.string() }),
|
||||
body: schema.buffer(),
|
||||
});
|
||||
expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
|
||||
expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
|
||||
expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
|
||||
expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
|
||||
expect(kibanaRequest.body).toEqual(body);
|
||||
expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
|
||||
});
|
||||
|
||||
it('should work with ValidationFunction', () => {
|
||||
const body = Buffer.from('body!');
|
||||
const request = {
|
||||
...hapiMocks.createRequest({
|
||||
params: { id: 'params' },
|
||||
query: { search: 'query' },
|
||||
}),
|
||||
payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
|
||||
} as any;
|
||||
const kibanaRequest = CoreKibanaRequest.from(request, {
|
||||
params: schema.object({ id: schema.string() }),
|
||||
query: schema.object({ search: schema.string() }),
|
||||
body: (data, { ok, badRequest }) => {
|
||||
if (Buffer.isBuffer(data)) {
|
||||
return ok(data);
|
||||
} else {
|
||||
return badRequest('It should be a Buffer', []);
|
||||
}
|
||||
},
|
||||
});
|
||||
expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
|
||||
expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
|
||||
expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
|
||||
expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
|
||||
expect(kibanaRequest.body).toEqual(body);
|
||||
expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,6 +26,8 @@ import {
|
|||
KibanaRequestState,
|
||||
KibanaRouteOptions,
|
||||
KibanaRequestRouteOptions,
|
||||
RawRequest,
|
||||
FakeRawRequest,
|
||||
} from '@kbn/core-http-server';
|
||||
import { isSafeMethod } from './route';
|
||||
import { KibanaSocket } from './socket';
|
||||
|
@ -52,7 +54,7 @@ export class CoreKibanaRequest<
|
|||
* @internal
|
||||
*/
|
||||
public static from<P, Q, B>(
|
||||
req: Request,
|
||||
req: RawRequest,
|
||||
routeSchemas: RouteValidator<P, Q, B> | RouteValidatorFullConfig<P, Q, B> = {},
|
||||
withoutSecretHeaders: boolean = true
|
||||
) {
|
||||
|
@ -74,13 +76,16 @@ export class CoreKibanaRequest<
|
|||
* @internal
|
||||
*/
|
||||
private static validate<P, Q, B>(
|
||||
req: Request,
|
||||
req: RawRequest,
|
||||
routeValidator: RouteValidator<P, Q, B>
|
||||
): {
|
||||
params: P;
|
||||
query: Q;
|
||||
body: B;
|
||||
} {
|
||||
if (isFakeRawRequest(req)) {
|
||||
return { query: {} as Q, params: {} as P, body: {} as B };
|
||||
}
|
||||
const params = routeValidator.getParams(req.params, 'request params');
|
||||
const query = routeValidator.getQuery(req.query, 'request query');
|
||||
const body = routeValidator.getBody(req.payload, 'request body');
|
||||
|
@ -105,6 +110,8 @@ export class CoreKibanaRequest<
|
|||
public readonly events: KibanaRequestEvents;
|
||||
/** {@inheritDoc IKibanaRequest.auth} */
|
||||
public readonly auth: KibanaRequestAuth;
|
||||
/** {@inheritDoc IKibanaRequest.isFakeRequest} */
|
||||
public readonly isFakeRequest: boolean;
|
||||
/** {@inheritDoc IKibanaRequest.rewrittenUrl} */
|
||||
public readonly rewrittenUrl?: URL;
|
||||
|
||||
|
@ -112,7 +119,7 @@ export class CoreKibanaRequest<
|
|||
protected readonly [requestSymbol]: Request;
|
||||
|
||||
constructor(
|
||||
request: Request,
|
||||
request: RawRequest,
|
||||
public readonly params: Params,
|
||||
public readonly query: Query,
|
||||
public readonly body: Body,
|
||||
|
@ -128,9 +135,10 @@ export class CoreKibanaRequest<
|
|||
this.uuid = appState?.requestUuid ?? uuid.v4();
|
||||
this.rewrittenUrl = appState?.rewrittenUrl;
|
||||
|
||||
this.url = request.url;
|
||||
this.headers = deepFreeze({ ...request.headers });
|
||||
this.isSystemRequest = request.headers['kbn-system-request'] === 'true';
|
||||
this.url = request.url ?? new URL('https://fake-request/url');
|
||||
this.headers = isRealRawRequest(request) ? deepFreeze({ ...request.headers }) : request.headers;
|
||||
this.isSystemRequest = this.headers['kbn-system-request'] === 'true';
|
||||
this.isFakeRequest = isFakeRawRequest(request);
|
||||
|
||||
// prevent Symbol exposure via Object.getOwnPropertySymbols()
|
||||
Object.defineProperty(this, requestSymbol, {
|
||||
|
@ -139,17 +147,19 @@ export class CoreKibanaRequest<
|
|||
});
|
||||
|
||||
this.route = deepFreeze(this.getRouteInfo(request));
|
||||
this.socket = new KibanaSocket(request.raw.req.socket);
|
||||
this.socket = isRealRawRequest(request)
|
||||
? new KibanaSocket(request.raw.req.socket)
|
||||
: KibanaSocket.getFakeSocket();
|
||||
this.events = this.getEvents(request);
|
||||
|
||||
this.auth = {
|
||||
// missing in fakeRequests, so we cast to false
|
||||
isAuthenticated: Boolean(request.auth?.isAuthenticated),
|
||||
isAuthenticated: request.auth?.isAuthenticated ?? false,
|
||||
};
|
||||
}
|
||||
|
||||
private getEvents(request: Request): KibanaRequestEvents {
|
||||
if (!request.raw.res) {
|
||||
private getEvents(request: RawRequest): KibanaRequestEvents {
|
||||
if (isFakeRawRequest(request)) {
|
||||
return {
|
||||
aborted$: NEVER,
|
||||
completed$: NEVER,
|
||||
|
@ -166,18 +176,18 @@ export class CoreKibanaRequest<
|
|||
} as const;
|
||||
}
|
||||
|
||||
private getRouteInfo(request: Request): KibanaRequestRoute<Method> {
|
||||
const method = request.method as Method;
|
||||
private getRouteInfo(request: RawRequest): KibanaRequestRoute<Method> {
|
||||
const method = (request.method as Method) ?? 'get';
|
||||
const {
|
||||
parse,
|
||||
maxBytes,
|
||||
allow,
|
||||
output,
|
||||
timeout: payloadTimeout,
|
||||
} = request.route.settings.payload || {};
|
||||
} = request.route?.settings?.payload || {};
|
||||
|
||||
// the socket is undefined when using @hapi/shot, or when a "fake request" is used
|
||||
const socketTimeout = request.raw.req.socket?.timeout;
|
||||
const socketTimeout = isRealRawRequest(request) ? request.raw.req.socket?.timeout : undefined;
|
||||
const options = {
|
||||
authRequired: this.getAuthRequired(request),
|
||||
// TypeScript note: Casting to `RouterOptions` to fix the following error:
|
||||
|
@ -189,8 +199,9 @@ export class CoreKibanaRequest<
|
|||
// a mistake. In v19, the `RouteSettings` interface does have an `app`
|
||||
// property.
|
||||
xsrfRequired:
|
||||
((request.route.settings as RouteOptions).app as KibanaRouteOptions)?.xsrfRequired ?? true, // some places in LP call KibanaRequest.from(request) manually. remove fallback to true before v8
|
||||
tags: request.route.settings.tags || [],
|
||||
((request.route?.settings as RouteOptions)?.app as KibanaRouteOptions)?.xsrfRequired ??
|
||||
true, // some places in LP call KibanaRequest.from(request) manually. remove fallback to true before v8
|
||||
tags: request.route?.settings?.tags || [],
|
||||
timeout: {
|
||||
payload: payloadTimeout,
|
||||
idleSocket: socketTimeout === 0 ? undefined : socketTimeout,
|
||||
|
@ -206,13 +217,17 @@ export class CoreKibanaRequest<
|
|||
} as unknown as KibanaRequestRouteOptions<Method>; // TS does not understand this is OK so I'm enforced to do this enforced casting
|
||||
|
||||
return {
|
||||
path: request.path,
|
||||
path: request.path ?? '/',
|
||||
method,
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
private getAuthRequired(request: Request): boolean | 'optional' {
|
||||
private getAuthRequired(request: RawRequest): boolean | 'optional' {
|
||||
if (isFakeRawRequest(request)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const authOptions = request.route.settings.auth;
|
||||
if (typeof authOptions === 'object') {
|
||||
// 'try' is used in the legacy platform
|
||||
|
@ -230,7 +245,9 @@ export class CoreKibanaRequest<
|
|||
}
|
||||
|
||||
// @ts-expect-error According to @types/hapi__hapi, `route.settings` should be of type `RouteSettings`, but it seems that it's actually `RouteOptions` (https://github.com/hapijs/hapi/blob/v18.4.2/lib/route.js#L139)
|
||||
if (authOptions === false) return false;
|
||||
if (authOptions === false) {
|
||||
return false;
|
||||
}
|
||||
throw new Error(
|
||||
`unexpected authentication options: ${JSON.stringify(authOptions)} for route: ${
|
||||
this.url.pathname
|
||||
|
@ -254,20 +271,29 @@ export function isKibanaRequest(request: unknown): request is CoreKibanaRequest
|
|||
return request instanceof CoreKibanaRequest;
|
||||
}
|
||||
|
||||
function isRequest(request: any): request is Request {
|
||||
function isRealRawRequest(request: any): request is Request {
|
||||
try {
|
||||
return request.raw.req && typeof request.raw.req === 'object';
|
||||
return (
|
||||
request.raw.req &&
|
||||
typeof request.raw.req === 'object' &&
|
||||
request.raw.res &&
|
||||
typeof request.raw.res === 'object'
|
||||
);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isFakeRawRequest(request: RawRequest): request is FakeRawRequest {
|
||||
return !isRealRawRequest(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an incoming request either KibanaRequest or Hapi.Request
|
||||
* @internal
|
||||
*/
|
||||
export function isRealRequest(request: unknown): request is KibanaRequest | Request {
|
||||
return isKibanaRequest(request) || isRequest(request);
|
||||
return isKibanaRequest(request) || isRealRawRequest(request);
|
||||
}
|
||||
|
||||
function isCompleted(request: Request) {
|
||||
|
|
|
@ -152,4 +152,13 @@ describe('KibanaSocket', () => {
|
|||
expect(socket.authorizationError).toBe(authorizationError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFakeSocket', () => {
|
||||
it('returns a stub', async () => {
|
||||
const fakeSocket = KibanaSocket.getFakeSocket();
|
||||
expect(fakeSocket.getPeerCertificate()).toBeNull();
|
||||
expect(fakeSocket.getProtocol()).toBeNull();
|
||||
await expect(fakeSocket.renegotiate({})).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,6 +12,14 @@ import { promisify } from 'util';
|
|||
import type { IKibanaSocket } from '@kbn/core-http-server';
|
||||
|
||||
export class KibanaSocket implements IKibanaSocket {
|
||||
public static getFakeSocket(): IKibanaSocket {
|
||||
return {
|
||||
getPeerCertificate: () => null,
|
||||
getProtocol: () => null,
|
||||
renegotiate: () => Promise.resolve(),
|
||||
};
|
||||
}
|
||||
|
||||
public get authorized() {
|
||||
return this.socket instanceof TLSSocket ? this.socket.authorized : undefined;
|
||||
}
|
||||
|
|
|
@ -97,6 +97,8 @@ export type {
|
|||
KibanaSuccessResponseFactory,
|
||||
KibanaResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
RawRequest,
|
||||
FakeRawRequest,
|
||||
} from './src/router';
|
||||
export { validBodyOutput, RouteValidationError } from './src/router';
|
||||
|
||||
|
|
|
@ -69,3 +69,4 @@ export type {
|
|||
KibanaResponseFactory,
|
||||
LifecycleResponseFactory,
|
||||
} from './response_factory';
|
||||
export type { RawRequest, FakeRawRequest } from './raw_request';
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { URL } from 'url';
|
||||
import type { Request, RouteOptionsPayload } from '@hapi/hapi';
|
||||
import type { KibanaRouteOptions } from './request';
|
||||
import type { Headers } from './headers';
|
||||
|
||||
/**
|
||||
* Represents a fake raw request.
|
||||
* Can be used to instantiate a `KibanaRequest`.
|
||||
*/
|
||||
export interface FakeRawRequest {
|
||||
/** The headers associated with the request. */
|
||||
headers: Headers;
|
||||
/** The path of the request */
|
||||
path: string;
|
||||
method?: string;
|
||||
url?: URL;
|
||||
app?: Record<string, unknown>;
|
||||
auth?: {
|
||||
isAuthenticated?: boolean;
|
||||
};
|
||||
route?: {
|
||||
settings?: {
|
||||
tags?: string[];
|
||||
app?: KibanaRouteOptions;
|
||||
payload?: RouteOptionsPayload;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export type RawRequest = Request | FakeRawRequest;
|
|
@ -127,6 +127,12 @@ export interface KibanaRequest<
|
|||
*/
|
||||
readonly isSystemRequest: boolean;
|
||||
|
||||
/**
|
||||
* Allows identifying requests that were created using a {@link FakeRawRequest}
|
||||
* Even if the API facade is the same, fake requests have some stubbed functionalities.
|
||||
*/
|
||||
readonly isFakeRequest: boolean;
|
||||
|
||||
/**
|
||||
* The socket associated with this request.
|
||||
* See {@link IKibanaSocket}.
|
||||
|
|
|
@ -35,6 +35,10 @@ export const createRequestMock = (customization: DeepPartial<Request> = {}): Req
|
|||
url: path,
|
||||
socket: {},
|
||||
},
|
||||
res: {
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
} as {},
|
||||
},
|
||||
},
|
||||
customization
|
||||
|
|
|
@ -194,6 +194,8 @@ export type {
|
|||
HttpServerInfo,
|
||||
HttpServicePreboot,
|
||||
HttpServiceStart,
|
||||
RawRequest,
|
||||
FakeRawRequest,
|
||||
} from '@kbn/core-http-server';
|
||||
export type { IExternalUrlPolicy } from '@kbn/core-http-common';
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import { pick } from 'lodash';
|
||||
import type { Request } from '@hapi/hapi';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import { map, fromNullable, getOrElse } from 'fp-ts/lib/Option';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/server';
|
||||
|
@ -18,6 +17,8 @@ import {
|
|||
SavedObjectReference,
|
||||
IBasePath,
|
||||
SavedObject,
|
||||
Headers,
|
||||
FakeRawRequest,
|
||||
} from '@kbn/core/server';
|
||||
import { RunContext } from '@kbn/task-manager-plugin/server';
|
||||
import { EncryptedSavedObjectsClient } from '@kbn/encrypted-saved-objects-plugin/server';
|
||||
|
@ -221,26 +222,19 @@ export class TaskRunnerFactory {
|
|||
}
|
||||
|
||||
function getFakeRequest(apiKey?: string) {
|
||||
const requestHeaders: Record<string, string> = {};
|
||||
const requestHeaders: Headers = {};
|
||||
if (apiKey) {
|
||||
requestHeaders.authorization = `ApiKey ${apiKey}`;
|
||||
}
|
||||
|
||||
// Since we're using API keys and accessing elasticsearch can only be done
|
||||
// via a request, we're faking one with the proper authorization headers.
|
||||
const fakeRequest = CoreKibanaRequest.from({
|
||||
const fakeRawRequest: FakeRawRequest = {
|
||||
headers: requestHeaders,
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: {
|
||||
href: '/',
|
||||
},
|
||||
raw: {
|
||||
req: {
|
||||
url: '/',
|
||||
},
|
||||
},
|
||||
} as unknown as Request);
|
||||
};
|
||||
|
||||
// Since we're using API keys and accessing elasticsearch can only be done
|
||||
// via a request, we're faking one with the proper authorization headers.
|
||||
const fakeRequest = CoreKibanaRequest.from(fakeRawRequest);
|
||||
|
||||
return fakeRequest;
|
||||
}
|
||||
|
|
|
@ -234,17 +234,6 @@ describe('rule_loader', () => {
|
|||
"authorization": "ApiKey rule-apikey",
|
||||
},
|
||||
"path": "/",
|
||||
"raw": Object {
|
||||
"req": Object {
|
||||
"url": "/",
|
||||
},
|
||||
},
|
||||
"route": Object {
|
||||
"settings": Object {},
|
||||
},
|
||||
"url": Object {
|
||||
"href": "/",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
@ -264,17 +253,6 @@ describe('rule_loader', () => {
|
|||
"authorization": "ApiKey rule-apikey",
|
||||
},
|
||||
"path": "/",
|
||||
"raw": Object {
|
||||
"req": Object {
|
||||
"url": "/",
|
||||
},
|
||||
},
|
||||
"route": Object {
|
||||
"settings": Object {},
|
||||
},
|
||||
"url": Object {
|
||||
"href": "/",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
@ -293,17 +271,6 @@ describe('rule_loader', () => {
|
|||
Object {
|
||||
"headers": Object {},
|
||||
"path": "/",
|
||||
"raw": Object {
|
||||
"req": Object {
|
||||
"url": "/",
|
||||
},
|
||||
},
|
||||
"route": Object {
|
||||
"settings": Object {},
|
||||
},
|
||||
"url": Object {
|
||||
"href": "/",
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
*/
|
||||
|
||||
import { PublicMethodsOf } from '@kbn/utility-types';
|
||||
import type { Request } from '@hapi/hapi';
|
||||
import { addSpaceIdToPath } from '@kbn/spaces-plugin/server';
|
||||
import { CoreKibanaRequest } from '@kbn/core/server';
|
||||
import { CoreKibanaRequest, FakeRawRequest, Headers } from '@kbn/core/server';
|
||||
import { TaskRunnerContext } from './task_runner_factory';
|
||||
import { ErrorWithReason, validateRuleTypeParams } from '../lib';
|
||||
import {
|
||||
|
@ -134,7 +133,7 @@ export function getFakeKibanaRequest(
|
|||
spaceId: string,
|
||||
apiKey: RawRule['apiKey']
|
||||
) {
|
||||
const requestHeaders: Record<string, string> = {};
|
||||
const requestHeaders: Headers = {};
|
||||
|
||||
if (apiKey) {
|
||||
requestHeaders.authorization = `ApiKey ${apiKey}`;
|
||||
|
@ -142,20 +141,12 @@ export function getFakeKibanaRequest(
|
|||
|
||||
const path = addSpaceIdToPath('/', spaceId);
|
||||
|
||||
const fakeRequest = CoreKibanaRequest.from({
|
||||
const fakeRawRequest: FakeRawRequest = {
|
||||
headers: requestHeaders,
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: {
|
||||
href: '/',
|
||||
},
|
||||
raw: {
|
||||
req: {
|
||||
url: '/',
|
||||
},
|
||||
},
|
||||
} as unknown as Request);
|
||||
};
|
||||
|
||||
const fakeRequest = CoreKibanaRequest.from(fakeRawRequest);
|
||||
context.basePathService.set(fakeRequest, path);
|
||||
|
||||
return fakeRequest;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import Hapi from '@hapi/hapi';
|
||||
import type {
|
||||
DocLinksServiceSetup,
|
||||
IBasePath,
|
||||
|
@ -17,8 +16,11 @@ import type {
|
|||
SavedObjectsServiceStart,
|
||||
StatusServiceSetup,
|
||||
UiSettingsServiceStart,
|
||||
KibanaRequest,
|
||||
FakeRawRequest,
|
||||
Headers,
|
||||
} from '@kbn/core/server';
|
||||
import { KibanaRequest, CoreKibanaRequest, ServiceStatusLevels } from '@kbn/core/server';
|
||||
import { CoreKibanaRequest, ServiceStatusLevels } from '@kbn/core/server';
|
||||
import type { PluginStart as DataPluginStart } from '@kbn/data-plugin/server';
|
||||
import type { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/server';
|
||||
|
@ -323,14 +325,16 @@ export class ReportingCore {
|
|||
}
|
||||
}
|
||||
|
||||
public getFakeRequest(baseRequest: object, spaceId: string | undefined, logger = this.logger) {
|
||||
const fakeRequest = CoreKibanaRequest.from({
|
||||
public getFakeRequest(
|
||||
headers: Headers,
|
||||
spaceId: string | undefined,
|
||||
logger = this.logger
|
||||
): KibanaRequest {
|
||||
const rawRequest: FakeRawRequest = {
|
||||
headers,
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: { href: '/' },
|
||||
raw: { req: { url: '/' } },
|
||||
...baseRequest,
|
||||
} as Hapi.Request);
|
||||
};
|
||||
const fakeRequest = CoreKibanaRequest.from(rawRequest);
|
||||
|
||||
const spacesService = this.getPluginSetupDeps().spaces?.spacesService;
|
||||
if (spacesService) {
|
||||
|
|
|
@ -15,7 +15,7 @@ export const getCustomLogo = async (
|
|||
spaceId: string | undefined,
|
||||
logger: Logger
|
||||
) => {
|
||||
const fakeRequest = reporting.getFakeRequest({ headers }, spaceId, logger);
|
||||
const fakeRequest = reporting.getFakeRequest(headers, spaceId, logger);
|
||||
const uiSettingsClient = await reporting.getUiSettingsClient(fakeRequest, logger);
|
||||
const logo: string = await uiSettingsClient.get(UI_SETTINGS_CUSTOM_PDF_LOGO);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export const runTaskFnFactory: RunTaskFnFactory<RunTaskFn<TaskPayloadCSV>> = (
|
|||
return async function runTask(jobId, job, cancellationToken, stream) {
|
||||
const logger = parentLogger.get(`execute-job:${jobId}`);
|
||||
const headers = await decryptJobHeaders(encryptionKey, job.headers, logger);
|
||||
const fakeRequest = reporting.getFakeRequest({ headers }, job.spaceId, logger);
|
||||
const fakeRequest = reporting.getFakeRequest(headers, job.spaceId, logger);
|
||||
const uiSettings = await reporting.getUiSettingsClient(fakeRequest, logger);
|
||||
const dataPluginStart = await reporting.getDataService();
|
||||
const fieldFormatsRegistry = await getFieldFormats().fieldFormatServiceFactory(uiSettings);
|
||||
|
|
|
@ -5,11 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Request } from '@hapi/hapi';
|
||||
|
||||
import type {
|
||||
Capabilities,
|
||||
CapabilitiesStart,
|
||||
FakeRawRequest,
|
||||
IBasePath,
|
||||
IClusterClient,
|
||||
KibanaRequest,
|
||||
|
@ -166,7 +165,7 @@ export class AnonymousAccessService {
|
|||
* anonymous service account credentials.
|
||||
*/
|
||||
private createFakeAnonymousRequest({ authenticateRequest }: { authenticateRequest: boolean }) {
|
||||
return CoreKibanaRequest.from({
|
||||
const fakeRawRequest: FakeRawRequest = {
|
||||
headers:
|
||||
authenticateRequest && this.httpAuthorizationHeader
|
||||
? { authorization: this.httpAuthorizationHeader.toString() }
|
||||
|
@ -175,9 +174,7 @@ export class AnonymousAccessService {
|
|||
// it should perform a privileges check or automatically disable all capabilities.
|
||||
auth: { isAuthenticated: authenticateRequest },
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: { href: '/' },
|
||||
raw: { req: { url: '/' } },
|
||||
} as unknown as Request);
|
||||
};
|
||||
return CoreKibanaRequest.from(fakeRawRequest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,3 +18,13 @@ jest.mock('../../lib', () => {
|
|||
validateKibanaPrivileges: mockValidateKibanaPrivileges,
|
||||
};
|
||||
});
|
||||
|
||||
export const mockGetFakeKibanaRequest = jest.fn();
|
||||
|
||||
jest.mock('./fake_kibana_request', () => {
|
||||
const actual = jest.requireActual('./fake_kibana_request');
|
||||
return {
|
||||
...actual,
|
||||
getFakeKibanaRequest: mockGetFakeKibanaRequest,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
// eslint-disable-next-line import/order
|
||||
import { mockValidateKibanaPrivileges } from './api_keys.test.mock';
|
||||
import { mockGetFakeKibanaRequest, mockValidateKibanaPrivileges } from './api_keys.test.mock';
|
||||
|
||||
import {
|
||||
elasticsearchServiceMock,
|
||||
|
@ -19,7 +19,6 @@ import { ALL_SPACES_ID } from '../../../common/constants';
|
|||
import type { SecurityLicense } from '../../../common/licensing';
|
||||
import { licenseMock } from '../../../common/licensing/index.mock';
|
||||
import { APIKeys } from './api_keys';
|
||||
import { getFakeKibanaRequest } from './fake_kibana_request';
|
||||
|
||||
const encodeToBase64 = (str: string) => Buffer.from(str).toString('base64');
|
||||
|
||||
|
@ -34,6 +33,7 @@ describe('API Keys', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
mockValidateKibanaPrivileges.mockReset().mockReturnValue({ validationErrors: [] });
|
||||
mockGetFakeKibanaRequest.mockReset().mockReturnValue(httpServerMock.createKibanaRequest());
|
||||
|
||||
mockClusterClient = elasticsearchServiceMock.createClusterClient();
|
||||
mockScopedClusterClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
|
@ -519,6 +519,8 @@ describe('API Keys', () => {
|
|||
});
|
||||
|
||||
it('calls callCluster with proper parameters', async () => {
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
mockGetFakeKibanaRequest.mockReturnValue(request);
|
||||
mockLicense.isEnabled.mockReturnValue(true);
|
||||
const params = {
|
||||
id: '123',
|
||||
|
@ -527,25 +529,22 @@ describe('API Keys', () => {
|
|||
const result = await apiKeys.validate(params);
|
||||
expect(result).toEqual(true);
|
||||
|
||||
const fakeRequest = getFakeKibanaRequest(params);
|
||||
|
||||
const { id, uuid, ...restFake } = fakeRequest;
|
||||
|
||||
expect(mockClusterClient.asScoped).toHaveBeenCalledWith(expect.objectContaining(restFake));
|
||||
expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request);
|
||||
expect(
|
||||
mockClusterClient.asScoped().asCurrentUser.security.authenticate
|
||||
).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('returns false if cannot authenticate with the API key', async () => {
|
||||
const request = httpServerMock.createKibanaRequest();
|
||||
mockGetFakeKibanaRequest.mockReturnValue(request);
|
||||
mockLicense.isEnabled.mockReturnValue(true);
|
||||
mockScopedClusterClient.asCurrentUser.security.authenticate.mockRejectedValue(new Error());
|
||||
const params = { id: '123', api_key: 'abc123' };
|
||||
|
||||
await expect(apiKeys.validate(params)).resolves.toEqual(false);
|
||||
|
||||
const { id, uuid, ...restFake } = getFakeKibanaRequest(params);
|
||||
expect(mockClusterClient.asScoped).toHaveBeenCalledWith(expect.objectContaining(restFake));
|
||||
expect(mockClusterClient.asScoped).toHaveBeenCalledWith(request);
|
||||
expect(
|
||||
mockClusterClient.asScoped().asCurrentUser.security.authenticate
|
||||
).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -5,28 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { Request } from '@hapi/hapi';
|
||||
|
||||
import type { FakeRawRequest, Headers } from '@kbn/core/server';
|
||||
import { CoreKibanaRequest } from '@kbn/core/server';
|
||||
|
||||
export function getFakeKibanaRequest(apiKey: { id: string; api_key: string }) {
|
||||
const requestHeaders: Record<string, string> = {};
|
||||
const requestHeaders: Headers = {};
|
||||
|
||||
requestHeaders.authorization = `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString(
|
||||
'base64'
|
||||
)}`;
|
||||
|
||||
return CoreKibanaRequest.from({
|
||||
const fakeRawRequest: FakeRawRequest = {
|
||||
headers: requestHeaders,
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: {
|
||||
href: '/',
|
||||
},
|
||||
raw: {
|
||||
req: {
|
||||
url: '/',
|
||||
},
|
||||
},
|
||||
} as unknown as Request);
|
||||
};
|
||||
|
||||
return CoreKibanaRequest.from(fakeRawRequest);
|
||||
}
|
||||
|
|
|
@ -5,27 +5,20 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Request } from '@hapi/hapi';
|
||||
import type { FakeRawRequest, Headers } from '@kbn/core/server';
|
||||
import { CoreKibanaRequest } from '@kbn/core/server';
|
||||
|
||||
export function getFakeKibanaRequest(apiKey: { id: string; api_key: string }) {
|
||||
const requestHeaders: Record<string, string> = {};
|
||||
const requestHeaders: Headers = {};
|
||||
|
||||
requestHeaders.authorization = `ApiKey ${Buffer.from(`${apiKey.id}:${apiKey.api_key}`).toString(
|
||||
'base64'
|
||||
)}`;
|
||||
|
||||
return CoreKibanaRequest.from({
|
||||
const fakeRawRequest: FakeRawRequest = {
|
||||
headers: requestHeaders,
|
||||
path: '/',
|
||||
route: { settings: {} },
|
||||
url: {
|
||||
href: '/',
|
||||
},
|
||||
raw: {
|
||||
req: {
|
||||
url: '/',
|
||||
},
|
||||
},
|
||||
} as unknown as Request);
|
||||
};
|
||||
|
||||
return CoreKibanaRequest.from(fakeRawRequest);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue