From e94ee5f00491c6214d6cef3368cb0b4f1c3060e4 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Wed, 25 Mar 2020 21:39:52 -0500 Subject: [PATCH] Check auth status via security plugin on our privileges endpoint (#61334) (#61383) * Accounts for security being disabled, adds tests * Updates other auth-aware endpoints (import timeline, graphql) to account for security being disabled. --- .../routes/__mocks__/request_responses.ts | 4 +- .../privileges/read_privileges_route.test.ts | 56 +++++++++++++++++-- .../privileges/read_privileges_route.ts | 9 ++- .../lib/framework/kibana_framework_adapter.ts | 2 +- .../timeline/routes/import_timelines_route.ts | 6 +- x-pack/legacy/plugins/siem/server/plugin.ts | 2 +- .../plugins/siem/server/routes/index.ts | 6 +- 7 files changed, 66 insertions(+), 19 deletions(-) diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 24f50c5ce87a0..3b24b722f7356 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -514,7 +514,7 @@ export const updateActionResult = (): ActionResult => ({ config: {}, }); -export const getMockPrivileges = () => ({ +export const getMockPrivilegesResult = () => ({ username: 'test-space', has_all_requested: false, cluster: { @@ -565,8 +565,6 @@ export const getMockPrivileges = () => ({ }, }, application: {}, - is_authenticated: false, - has_encryption_key: true, }); export const getFindResultStatusEmpty = (): SavedObjectsFindResponse => ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts index 44050f21a1259..aa4f6150889f9 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.test.ts @@ -4,20 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ +import { securityMock } from '../../../../../../../../plugins/security/server/mocks'; import { readPrivilegesRoute } from './read_privileges_route'; import { serverMock, requestContextMock } from '../__mocks__'; -import { getPrivilegeRequest, getMockPrivileges } from '../__mocks__/request_responses'; +import { getPrivilegeRequest, getMockPrivilegesResult } from '../__mocks__/request_responses'; -describe('read_privileges', () => { +describe('read_privileges route', () => { let server: ReturnType; let { clients, context } = requestContextMock.createTools(); + let mockSecurity: ReturnType; beforeEach(() => { server = serverMock.create(); ({ clients, context } = requestContextMock.createTools()); - clients.clusterClient.callAsCurrentUser.mockResolvedValue(getMockPrivileges()); - readPrivilegesRoute(server.router, false); + mockSecurity = securityMock.createSetup(); + mockSecurity.authc.isAuthenticated.mockReturnValue(false); + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getMockPrivilegesResult()); + readPrivilegesRoute(server.router, mockSecurity, false); }); describe('normal status codes', () => { @@ -26,10 +30,28 @@ describe('read_privileges', () => { expect(response.status).toEqual(200); }); - test.skip('returns the payload when doing a normal request', async () => { + test('returns the payload when doing a normal request', async () => { const response = await server.inject(getPrivilegeRequest(), context); + const expectedBody = { + ...getMockPrivilegesResult(), + is_authenticated: false, + has_encryption_key: true, + }; expect(response.status).toEqual(200); - expect(response.body).toEqual(getMockPrivileges()); + expect(response.body).toEqual(expectedBody); + }); + + test('is authenticated when security says so', async () => { + mockSecurity.authc.isAuthenticated.mockReturnValue(true); + const expectedBody = { + ...getMockPrivilegesResult(), + is_authenticated: true, + has_encryption_key: true, + }; + + const response = await server.inject(getPrivilegeRequest(), context); + expect(response.status).toEqual(200); + expect(response.body).toEqual(expectedBody); }); test('returns 500 when bad response from cluster', async () => { @@ -41,4 +63,26 @@ describe('read_privileges', () => { expect(response.body).toEqual({ message: 'Test error', status_code: 500 }); }); }); + + describe('when security plugin is disabled', () => { + beforeEach(() => { + server = serverMock.create(); + ({ clients, context } = requestContextMock.createTools()); + + clients.clusterClient.callAsCurrentUser.mockResolvedValue(getMockPrivilegesResult()); + readPrivilegesRoute(server.router, undefined, false); + }); + + it('returns unauthenticated', async () => { + const expectedBody = { + ...getMockPrivilegesResult(), + is_authenticated: false, + has_encryption_key: true, + }; + + const response = await server.inject(getPrivilegeRequest(), context); + expect(response.status).toEqual(200); + expect(response.body).toEqual(expectedBody); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts index 81ce4a9c3f2fc..2f5ea4d1ec767 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/privileges/read_privileges_route.ts @@ -8,10 +8,15 @@ import { merge } from 'lodash/fp'; import { IRouter } from '../../../../../../../../../src/core/server'; import { DETECTION_ENGINE_PRIVILEGES_URL } from '../../../../../common/constants'; +import { SetupPlugins } from '../../../../plugin'; import { buildSiemResponse, transformError } from '../utils'; import { readPrivileges } from '../../privileges/read_privileges'; -export const readPrivilegesRoute = (router: IRouter, usingEphemeralEncryptionKey: boolean) => { +export const readPrivilegesRoute = ( + router: IRouter, + security: SetupPlugins['security'], + usingEphemeralEncryptionKey: boolean +) => { router.get( { path: DETECTION_ENGINE_PRIVILEGES_URL, @@ -29,7 +34,7 @@ export const readPrivilegesRoute = (router: IRouter, usingEphemeralEncryptionKey const index = siemClient.signalsIndex; const clusterPrivileges = await readPrivileges(clusterClient.callAsCurrentUser, index); const privileges = merge(clusterPrivileges, { - is_authenticated: true, // until we support optional auth: https://github.com/elastic/kibana/pull/55327#issuecomment-577159911 + is_authenticated: security?.authc.isAuthenticated(request) ?? false, has_encryption_key: !usingEphemeralEncryptionKey, }); diff --git a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts index 004ac36bad5b4..6b41426e047ca 100644 --- a/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/framework/kibana_framework_adapter.ts @@ -122,7 +122,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter { private async getCurrentUserInfo(request: KibanaRequest): Promise { try { - const user = await this.security.authc.getCurrentUser(request); + const user = (await this.security?.authc.getCurrentUser(request)) ?? null; return user; } catch { return null; diff --git a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts index fefe31b2f36d0..2b41b4e7843a7 100644 --- a/x-pack/legacy/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/timeline/routes/import_timelines_route.ts @@ -30,6 +30,7 @@ import { import { IRouter } from '../../../../../../../../src/core/server'; import { TIMELINE_IMPORT_URL } from '../../../../common/constants'; +import { SetupPlugins } from '../../../plugin'; import { importTimelinesPayloadSchema } from './schemas/import_timelines_schema'; import { importRulesSchema } from '../../detection_engine/routes/schemas/response/import_rules_schema'; import { LegacyServices } from '../../../types'; @@ -37,7 +38,6 @@ import { LegacyServices } from '../../../types'; import { Timeline } from '../saved_object'; import { validate } from '../../detection_engine/routes/rules/validate'; import { FrameworkRequest } from '../../framework'; -import { SecurityPluginSetup } from '../../../../../../../plugins/security/server'; const CHUNK_PARSED_OBJECT_SIZE = 10; @@ -46,7 +46,7 @@ const timelineLib = new Timeline(); export const importTimelinesRoute = ( router: IRouter, config: LegacyServices['config'], - securityPluginSetup: SecurityPluginSetup + security: SetupPlugins['security'] ) => { router.post( { @@ -96,7 +96,7 @@ export const importTimelinesRoute = ( const chunkParseObjects = chunk(CHUNK_PARSED_OBJECT_SIZE, uniqueParsedObjects); let importTimelineResponse: ImportTimelineResponse[] = []; - const user = await securityPluginSetup.authc.getCurrentUser(request); + const user = await security?.authc.getCurrentUser(request); let frameworkRequest = set('context.core.savedObjects.client', savedObjectsClient, request); frameworkRequest = set('user', user, frameworkRequest); diff --git a/x-pack/legacy/plugins/siem/server/plugin.ts b/x-pack/legacy/plugins/siem/server/plugin.ts index 2bce9b6a7e1aa..98631ea220a54 100644 --- a/x-pack/legacy/plugins/siem/server/plugin.ts +++ b/x-pack/legacy/plugins/siem/server/plugin.ts @@ -46,7 +46,7 @@ export interface SetupPlugins { encryptedSavedObjects: EncryptedSavedObjectsSetup; features: FeaturesSetup; licensing: LicensingPluginSetup; - security: SecuritySetup; + security?: SecuritySetup; spaces?: SpacesSetup; } diff --git a/x-pack/legacy/plugins/siem/server/routes/index.ts b/x-pack/legacy/plugins/siem/server/routes/index.ts index 29c21ad157235..8c9f92890c26a 100644 --- a/x-pack/legacy/plugins/siem/server/routes/index.ts +++ b/x-pack/legacy/plugins/siem/server/routes/index.ts @@ -31,13 +31,13 @@ import { findRulesStatusesRoute } from '../lib/detection_engine/routes/rules/fin import { getPrepackagedRulesStatusRoute } from '../lib/detection_engine/routes/rules/get_prepackaged_rules_status_route'; import { importTimelinesRoute } from '../lib/timeline/routes/import_timelines_route'; import { exportTimelinesRoute } from '../lib/timeline/routes/export_timelines_route'; -import { SecurityPluginSetup } from '../../../../../plugins/security/server/'; +import { SetupPlugins } from '../plugin'; export const initRoutes = ( router: IRouter, config: LegacyServices['config'], usingEphemeralEncryptionKey: boolean, - security: SecurityPluginSetup + security: SetupPlugins['security'] ) => { // Detection Engine Rule routes that have the REST endpoints of /api/detection_engine/rules // All REST rule creation, deletion, updating, etc...... @@ -79,5 +79,5 @@ export const initRoutes = ( readTagsRoute(router); // Privileges API to get the generic user privileges - readPrivilegesRoute(router, usingEphemeralEncryptionKey); + readPrivilegesRoute(router, security, usingEphemeralEncryptionKey); };