From f8499e0ab7e6eab9a0a69c78898d5e0b309fae7e 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 | 41 ++++++++++++--- .../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, 111 insertions(+), 9 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 bcb212e7bbf94..14475e54e3645 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.test.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.test.ts @@ -142,4 +142,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 2b1a93d907471..8174d16a2f4f0 100644 --- a/x-pack/plugins/security/server/authentication/api_keys.ts +++ b/x-pack/plugins/security/server/authentication/api_keys.ts @@ -136,26 +136,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'); + this.logger.debug('Trying to invalidate an API key as current user'); - // User needs `manage_api_key` privilege to use this API let result: InvalidateAPIKeyResult; try { - result = (await this.clusterClient + // User needs `manage_api_key` privilege to use this API + result = await this.clusterClient .asScoped(request) .callAsCurrentUser('shield.invalidateAPIKey', { body: { id: params.id, }, - })) as InvalidateAPIKeyResult; + }); + 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'); + + let result: InvalidateAPIKeyResult; + try { + // 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 c634e2c80c299..b734908183bf9 100644 --- a/x-pack/plugins/security/server/authentication/index.mock.ts +++ b/x-pack/plugins/security/server/authentication/index.mock.ts @@ -14,6 +14,7 @@ export const authenticationMock = { createAPIKey: jest.fn(), getCurrentUser: 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 973c322285db1..37852fbb8549b 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, @@ -438,4 +438,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 5109c174344d8..d346d819e6601 100644 --- a/x-pack/plugins/security/server/authentication/index.ts +++ b/x-pack/plugins/security/server/authentication/index.ts @@ -183,6 +183,8 @@ export async function setupAuthentication({ apiKeys.create(request, params), 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 a1ef352056d6a..eeb4985f97f79 100644 --- a/x-pack/plugins/security/server/plugin.test.ts +++ b/x-pack/plugins/security/server/plugin.test.ts @@ -75,6 +75,7 @@ describe('Security Plugin', () => { "getCurrentUser": [Function], "getSessionInfo": [Function], "invalidateAPIKey": [Function], + "invalidateAPIKeyAsInternalUser": [Function], "isAuthenticated": [Function], "isProviderEnabled": [Function], "login": [Function],