mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Reporting] server side code clean up (#106940)
* clean up the enqueue job function
* clean up the screenshots observable
* clean up authorized user pre routing
* clean up get_user
* fix download job response handlers
* clean up jobs query factory repetition
* clean up setup deps made available from plugin.ts
* update test for screenshots observable
* Revert "clean up setup deps made available from plugin.ts"
This reverts commit 91de680ebf
.
* revert renames
* minor rename
* fix test after rename
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
c0395c9ef6
commit
e4e22ab928
19 changed files with 375 additions and 439 deletions
|
@ -29,7 +29,6 @@ import { ReportingConfig, ReportingSetup } from './';
|
|||
import { HeadlessChromiumDriverFactory } from './browsers/chromium/driver_factory';
|
||||
import { ReportingConfigType } from './config';
|
||||
import { checkLicense, getExportTypesRegistry, LevelLogger } from './lib';
|
||||
import { screenshotsObservableFactory, ScreenshotsObservableFn } from './lib/screenshots';
|
||||
import { ReportingStore } from './lib/store';
|
||||
import { ExecuteReportTask, MonitorReportsTask, ReportTaskParams } from './lib/tasks';
|
||||
import { ReportingPluginRouter } from './types';
|
||||
|
@ -237,12 +236,6 @@ export class ReportingCore {
|
|||
.toPromise();
|
||||
}
|
||||
|
||||
public async getScreenshotsObservable(): Promise<ScreenshotsObservableFn> {
|
||||
const config = this.getConfig();
|
||||
const { browserDriverFactory } = await this.getPluginStartDeps();
|
||||
return screenshotsObservableFactory(config.get('capture'), browserDriverFactory);
|
||||
}
|
||||
|
||||
public getEnableScreenshotMode() {
|
||||
const { screenshotMode } = this.getPluginSetupDeps();
|
||||
return screenshotMode.setScreenshotModeEnabled;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { finalize, map, tap } from 'rxjs/operators';
|
|||
import { ReportingCore } from '../../../';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { LayoutParams, PreserveLayout } from '../../../lib/layouts';
|
||||
import { ScreenshotResults } from '../../../lib/screenshots';
|
||||
import { getScreenshots$, ScreenshotResults } from '../../../lib/screenshots';
|
||||
import { ConditionalHeaders } from '../../common';
|
||||
|
||||
function getBase64DecodedSize(value: string) {
|
||||
|
@ -24,7 +24,9 @@ function getBase64DecodedSize(value: string) {
|
|||
}
|
||||
|
||||
export async function generatePngObservableFactory(reporting: ReportingCore) {
|
||||
const getScreenshots = await reporting.getScreenshotsObservable();
|
||||
const config = reporting.getConfig();
|
||||
const captureConfig = config.get('capture');
|
||||
const { browserDriverFactory } = await reporting.getPluginStartDeps();
|
||||
|
||||
return function generatePngObservable(
|
||||
logger: LevelLogger,
|
||||
|
@ -43,7 +45,7 @@ export async function generatePngObservableFactory(reporting: ReportingCore) {
|
|||
|
||||
const apmScreenshots = apmTrans?.startSpan('screenshots_pipeline', 'setup');
|
||||
let apmBuffer: typeof apm.currentSpan;
|
||||
const screenshots$ = getScreenshots({
|
||||
const screenshots$ = getScreenshots$(captureConfig, browserDriverFactory, {
|
||||
logger,
|
||||
urls: [url],
|
||||
conditionalHeaders,
|
||||
|
|
|
@ -11,7 +11,7 @@ import { mergeMap } from 'rxjs/operators';
|
|||
import { ReportingCore } from '../../../';
|
||||
import { LevelLogger } from '../../../lib';
|
||||
import { createLayout, LayoutParams } from '../../../lib/layouts';
|
||||
import { ScreenshotResults } from '../../../lib/screenshots';
|
||||
import { getScreenshots$, ScreenshotResults } from '../../../lib/screenshots';
|
||||
import { ConditionalHeaders } from '../../common';
|
||||
import { PdfMaker } from './pdf';
|
||||
import { getTracker } from './tracker';
|
||||
|
@ -29,7 +29,7 @@ const getTimeRange = (urlScreenshots: ScreenshotResults[]) => {
|
|||
export async function generatePdfObservableFactory(reporting: ReportingCore) {
|
||||
const config = reporting.getConfig();
|
||||
const captureConfig = config.get('capture');
|
||||
const getScreenshots = await reporting.getScreenshotsObservable();
|
||||
const { browserDriverFactory } = await reporting.getPluginStartDeps();
|
||||
|
||||
return function generatePdfObservable(
|
||||
logger: LevelLogger,
|
||||
|
@ -48,7 +48,7 @@ export async function generatePdfObservableFactory(reporting: ReportingCore) {
|
|||
tracker.endLayout();
|
||||
|
||||
tracker.startScreenshots();
|
||||
const screenshots$ = getScreenshots({
|
||||
const screenshots$ = getScreenshots$(captureConfig, browserDriverFactory, {
|
||||
logger,
|
||||
urls,
|
||||
conditionalHeaders,
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
} from '../test_helpers';
|
||||
import { ReportingRequestHandlerContext } from '../types';
|
||||
import { ExportTypesRegistry, ReportingStore } from './';
|
||||
import { enqueueJobFactory } from './enqueue_job';
|
||||
import { enqueueJob } from './enqueue_job';
|
||||
import { Report } from './store';
|
||||
|
||||
describe('Enqueue Job', () => {
|
||||
|
@ -72,13 +72,14 @@ describe('Enqueue Job', () => {
|
|||
});
|
||||
|
||||
it('returns a Report object', async () => {
|
||||
const enqueueJob = enqueueJobFactory(mockReporting, logger);
|
||||
const report = await enqueueJob(
|
||||
mockReporting,
|
||||
({} as unknown) as KibanaRequest,
|
||||
({} as unknown) as ReportingRequestHandlerContext,
|
||||
false,
|
||||
'printablePdf',
|
||||
mockBaseParams,
|
||||
false,
|
||||
({} as unknown) as ReportingRequestHandlerContext,
|
||||
({} as unknown) as KibanaRequest
|
||||
logger
|
||||
);
|
||||
|
||||
const { _id, created_at: _created_at, ...snapObj } = report;
|
||||
|
@ -117,14 +118,15 @@ describe('Enqueue Job', () => {
|
|||
});
|
||||
|
||||
it('provides a default kibana version field for older POST URLs', async () => {
|
||||
const enqueueJob = enqueueJobFactory(mockReporting, logger);
|
||||
mockBaseParams.version = undefined;
|
||||
const report = await enqueueJob(
|
||||
mockReporting,
|
||||
({} as unknown) as KibanaRequest,
|
||||
({} as unknown) as ReportingRequestHandlerContext,
|
||||
false,
|
||||
'printablePdf',
|
||||
mockBaseParams,
|
||||
false,
|
||||
({} as unknown) as ReportingRequestHandlerContext,
|
||||
({} as unknown) as KibanaRequest
|
||||
logger
|
||||
);
|
||||
|
||||
const { _id, created_at: _created_at, ...snapObj } = report;
|
||||
|
|
|
@ -12,64 +12,53 @@ import { BaseParams, ReportingUser } from '../types';
|
|||
import { checkParamsVersion, LevelLogger } from './';
|
||||
import { Report } from './store';
|
||||
|
||||
export type EnqueueJobFn = (
|
||||
export async function enqueueJob(
|
||||
reporting: ReportingCore,
|
||||
request: KibanaRequest,
|
||||
context: ReportingRequestHandlerContext,
|
||||
user: ReportingUser,
|
||||
exportTypeId: string,
|
||||
jobParams: BaseParams,
|
||||
user: ReportingUser,
|
||||
context: ReportingRequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
) => Promise<Report>;
|
||||
|
||||
export function enqueueJobFactory(
|
||||
reporting: ReportingCore,
|
||||
parentLogger: LevelLogger
|
||||
): EnqueueJobFn {
|
||||
): Promise<Report> {
|
||||
const logger = parentLogger.clone(['createJob']);
|
||||
return async function enqueueJob(
|
||||
exportTypeId: string,
|
||||
jobParams: BaseParams,
|
||||
user: ReportingUser,
|
||||
context: ReportingRequestHandlerContext,
|
||||
request: KibanaRequest
|
||||
) {
|
||||
const exportType = reporting.getExportTypesRegistry().getById(exportTypeId);
|
||||
const exportType = reporting.getExportTypesRegistry().getById(exportTypeId);
|
||||
|
||||
if (exportType == null) {
|
||||
throw new Error(`Export type ${exportTypeId} does not exist in the registry!`);
|
||||
}
|
||||
if (exportType == null) {
|
||||
throw new Error(`Export type ${exportTypeId} does not exist in the registry!`);
|
||||
}
|
||||
|
||||
if (!exportType.createJobFnFactory) {
|
||||
throw new Error(`Export type ${exportTypeId} is not an async job type!`);
|
||||
}
|
||||
if (!exportType.createJobFnFactory) {
|
||||
throw new Error(`Export type ${exportTypeId} is not an async job type!`);
|
||||
}
|
||||
|
||||
const [createJob, store] = await Promise.all([
|
||||
exportType.createJobFnFactory(reporting, logger.clone([exportType.id])),
|
||||
reporting.getStore(),
|
||||
]);
|
||||
const [createJob, store] = await Promise.all([
|
||||
exportType.createJobFnFactory(reporting, logger.clone([exportType.id])),
|
||||
reporting.getStore(),
|
||||
]);
|
||||
|
||||
jobParams.version = checkParamsVersion(jobParams, logger);
|
||||
const job = await createJob!(jobParams, context, request);
|
||||
jobParams.version = checkParamsVersion(jobParams, logger);
|
||||
const job = await createJob!(jobParams, context, request);
|
||||
|
||||
// 1. Add the report to ReportingStore to show as pending
|
||||
const report = await store.addReport(
|
||||
new Report({
|
||||
jobtype: exportType.jobType,
|
||||
created_by: user ? user.username : false,
|
||||
payload: job,
|
||||
meta: {
|
||||
objectType: jobParams.objectType,
|
||||
layout: jobParams.layout?.id,
|
||||
},
|
||||
})
|
||||
);
|
||||
logger.debug(`Successfully stored pending job: ${report._index}/${report._id}`);
|
||||
// 1. Add the report to ReportingStore to show as pending
|
||||
const report = await store.addReport(
|
||||
new Report({
|
||||
jobtype: exportType.jobType,
|
||||
created_by: user ? user.username : false,
|
||||
payload: job,
|
||||
meta: {
|
||||
objectType: jobParams.objectType,
|
||||
layout: jobParams.layout?.id,
|
||||
},
|
||||
})
|
||||
);
|
||||
logger.debug(`Successfully stored pending job: ${report._index}/${report._id}`);
|
||||
|
||||
// 2. Schedule the report with Task Manager
|
||||
const task = await reporting.scheduleTask(report.toReportTaskJSON());
|
||||
logger.info(
|
||||
`Scheduled ${exportType.name} reporting task. Task ID: task:${task.id}. Report ID: ${report._id}`
|
||||
);
|
||||
// 2. Schedule the report with Task Manager
|
||||
const task = await reporting.scheduleTask(report.toReportTaskJSON());
|
||||
logger.info(
|
||||
`Scheduled ${exportType.name} reporting task. Task ID: task:${task.id}. Report ID: ${report._id}`
|
||||
);
|
||||
|
||||
return report;
|
||||
};
|
||||
return report;
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import * as Rx from 'rxjs';
|
||||
import { LevelLogger } from '../';
|
||||
import { ConditionalHeaders } from '../../export_types/common';
|
||||
import { LayoutInstance } from '../layouts';
|
||||
|
||||
export { screenshotsObservableFactory } from './observable';
|
||||
export { getScreenshots$ } from './observable';
|
||||
|
||||
export interface ScreenshotObservableOpts {
|
||||
logger: LevelLogger;
|
||||
|
@ -55,11 +54,3 @@ export interface ScreenshotResults {
|
|||
error?: Error;
|
||||
elementsPositionAndAttributes?: ElementsPositionAndAttribute[]; // NOTE: for testing
|
||||
}
|
||||
|
||||
export type ScreenshotsObservableFn = ({
|
||||
logger,
|
||||
urls,
|
||||
conditionalHeaders,
|
||||
layout,
|
||||
browserTimezone,
|
||||
}: ScreenshotObservableOpts) => Rx.Observable<ScreenshotResults[]>;
|
||||
|
|
|
@ -32,7 +32,7 @@ import {
|
|||
} from '../../test_helpers';
|
||||
import { ElementsPositionAndAttribute } from './';
|
||||
import * as contexts from './constants';
|
||||
import { screenshotsObservableFactory } from './observable';
|
||||
import { getScreenshots$ } from './';
|
||||
|
||||
/*
|
||||
* Mocks
|
||||
|
@ -67,8 +67,7 @@ describe('Screenshot Observable Pipeline', () => {
|
|||
});
|
||||
|
||||
it('pipelines a single url into screenshot and timeRange', async () => {
|
||||
const getScreenshots$ = screenshotsObservableFactory(captureConfig, mockBrowserDriverFactory);
|
||||
const result = await getScreenshots$({
|
||||
const result = await getScreenshots$(captureConfig, mockBrowserDriverFactory, {
|
||||
logger,
|
||||
urls: ['/welcome/home/start/index.htm'],
|
||||
conditionalHeaders: {} as ConditionalHeaders,
|
||||
|
@ -128,8 +127,7 @@ describe('Screenshot Observable Pipeline', () => {
|
|||
});
|
||||
|
||||
// test
|
||||
const getScreenshots$ = screenshotsObservableFactory(captureConfig, mockBrowserDriverFactory);
|
||||
const result = await getScreenshots$({
|
||||
const result = await getScreenshots$(captureConfig, mockBrowserDriverFactory, {
|
||||
logger,
|
||||
urls: ['/welcome/home/start/index2.htm', '/welcome/home/start/index.php3?page=./home.php'],
|
||||
conditionalHeaders: {} as ConditionalHeaders,
|
||||
|
@ -227,9 +225,8 @@ describe('Screenshot Observable Pipeline', () => {
|
|||
});
|
||||
|
||||
// test
|
||||
const getScreenshots$ = screenshotsObservableFactory(captureConfig, mockBrowserDriverFactory);
|
||||
const getScreenshot = async () => {
|
||||
return await getScreenshots$({
|
||||
return await getScreenshots$(captureConfig, mockBrowserDriverFactory, {
|
||||
logger,
|
||||
urls: [
|
||||
'/welcome/home/start/index2.htm',
|
||||
|
@ -322,9 +319,8 @@ describe('Screenshot Observable Pipeline', () => {
|
|||
});
|
||||
|
||||
// test
|
||||
const getScreenshots$ = screenshotsObservableFactory(captureConfig, mockBrowserDriverFactory);
|
||||
const getScreenshot = async () => {
|
||||
return await getScreenshots$({
|
||||
return await getScreenshots$(captureConfig, mockBrowserDriverFactory, {
|
||||
logger,
|
||||
urls: ['/welcome/home/start/index.php3?page=./home.php3'],
|
||||
conditionalHeaders: {} as ConditionalHeaders,
|
||||
|
@ -354,50 +350,46 @@ describe('Screenshot Observable Pipeline', () => {
|
|||
});
|
||||
mockLayout.getViewport = () => null;
|
||||
|
||||
// test
|
||||
const getScreenshots$ = screenshotsObservableFactory(captureConfig, mockBrowserDriverFactory);
|
||||
const getScreenshot = async () => {
|
||||
return await getScreenshots$({
|
||||
logger,
|
||||
urls: ['/welcome/home/start/index.php3?page=./home.php3'],
|
||||
conditionalHeaders: {} as ConditionalHeaders,
|
||||
layout: mockLayout,
|
||||
browserTimezone: 'UTC',
|
||||
}).toPromise();
|
||||
};
|
||||
const screenshots = await getScreenshots$(captureConfig, mockBrowserDriverFactory, {
|
||||
logger,
|
||||
urls: ['/welcome/home/start/index.php3?page=./home.php3'],
|
||||
conditionalHeaders: {} as ConditionalHeaders,
|
||||
layout: mockLayout,
|
||||
browserTimezone: 'UTC',
|
||||
}).toPromise();
|
||||
|
||||
await expect(getScreenshot()).resolves.toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"elementsPositionAndAttributes": Array [
|
||||
Object {
|
||||
"attributes": Object {},
|
||||
"position": Object {
|
||||
"boundingClientRect": Object {
|
||||
"height": 1200,
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": 1800,
|
||||
},
|
||||
"scroll": Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"error": undefined,
|
||||
"screenshots": Array [
|
||||
Object {
|
||||
"base64EncodedData": "allyourBase64",
|
||||
"description": undefined,
|
||||
"title": undefined,
|
||||
},
|
||||
],
|
||||
"timeRange": undefined,
|
||||
expect(screenshots).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"elementsPositionAndAttributes": Array [
|
||||
Object {
|
||||
"attributes": Object {},
|
||||
"position": Object {
|
||||
"boundingClientRect": Object {
|
||||
"height": 1200,
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": 1800,
|
||||
},
|
||||
"scroll": Object {
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
},
|
||||
},
|
||||
]
|
||||
`);
|
||||
},
|
||||
],
|
||||
"error": undefined,
|
||||
"screenshots": Array [
|
||||
Object {
|
||||
"base64EncodedData": "allyourBase64",
|
||||
"description": undefined,
|
||||
"title": undefined,
|
||||
},
|
||||
],
|
||||
"timeRange": undefined,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,12 +10,7 @@ import * as Rx from 'rxjs';
|
|||
import { catchError, concatMap, first, mergeMap, take, takeUntil, toArray } from 'rxjs/operators';
|
||||
import { HeadlessChromiumDriverFactory } from '../../browsers';
|
||||
import { CaptureConfig } from '../../types';
|
||||
import {
|
||||
ElementsPositionAndAttribute,
|
||||
ScreenshotObservableOpts,
|
||||
ScreenshotResults,
|
||||
ScreenshotsObservableFn,
|
||||
} from './';
|
||||
import { ElementsPositionAndAttribute, ScreenshotObservableOpts, ScreenshotResults } from './';
|
||||
import { checkPageIsOpen } from './check_browser_open';
|
||||
import { DEFAULT_PAGELOAD_SELECTOR } from './constants';
|
||||
import { getElementPositionAndAttributes } from './get_element_position_data';
|
||||
|
@ -36,117 +31,110 @@ interface ScreenSetupData {
|
|||
error?: Error;
|
||||
}
|
||||
|
||||
export function screenshotsObservableFactory(
|
||||
export function getScreenshots$(
|
||||
captureConfig: CaptureConfig,
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory
|
||||
): ScreenshotsObservableFn {
|
||||
return function screenshotsObservable({
|
||||
logger,
|
||||
urls,
|
||||
conditionalHeaders,
|
||||
layout,
|
||||
browserTimezone,
|
||||
}: ScreenshotObservableOpts): Rx.Observable<ScreenshotResults[]> {
|
||||
const apmTrans = apm.startTransaction(`reporting screenshot pipeline`, 'reporting');
|
||||
browserDriverFactory: HeadlessChromiumDriverFactory,
|
||||
{ logger, urls, conditionalHeaders, layout, browserTimezone }: ScreenshotObservableOpts
|
||||
): Rx.Observable<ScreenshotResults[]> {
|
||||
const apmTrans = apm.startTransaction(`reporting screenshot pipeline`, 'reporting');
|
||||
|
||||
const apmCreatePage = apmTrans?.startSpan('create_page', 'wait');
|
||||
const create$ = browserDriverFactory.createPage(
|
||||
{ viewport: layout.getBrowserViewport(), browserTimezone },
|
||||
logger
|
||||
);
|
||||
const apmCreatePage = apmTrans?.startSpan('create_page', 'wait');
|
||||
const create$ = browserDriverFactory.createPage(
|
||||
{ viewport: layout.getBrowserViewport(), browserTimezone },
|
||||
logger
|
||||
);
|
||||
|
||||
return create$.pipe(
|
||||
mergeMap(({ driver, exit$ }) => {
|
||||
apmCreatePage?.end();
|
||||
exit$.subscribe({ error: () => apmTrans?.end() });
|
||||
return create$.pipe(
|
||||
mergeMap(({ driver, exit$ }) => {
|
||||
apmCreatePage?.end();
|
||||
exit$.subscribe({ error: () => apmTrans?.end() });
|
||||
|
||||
return Rx.from(urls).pipe(
|
||||
concatMap((url, index) => {
|
||||
const setup$: Rx.Observable<ScreenSetupData> = Rx.of(1).pipe(
|
||||
mergeMap(() => {
|
||||
// If we're moving to another page in the app, we'll want to wait for the app to tell us
|
||||
// it's loaded the next page.
|
||||
const page = index + 1;
|
||||
const pageLoadSelector =
|
||||
page > 1 ? `[data-shared-page="${page}"]` : DEFAULT_PAGELOAD_SELECTOR;
|
||||
return Rx.from(urls).pipe(
|
||||
concatMap((url, index) => {
|
||||
const setup$: Rx.Observable<ScreenSetupData> = Rx.of(1).pipe(
|
||||
mergeMap(() => {
|
||||
// If we're moving to another page in the app, we'll want to wait for the app to tell us
|
||||
// it's loaded the next page.
|
||||
const page = index + 1;
|
||||
const pageLoadSelector =
|
||||
page > 1 ? `[data-shared-page="${page}"]` : DEFAULT_PAGELOAD_SELECTOR;
|
||||
|
||||
return openUrl(
|
||||
captureConfig,
|
||||
driver,
|
||||
url,
|
||||
pageLoadSelector,
|
||||
conditionalHeaders,
|
||||
logger
|
||||
);
|
||||
}),
|
||||
mergeMap(() => getNumberOfItems(captureConfig, driver, layout, logger)),
|
||||
mergeMap(async (itemsCount) => {
|
||||
// set the viewport to the dimentions from the job, to allow elements to flow into the expected layout
|
||||
const viewport = layout.getViewport(itemsCount) || getDefaultViewPort();
|
||||
await Promise.all([
|
||||
driver.setViewport(viewport, logger),
|
||||
waitForVisualizations(captureConfig, driver, itemsCount, layout, logger),
|
||||
]);
|
||||
}),
|
||||
mergeMap(async () => {
|
||||
// Waiting till _after_ elements have rendered before injecting our CSS
|
||||
// allows for them to be displayed properly in many cases
|
||||
await injectCustomCss(driver, layout, logger);
|
||||
return openUrl(
|
||||
captureConfig,
|
||||
driver,
|
||||
url,
|
||||
pageLoadSelector,
|
||||
conditionalHeaders,
|
||||
logger
|
||||
);
|
||||
}),
|
||||
mergeMap(() => getNumberOfItems(captureConfig, driver, layout, logger)),
|
||||
mergeMap(async (itemsCount) => {
|
||||
// set the viewport to the dimentions from the job, to allow elements to flow into the expected layout
|
||||
const viewport = layout.getViewport(itemsCount) || getDefaultViewPort();
|
||||
await Promise.all([
|
||||
driver.setViewport(viewport, logger),
|
||||
waitForVisualizations(captureConfig, driver, itemsCount, layout, logger),
|
||||
]);
|
||||
}),
|
||||
mergeMap(async () => {
|
||||
// Waiting till _after_ elements have rendered before injecting our CSS
|
||||
// allows for them to be displayed properly in many cases
|
||||
await injectCustomCss(driver, layout, logger);
|
||||
|
||||
const apmPositionElements = apmTrans?.startSpan('position_elements', 'correction');
|
||||
if (layout.positionElements) {
|
||||
// position panel elements for print layout
|
||||
await layout.positionElements(driver, logger);
|
||||
}
|
||||
if (apmPositionElements) apmPositionElements.end();
|
||||
const apmPositionElements = apmTrans?.startSpan('position_elements', 'correction');
|
||||
if (layout.positionElements) {
|
||||
// position panel elements for print layout
|
||||
await layout.positionElements(driver, logger);
|
||||
}
|
||||
if (apmPositionElements) apmPositionElements.end();
|
||||
|
||||
await waitForRenderComplete(captureConfig, driver, layout, logger);
|
||||
}),
|
||||
mergeMap(async () => {
|
||||
return await Promise.all([
|
||||
getTimeRange(driver, layout, logger),
|
||||
getElementPositionAndAttributes(driver, layout, logger),
|
||||
]).then(([timeRange, elementsPositionAndAttributes]) => ({
|
||||
elementsPositionAndAttributes,
|
||||
await waitForRenderComplete(captureConfig, driver, layout, logger);
|
||||
}),
|
||||
mergeMap(async () => {
|
||||
return await Promise.all([
|
||||
getTimeRange(driver, layout, logger),
|
||||
getElementPositionAndAttributes(driver, layout, logger),
|
||||
]).then(([timeRange, elementsPositionAndAttributes]) => ({
|
||||
elementsPositionAndAttributes,
|
||||
timeRange,
|
||||
}));
|
||||
}),
|
||||
catchError((err) => {
|
||||
checkPageIsOpen(driver); // if browser has closed, throw a relevant error about it
|
||||
|
||||
logger.error(err);
|
||||
return Rx.of({ elementsPositionAndAttributes: null, timeRange: null, error: err });
|
||||
})
|
||||
);
|
||||
|
||||
return setup$.pipe(
|
||||
takeUntil(exit$),
|
||||
mergeMap(
|
||||
async (data: ScreenSetupData): Promise<ScreenshotResults> => {
|
||||
checkPageIsOpen(driver); // re-check that the browser has not closed
|
||||
|
||||
const elements = data.elementsPositionAndAttributes
|
||||
? data.elementsPositionAndAttributes
|
||||
: getDefaultElementPosition(layout.getViewport(1));
|
||||
const screenshots = await getScreenshots(driver, layout, elements, logger);
|
||||
const { timeRange, error: setupError } = data;
|
||||
return {
|
||||
timeRange,
|
||||
}));
|
||||
}),
|
||||
catchError((err) => {
|
||||
checkPageIsOpen(driver); // if browser has closed, throw a relevant error about it
|
||||
|
||||
logger.error(err);
|
||||
return Rx.of({ elementsPositionAndAttributes: null, timeRange: null, error: err });
|
||||
})
|
||||
);
|
||||
|
||||
return setup$.pipe(
|
||||
takeUntil(exit$),
|
||||
mergeMap(
|
||||
async (data: ScreenSetupData): Promise<ScreenshotResults> => {
|
||||
checkPageIsOpen(driver); // re-check that the browser has not closed
|
||||
|
||||
const elements = data.elementsPositionAndAttributes
|
||||
? data.elementsPositionAndAttributes
|
||||
: getDefaultElementPosition(layout.getViewport(1));
|
||||
const screenshots = await getScreenshots(driver, layout, elements, logger);
|
||||
const { timeRange, error: setupError } = data;
|
||||
return {
|
||||
timeRange,
|
||||
screenshots,
|
||||
error: setupError,
|
||||
elementsPositionAndAttributes: elements,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}),
|
||||
take(urls.length),
|
||||
toArray()
|
||||
);
|
||||
}),
|
||||
first()
|
||||
);
|
||||
};
|
||||
screenshots,
|
||||
error: setupError,
|
||||
elementsPositionAndAttributes: elements,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}),
|
||||
take(urls.length),
|
||||
toArray()
|
||||
);
|
||||
}),
|
||||
first()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -13,7 +13,7 @@ import { runTaskFnFactory } from '../export_types/csv_searchsource_immediate/exe
|
|||
import { JobParamsDownloadCSV } from '../export_types/csv_searchsource_immediate/types';
|
||||
import { LevelLogger as Logger } from '../lib';
|
||||
import { TaskRunResult } from '../lib/tasks';
|
||||
import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from './lib/authorized_user_pre_routing';
|
||||
import { HandlerErrorFunction } from './types';
|
||||
|
||||
const API_BASE_URL_V1 = '/api/reporting/v1';
|
||||
|
@ -36,7 +36,6 @@ export function registerGenerateCsvFromSavedObjectImmediate(
|
|||
parentLogger: Logger
|
||||
) {
|
||||
const setupDeps = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
|
||||
// TODO: find a way to abstract this using ExportTypeRegistry: it needs a new
|
||||
|
@ -63,47 +62,50 @@ export function registerGenerateCsvFromSavedObjectImmediate(
|
|||
tags: kibanaAccessControlTags,
|
||||
},
|
||||
},
|
||||
userHandler(async (_user, context, req: CsvFromSavedObjectRequest, res) => {
|
||||
const logger = parentLogger.clone(['csv_searchsource_immediate']);
|
||||
const runTaskFn = runTaskFnFactory(reporting, logger);
|
||||
authorizedUserPreRouting(
|
||||
reporting,
|
||||
async (_user, context, req: CsvFromSavedObjectRequest, res) => {
|
||||
const logger = parentLogger.clone(['csv_searchsource_immediate']);
|
||||
const runTaskFn = runTaskFnFactory(reporting, logger);
|
||||
|
||||
try {
|
||||
let buffer = Buffer.from('');
|
||||
const stream = new Writable({
|
||||
write(chunk, encoding, callback) {
|
||||
buffer = Buffer.concat([
|
||||
buffer,
|
||||
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding),
|
||||
]);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
try {
|
||||
let buffer = Buffer.from('');
|
||||
const stream = new Writable({
|
||||
write(chunk, encoding, callback) {
|
||||
buffer = Buffer.concat([
|
||||
buffer,
|
||||
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding),
|
||||
]);
|
||||
callback();
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
content_type: jobOutputContentType,
|
||||
size: jobOutputSize,
|
||||
}: TaskRunResult = await runTaskFn(null, req.body, context, stream, req);
|
||||
stream.end();
|
||||
const jobOutputContent = buffer.toString();
|
||||
const {
|
||||
content_type: jobOutputContentType,
|
||||
size: jobOutputSize,
|
||||
}: TaskRunResult = await runTaskFn(null, req.body, context, stream, req);
|
||||
stream.end();
|
||||
const jobOutputContent = buffer.toString();
|
||||
|
||||
logger.info(`Job output size: ${jobOutputSize} bytes`);
|
||||
logger.info(`Job output size: ${jobOutputSize} bytes`);
|
||||
|
||||
// convert null to undefined so the value can be sent to h.response()
|
||||
if (jobOutputContent === null) {
|
||||
logger.warn('CSV Job Execution created empty content result');
|
||||
// convert null to undefined so the value can be sent to h.response()
|
||||
if (jobOutputContent === null) {
|
||||
logger.warn('CSV Job Execution created empty content result');
|
||||
}
|
||||
|
||||
return res.ok({
|
||||
body: jobOutputContent || '',
|
||||
headers: {
|
||||
'content-type': jobOutputContentType ? jobOutputContentType : [],
|
||||
'accept-ranges': 'none',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return handleError(res, err);
|
||||
}
|
||||
|
||||
return res.ok({
|
||||
body: jobOutputContent || '',
|
||||
headers: {
|
||||
'content-type': jobOutputContentType ? jobOutputContentType : [],
|
||||
'accept-ranges': 'none',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return handleError(res, err);
|
||||
}
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ReportingCore } from '../..';
|
|||
import { API_DIAGNOSE_URL } from '../../../common/constants';
|
||||
import { browserStartLogs } from '../../browsers/chromium/driver_factory/start_logs';
|
||||
import { LevelLogger as Logger } from '../../lib';
|
||||
import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing';
|
||||
import { DiagnosticResponse } from './';
|
||||
|
||||
const logsToHelpMap = {
|
||||
|
@ -47,14 +47,13 @@ const logsToHelpMap = {
|
|||
|
||||
export const registerDiagnoseBrowser = (reporting: ReportingCore, logger: Logger) => {
|
||||
const { router } = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
|
||||
router.post(
|
||||
{
|
||||
path: `${API_DIAGNOSE_URL}/browser`,
|
||||
validate: {},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (_user, _context, _req, res) => {
|
||||
try {
|
||||
const logs = await browserStartLogs(reporting, logger).toPromise();
|
||||
const knownIssues = Object.keys(logsToHelpMap) as Array<keyof typeof logsToHelpMap>;
|
||||
|
|
|
@ -11,7 +11,7 @@ import { defaults, get } from 'lodash';
|
|||
import { ReportingCore } from '../..';
|
||||
import { API_DIAGNOSE_URL } from '../../../common/constants';
|
||||
import { LevelLogger as Logger } from '../../lib';
|
||||
import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing';
|
||||
import { DiagnosticResponse } from './';
|
||||
|
||||
const KIBANA_MAX_SIZE_BYTES_PATH = 'csv.maxSizeBytes';
|
||||
|
@ -27,7 +27,6 @@ const numberToByteSizeValue = (value: number | ByteSizeValue) => {
|
|||
|
||||
export const registerDiagnoseConfig = (reporting: ReportingCore, logger: Logger) => {
|
||||
const setupDeps = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
|
||||
router.post(
|
||||
|
@ -35,7 +34,7 @@ export const registerDiagnoseConfig = (reporting: ReportingCore, logger: Logger)
|
|||
path: `${API_DIAGNOSE_URL}/config`,
|
||||
validate: {},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (_user, _context, _req, res) => {
|
||||
const warnings = [];
|
||||
const { asInternalUser: elasticsearchClient } = await reporting.getEsClient();
|
||||
const config = reporting.getConfig();
|
||||
|
|
|
@ -13,12 +13,11 @@ import { omitBlockedHeaders } from '../../export_types/common';
|
|||
import { getAbsoluteUrlFactory } from '../../export_types/common/get_absolute_url';
|
||||
import { generatePngObservableFactory } from '../../export_types/png/lib/generate_png';
|
||||
import { LevelLogger as Logger } from '../../lib';
|
||||
import { authorizedUserPreRoutingFactory } from '../lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from '../lib/authorized_user_pre_routing';
|
||||
import { DiagnosticResponse } from './';
|
||||
|
||||
export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Logger) => {
|
||||
const setupDeps = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
|
||||
router.post(
|
||||
|
@ -26,7 +25,7 @@ export const registerDiagnoseScreenshot = (reporting: ReportingCore, logger: Log
|
|||
path: `${API_DIAGNOSE_URL}/screenshot`,
|
||||
validate: {},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (_user, _context, req, res) => {
|
||||
const generatePngObservable = await generatePngObservableFactory(reporting);
|
||||
const config = reporting.getConfig();
|
||||
const decryptedHeaders = req.headers as Record<string, string>;
|
||||
|
|
|
@ -10,7 +10,7 @@ import rison from 'rison-node';
|
|||
import { ReportingCore } from '../';
|
||||
import { API_BASE_URL } from '../../common/constants';
|
||||
import { BaseParams } from '../types';
|
||||
import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from './lib/authorized_user_pre_routing';
|
||||
import { HandlerErrorFunction, HandlerFunction } from './types';
|
||||
|
||||
const BASE_GENERATE = `${API_BASE_URL}/generate`;
|
||||
|
@ -21,7 +21,6 @@ export function registerGenerateFromJobParams(
|
|||
handleError: HandlerErrorFunction
|
||||
) {
|
||||
const setupDeps = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
|
||||
// TODO: find a way to abstract this using ExportTypeRegistry: it needs a new
|
||||
|
@ -41,7 +40,7 @@ export function registerGenerateFromJobParams(
|
|||
},
|
||||
options: { tags: kibanaAccessControlTags },
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, req, res) => {
|
||||
let jobParamsRison: null | string = null;
|
||||
|
||||
if (req.body) {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { kibanaResponseFactory } from 'src/core/server';
|
|||
import { ReportingCore } from '../';
|
||||
import { API_BASE_URL } from '../../common/constants';
|
||||
import { LevelLogger as Logger } from '../lib';
|
||||
import { enqueueJobFactory } from '../lib/enqueue_job';
|
||||
import { enqueueJob } from '../lib/enqueue_job';
|
||||
import { registerGenerateFromJobParams } from './generate_from_jobparams';
|
||||
import { registerGenerateCsvFromSavedObjectImmediate } from './csv_searchsource_immediate';
|
||||
import { HandlerFunction } from './types';
|
||||
|
@ -42,8 +42,15 @@ export function registerJobGenerationRoutes(reporting: ReportingCore, logger: Lo
|
|||
}
|
||||
|
||||
try {
|
||||
const enqueueJob = enqueueJobFactory(reporting, logger);
|
||||
const report = await enqueueJob(exportTypeId, jobParams, user, context, req);
|
||||
const report = await enqueueJob(
|
||||
reporting,
|
||||
req,
|
||||
context,
|
||||
user,
|
||||
exportTypeId,
|
||||
jobParams,
|
||||
logger
|
||||
);
|
||||
|
||||
// return task manager's task information and the download URL
|
||||
const downloadBaseUrl = getDownloadBaseUrl(reporting);
|
||||
|
|
|
@ -10,12 +10,9 @@ import Boom from '@hapi/boom';
|
|||
import { ROUTE_TAG_CAN_REDIRECT } from '../../../security/server';
|
||||
import { ReportingCore } from '../';
|
||||
import { API_BASE_URL } from '../../common/constants';
|
||||
import { authorizedUserPreRoutingFactory } from './lib/authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from './lib/authorized_user_pre_routing';
|
||||
import { jobsQueryFactory } from './lib/jobs_query';
|
||||
import {
|
||||
deleteJobResponseHandlerFactory,
|
||||
downloadJobResponseHandlerFactory,
|
||||
} from './lib/job_response_handler';
|
||||
import { deleteJobResponseHandler, downloadJobResponseHandler } from './lib/job_response_handler';
|
||||
|
||||
const MAIN_ENTRY = `${API_BASE_URL}/jobs`;
|
||||
|
||||
|
@ -25,8 +22,8 @@ const handleUnavailable = (res: any) => {
|
|||
|
||||
export function registerJobInfoRoutes(reporting: ReportingCore) {
|
||||
const setupDeps = reporting.getPluginSetupDeps();
|
||||
const userHandler = authorizedUserPreRoutingFactory(reporting);
|
||||
const { router } = setupDeps;
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
|
||||
// list jobs in the queue, paginated
|
||||
router.get(
|
||||
|
@ -40,7 +37,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, req, res) => {
|
||||
// ensure the async dependencies are loaded
|
||||
if (!context.reporting) {
|
||||
return handleUnavailable(res);
|
||||
|
@ -53,7 +50,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
const page = parseInt(queryPage, 10) || 0;
|
||||
const size = Math.min(100, parseInt(querySize, 10) || 10);
|
||||
const jobIds = queryIds ? queryIds.split(',') : null;
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
const results = await jobsQuery.list(jobTypes, user, page, size, jobIds);
|
||||
|
||||
return res.ok({
|
||||
|
@ -71,7 +67,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
path: `${MAIN_ENTRY}/count`,
|
||||
validate: false,
|
||||
},
|
||||
userHandler(async (user, context, _req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, _req, res) => {
|
||||
// ensure the async dependencies are loaded
|
||||
if (!context.reporting) {
|
||||
return handleUnavailable(res);
|
||||
|
@ -81,7 +77,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
management: { jobTypes = [] },
|
||||
} = await reporting.getLicenseInfo();
|
||||
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
const count = await jobsQuery.count(jobTypes, user);
|
||||
|
||||
return res.ok({
|
||||
|
@ -103,7 +98,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, req, res) => {
|
||||
// ensure the async dependencies are loaded
|
||||
if (!context.reporting) {
|
||||
return res.custom({ statusCode: 503 });
|
||||
|
@ -114,7 +109,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
management: { jobTypes = [] },
|
||||
} = await reporting.getLicenseInfo();
|
||||
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
const result = await jobsQuery.get(user, docId);
|
||||
|
||||
if (!result) {
|
||||
|
@ -137,8 +131,6 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
);
|
||||
|
||||
// trigger a download of the output from a job
|
||||
const downloadResponseHandler = downloadJobResponseHandlerFactory(reporting);
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: `${MAIN_ENTRY}/download/{docId}`,
|
||||
|
@ -149,7 +141,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
},
|
||||
options: { tags: [ROUTE_TAG_CAN_REDIRECT] },
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, req, res) => {
|
||||
// ensure the async dependencies are loaded
|
||||
if (!context.reporting) {
|
||||
return handleUnavailable(res);
|
||||
|
@ -160,12 +152,11 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
management: { jobTypes = [] },
|
||||
} = await reporting.getLicenseInfo();
|
||||
|
||||
return downloadResponseHandler(res, jobTypes, user, { docId });
|
||||
return downloadJobResponseHandler(reporting, res, jobTypes, user, { docId });
|
||||
})
|
||||
);
|
||||
|
||||
// allow a report to be deleted
|
||||
const deleteResponseHandler = deleteJobResponseHandlerFactory(reporting);
|
||||
router.delete(
|
||||
{
|
||||
path: `${MAIN_ENTRY}/delete/{docId}`,
|
||||
|
@ -175,7 +166,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
}),
|
||||
},
|
||||
},
|
||||
userHandler(async (user, context, req, res) => {
|
||||
authorizedUserPreRouting(reporting, async (user, context, req, res) => {
|
||||
// ensure the async dependencies are loaded
|
||||
if (!context.reporting) {
|
||||
return handleUnavailable(res);
|
||||
|
@ -186,7 +177,7 @@ export function registerJobInfoRoutes(reporting: ReportingCore) {
|
|||
management: { jobTypes = [] },
|
||||
} = await reporting.getLicenseInfo();
|
||||
|
||||
return deleteResponseHandler(res, jobTypes, user, { docId });
|
||||
return deleteJobResponseHandler(reporting, res, jobTypes, user, { docId });
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import { ReportingCore } from '../../';
|
|||
import { ReportingInternalSetup } from '../../core';
|
||||
import { createMockConfigSchema, createMockReportingCore } from '../../test_helpers';
|
||||
import type { ReportingRequestHandlerContext } from '../../types';
|
||||
import { authorizedUserPreRoutingFactory } from './authorized_user_pre_routing';
|
||||
import { authorizedUserPreRouting } from './authorized_user_pre_routing';
|
||||
|
||||
let mockCore: ReportingCore;
|
||||
const mockReportingConfig = createMockConfigSchema({ roles: { enabled: false } });
|
||||
|
@ -46,11 +46,10 @@ describe('authorized_user_pre_routing', function () {
|
|||
...mockCore.pluginSetupDeps,
|
||||
security: undefined, // disable security
|
||||
} as unknown) as ReportingInternalSetup);
|
||||
const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockCore);
|
||||
const mockResponseFactory = httpServerMock.createResponseFactory() as KibanaResponseFactory;
|
||||
|
||||
let handlerCalled = false;
|
||||
authorizedUserPreRouting((user: unknown) => {
|
||||
authorizedUserPreRouting(mockCore, (user: unknown) => {
|
||||
expect(user).toBe(false); // verify the user is a false value
|
||||
handlerCalled = true;
|
||||
return Promise.resolve({ status: 200, options: {} });
|
||||
|
@ -70,11 +69,10 @@ describe('authorized_user_pre_routing', function () {
|
|||
},
|
||||
}, // disable security
|
||||
} as unknown) as ReportingInternalSetup);
|
||||
const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockCore);
|
||||
const mockResponseFactory = httpServerMock.createResponseFactory() as KibanaResponseFactory;
|
||||
|
||||
let handlerCalled = false;
|
||||
authorizedUserPreRouting((user: unknown) => {
|
||||
authorizedUserPreRouting(mockCore, (user: unknown) => {
|
||||
expect(user).toBe(false); // verify the user is a false value
|
||||
handlerCalled = true;
|
||||
return Promise.resolve({ status: 200, options: {} });
|
||||
|
@ -93,11 +91,10 @@ describe('authorized_user_pre_routing', function () {
|
|||
authc: { getCurrentUser: () => null },
|
||||
},
|
||||
} as unknown) as ReportingInternalSetup);
|
||||
const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockCore);
|
||||
const mockHandler = () => {
|
||||
throw new Error('Handler callback should not be called');
|
||||
};
|
||||
const requestHandler = authorizedUserPreRouting(mockHandler);
|
||||
const requestHandler = authorizedUserPreRouting(mockCore, mockHandler);
|
||||
const mockResponseFactory = getMockResponseFactory();
|
||||
|
||||
expect(requestHandler(getMockContext(), getMockRequest(), mockResponseFactory)).toMatchObject({
|
||||
|
@ -126,14 +123,13 @@ describe('authorized_user_pre_routing', function () {
|
|||
authc: { getCurrentUser: () => ({ username: 'friendlyuser', roles: ['cowboy'] }) },
|
||||
},
|
||||
} as unknown) as ReportingInternalSetup);
|
||||
const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockCore);
|
||||
const mockResponseFactory = getMockResponseFactory();
|
||||
|
||||
const mockHandler = () => {
|
||||
throw new Error('Handler callback should not be called');
|
||||
};
|
||||
expect(
|
||||
authorizedUserPreRouting(mockHandler)(
|
||||
authorizedUserPreRouting(mockCore, mockHandler)(
|
||||
getMockContext(),
|
||||
getMockRequest(),
|
||||
mockResponseFactory
|
||||
|
@ -153,10 +149,9 @@ describe('authorized_user_pre_routing', function () {
|
|||
},
|
||||
},
|
||||
} as unknown) as ReportingInternalSetup);
|
||||
const authorizedUserPreRouting = authorizedUserPreRoutingFactory(mockCore);
|
||||
const mockResponseFactory = getMockResponseFactory();
|
||||
|
||||
authorizedUserPreRouting((user) => {
|
||||
authorizedUserPreRouting(mockCore, (user) => {
|
||||
expect(user).toMatchObject({ roles: ['reporting_user'], username: 'friendlyuser' });
|
||||
done();
|
||||
return Promise.resolve({ status: 200, options: {} });
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
import { RequestHandler, RouteMethod } from 'src/core/server';
|
||||
import { AuthenticatedUser } from '../../../../security/server';
|
||||
import { ReportingCore } from '../../core';
|
||||
import { getUserFactory } from './get_user';
|
||||
import { getUser } from './get_user';
|
||||
import type { ReportingRequestHandlerContext } from '../../types';
|
||||
|
||||
const superuserRole = 'superuser';
|
||||
|
||||
type ReportingRequestUser = AuthenticatedUser | false;
|
||||
|
||||
export type RequestHandlerUser<P, Q, B> = RequestHandler<
|
||||
P,
|
||||
Q,
|
||||
|
@ -23,43 +24,40 @@ export type RequestHandlerUser<P, Q, B> = RequestHandler<
|
|||
? (user: ReportingRequestUser, ...a: U) => R
|
||||
: never;
|
||||
|
||||
export const authorizedUserPreRoutingFactory = function authorizedUserPreRoutingFn(
|
||||
reporting: ReportingCore
|
||||
) {
|
||||
export const authorizedUserPreRouting = <P, Q, B>(
|
||||
reporting: ReportingCore,
|
||||
handler: RequestHandlerUser<P, Q, B>
|
||||
): RequestHandler<P, Q, B, ReportingRequestHandlerContext, RouteMethod> => {
|
||||
const { logger, security } = reporting.getPluginSetupDeps();
|
||||
const getUser = getUserFactory(security);
|
||||
return <P, Q, B>(
|
||||
handler: RequestHandlerUser<P, Q, B>
|
||||
): RequestHandler<P, Q, B, ReportingRequestHandlerContext, RouteMethod> => {
|
||||
return (context, req, res) => {
|
||||
try {
|
||||
let user: ReportingRequestUser = false;
|
||||
if (security && security.license.isEnabled()) {
|
||||
// find the authenticated user, or null if security is not enabled
|
||||
user = getUser(req);
|
||||
if (!user) {
|
||||
// security is enabled but the user is null
|
||||
return res.unauthorized({ body: `Sorry, you aren't authenticated` });
|
||||
}
|
||||
|
||||
return (context, req, res) => {
|
||||
try {
|
||||
let user: ReportingRequestUser = false;
|
||||
if (security && security.license.isEnabled()) {
|
||||
// find the authenticated user, or null if security is not enabled
|
||||
user = getUser(req, security);
|
||||
if (!user) {
|
||||
// security is enabled but the user is null
|
||||
return res.unauthorized({ body: `Sorry, you aren't authenticated` });
|
||||
}
|
||||
|
||||
const deprecatedAllowedRoles = reporting.getDeprecatedAllowedRoles();
|
||||
if (user && deprecatedAllowedRoles !== false) {
|
||||
// check allowance with the configured set of roleas + "superuser"
|
||||
const allowedRoles = deprecatedAllowedRoles || [];
|
||||
const authorizedRoles = [superuserRole, ...allowedRoles];
|
||||
|
||||
if (!user.roles.find((role) => authorizedRoles.includes(role))) {
|
||||
// user's roles do not allow
|
||||
return res.forbidden({ body: `Sorry, you don't have access to Reporting` });
|
||||
}
|
||||
}
|
||||
|
||||
return handler(user, context, req, res);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.custom({ statusCode: 500 });
|
||||
}
|
||||
};
|
||||
|
||||
const deprecatedAllowedRoles = reporting.getDeprecatedAllowedRoles();
|
||||
if (user && deprecatedAllowedRoles !== false) {
|
||||
// check allowance with the configured set of roleas + "superuser"
|
||||
const allowedRoles = deprecatedAllowedRoles || [];
|
||||
const authorizedRoles = [superuserRole, ...allowedRoles];
|
||||
|
||||
if (!user.roles.find((role) => authorizedRoles.includes(role))) {
|
||||
// user's roles do not allow
|
||||
return res.forbidden({ body: `Sorry, you don't have access to Reporting` });
|
||||
}
|
||||
}
|
||||
|
||||
return handler(user, context, req, res);
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return res.custom({ statusCode: 500 });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
import { KibanaRequest } from 'kibana/server';
|
||||
import { SecurityPluginSetup } from '../../../../security/server';
|
||||
|
||||
export function getUserFactory(security?: SecurityPluginSetup) {
|
||||
return (request: KibanaRequest) => {
|
||||
return security?.authc.getCurrentUser(request) ?? false;
|
||||
};
|
||||
export function getUser(request: KibanaRequest, security?: SecurityPluginSetup) {
|
||||
return security?.authc.getCurrentUser(request) ?? false;
|
||||
}
|
||||
|
|
|
@ -16,93 +16,85 @@ interface JobResponseHandlerParams {
|
|||
docId: string;
|
||||
}
|
||||
|
||||
interface JobResponseHandlerOpts {
|
||||
excludeContent?: boolean;
|
||||
}
|
||||
|
||||
export function downloadJobResponseHandlerFactory(reporting: ReportingCore) {
|
||||
export async function downloadJobResponseHandler(
|
||||
reporting: ReportingCore,
|
||||
res: typeof kibanaResponseFactory,
|
||||
validJobTypes: string[],
|
||||
user: ReportingUser,
|
||||
params: JobResponseHandlerParams
|
||||
) {
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
const getDocumentPayload = getDocumentPayloadFactory(reporting);
|
||||
|
||||
return async function jobResponseHandler(
|
||||
res: typeof kibanaResponseFactory,
|
||||
validJobTypes: string[],
|
||||
user: ReportingUser,
|
||||
params: JobResponseHandlerParams,
|
||||
opts: JobResponseHandlerOpts = {}
|
||||
) {
|
||||
try {
|
||||
const { docId } = params;
|
||||
|
||||
const doc = await jobsQuery.get(user, docId);
|
||||
if (!doc) {
|
||||
return res.notFound();
|
||||
}
|
||||
|
||||
if (!validJobTypes.includes(doc.jobtype)) {
|
||||
return res.unauthorized({
|
||||
body: `Sorry, you are not authorized to download ${doc.jobtype} reports`,
|
||||
});
|
||||
}
|
||||
|
||||
const payload = await getDocumentPayload(doc);
|
||||
|
||||
if (!payload.contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(payload.contentType)) {
|
||||
return res.badRequest({
|
||||
body: `Unsupported content-type of ${payload.contentType} specified by job output`,
|
||||
});
|
||||
}
|
||||
|
||||
return res.custom({
|
||||
body: typeof payload.content === 'string' ? Buffer.from(payload.content) : payload.content,
|
||||
statusCode: payload.statusCode,
|
||||
headers: {
|
||||
...payload.headers,
|
||||
'content-type': payload.contentType || '',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
const { logger } = reporting.getPluginSetupDeps();
|
||||
logger.error(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteJobResponseHandlerFactory(reporting: ReportingCore) {
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
|
||||
return async function deleteJobResponseHander(
|
||||
res: typeof kibanaResponseFactory,
|
||||
validJobTypes: string[],
|
||||
user: ReportingUser,
|
||||
params: JobResponseHandlerParams
|
||||
) {
|
||||
try {
|
||||
const { docId } = params;
|
||||
const doc = await jobsQuery.get(user, docId);
|
||||
|
||||
const doc = await jobsQuery.get(user, docId);
|
||||
if (!doc) {
|
||||
return res.notFound();
|
||||
}
|
||||
|
||||
const { jobtype: jobType } = doc;
|
||||
|
||||
if (!validJobTypes.includes(jobType)) {
|
||||
if (!validJobTypes.includes(doc.jobtype)) {
|
||||
return res.unauthorized({
|
||||
body: `Sorry, you are not authorized to delete ${jobType} reports`,
|
||||
body: `Sorry, you are not authorized to download ${doc.jobtype} reports`,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const docIndex = doc.index;
|
||||
await jobsQuery.delete(docIndex, docId);
|
||||
return res.ok({
|
||||
body: { deleted: true },
|
||||
});
|
||||
} catch (error) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error.message,
|
||||
const payload = await getDocumentPayload(doc);
|
||||
|
||||
if (!payload.contentType || !ALLOWED_JOB_CONTENT_TYPES.includes(payload.contentType)) {
|
||||
return res.badRequest({
|
||||
body: `Unsupported content-type of ${payload.contentType} specified by job output`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return res.custom({
|
||||
body: typeof payload.content === 'string' ? Buffer.from(payload.content) : payload.content,
|
||||
statusCode: payload.statusCode,
|
||||
headers: {
|
||||
...payload.headers,
|
||||
'content-type': payload.contentType || '',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
const { logger } = reporting.getPluginSetupDeps();
|
||||
logger.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteJobResponseHandler(
|
||||
reporting: ReportingCore,
|
||||
res: typeof kibanaResponseFactory,
|
||||
validJobTypes: string[],
|
||||
user: ReportingUser,
|
||||
params: JobResponseHandlerParams
|
||||
) {
|
||||
const jobsQuery = jobsQueryFactory(reporting);
|
||||
|
||||
const { docId } = params;
|
||||
const doc = await jobsQuery.get(user, docId);
|
||||
|
||||
if (!doc) {
|
||||
return res.notFound();
|
||||
}
|
||||
|
||||
const { jobtype: jobType } = doc;
|
||||
|
||||
if (!validJobTypes.includes(jobType)) {
|
||||
return res.unauthorized({
|
||||
body: `Sorry, you are not authorized to delete ${jobType} reports`,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const docIndex = doc.index;
|
||||
await jobsQuery.delete(docIndex, docId);
|
||||
return res.ok({
|
||||
body: { deleted: true },
|
||||
});
|
||||
} catch (error) {
|
||||
return res.customError({
|
||||
statusCode: error.statusCode,
|
||||
body: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue