Skip to content

Commit

Permalink
Check auth status via security plugin on our privileges endpoint (#61334
Browse files Browse the repository at this point in the history
)

* Accounts for security being disabled, adds tests
* Updates other auth-aware endpoints (import timeline, graphql) to
account for security being disabled.
  • Loading branch information
rylnd committed Mar 26, 2020
1 parent 43d95da commit 9243b6d
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ export const updateActionResult = (): ActionResult => ({
config: {},
});

export const getMockPrivileges = () => ({
export const getMockPrivilegesResult = () => ({
username: 'test-space',
has_all_requested: false,
cluster: {
Expand Down Expand Up @@ -565,8 +565,6 @@ export const getMockPrivileges = () => ({
},
},
application: {},
is_authenticated: false,
has_encryption_key: true,
});

export const getFindResultStatusEmpty = (): SavedObjectsFindResponse<IRuleSavedAttributesSavedObjectAttributes> => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof serverMock.create>;
let { clients, context } = requestContextMock.createTools();
let mockSecurity: ReturnType<typeof securityMock.createSetup>;

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', () => {
Expand All @@ -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 () => {
Expand All @@ -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);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class KibanaBackendFrameworkAdapter implements FrameworkAdapter {

private async getCurrentUserInfo(request: KibanaRequest): Promise<AuthenticatedUser | null> {
try {
const user = await this.security.authc.getCurrentUser(request);
const user = (await this.security?.authc.getCurrentUser(request)) ?? null;
return user;
} catch {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ 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';

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;

Expand All @@ -46,7 +46,7 @@ const timelineLib = new Timeline();
export const importTimelinesRoute = (
router: IRouter,
config: LegacyServices['config'],
securityPluginSetup: SecurityPluginSetup
security: SetupPlugins['security']
) => {
router.post(
{
Expand Down Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/siem/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export interface SetupPlugins {
encryptedSavedObjects: EncryptedSavedObjectsSetup;
features: FeaturesSetup;
licensing: LicensingPluginSetup;
security: SecuritySetup;
security?: SecuritySetup;
spaces?: SpacesSetup;
}

Expand Down
6 changes: 3 additions & 3 deletions x-pack/legacy/plugins/siem/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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......
Expand Down Expand Up @@ -79,5 +79,5 @@ export const initRoutes = (
readTagsRoute(router);

// Privileges API to get the generic user privileges
readPrivilegesRoute(router, usingEphemeralEncryptionKey);
readPrivilegesRoute(router, security, usingEphemeralEncryptionKey);
};

0 comments on commit 9243b6d

Please sign in to comment.