Skip to content

Commit cc95f11

Browse files
ymao1kibanamachineelasticmachine
authored
[Response Ops][Task Manager] Pass fakeRequest for tasks with associated API key instead of actual key (#217327)
Resolves #216808 ## Summary Instead of directly passing the `apiKey` and `userScope` (when available) into each task type runner, we create a fake Kibana Request with an Authorization ApiKey header in the task manager and pass that request into the task type runner. --------- Co-authored-by: kibanamachine <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
1 parent e2f0fdd commit cc95f11

File tree

9 files changed

+85
-26
lines changed

9 files changed

+85
-26
lines changed

x-pack/examples/triggers_actions_ui_example/server/plugin.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
* 2.0.
66
*/
77

8-
import { Plugin, CoreSetup, FakeRawRequest } from '@kbn/core/server';
8+
import { Plugin, CoreSetup } from '@kbn/core/server';
99

1010
import { PluginSetupContract as ActionsSetup } from '@kbn/actions-plugin/server';
1111
import { AlertingServerSetup } from '@kbn/alerting-plugin/server';
12-
import { addSpaceIdToPath } from '@kbn/spaces-plugin/common';
13-
import { kibanaRequestFactory } from '@kbn/core-http-server-utils';
1412
import {
1513
TaskManagerSetupContract,
1614
TaskManagerStartContract,
@@ -140,22 +138,8 @@ export class TriggersActionsUiExamplePlugin
140138
taskManager.registerTaskDefinitions({
141139
taskWithApiKey: {
142140
title: 'taskWithApiKey',
143-
createTaskRunner: ({ taskInstance }) => ({
141+
createTaskRunner: ({ taskInstance, fakeRequest }) => ({
144142
run: async () => {
145-
const services = await core.getStartServices();
146-
const fakeRawRequest: FakeRawRequest = {
147-
headers: {
148-
authorization: `ApiKey ${taskInstance?.apiKey}`,
149-
},
150-
path: '/',
151-
};
152-
153-
const path = addSpaceIdToPath('/', taskInstance.userScope?.spaceId || 'default');
154-
155-
// Fake request from the API key
156-
const fakeRequest = kibanaRequestFactory(fakeRawRequest);
157-
services[0].http.basePath.set(fakeRequest, path);
158-
159143
return {
160144
state: {},
161145
};

x-pack/examples/triggers_actions_ui_example/tsconfig.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@
3434
"@kbn/field-formats-plugin",
3535
"@kbn/licensing-plugin",
3636
"@kbn/response-ops-rule-form",
37-
"@kbn/spaces-plugin",
38-
"@kbn/core-http-server-utils",
3937
"@kbn/task-manager-plugin",
4038
"@kbn/fields-metadata-plugin",
4139
]

x-pack/platform/plugins/shared/task_manager/server/plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export class TaskManagerPlugin
287287
}
288288

289289
public start(
290-
{ savedObjects, elasticsearch, executionContext, security }: CoreStart,
290+
{ http, savedObjects, elasticsearch, executionContext, security }: CoreStart,
291291
{ cloud, spaces }: TaskManagerPluginsStart
292292
): TaskManagerStartContract {
293293
const savedObjectsRepository = savedObjects.createInternalRepository([
@@ -367,6 +367,7 @@ export class TaskManagerPlugin
367367
});
368368

369369
this.taskPollingLifecycle = new TaskPollingLifecycle({
370+
basePathService: http.basePath,
370371
config: this.config!,
371372
definitions: this.definitions,
372373
logger: this.logger,

x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import type {
2323
import type { Err, Ok } from './lib/result_type';
2424
import { asOk, isErr, isOk } from './lib/result_type';
2525
import { FillPoolResult } from './lib/fill_pool';
26-
import { executionContextServiceMock } from '@kbn/core/server/mocks';
26+
import { executionContextServiceMock, httpServiceMock } from '@kbn/core/server/mocks';
2727
import { TaskCost } from './task';
2828
import { CLAIM_STRATEGY_MGET, DEFAULT_KIBANAS_PER_PARTITION } from './config';
2929
import { TaskPartitioner } from './lib/task_partitioner';
@@ -105,6 +105,7 @@ describe('TaskPollingLifecycle', () => {
105105
},
106106
auto_calculate_default_ech_capacity: false,
107107
},
108+
basePathService: httpServiceMock.createBasePath(),
108109
taskStore: mockTaskStore,
109110
logger: taskManagerLogger,
110111
definitions: new TaskTypeDictionary(taskManagerLogger),

x-pack/platform/plugins/shared/task_manager/server/polling_lifecycle.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
1212
import { map as mapOptional, none } from 'fp-ts/lib/Option';
1313
import { tap } from 'rxjs';
1414
import type { UsageCounter } from '@kbn/usage-collection-plugin/server';
15-
import type { Logger, ExecutionContextStart } from '@kbn/core/server';
15+
import type { Logger, ExecutionContextStart, IBasePath } from '@kbn/core/server';
1616

1717
import type { Result } from './lib/result_type';
1818
import { asErr, mapErr, asOk, map, mapOk, isOk } from './lib/result_type';
@@ -69,6 +69,7 @@ export interface ITaskEventEmitter<T> {
6969
}
7070

7171
export interface TaskPollingLifecycleOpts {
72+
basePathService: IBasePath;
7273
logger: Logger;
7374
definitions: TaskTypeDictionary;
7475
taskStore: TaskStore;
@@ -99,6 +100,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven
99100
private store: TaskStore;
100101
private taskClaiming: TaskClaiming;
101102
private bufferedStore: BufferedTaskStore;
103+
private readonly basePathService: IBasePath;
102104
private readonly executionContext: ExecutionContextStart;
103105

104106
private logger: Logger;
@@ -126,6 +128,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven
126128
* mechanism.
127129
*/
128130
constructor({
131+
basePathService,
129132
logger,
130133
middleware,
131134
config,
@@ -138,6 +141,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven
138141
taskPartitioner,
139142
startingCapacity,
140143
}: TaskPollingLifecycleOpts) {
144+
this.basePathService = basePathService;
141145
this.logger = logger;
142146
this.middleware = middleware;
143147
this.definitions = definitions;
@@ -251,6 +255,7 @@ export class TaskPollingLifecycle implements ITaskEventEmitter<TaskLifecycleEven
251255

252256
private createTaskRunnerForTask = (instance: ConcreteTaskInstance) => {
253257
return new TaskManagerRunner({
258+
basePathService: this.basePathService,
254259
logger: this.logger,
255260
instance,
256261
store: this.bufferedStore,

x-pack/platform/plugins/shared/task_manager/server/task.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ export interface RunContext {
5555
* The document describing the task instance, its params, state, id, etc.
5656
*/
5757
taskInstance: ConcreteTaskInstance;
58+
59+
/**
60+
* If an API key is associated with the task, a fake KibanaRequest object
61+
* is generated using the API key and passed as part of the run context.
62+
*/
63+
fakeRequest?: KibanaRequest;
5864
}
5965

6066
/**

x-pack/platform/plugins/shared/task_manager/server/task_running/task_runner.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { TaskTypeDictionary } from '../task_type_dictionary';
3232
import { mockLogger } from '../test_utils';
3333
import { throwRetryableError, throwUnrecoverableError } from './errors';
3434
import apm from 'elastic-apm-node';
35-
import { executionContextServiceMock } from '@kbn/core/server/mocks';
35+
import { executionContextServiceMock, httpServiceMock } from '@kbn/core/server/mocks';
3636
import { usageCountersServiceMock } from '@kbn/usage-collection-plugin/server/usage_counters/usage_counters_service.mock';
3737
import { bufferedTaskStoreMock } from '../buffered_task_store.mock';
3838
import {
@@ -984,6 +984,31 @@ describe('TaskManagerRunner', () => {
984984
expect.any(Function)
985985
);
986986
});
987+
test('provides fakeRequest when task has apiKey', async () => {
988+
const createTaskRunnerFn = jest.fn();
989+
const instance = mockInstance();
990+
const { runner } = await readyToRunStageSetup({
991+
instance: {
992+
...instance,
993+
apiKey: 'aw4badfg333',
994+
userScope: {
995+
apiKeyId: 'abcdefg',
996+
spaceId: 'default',
997+
apiKeyCreatedByUser: false,
998+
},
999+
},
1000+
definitions: {
1001+
bar: {
1002+
title: 'Bar!',
1003+
createTaskRunner: createTaskRunnerFn,
1004+
},
1005+
},
1006+
});
1007+
await runner.run();
1008+
const createTaskRunnerParams = createTaskRunnerFn.mock.calls[0][0];
1009+
expect(createTaskRunnerParams.fakeRequest).toBeDefined();
1010+
expect(createTaskRunnerParams.taskInstance).toEqual(instance);
1011+
});
9871012
test('queues a reattempt if the task fails', async () => {
9881013
const initialAttempts = _.random(0, 2);
9891014
const id = Date.now().toString();
@@ -2565,6 +2590,7 @@ describe('TaskManagerRunner', () => {
25652590
}
25662591

25672592
const runner = new TaskManagerRunner({
2593+
basePathService: httpServiceMock.createBasePath(),
25682594
defaultMaxAttempts: 5,
25692595
beforeRun: (context) => Promise.resolve(context),
25702596
beforeMarkRunning: (context) => Promise.resolve(context),

x-pack/platform/plugins/shared/task_manager/server/task_running/task_runner.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ import apm from 'elastic-apm-node';
1515
import { v4 as uuidv4 } from 'uuid';
1616
import { withSpan } from '@kbn/apm-utils';
1717
import { flow, identity, omit } from 'lodash';
18-
import type { ExecutionContextStart, Logger } from '@kbn/core/server';
18+
import type {
19+
ExecutionContextStart,
20+
FakeRawRequest,
21+
Headers,
22+
IBasePath,
23+
KibanaRequest,
24+
Logger,
25+
} from '@kbn/core/server';
1926
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
2027
import type { UsageCounter } from '@kbn/usage-collection-plugin/server';
28+
import { addSpaceIdToPath } from '@kbn/spaces-plugin/server';
29+
import { kibanaRequestFactory } from '@kbn/core-http-server-utils';
2130
import type { Middleware } from '../lib/middleware';
2231
import type { Result } from '../lib/result_type';
2332
import {
@@ -103,6 +112,7 @@ export interface Updatable {
103112
}
104113

105114
type Opts = {
115+
basePathService: IBasePath;
106116
logger: Logger;
107117
definitions: TaskTypeDictionary;
108118
instance: ConcreteTaskInstance;
@@ -162,6 +172,7 @@ export class TaskManagerRunner implements TaskRunner {
162172
private onTaskEvent: (event: TaskRun | TaskMarkRunning | TaskManagerStat) => void;
163173
private defaultMaxAttempts: number;
164174
private uuid: string;
175+
private readonly basePathService: IBasePath;
165176
private readonly executionContext: ExecutionContextStart;
166177
private usageCounter?: UsageCounter;
167178
private config: TaskManagerConfig;
@@ -180,6 +191,7 @@ export class TaskManagerRunner implements TaskRunner {
180191
* @memberof TaskManagerRunner
181192
*/
182193
constructor({
194+
basePathService,
183195
instance,
184196
definitions,
185197
logger,
@@ -195,6 +207,7 @@ export class TaskManagerRunner implements TaskRunner {
195207
strategy,
196208
getPollInterval,
197209
}: Opts) {
210+
this.basePathService = basePathService;
198211
this.instance = asPending(sanitizeInstance(instance));
199212
this.definitions = definitions;
200213
this.logger = logger;
@@ -374,7 +387,12 @@ export class TaskManagerRunner implements TaskRunner {
374387
);
375388

376389
try {
377-
this.task = definition.createTaskRunner(modifiedContext);
390+
const sanitizedTaskInstance = omit(modifiedContext.taskInstance, ['apiKey', 'userScope']);
391+
const fakeRequest = this.getFakeKibanaRequest(
392+
modifiedContext.taskInstance.apiKey,
393+
modifiedContext.taskInstance.userScope?.spaceId
394+
);
395+
this.task = definition.createTaskRunner({ taskInstance: sanitizedTaskInstance, fakeRequest });
378396

379397
const ctx = {
380398
type: 'task manager',
@@ -825,6 +843,25 @@ export class TaskManagerRunner implements TaskRunner {
825843
private getMaxAttempts() {
826844
return this.definition?.maxAttempts ?? this.defaultMaxAttempts;
827845
}
846+
847+
private getFakeKibanaRequest(apiKey?: string, spaceId?: string): KibanaRequest | undefined {
848+
if (apiKey) {
849+
const requestHeaders: Headers = {};
850+
851+
requestHeaders.authorization = `ApiKey ${apiKey}`;
852+
const path = addSpaceIdToPath('/', spaceId || 'default');
853+
854+
const fakeRawRequest: FakeRawRequest = {
855+
headers: requestHeaders,
856+
path: '/',
857+
};
858+
859+
const fakeRequest = kibanaRequestFactory(fakeRawRequest);
860+
this.basePathService.set(fakeRequest, path);
861+
862+
return fakeRequest;
863+
}
864+
}
828865
}
829866

830867
function sanitizeInstance(instance: ConcreteTaskInstance): ConcreteTaskInstance {

x-pack/platform/plugins/shared/task_manager/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"@kbn/spaces-plugin",
3535
"@kbn/encrypted-saved-objects-shared",
3636
"@kbn/core-saved-objects-api-server-mocks",
37+
"@kbn/core-http-server-utils",
3738
],
3839
"exclude": ["target/**/*"]
3940
}

0 commit comments

Comments
 (0)