From 91e8e3e883d51634a6b958c0ccd8d0011fdc5559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Mon, 23 Mar 2020 12:39:55 -0400 Subject: [PATCH] Adding `authc.invalidateAPIKeyAsInternalUser` (#60717) * Initial work * Fix type check issues * Fix test failures * Fix ESLint issues * Add back comment * PR feedback Co-authored-by: Elastic Machine --- .../server/authentication/api_keys.test.ts | 52 +++++++++++++++++++ .../server/authentication/api_keys.ts | 49 ++++++++++++----- .../server/authentication/index.mock.ts | 1 + .../server/authentication/index.test.ts | 23 +++++++- .../security/server/authentication/index.ts | 2 + x-pack/plugins/security/server/plugin.test.ts | 1 + 6 files changed, 115 insertions(+), 13 deletions(-) diff --git a/x-pack/plugins/security/server/authentication/api_keys.test.ts b/x-pack/plugins/security/server/authentication/api_keys.test.ts index 78b1d5f8e30b..836740d0a547 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.test.ts @@ -225,4 +225,56 @@ describe('API Keys', () => { ); }); }); + + describe('invalidateAsInternalUser()', () => { + it('returns null when security feature is disabled', async () => { + mockLicense.isEnabled.mockReturnValue(false); + const result = await apiKeys.invalidateAsInternalUser({ id: '123' }); + expect(result).toBeNull(); + expect(mockClusterClient.callAsInternalUser).not.toHaveBeenCalled(); + }); + + it('calls callCluster with proper parameters', async () => { + mockLicense.isEnabled.mockReturnValue(true); + mockClusterClient.callAsInternalUser.mockResolvedValueOnce({ + invalidated_api_keys: ['api-key-id-1'], + previously_invalidated_api_keys: [], + error_count: 0, + }); + const result = await apiKeys.invalidateAsInternalUser({ id: '123' }); + expect(result).toEqual({ + invalidated_api_keys: ['api-key-id-1'], + previously_invalidated_api_keys: [], + error_count: 0, + }); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.invalidateAPIKey', { + body: { + id: '123', + }, + }); + }); + + it('Only passes id as a parameter', async () => { + mockLicense.isEnabled.mockReturnValue(true); + mockClusterClient.callAsInternalUser.mockResolvedValueOnce({ + invalidated_api_keys: ['api-key-id-1'], + previously_invalidated_api_keys: [], + error_count: 0, + }); + const result = await apiKeys.invalidateAsInternalUser({ + id: '123', + name: 'abc', + } as any); + expect(result).toEqual({ + invalidated_api_keys: ['api-key-id-1'], + previously_invalidated_api_keys: [], + error_count: 0, + }); + expect(mockClusterClient.callAsInternalUser).toHaveBeenCalledWith('shield.invalidateAPIKey', { + body: { + id: '123', + }, + }); + }); + }); }); diff --git a/x-pack/plugins/security/server/authentication/api_keys.ts b/x-pack/plugins/security/server/authentication/api_keys.ts index 0d77207e390a..9df7219cec33 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.ts @@ -193,26 +193,51 @@ export class APIKeys { * @param request Request instance. * @param params The params to invalidate an API key. */ - async invalidate( - request: KibanaRequest, - params: InvalidateAPIKeyParams - ): Promise { + async invalidate(request: KibanaRequest, params: InvalidateAPIKeyParams) { + if (!this.license.isEnabled()) { + return null; + } + + this.logger.debug('Trying to invalidate an API key as current user'); + + let result: InvalidateAPIKeyResult; + try { + // User needs `manage_api_key` privilege to use this API + result = await this.clusterClient + .asScoped(request) + .callAsCurrentUser('shield.invalidateAPIKey', { + body: { + id: params.id, + }, + }); + this.logger.debug('API key was invalidated successfully as current user'); + } catch (e) { + this.logger.error(`Failed to invalidate API key as current user: ${e.message}`); + throw e; + } + + return result; + } + + /** + * Tries to invalidate an API key by using the internal user. + * @param params The params to invalidate an API key. + */ + async invalidateAsInternalUser(params: InvalidateAPIKeyParams) { if (!this.license.isEnabled()) { return null; } this.logger.debug('Trying to invalidate an API key'); - // User needs `manage_api_key` privilege to use this API let result: InvalidateAPIKeyResult; try { - result = (await this.clusterClient - .asScoped(request) - .callAsCurrentUser('shield.invalidateAPIKey', { - body: { - id: params.id, - }, - })) as InvalidateAPIKeyResult; + // Internal user needs `cluster:admin/xpack/security/api_key/invalidate` privilege to use this API + result = await this.clusterClient.callAsInternalUser('shield.invalidateAPIKey', { + body: { + id: params.id, + }, + }); this.logger.debug('API key was invalidated successfully'); } catch (e) { this.logger.error(`Failed to invalidate API key: ${e.message}`); diff --git a/x-pack/plugins/security/server/authentication/index.mock.ts b/x-pack/plugins/security/server/authentication/index.mock.ts index 512de9626a98..43892753f0d3 100644 --- a/x-pack/plugins/security/server/authentication/index.mock.ts +++ b/x-pack/plugins/security/server/authentication/index.mock.ts @@ -15,6 +15,7 @@ export const authenticationMock = { getCurrentUser: jest.fn(), grantAPIKeyAsInternalUser: jest.fn(), invalidateAPIKey: jest.fn(), + invalidateAPIKeyAsInternalUser: jest.fn(), isAuthenticated: jest.fn(), getSessionInfo: jest.fn(), }), diff --git a/x-pack/plugins/security/server/authentication/index.test.ts b/x-pack/plugins/security/server/authentication/index.test.ts index e364dbf39db6..21e5f18bc028 100644 --- a/x-pack/plugins/security/server/authentication/index.test.ts +++ b/x-pack/plugins/security/server/authentication/index.test.ts @@ -33,7 +33,7 @@ import { import { AuthenticatedUser } from '../../common/model'; import { ConfigType, createConfig$ } from '../config'; import { AuthenticationResult } from './authentication_result'; -import { setupAuthentication } from '.'; +import { Authentication, setupAuthentication } from '.'; import { CreateAPIKeyResult, CreateAPIKeyParams, @@ -410,4 +410,25 @@ describe('setupAuthentication()', () => { expect(apiKeysInstance.invalidate).toHaveBeenCalledWith(request, params); }); }); + + describe('invalidateAPIKeyAsInternalUser()', () => { + let invalidateAPIKeyAsInternalUser: Authentication['invalidateAPIKeyAsInternalUser']; + + beforeEach(async () => { + invalidateAPIKeyAsInternalUser = (await setupAuthentication(mockSetupAuthenticationParams)) + .invalidateAPIKeyAsInternalUser; + }); + + it('calls invalidateAPIKeyAsInternalUser with given arguments', async () => { + const apiKeysInstance = jest.requireMock('./api_keys').APIKeys.mock.instances[0]; + const params = { + id: '123', + }; + apiKeysInstance.invalidateAsInternalUser.mockResolvedValueOnce({ success: true }); + await expect(invalidateAPIKeyAsInternalUser(params)).resolves.toEqual({ + success: true, + }); + expect(apiKeysInstance.invalidateAsInternalUser).toHaveBeenCalledWith(params); + }); + }); }); diff --git a/x-pack/plugins/security/server/authentication/index.ts b/x-pack/plugins/security/server/authentication/index.ts index 8b42b2325ee1..c5c72853e68e 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -176,6 +176,8 @@ export async function setupAuthentication({ grantAPIKeyAsInternalUser: (request: KibanaRequest) => apiKeys.grantAsInternalUser(request), invalidateAPIKey: (request: KibanaRequest, params: InvalidateAPIKeyParams) => apiKeys.invalidate(request, params), + invalidateAPIKeyAsInternalUser: (params: InvalidateAPIKeyParams) => + apiKeys.invalidateAsInternalUser(params), isAuthenticated: (request: KibanaRequest) => http.auth.isAuthenticated(request), }; } diff --git a/x-pack/plugins/security/server/plugin.test.ts b/x-pack/plugins/security/server/plugin.test.ts index b817bcc0858a..a011f7e7be11 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -76,6 +76,7 @@ describe('Security Plugin', () => { "getSessionInfo": [Function], "grantAPIKeyAsInternalUser": [Function], "invalidateAPIKey": [Function], + "invalidateAPIKeyAsInternalUser": [Function], "isAuthenticated": [Function], "isProviderEnabled": [Function], "login": [Function],