mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Cases] Get all categories API (#159608)
## Summary An API to return all categories available to a user. This will be used for filtering and for the select box when setting the category of a case.
This commit is contained in:
parent
1a70ede796
commit
d8185993f1
17 changed files with 457 additions and 4 deletions
|
@ -383,9 +383,20 @@ export const AllTagsFindRequestRt = rt.exact(
|
|||
})
|
||||
);
|
||||
|
||||
export const AllCategoriesFindRequestRt = rt.exact(
|
||||
rt.partial({
|
||||
/**
|
||||
* The owner of the cases to retrieve the categories from. If no owner is provided the categories
|
||||
* from all cases that the user has access to will be returned.
|
||||
*/
|
||||
owner: rt.union([rt.array(rt.string), rt.string]),
|
||||
})
|
||||
);
|
||||
|
||||
export const AllReportersFindRequestRt = AllTagsFindRequestRt;
|
||||
|
||||
export const GetTagsResponseRt = rt.array(rt.string);
|
||||
export const GetCategoriesResponseRt = rt.array(rt.string);
|
||||
export const GetReportersResponseRt = rt.array(UserRt);
|
||||
|
||||
export const CasesBulkGetRequestRt = rt.strict({
|
||||
|
@ -421,6 +432,7 @@ export type ExternalServiceResponse = rt.TypeOf<typeof ExternalServiceResponseRt
|
|||
export type CaseExternalServiceBasic = rt.TypeOf<typeof CaseExternalServiceBasicRt>;
|
||||
|
||||
export type AllTagsFindRequest = rt.TypeOf<typeof AllTagsFindRequestRt>;
|
||||
export type AllCategoriesFindRequest = rt.TypeOf<typeof AllCategoriesFindRequestRt>;
|
||||
export type AllReportersFindRequest = AllTagsFindRequest;
|
||||
|
||||
export type AttachmentTotals = rt.TypeOf<typeof AttachmentTotalsRt>;
|
||||
|
|
|
@ -81,6 +81,7 @@ export const INTERNAL_GET_CASE_USER_ACTIONS_STATS_URL =
|
|||
export const INTERNAL_CASE_USERS_URL = `${CASES_INTERNAL_URL}/{case_id}/_users` as const;
|
||||
export const INTERNAL_DELETE_FILE_ATTACHMENTS_URL =
|
||||
`${CASES_INTERNAL_URL}/{case_id}/attachments/files/_bulk_delete` as const;
|
||||
export const INTERNAL_GET_CASE_CATEGORIES_URL = `${CASES_INTERNAL_URL}/categories` as const;
|
||||
|
||||
/**
|
||||
* Action routes
|
||||
|
|
|
@ -1764,6 +1764,90 @@ Object {
|
|||
}
|
||||
`;
|
||||
|
||||
exports[`audit_logger log function event structure creates the correct audit event for operation: "getCategories" with an error and entity 1`] = `
|
||||
Object {
|
||||
"error": Object {
|
||||
"code": "Error",
|
||||
"message": "an error",
|
||||
},
|
||||
"event": Object {
|
||||
"action": "case_categories_get",
|
||||
"category": Array [
|
||||
"database",
|
||||
],
|
||||
"outcome": "failure",
|
||||
"type": Array [
|
||||
"access",
|
||||
],
|
||||
},
|
||||
"kibana": Object {
|
||||
"saved_object": Object {
|
||||
"id": "1",
|
||||
"type": "cases",
|
||||
},
|
||||
},
|
||||
"message": "Failed attempt to access cases [id=1] as owner \\"awesome\\"",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`audit_logger log function event structure creates the correct audit event for operation: "getCategories" with an error but no entity 1`] = `
|
||||
Object {
|
||||
"error": Object {
|
||||
"code": "Error",
|
||||
"message": "an error",
|
||||
},
|
||||
"event": Object {
|
||||
"action": "case_categories_get",
|
||||
"category": Array [
|
||||
"database",
|
||||
],
|
||||
"outcome": "failure",
|
||||
"type": Array [
|
||||
"access",
|
||||
],
|
||||
},
|
||||
"message": "Failed attempt to access a cases as any owners",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`audit_logger log function event structure creates the correct audit event for operation: "getCategories" without an error but with an entity 1`] = `
|
||||
Object {
|
||||
"event": Object {
|
||||
"action": "case_categories_get",
|
||||
"category": Array [
|
||||
"database",
|
||||
],
|
||||
"outcome": "success",
|
||||
"type": Array [
|
||||
"access",
|
||||
],
|
||||
},
|
||||
"kibana": Object {
|
||||
"saved_object": Object {
|
||||
"id": "5",
|
||||
"type": "cases",
|
||||
},
|
||||
},
|
||||
"message": "User has accessed cases [id=5] as owner \\"super\\"",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`audit_logger log function event structure creates the correct audit event for operation: "getCategories" without an error or entity 1`] = `
|
||||
Object {
|
||||
"event": Object {
|
||||
"action": "case_categories_get",
|
||||
"category": Array [
|
||||
"database",
|
||||
],
|
||||
"outcome": "success",
|
||||
"type": Array [
|
||||
"access",
|
||||
],
|
||||
},
|
||||
"message": "User has accessed a cases as any owners",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`audit_logger log function event structure creates the correct audit event for operation: "getComment" with an error and entity 1`] = `
|
||||
Object {
|
||||
"error": Object {
|
||||
|
|
|
@ -367,4 +367,12 @@ export const Operations: Record<ReadOperations | WriteOperations, OperationDetai
|
|||
docType: 'user actions',
|
||||
savedObjectType: CASE_USER_ACTION_SAVED_OBJECT,
|
||||
},
|
||||
[ReadOperations.GetCategories]: {
|
||||
ecsType: EVENT_TYPES.access,
|
||||
name: ACCESS_CASE_OPERATION,
|
||||
action: 'case_categories_get',
|
||||
verbs: accessVerbs,
|
||||
docType: 'cases',
|
||||
savedObjectType: CASE_SAVED_OBJECT,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ export enum ReadOperations {
|
|||
GetAllComments = 'getAllComments',
|
||||
FindComments = 'findComments',
|
||||
GetTags = 'getTags',
|
||||
GetCategories = 'getCategories',
|
||||
GetReporters = 'getReporters',
|
||||
FindConfigurations = 'findConfigurations',
|
||||
FindUserActions = 'findUserActions',
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
|||
CasesFindRequest,
|
||||
User,
|
||||
AllTagsFindRequest,
|
||||
AllCategoriesFindRequest,
|
||||
AllReportersFindRequest,
|
||||
CasesByAlertId,
|
||||
CasesBulkGetRequest,
|
||||
|
@ -33,7 +34,7 @@ import { create } from './create';
|
|||
import { deleteCases } from './delete';
|
||||
import { find } from './find';
|
||||
import type { CasesByAlertIDParams, GetParams } from './get';
|
||||
import { get, resolve, getCasesByAlertID, getReporters, getTags } from './get';
|
||||
import { get, resolve, getCasesByAlertID, getReporters, getTags, getCategories } from './get';
|
||||
import type { PushParams } from './push';
|
||||
import { push } from './push';
|
||||
import { update } from './update';
|
||||
|
@ -83,6 +84,10 @@ export interface CasesSubClient {
|
|||
* Retrieves all the tags across all cases the user making the request has access to.
|
||||
*/
|
||||
getTags(params: AllTagsFindRequest): Promise<string[]>;
|
||||
/**
|
||||
* Retrieves all the categories across all cases the user making the request has access to.
|
||||
*/
|
||||
getCategories(params: AllCategoriesFindRequest): Promise<string[]>;
|
||||
/**
|
||||
* Retrieves all the reporters across all accessible cases.
|
||||
*/
|
||||
|
@ -113,6 +118,7 @@ export const createCasesSubClient = (
|
|||
update: (cases: CasesPatchRequest) => update(cases, clientArgs),
|
||||
delete: (ids: string[]) => deleteCases(ids, clientArgs),
|
||||
getTags: (params: AllTagsFindRequest) => getTags(params, clientArgs),
|
||||
getCategories: (params: AllCategoriesFindRequest) => getCategories(params, clientArgs),
|
||||
getReporters: (params: AllReportersFindRequest) => getReporters(params, clientArgs),
|
||||
getCasesByAlertID: (params: CasesByAlertIDParams) => getCasesByAlertID(params, clientArgs),
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { createCasesClientMockArgs } from '../mocks';
|
||||
import { getCasesByAlertID, getTags, getReporters } from './get';
|
||||
import { getCasesByAlertID, getTags, getReporters, getCategories } from './get';
|
||||
|
||||
describe('get', () => {
|
||||
const clientArgs = createCasesClientMockArgs();
|
||||
|
@ -44,4 +44,13 @@ describe('get', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCategories', () => {
|
||||
it('throws with excess fields', async () => {
|
||||
// @ts-expect-error: excess attribute
|
||||
await expect(getCategories({ owner: 'cases', foo: 'bar' }, clientArgs)).rejects.toThrow(
|
||||
'invalid keys "foo"'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
|||
CaseResolveResponse,
|
||||
User,
|
||||
AllTagsFindRequest,
|
||||
AllCategoriesFindRequest,
|
||||
AllReportersFindRequest,
|
||||
CasesByAlertIDRequest,
|
||||
CasesByAlertId,
|
||||
|
@ -18,15 +19,17 @@ import type {
|
|||
AttachmentTotals,
|
||||
} from '../../../common/api';
|
||||
import {
|
||||
AllTagsFindRequestRt,
|
||||
AllCategoriesFindRequestRt,
|
||||
CaseRt,
|
||||
CaseResolveResponseRt,
|
||||
AllTagsFindRequestRt,
|
||||
decodeWithExcessOrThrow,
|
||||
AllReportersFindRequestRt,
|
||||
CasesByAlertIDRequestRt,
|
||||
CasesByAlertIdRt,
|
||||
GetTagsResponseRt,
|
||||
GetReportersResponseRt,
|
||||
GetCategoriesResponseRt,
|
||||
} from '../../../common/api';
|
||||
import { createCaseError } from '../../common/error';
|
||||
import { countAlertsForID, flattenCaseSavedObject } from '../../common/utils';
|
||||
|
@ -299,7 +302,7 @@ export async function getTags(
|
|||
const queryParams = decodeWithExcessOrThrow(AllTagsFindRequestRt)(params);
|
||||
|
||||
const { filter: authorizationFilter } = await authorization.getAuthorizationFilter(
|
||||
Operations.findCases
|
||||
Operations.getTags
|
||||
);
|
||||
|
||||
const filter = combineAuthorizedAndOwnerFilter(queryParams.owner, authorizationFilter);
|
||||
|
@ -348,3 +351,37 @@ export async function getReporters(
|
|||
throw createCaseError({ message: `Failed to get reporters: ${error}`, error, logger });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the categories from all the cases.
|
||||
*/
|
||||
export async function getCategories(
|
||||
params: AllCategoriesFindRequest,
|
||||
clientArgs: CasesClientArgs
|
||||
): Promise<string[]> {
|
||||
const {
|
||||
unsecuredSavedObjectsClient,
|
||||
services: { caseService },
|
||||
logger,
|
||||
authorization,
|
||||
} = clientArgs;
|
||||
|
||||
try {
|
||||
const queryParams = decodeWithExcessOrThrow(AllCategoriesFindRequestRt)(params);
|
||||
|
||||
const { filter: authorizationFilter } = await authorization.getAuthorizationFilter(
|
||||
Operations.getCategories
|
||||
);
|
||||
|
||||
const filter = combineAuthorizedAndOwnerFilter(queryParams.owner, authorizationFilter);
|
||||
|
||||
const categories = await caseService.getCategories({
|
||||
unsecuredSavedObjectsClient,
|
||||
filter,
|
||||
});
|
||||
|
||||
return decodeOrThrow(GetCategoriesResponseRt)(categories);
|
||||
} catch (error) {
|
||||
throw createCaseError({ message: `Failed to get categories: ${error}`, error, logger });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ const createCasesSubClientMock = (): CasesSubClientMock => {
|
|||
getTags: jest.fn(),
|
||||
getReporters: jest.fn(),
|
||||
getCasesByAlertID: jest.fn(),
|
||||
getCategories: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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 { AllCategoriesFindRequest } from '../../../../../common/api';
|
||||
|
||||
import { INTERNAL_GET_CASE_CATEGORIES_URL } from '../../../../../common/constants';
|
||||
import { createCaseError } from '../../../../common/error';
|
||||
import { createCasesRoute } from '../../create_cases_route';
|
||||
|
||||
export const getCategoriesRoute = createCasesRoute({
|
||||
method: 'get',
|
||||
path: INTERNAL_GET_CASE_CATEGORIES_URL,
|
||||
handler: async ({ context, request, response }) => {
|
||||
try {
|
||||
const caseContext = await context.cases;
|
||||
const client = await caseContext.getCasesClient();
|
||||
const options = request.query as AllCategoriesFindRequest;
|
||||
|
||||
return response.ok({ body: await client.cases.getCategories(options) });
|
||||
} catch (error) {
|
||||
throw createCaseError({
|
||||
message: `Failed to retrieve categories in route: ${error}`,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
|
@ -15,6 +15,7 @@ import type { CaseRoute } from './types';
|
|||
import { bulkGetAttachmentsRoute } from './internal/bulk_get_attachments';
|
||||
import { getCaseUsersRoute } from './internal/get_case_users';
|
||||
import { bulkDeleteFileAttachments } from './internal/bulk_delete_file_attachments';
|
||||
import { getCategoriesRoute } from './cases/categories/get_categories';
|
||||
|
||||
export const getInternalRoutes = (userProfileService: UserProfileService) =>
|
||||
[
|
||||
|
@ -26,4 +27,5 @@ export const getInternalRoutes = (userProfileService: UserProfileService) =>
|
|||
bulkGetAttachmentsRoute,
|
||||
getCaseUsersRoute,
|
||||
bulkDeleteFileAttachments,
|
||||
getCategoriesRoute,
|
||||
] as CaseRoute[];
|
||||
|
|
|
@ -69,6 +69,7 @@ import type {
|
|||
PostCaseArgs,
|
||||
PatchCaseArgs,
|
||||
PatchCasesArgs,
|
||||
GetCategoryArgs,
|
||||
} from './types';
|
||||
import type { AttachmentTransformedAttributes } from '../../common/types/attachments';
|
||||
import { bulkDecodeSOAttributes } from '../utils';
|
||||
|
@ -557,6 +558,36 @@ export class CasesService {
|
|||
}
|
||||
}
|
||||
|
||||
public async getCategories({ filter }: GetCategoryArgs): Promise<string[]> {
|
||||
try {
|
||||
this.log.debug(`Attempting to GET all categories`);
|
||||
|
||||
const results = await this.unsecuredSavedObjectsClient.find<
|
||||
unknown,
|
||||
{ categories: { buckets: Array<{ key: string }> } }
|
||||
>({
|
||||
type: CASE_SAVED_OBJECT,
|
||||
page: 1,
|
||||
perPage: 1,
|
||||
filter,
|
||||
aggs: {
|
||||
categories: {
|
||||
terms: {
|
||||
field: `${CASE_SAVED_OBJECT}.attributes.category`,
|
||||
size: MAX_DOCS_PER_PAGE,
|
||||
order: { _key: 'asc' },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return results?.aggregations?.categories?.buckets.map(({ key }) => key) ?? [];
|
||||
} catch (error) {
|
||||
this.log.error(`Error on GET categories: ${error}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async postNewCase({
|
||||
attributes,
|
||||
id,
|
||||
|
|
|
@ -81,6 +81,11 @@ export interface GetReportersArgs {
|
|||
filter?: KueryNode;
|
||||
}
|
||||
|
||||
export interface GetCategoryArgs {
|
||||
unsecuredSavedObjectsClient: SavedObjectsClientContract;
|
||||
filter?: KueryNode;
|
||||
}
|
||||
|
||||
export interface GetCaseIdsByAlertIdAggs {
|
||||
references: {
|
||||
doc_count: number;
|
||||
|
|
|
@ -62,6 +62,7 @@ export const createCaseServiceMock = (): CaseServiceMock => {
|
|||
getCaseStatusStats: jest.fn(),
|
||||
executeAggregations: jest.fn(),
|
||||
bulkDeleteCaseEntities: jest.fn(),
|
||||
getCategories: jest.fn(),
|
||||
};
|
||||
|
||||
// the cast here is required because jest.Mocked tries to include private members and would throw an error
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
CASE_STATUS_URL,
|
||||
CASE_TAGS_URL,
|
||||
CASE_USER_ACTION_SAVED_OBJECT,
|
||||
INTERNAL_GET_CASE_CATEGORIES_URL,
|
||||
} from '@kbn/cases-plugin/common/constants';
|
||||
import {
|
||||
Configuration,
|
||||
|
@ -660,6 +661,27 @@ export const getReporters = async ({
|
|||
return res;
|
||||
};
|
||||
|
||||
export const getCategories = async ({
|
||||
supertest,
|
||||
query = {},
|
||||
expectedHttpCode = 200,
|
||||
auth = { user: superUser, space: null },
|
||||
}: {
|
||||
supertest: SuperTest.SuperTest<SuperTest.Test>;
|
||||
query?: Record<string, unknown>;
|
||||
expectedHttpCode?: number;
|
||||
auth?: { user: User; space: string | null };
|
||||
}): Promise<CasesFindResponse> => {
|
||||
const { body: res } = await supertest
|
||||
.get(`${getSpaceUrlPrefix(auth.space)}${INTERNAL_GET_CASE_CATEGORIES_URL}`)
|
||||
.auth(auth.user.username, auth.user.password)
|
||||
.set('kbn-xsrf', 'true')
|
||||
.query({ ...query })
|
||||
.expect(expectedHttpCode);
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
export const pushCase = async ({
|
||||
supertest,
|
||||
caseId,
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* 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 expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../../../common/ftr_provider_context';
|
||||
|
||||
import { deleteCasesByESQuery, createCase, getCategories } from '../../../../../common/lib/api';
|
||||
import { getPostCaseRequest } from '../../../../../common/lib/mock';
|
||||
import {
|
||||
secOnly,
|
||||
obsOnly,
|
||||
globalRead,
|
||||
superUser,
|
||||
secOnlyRead,
|
||||
obsOnlyRead,
|
||||
obsSecRead,
|
||||
noKibanaPrivileges,
|
||||
obsSec,
|
||||
} from '../../../../../common/lib/authentication/users';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default ({ getService }: FtrProviderContext): void => {
|
||||
const supertest = getService('supertest');
|
||||
const supertestWithoutAuth = getService('supertestWithoutAuth');
|
||||
const es = getService('es');
|
||||
|
||||
describe('get_categories', () => {
|
||||
afterEach(async () => {
|
||||
await deleteCasesByESQuery(es);
|
||||
});
|
||||
|
||||
it('should return case categories', async () => {
|
||||
await createCase(supertest, getPostCaseRequest({ category: 'foo' }));
|
||||
await createCase(supertest, getPostCaseRequest({ category: 'bar' }));
|
||||
|
||||
const categories = await getCategories({ supertest });
|
||||
expect(categories).to.eql(['bar', 'foo']);
|
||||
});
|
||||
|
||||
it('should return unique categories', async () => {
|
||||
await createCase(supertest, getPostCaseRequest({ category: 'foobar' }));
|
||||
await createCase(supertest, getPostCaseRequest({ category: 'foobar' }));
|
||||
|
||||
const categories = await getCategories({ supertest });
|
||||
expect(categories).to.eql(['foobar']);
|
||||
});
|
||||
|
||||
describe('rbac', () => {
|
||||
it('should read the correct categories', async () => {
|
||||
await createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'securitySolutionFixture', category: 'sec' }),
|
||||
200,
|
||||
{
|
||||
user: secOnly,
|
||||
space: 'space1',
|
||||
}
|
||||
);
|
||||
|
||||
await createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'observabilityFixture', category: 'obs' }),
|
||||
200,
|
||||
{
|
||||
user: obsOnly,
|
||||
space: 'space1',
|
||||
}
|
||||
);
|
||||
|
||||
for (const scenario of [
|
||||
{
|
||||
user: globalRead,
|
||||
expectedCategories: ['obs', 'sec'],
|
||||
},
|
||||
{
|
||||
user: superUser,
|
||||
expectedCategories: ['obs', 'sec'],
|
||||
},
|
||||
{ user: secOnlyRead, expectedCategories: ['sec'] },
|
||||
{ user: obsOnlyRead, expectedCategories: ['obs'] },
|
||||
{
|
||||
user: obsSecRead,
|
||||
expectedCategories: ['obs', 'sec'],
|
||||
},
|
||||
]) {
|
||||
const categories = await getCategories({
|
||||
supertest: supertestWithoutAuth,
|
||||
expectedHttpCode: 200,
|
||||
auth: {
|
||||
user: scenario.user,
|
||||
space: 'space1',
|
||||
},
|
||||
});
|
||||
|
||||
expect(categories).to.eql(scenario.expectedCategories);
|
||||
}
|
||||
});
|
||||
|
||||
for (const scenario of [
|
||||
{ user: noKibanaPrivileges, space: 'space1' },
|
||||
{ user: secOnly, space: 'space2' },
|
||||
]) {
|
||||
it(`User ${scenario.user.username} with role(s) ${scenario.user.roles.join()} and space ${
|
||||
scenario.space
|
||||
} - should NOT get all categories`, async () => {
|
||||
// super user creates a case at the appropriate space
|
||||
await createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'securitySolutionFixture', category: 'sec' }),
|
||||
200,
|
||||
{
|
||||
user: superUser,
|
||||
space: scenario.space,
|
||||
}
|
||||
);
|
||||
|
||||
// user should not be able to get all categories at the appropriate space
|
||||
await getCategories({
|
||||
supertest: supertestWithoutAuth,
|
||||
expectedHttpCode: 403,
|
||||
auth: { user: scenario.user, space: scenario.space },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
it('should respect the owner filter when having permissions', async () => {
|
||||
await Promise.all([
|
||||
createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'securitySolutionFixture', category: 'sec' }),
|
||||
200,
|
||||
{
|
||||
user: obsSec,
|
||||
space: 'space1',
|
||||
}
|
||||
),
|
||||
createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'observabilityFixture', category: 'obs' }),
|
||||
200,
|
||||
{
|
||||
user: obsSec,
|
||||
space: 'space1',
|
||||
}
|
||||
),
|
||||
]);
|
||||
|
||||
const categories = await getCategories({
|
||||
supertest: supertestWithoutAuth,
|
||||
auth: {
|
||||
user: obsSec,
|
||||
space: 'space1',
|
||||
},
|
||||
query: { owner: 'securitySolutionFixture' },
|
||||
});
|
||||
|
||||
expect(categories).to.eql(['sec']);
|
||||
});
|
||||
|
||||
it('should return the correct cases when trying to exploit RBAC through the owner query parameter', async () => {
|
||||
await Promise.all([
|
||||
createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'securitySolutionFixture', category: 'sec' }),
|
||||
200,
|
||||
{
|
||||
user: obsSec,
|
||||
space: 'space1',
|
||||
}
|
||||
),
|
||||
createCase(
|
||||
supertestWithoutAuth,
|
||||
getPostCaseRequest({ owner: 'observabilityFixture', category: 'obs' }),
|
||||
200,
|
||||
{
|
||||
user: obsSec,
|
||||
space: 'space1',
|
||||
}
|
||||
),
|
||||
]);
|
||||
|
||||
// User with permissions only to security solution request categories from observability
|
||||
const categories = await getCategories({
|
||||
supertest: supertestWithoutAuth,
|
||||
auth: {
|
||||
user: secOnly,
|
||||
space: 'space1',
|
||||
},
|
||||
query: { owner: ['securitySolutionFixture', 'observabilityFixture'] },
|
||||
});
|
||||
|
||||
// Only security solution categories are being returned
|
||||
expect(categories).to.eql(['sec']);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
|
@ -30,6 +30,7 @@ export default ({ loadTestFile }: FtrProviderContext): void => {
|
|||
loadTestFile(require.resolve('./cases/reporters/get_reporters'));
|
||||
loadTestFile(require.resolve('./cases/status/get_status'));
|
||||
loadTestFile(require.resolve('./cases/tags/get_tags'));
|
||||
loadTestFile(require.resolve('./cases/categories/get_categories'));
|
||||
loadTestFile(require.resolve('./user_actions/get_all_user_actions'));
|
||||
loadTestFile(require.resolve('./user_actions/find_user_actions'));
|
||||
loadTestFile(require.resolve('./user_actions/get_user_action_stats'));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue