Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Cases] Add security only tests #99679

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions x-pack/scripts/functional_tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const onlyNotInCoverageTests = [
require.resolve('../test/case_api_integration/security_and_spaces/config_basic.ts'),
require.resolve('../test/case_api_integration/security_and_spaces/config_trial.ts'),
require.resolve('../test/case_api_integration/spaces_only/config.ts'),
require.resolve('../test/case_api_integration/security_only/config.ts'),
require.resolve('../test/apm_api_integration/basic/config.ts'),
require.resolve('../test/apm_api_integration/trial/config.ts'),
require.resolve('../test/apm_api_integration/rules/config.ts'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,23 @@ export const createSpaces = async (getService: CommonFtrProviderContext['getServ
}
};

const createUsersAndRoles = async (getService: CommonFtrProviderContext['getService']) => {
export const createUsersAndRoles = async (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cnasikas let me know what you think of my changes here. Basically the approach I took was when the spaces plugin is disabled we can pass in ['*'] so each role has access to all spaces (in reality that'll only be the default space because the spaces plugin is disabled). If we don't give each role access to the default space, then the tests won't work because the space they do have access to (e.g. space1 etc) don't exist.

getService: CommonFtrProviderContext['getService'],
overrideSpaces?: string[]
) => {
const security = getService('security');

const createRole = async ({ name, privileges }: Role) => {
return await security.role.create(name, privileges);
const modifiedPrivileges = {
...privileges,
// for roles that don't have kibana set this will just return undefined
kibana: privileges.kibana?.map((kibanaEntry) => ({
...kibanaEntry,
spaces: overrideSpaces != null ? overrideSpaces : kibanaEntry.spaces,
})),
};

return await security.role.create(name, modifiedPrivileges);
};

const createUser = async (user: User) => {
Expand Down Expand Up @@ -61,7 +73,8 @@ export const deleteSpaces = async (getService: CommonFtrProviderContext['getServ
}
}
};
const deleteUsersAndRoles = async (getService: CommonFtrProviderContext['getService']) => {

export const deleteUsersAndRoles = async (getService: CommonFtrProviderContext['getService']) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exporting this so the security_only tests can call it directly and not create any spaces.

const security = getService('security');

for (const user of users) {
Expand Down
11 changes: 10 additions & 1 deletion x-pack/test/case_api_integration/common/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -546,10 +546,19 @@ export const superUserSpace1Auth = getAuthWithSuperUser();
* Returns an auth object with the specified space and user set as super user. The result can be passed to other utility
* functions.
*/
export function getAuthWithSuperUser(space: string = 'space1'): { user: User; space: string } {
export function getAuthWithSuperUser(
space: string | null = 'space1'
): { user: User; space: string | null } {
return { user: superUser, space };
}

/**
* Converts the space into the appropriate string for use by the actions remover utility object.
*/
export function getActionsSpace(space: string | null) {
return space ?? 'default';
}

export const getSpaceUrlPrefix = (spaceId: string | undefined | null) => {
return spaceId && spaceId !== 'default' ? `/s/${spaceId}` : ``;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export default ({ getService }: FtrProviderContext): void => {
);

await deleteCases({
supertest,
supertest: supertestWithoutAuth,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

caseIDs: [postedCase.id],
expectedHttpCode: 204,
auth: { user: secOnly, space: 'space1' },
Expand Down
16 changes: 16 additions & 0 deletions x-pack/test/case_api_integration/security_only/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 { createTestConfig } from '../common/config';

// eslint-disable-next-line import/no-default-export
export default createTestConfig('security_only', {
disabledPlugins: ['spaces'],
license: 'trial',
ssl: true,
testFiles: [require.resolve('./tests/trial')],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
* 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 { getPostCaseRequest, postCommentAlertReq } from '../../../../common/lib/mock';
import {
createCase,
createComment,
getCaseIDsByAlert,
deleteAllCaseItems,
} from '../../../../common/lib/utils';
import {
globalRead,
noKibanaPrivileges,
obsOnlyRead,
obsSec,
obsSecRead,
secOnlyRead,
superUser,
} from '../../../../common/lib/authentication/users';
import {
obsOnlyDefaultSpaceAuth,
secOnlyDefaultSpaceAuth,
superUserDefaultSpaceAuth,
obsSecDefaultSpaceAuth,
} from '../../../utils';

// eslint-disable-next-line import/no-default-export
export default ({ getService }: FtrProviderContext): void => {
const supertest = getService('supertest');
const es = getService('es');

describe('get_cases using alertID', () => {
afterEach(async () => {
await deleteAllCaseItems(es);
});

const supertestWithoutAuth = getService('supertestWithoutAuth');

it('should return the correct case IDs', async () => {
const [case1, case2, case3] = await Promise.all([
createCase(supertestWithoutAuth, getPostCaseRequest(), 200, secOnlyDefaultSpaceAuth),
createCase(supertestWithoutAuth, getPostCaseRequest(), 200, secOnlyDefaultSpaceAuth),
createCase(
supertestWithoutAuth,
getPostCaseRequest({ owner: 'observabilityFixture' }),
200,
obsOnlyDefaultSpaceAuth
),
]);

await Promise.all([
createComment({
supertest: supertestWithoutAuth,
caseId: case1.id,
params: postCommentAlertReq,
auth: secOnlyDefaultSpaceAuth,
}),
createComment({
supertest: supertestWithoutAuth,
caseId: case2.id,
params: postCommentAlertReq,
auth: secOnlyDefaultSpaceAuth,
}),
createComment({
supertest: supertestWithoutAuth,
caseId: case3.id,
params: { ...postCommentAlertReq, owner: 'observabilityFixture' },
auth: obsOnlyDefaultSpaceAuth,
}),
]);

for (const scenario of [
{
user: globalRead,
caseIDs: [case1.id, case2.id, case3.id],
},
{
user: superUser,
caseIDs: [case1.id, case2.id, case3.id],
},
{ user: secOnlyRead, caseIDs: [case1.id, case2.id] },
{ user: obsOnlyRead, caseIDs: [case3.id] },
{
user: obsSecRead,
caseIDs: [case1.id, case2.id, case3.id],
},
]) {
const res = await getCaseIDsByAlert({
supertest: supertestWithoutAuth,
// cast because the official type is string | string[] but the ids will always be a single value in the tests
alertID: postCommentAlertReq.alertId as string,
auth: {
user: scenario.user,
space: null,
},
});
expect(res.length).to.eql(scenario.caseIDs.length);
for (const caseID of scenario.caseIDs) {
expect(res).to.contain(caseID);
}
}
});

it(`User ${
noKibanaPrivileges.username
} with role(s) ${noKibanaPrivileges.roles.join()} - should not get cases`, async () => {
const caseInfo = await createCase(supertest, getPostCaseRequest(), 200, {
user: superUser,
space: null,
});

await createComment({
supertest: supertestWithoutAuth,
caseId: caseInfo.id,
params: postCommentAlertReq,
auth: superUserDefaultSpaceAuth,
});

await getCaseIDsByAlert({
supertest: supertestWithoutAuth,
alertID: postCommentAlertReq.alertId as string,
auth: { user: noKibanaPrivileges, space: null },
expectedHttpCode: 403,
});
});

it('should return a 404 when attempting to access a space', async () => {
const [case1, case2] = await Promise.all([
createCase(supertestWithoutAuth, getPostCaseRequest(), 200, obsSecDefaultSpaceAuth),
createCase(
supertestWithoutAuth,
{ ...getPostCaseRequest(), owner: 'observabilityFixture' },
200,
obsSecDefaultSpaceAuth
),
]);

await Promise.all([
createComment({
supertest: supertestWithoutAuth,
caseId: case1.id,
params: postCommentAlertReq,
auth: obsSecDefaultSpaceAuth,
}),
createComment({
supertest: supertestWithoutAuth,
caseId: case2.id,
params: { ...postCommentAlertReq, owner: 'observabilityFixture' },
auth: obsSecDefaultSpaceAuth,
}),
]);

await getCaseIDsByAlert({
supertest: supertestWithoutAuth,
alertID: postCommentAlertReq.alertId as string,
auth: { user: obsSec, space: 'space1' },
query: { owner: 'securitySolutionFixture' },
expectedHttpCode: 404,
});
});

it('should respect the owner filter when have permissions', async () => {
const [case1, case2] = await Promise.all([
createCase(supertestWithoutAuth, getPostCaseRequest(), 200, obsSecDefaultSpaceAuth),
createCase(
supertestWithoutAuth,
{ ...getPostCaseRequest(), owner: 'observabilityFixture' },
200,
obsSecDefaultSpaceAuth
),
]);

await Promise.all([
createComment({
supertest: supertestWithoutAuth,
caseId: case1.id,
params: postCommentAlertReq,
auth: obsSecDefaultSpaceAuth,
}),
createComment({
supertest: supertestWithoutAuth,
caseId: case2.id,
params: { ...postCommentAlertReq, owner: 'observabilityFixture' },
auth: obsSecDefaultSpaceAuth,
}),
]);

const res = await getCaseIDsByAlert({
supertest: supertestWithoutAuth,
alertID: postCommentAlertReq.alertId as string,
auth: obsSecDefaultSpaceAuth,
query: { owner: 'securitySolutionFixture' },
});

expect(res).to.eql([case1.id]);
});

it('should return the correct case IDs when the owner query parameter contains unprivileged values', async () => {
const [case1, case2] = await Promise.all([
createCase(supertestWithoutAuth, getPostCaseRequest(), 200, obsSecDefaultSpaceAuth),
createCase(
supertestWithoutAuth,
{ ...getPostCaseRequest(), owner: 'observabilityFixture' },
200,
obsSecDefaultSpaceAuth
),
]);

await Promise.all([
createComment({
supertest: supertestWithoutAuth,
caseId: case1.id,
params: postCommentAlertReq,
auth: obsSecDefaultSpaceAuth,
}),
createComment({
supertest: supertestWithoutAuth,
caseId: case2.id,
params: { ...postCommentAlertReq, owner: 'observabilityFixture' },
auth: obsSecDefaultSpaceAuth,
}),
]);

const res = await getCaseIDsByAlert({
supertest: supertestWithoutAuth,
alertID: postCommentAlertReq.alertId as string,
auth: secOnlyDefaultSpaceAuth,
// The secOnly user does not have permissions for observability cases, so it should only return the security solution one
query: { owner: ['securitySolutionFixture', 'observabilityFixture'] },
});

expect(res).to.eql([case1.id]);
});
});
};
Loading