Skip to content

Commit

Permalink
[Cases] RBAC: Create & Find integration tests (#95511)
Browse files Browse the repository at this point in the history
  • Loading branch information
cnasikas authored Apr 2, 2021
1 parent 7cf9172 commit 0a95e55
Show file tree
Hide file tree
Showing 42 changed files with 851 additions and 158 deletions.
4 changes: 2 additions & 2 deletions x-pack/plugins/cases/common/api/cases/case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,10 @@ export const CasesFindRequestRt = rt.partial({
page: NumberFromString,
perPage: NumberFromString,
search: rt.string,
searchFields: rt.array(rt.string),
searchFields: rt.union([rt.array(rt.string), rt.string]),
sortField: rt.string,
sortOrder: rt.union([rt.literal('desc'), rt.literal('asc')]),
class: rt.string,
owner: rt.union([rt.array(rt.string), rt.string]),
});

export const CaseResponseRt = rt.intersection([
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/cases/common/api/runtime_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ const getExcessProps = (props: rt.Props, r: Record<string, unknown>): string[] =
return ex;
};

export function excess<C extends rt.InterfaceType<rt.Props>>(codec: C): C {
export function excess<C extends rt.InterfaceType<rt.Props> | rt.PartialType<rt.Props>>(
codec: C
): C {
const r = new rt.InterfaceType(
codec.name,
codec.is,
Expand Down
5 changes: 4 additions & 1 deletion x-pack/plugins/cases/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export const CASE_CONFIGURE_SAVED_OBJECT = 'cases-configure';
export const SAVED_OBJECT_TYPES = [
CASE_SAVED_OBJECT,
CASE_CONNECTOR_MAPPINGS_SAVED_OBJECT,
SUB_CASE_SAVED_OBJECT,
CASE_USER_ACTION_SAVED_OBJECT,
CASE_COMMENT_SAVED_OBJECT,
CASE_CONFIGURE_SAVED_OBJECT,
Expand Down Expand Up @@ -82,3 +81,7 @@ export const SECURITY_SOLUTION_OWNER = 'securitySolution';
* This flag governs enabling the case as a connector feature. It is disabled by default as the feature is not complete.
*/
export const ENABLE_CASE_CONNECTOR = false;

if (ENABLE_CASE_CONNECTOR) {
SAVED_OBJECT_TYPES.push(SUB_CASE_SAVED_OBJECT);
}
20 changes: 20 additions & 0 deletions x-pack/plugins/cases/server/authorization/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import type { PublicMethodsOf } from '@kbn/utility-types';
import { Authorization } from './authorization';

type Schema = PublicMethodsOf<Authorization>;
export type AuthorizationMock = jest.Mocked<Schema>;

export const createAuthorizationMock = () => {
const mocked: AuthorizationMock = {
ensureAuthorized: jest.fn(),
getFindAuthorizationFilter: jest.fn(),
};
return mocked;
};
3 changes: 2 additions & 1 deletion x-pack/plugins/cases/server/client/cases/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import type { PublicMethodsOf } from '@kbn/utility-types';
import { SavedObjectsClientContract, Logger } from 'src/core/server';
import { flattenCaseSavedObject, transformNewCase } from '../../routes/api/utils';

Expand Down Expand Up @@ -47,7 +48,7 @@ interface CreateCaseArgs {
userActionService: CaseUserActionServiceSetup;
theCase: CasePostRequest;
logger: Logger;
auth: Authorization;
auth: PublicMethodsOf<Authorization>;
}

/**
Expand Down
13 changes: 11 additions & 2 deletions x-pack/plugins/cases/server/client/cases/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import type { PublicMethodsOf } from '@kbn/utility-types';
import {
CasesFindResponse,
CasesFindRequest,
CasesFindRequestRt,
throwErrors,
caseStatuses,
CasesFindResponseRt,
excess,
} from '../../../common/api';

import { CASE_SAVED_OBJECT } from '../../../common/constants';
Expand All @@ -32,7 +34,7 @@ interface FindParams {
savedObjectsClient: SavedObjectsClientContract;
caseService: CaseServiceSetup;
logger: Logger;
auth: Authorization;
auth: PublicMethodsOf<Authorization>;
options: CasesFindRequest;
}

Expand All @@ -48,7 +50,7 @@ export const find = async ({
}: FindParams): Promise<CasesFindResponse> => {
try {
const queryParams = pipe(
CasesFindRequestRt.decode(options),
excess(CasesFindRequestRt).decode(options),
fold(throwErrors(Boom.badRequest), identity)
);

Expand All @@ -64,6 +66,7 @@ export const find = async ({
sortByField: queryParams.sortField,
status: queryParams.status,
caseType: queryParams.type,
owner: queryParams.owner,
};

const caseQueries = constructQueryOptions({ ...queryArgs, authorizationFilter });
Expand All @@ -72,6 +75,12 @@ export const find = async ({
caseOptions: {
...queryParams,
...caseQueries.case,
searchFields:
queryParams.searchFields != null
? Array.isArray(queryParams.searchFields)
? queryParams.searchFields
: [queryParams.searchFields]
: queryParams.searchFields,
fields: queryParams.fields
? includeFieldsRequiredForAuthentication(queryParams.fields)
: queryParams.fields,
Expand Down
15 changes: 13 additions & 2 deletions x-pack/plugins/cases/server/client/cases/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
SavedObjectsFindResult,
Logger,
} from 'kibana/server';

import { nodeBuilder } from '../../../../../../src/plugins/data/common';
import {
flattenCaseSavedObject,
isCommentRequestTypeAlertOrGenAlert,
Expand Down Expand Up @@ -134,7 +136,13 @@ async function throwIfInvalidUpdateOfTypeWithAlerts({
options: {
fields: [],
// there should never be generated alerts attached to an individual case but we'll check anyway
filter: `${CASE_COMMENT_SAVED_OBJECT}.attributes.type: ${CommentType.alert} OR ${CASE_COMMENT_SAVED_OBJECT}.attributes.type: ${CommentType.generatedAlert}`,
filter: nodeBuilder.or([
nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`, CommentType.alert),
nodeBuilder.is(
`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`,
CommentType.generatedAlert
),
]),
page: 1,
perPage: 1,
},
Expand Down Expand Up @@ -191,7 +199,10 @@ async function getAlertComments({
id: idsOfCasesToSync,
includeSubCaseComments: true,
options: {
filter: `${CASE_COMMENT_SAVED_OBJECT}.attributes.type: ${CommentType.alert} OR ${CASE_COMMENT_SAVED_OBJECT}.attributes.type: ${CommentType.generatedAlert}`,
filter: nodeBuilder.or([
nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`, CommentType.alert),
nodeBuilder.is(`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`, CommentType.generatedAlert),
]),
},
});
}
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/cases/server/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import type { PublicMethodsOf } from '@kbn/utility-types';
import { ElasticsearchClient, SavedObjectsClientContract, Logger } from 'src/core/server';
import {
CasesClientConstructorArguments,
Expand Down Expand Up @@ -53,7 +54,7 @@ export class CasesClientHandler implements CasesClient {
private readonly _userActionService: CaseUserActionServiceSetup;
private readonly _alertsService: AlertServiceContract;
private readonly logger: Logger;
private readonly authorization: Authorization;
private readonly authorization: PublicMethodsOf<Authorization>;

constructor(clientArgs: CasesClientConstructorArguments) {
this._scopedClusterClient = clientArgs.scopedClusterClient;
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/cases/server/client/comments/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import { SavedObject, SavedObjectsClientContract, Logger } from 'src/core/server';
import { nodeBuilder } from '../../../../../../src/plugins/data/common';
import { decodeCommentRequest, isCommentRequestTypeGenAlert } from '../../routes/api/utils';

import {
Expand Down Expand Up @@ -63,7 +64,10 @@ async function getSubCase({
id: mostRecentSubCase.id,
options: {
fields: [],
filter: `${CASE_COMMENT_SAVED_OBJECT}.attributes.type: ${CommentType.generatedAlert}`,
filter: nodeBuilder.is(
`${CASE_COMMENT_SAVED_OBJECT}.attributes.type`,
CommentType.generatedAlert
),
page: 1,
perPage: 1,
},
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/cases/server/client/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
createUserActionServiceMock,
createAlertServiceMock,
} from '../services/mocks';
import { createAuthorizationMock } from '../authorization/mock';

jest.mock('./client');
import { CasesClientHandler } from './client';
Expand All @@ -31,6 +32,7 @@ const caseService = createCaseServiceMock();
const connectorMappingsService = connectorMappingsServiceMock();
const savedObjectsClient = savedObjectsClientMock.create();
const userActionService = createUserActionServiceMock();
const authorization = createAuthorizationMock();

describe('createExternalCasesClient()', () => {
test('it creates the client correctly', async () => {
Expand All @@ -44,6 +46,7 @@ describe('createExternalCasesClient()', () => {
savedObjectsClient,
userActionService,
logger,
authorization,
});
expect(CasesClientHandler).toHaveBeenCalledTimes(1);
});
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/cases/server/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import type { PublicMethodsOf } from '@kbn/utility-types';
import { ElasticsearchClient, SavedObjectsClientContract, Logger } from 'kibana/server';
import { ActionsClient } from '../../../actions/server';
import {
Expand Down Expand Up @@ -78,7 +79,7 @@ export interface CasesClientConstructorArguments {
userActionService: CaseUserActionServiceSetup;
alertsService: AlertServiceContract;
logger: Logger;
authorization: Authorization;
authorization: PublicMethodsOf<Authorization>;
}

export interface ConfigureFields {
Expand Down
32 changes: 1 addition & 31 deletions x-pack/plugins/cases/server/common/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { SavedObjectsFindResponse } from 'kibana/server';
import { AssociationType, CommentAttributes, CommentRequest, CommentType } from '../../common/api';
import { transformNewComment } from '../routes/api/utils';
import { combineFilters, countAlerts, countAlertsForID, groupTotalAlertsByID } from './utils';
import { countAlerts, countAlertsForID, groupTotalAlertsByID } from './utils';

interface CommentReference {
ids: string[];
Expand Down Expand Up @@ -47,36 +47,6 @@ function createCommentFindResponse(
}

describe('common utils', () => {
describe('combineFilters', () => {
it("creates a filter string with two values and'd together", () => {
expect(combineFilters(['a', 'b'], 'AND')).toBe('(a AND b)');
});

it('creates a filter string with three values or together', () => {
expect(combineFilters(['a', 'b', 'c'], 'OR')).toBe('(a OR b OR c)');
});

it('ignores empty strings', () => {
expect(combineFilters(['', 'a', '', 'b'], 'AND')).toBe('(a AND b)');
});

it('returns an empty string if all filters are empty strings', () => {
expect(combineFilters(['', ''], 'OR')).toBe('');
});

it('returns an empty string if the filters are undefined', () => {
expect(combineFilters(undefined, 'OR')).toBe('');
});

it('returns a value without parenthesis when only a single filter is provided', () => {
expect(combineFilters(['a'], 'OR')).toBe('a');
});

it('returns a string without parenthesis when only a single non empty filter is provided', () => {
expect(combineFilters(['', ''], 'AND')).toBe('');
});
});

describe('countAlerts', () => {
it('returns 0 when no alerts are found', () => {
expect(
Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/cases/server/connectors/case/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,7 @@ describe('case connector', () => {
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
};

mockCasesClient.create.mockReturnValue(Promise.resolve(createReturn));
Expand Down Expand Up @@ -1077,6 +1078,7 @@ describe('case connector', () => {
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
},
];

Expand Down Expand Up @@ -1168,6 +1170,7 @@ describe('case connector', () => {
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
};

mockCasesClient.addComment.mockReturnValue(Promise.resolve(commentReturn));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
},
references: [],
updated_at: '2019-11-25T21:54:48.952Z',
Expand Down Expand Up @@ -96,6 +97,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
},
references: [],
updated_at: '2019-11-25T22:32:00.900Z',
Expand Down Expand Up @@ -138,6 +140,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
},
references: [],
updated_at: '2019-11-25T22:32:17.947Z',
Expand Down Expand Up @@ -184,6 +187,7 @@ export const mockCases: Array<SavedObject<ESCaseAttributes>> = [
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
},
references: [],
updated_at: '2019-11-25T22:32:17.947Z',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const newCase: CasePostRequest = {
settings: {
syncAlerts: true,
},
owner: 'securitySolution',
};

export const getActions = (): FindActionResult[] => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { pipe } from 'fp-ts/lib/pipeable';
import { fold } from 'fp-ts/lib/Either';
import { identity } from 'fp-ts/lib/function';

import { esKuery } from '../../../../../../../../src/plugins/data/server';
import {
AssociationType,
CommentsResponseRt,
Expand Down Expand Up @@ -63,6 +64,7 @@ export function initFindCaseCommentsApi({ caseService, router, logger }: RouteDe

const id = query.subCaseId ?? request.params.case_id;
const associationType = query.subCaseId ? AssociationType.subCase : AssociationType.case;
const { filter, ...queryWithoutFilter } = query;
const args = query
? {
caseService,
Expand All @@ -75,7 +77,8 @@ export function initFindCaseCommentsApi({ caseService, router, logger }: RouteDe
page: defaultPage,
perPage: defaultPerPage,
sortField: 'created_at',
...query,
filter: filter != null ? esKuery.fromKueryExpression(filter) : filter,
...queryWithoutFilter,
},
associationType,
}
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/cases/server/routes/api/cases/find_cases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function initFindCasesApi({ caseService, router, logger }: RouteDeps) {
return response.badRequest({ body: 'RouteHandlerContext is not registered for cases' });
}
const casesClient = await context.cases.getCasesClient();
const options = request.body as CasesFindRequest;
const options = request.query as CasesFindRequest;

return response.ok({
body: await casesClient.find({ ...options }),
Expand Down
Loading

0 comments on commit 0a95e55

Please sign in to comment.