diff --git a/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.test.ts b/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.test.ts index 00afeca65875b..f15871c118594 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.test.ts @@ -76,7 +76,7 @@ describe('api_key_utils', () => { api_key: 'apiKey', }); - const result = await createApiKey([mockTask], request, true, coreStart.security); + const result = await createApiKey([mockTask], request, coreStart.security); const apiKeyResult = result.get('task'); const decodedApiKey = Buffer.from(apiKeyResult!.apiKey, 'base64').toString(); expect(decodedApiKey).toEqual('apiKeyId:apiKey'); @@ -107,7 +107,7 @@ describe('api_key_utils', () => { coreStart.security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValueOnce(true); coreStart.security.authc.getCurrentUser = jest.fn().mockReturnValue(mockUser); - const result = await createApiKey([mockTask], request, true, coreStart.security); + const result = await createApiKey([mockTask], request, coreStart.security); const apiKeyResult = result.get('task'); const decodedApiKey = Buffer.from(apiKeyResult!.apiKey, 'base64').toString(); expect(decodedApiKey).toEqual('apiKeyId:apiKey'); @@ -117,26 +117,13 @@ describe('api_key_utils', () => { expect(coreStart.security.authc.apiKeys.grantAsInternalUser).not.toHaveBeenCalled(); }); - test('should throw if canEncryptSo is false', async () => { - const request = httpServerMock.createKibanaRequest(); - const coreStart = coreMock.createStart(); - await expect( - createApiKey([mockTask], request, false, coreStart.security) - ).rejects.toMatchObject({ - message: - 'Unable to create API keys because the Encrypted Saved Objects plugin has not been registered or is missing encryption key.', - }); - }); - test('should throw if API keys are not enabled', async () => { const request = httpServerMock.createKibanaRequest(); const coreStart = coreMock.createStart(); coreStart.security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValueOnce(false); coreStart.security.authc.getCurrentUser = jest.fn().mockReturnValue(null); - await expect( - createApiKey([mockTask], request, true, coreStart.security) - ).rejects.toMatchObject({ + await expect(createApiKey([mockTask], request, coreStart.security)).rejects.toMatchObject({ message: 'API keys are not enabled, cannot create API key.', }); }); @@ -146,9 +133,7 @@ describe('api_key_utils', () => { const coreStart = coreMock.createStart(); coreStart.security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValueOnce(true); - await expect( - createApiKey([mockTask], request, true, coreStart.security) - ).rejects.toMatchObject({ + await expect(createApiKey([mockTask], request, coreStart.security)).rejects.toMatchObject({ message: 'Cannot authenticate current user.', }); }); @@ -163,9 +148,7 @@ describe('api_key_utils', () => { coreStart.security.authc.apiKeys.areAPIKeysEnabled = jest.fn().mockReturnValueOnce(true); coreStart.security.authc.getCurrentUser = jest.fn().mockReturnValueOnce(mockUser); coreStart.security.authc.apiKeys.grantAsInternalUser = jest.fn().mockResolvedValueOnce(null); - await expect( - createApiKey([mockTask], request, true, coreStart.security) - ).rejects.toMatchObject({ + await expect(createApiKey([mockTask], request, coreStart.security)).rejects.toMatchObject({ message: 'Could not create API key.', }); }); @@ -198,7 +181,6 @@ describe('api_key_utils', () => { const result = await getApiKeyAndUserScope( [mockTask], request, - true, coreStart.security, spacesStart ); @@ -235,7 +217,6 @@ describe('api_key_utils', () => { const result = await getApiKeyAndUserScope( [mockTask], request, - true, coreStart.security, spacesStart ); @@ -271,7 +252,6 @@ describe('api_key_utils', () => { const result = await getApiKeyAndUserScope( [mockTask], request, - true, coreStart.security, spacesStart ); diff --git a/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.ts b/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.ts index 6479ac1e61a0b..44fcf66ff7098 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/lib/api_key_utils.ts @@ -55,15 +55,8 @@ export const getApiKeyFromRequest = (request: KibanaRequest) => { export const createApiKey = async ( taskInstances: TaskInstance[], request: KibanaRequest, - canEncryptSo: boolean, security: SecurityServiceStart ) => { - if (!canEncryptSo) { - throw Error( - 'Unable to create API keys because the Encrypted Saved Objects plugin has not been registered or is missing encryption key.' - ); - } - if (!(await security.authc.apiKeys.areAPIKeysEnabled())) { throw Error('API keys are not enabled, cannot create API key.'); } @@ -132,11 +125,10 @@ export const createApiKey = async ( export const getApiKeyAndUserScope = async ( taskInstances: TaskInstance[], request: KibanaRequest, - canEncryptSo: boolean, security: SecurityServiceStart, spaces?: SpacesPluginStart ): Promise> => { - const apiKeyByTaskIdMap = await createApiKey(taskInstances, request, canEncryptSo, security); + const apiKeyByTaskIdMap = await createApiKey(taskInstances, request, security); const space = await spaces?.spacesService.getActiveSpace(request); const user = security.authc.getCurrentUser(request); diff --git a/x-pack/platform/plugins/shared/task_manager/server/task_store.test.ts b/x-pack/platform/plugins/shared/task_manager/server/task_store.test.ts index 048f06cb86528..ce4842c6a1e9a 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/task_store.test.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/task_store.test.ts @@ -130,7 +130,7 @@ describe('TaskStore', () => { describe('schedule', () => { let store: TaskStore; - beforeAll(() => { + beforeEach(() => { store = new TaskStore({ logger: mockLogger(), index: 'tasky', @@ -322,7 +322,6 @@ describe('TaskStore', () => { expect(getApiKeyAndUserScope).toHaveBeenCalledWith( [task], request, - true, coreStart.security, spacesStart ); @@ -348,6 +347,41 @@ describe('TaskStore', () => { }); }); + test('errors when scheduling a task with API key if unable to encrypt SO', async () => { + store = new TaskStore({ + logger: mockLogger(), + index: 'tasky', + taskManagerId: '', + serializer, + esClient: elasticsearchServiceMock.createClusterClient().asInternalUser, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + adHocTaskCounter, + allowReadingInvalidState: false, + requestTimeouts: { + update_by_query: 1000, + }, + savedObjectsService: coreStart.savedObjects, + security: coreStart.security, + spaces: spacesStart, + canEncryptSavedObjects: false, + }); + + const task = { + id: 'id', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + traceparent: 'apmTraceparent', + }; + + const request = httpServerMock.createKibanaRequest(); + + await expect(store.schedule(task as TaskInstance, { request })).rejects.toThrow( + 'Unable to schedule task(s) with API keys because the Encrypted Saved Objects plugin has not been registered or is missing encryption key.' + ); + }); + test('errors if the task type is unknown', async () => { await expect(testSchedule({ taskType: 'nope', params: {}, state: {} })).rejects.toThrow( /Unsupported task type "nope"/i @@ -2073,7 +2107,7 @@ describe('TaskStore', () => { describe('bulkSchedule', () => { let store: TaskStore; - beforeAll(() => { + beforeEach(() => { store = new TaskStore({ logger: mockLogger(), index: 'tasky', @@ -2296,7 +2330,6 @@ describe('TaskStore', () => { expect(getApiKeyAndUserScope).toHaveBeenCalledWith( [task1, task2], request, - true, coreStart.security, spacesStart ); @@ -2343,6 +2376,40 @@ describe('TaskStore', () => { ]); }); + test('errors when bulk scheduling a task with API key if unable to encrypt SO', async () => { + store = new TaskStore({ + logger: mockLogger(), + index: 'tasky', + taskManagerId: '', + serializer, + esClient: elasticsearchServiceMock.createClusterClient().asInternalUser, + definitions: taskDefinitions, + savedObjectsRepository: savedObjectsClient, + adHocTaskCounter, + allowReadingInvalidState: false, + requestTimeouts: { + update_by_query: 1000, + }, + savedObjectsService: coreStart.savedObjects, + security: coreStart.security, + spaces: spacesStart, + canEncryptSavedObjects: false, + }); + + const task1 = { + id: 'task1', + params: { hello: 'world' }, + state: { foo: 'bar' }, + taskType: 'report', + }; + + const request = httpServerMock.createKibanaRequest(); + + await expect(store.bulkSchedule([task1 as TaskInstance], { request })).rejects.toThrow( + 'Unable to schedule task(s) with API keys because the Encrypted Saved Objects plugin has not been registered or is missing encryption key.' + ); + }); + test('errors if API key could not be created', async () => { const task = { id: 'id', diff --git a/x-pack/platform/plugins/shared/task_manager/server/task_store.ts b/x-pack/platform/plugins/shared/task_manager/server/task_store.ts index 78142627f3aec..eff2ca1add26e 100644 --- a/x-pack/platform/plugins/shared/task_manager/server/task_store.ts +++ b/x-pack/platform/plugins/shared/task_manager/server/task_store.ts @@ -196,6 +196,17 @@ export class TaskStore { return !!(this.esoClient && this.canEncryptSavedObjects); } + private validateCanEncryptSavedObjects(request?: KibanaRequest) { + if (!request) { + return; + } + if (!this.canEncryptSo()) { + throw Error( + 'Unable to schedule task(s) with API keys because the Encrypted Saved Objects plugin has not been registered or is missing encryption key.' + ); + } + } + private getSoClientForCreate(options: ApiKeyOptions) { if (options.request) { return this.savedObjectsService.getScopedClient(options.request, { @@ -206,7 +217,7 @@ export class TaskStore { return this.savedObjectsRepository; } - private async maybeGetApiKeyFromRequest(taskInstances: TaskInstance[], request?: KibanaRequest) { + private async getApiKeyFromRequest(taskInstances: TaskInstance[], request?: KibanaRequest) { if (!request) { return null; } @@ -216,7 +227,6 @@ export class TaskStore { userScopeAndApiKey = await getApiKeyAndUserScope( taskInstances, request, - this.canEncryptSo(), this.security, this.spaces ); @@ -303,10 +313,16 @@ export class TaskStore { taskInstance: TaskInstance, options?: ApiKeyOptions ): Promise { + try { + this.validateCanEncryptSavedObjects(options?.request); + } catch (e) { + this.errors$.next(e); + throw e; + } this.definitions.ensureHas(taskInstance.taskType); const apiKeyAndUserScopeMap = - (await this.maybeGetApiKeyFromRequest([taskInstance], options?.request)) || new Map(); + (await this.getApiKeyFromRequest([taskInstance], options?.request)) || new Map(); const { apiKey, userScope } = apiKeyAndUserScopeMap.get(taskInstance.id) || {}; const soClient = this.getSoClientForCreate(options || {}); @@ -351,8 +367,14 @@ export class TaskStore { taskInstances: TaskInstance[], options?: ApiKeyOptions ): Promise { + try { + this.validateCanEncryptSavedObjects(options?.request); + } catch (e) { + this.errors$.next(e); + throw e; + } const apiKeyAndUserScopeMap = - (await this.maybeGetApiKeyFromRequest(taskInstances, options?.request)) || new Map(); + (await this.getApiKeyFromRequest(taskInstances, options?.request)) || new Map(); const soClient = this.getSoClientForCreate(options || {});