Skip to content

Commit

Permalink
[Security Solution][Endpoint] Change Trusted Apps to use item_id as…
Browse files Browse the repository at this point in the history
… its identifier and Enable Trusted Apps filtering by id in the UI (elastic#115276) (elastic#115509)

* Add `item_id` to list of searchable fields
* trusted apps api changes to use `item_id` instead of SO `id`
* Change Policy Details Trusted App "View all details" action URL to show TA list filtered by the TA id

Co-authored-by: Paul Tavares <[email protected]>
  • Loading branch information
kibanamachine and paul-tavares authored Oct 19, 2021
1 parent 2d953bc commit 65786cf
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('when rendering the PolicyTrustedAppsList', () => {
expect(appTestContext.coreStart.application.navigateToApp).toHaveBeenCalledWith(
APP_ID,
expect.objectContaining({
path: '/administration/trusted_apps?show=edit&id=89f72d8a-05b5-4350-8cad-0dc3661d6e67',
path: '/administration/trusted_apps?filter=89f72d8a-05b5-4350-8cad-0dc3661d6e67',
})
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export const PolicyTrustedAppsList = memo<PolicyTrustedAppsListProps>(

for (const trustedApp of trustedAppItems) {
const isGlobal = trustedApp.effectScope.type === 'global';
const viewUrlPath = getTrustedAppsListPath({ id: trustedApp.id, show: 'edit' });
const viewUrlPath = getTrustedAppsListPath({ filter: trustedApp.id });
const assignedPoliciesMenuItems: ArtifactEntryCollapsibleCardProps['policies'] =
trustedApp.effectScope.type === 'global'
? undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
export const SEARCHABLE_FIELDS: Readonly<string[]> = [
`name`,
`description`,
'item_id',
`entries.value`,
`entries.entries.value`,
];
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const Gold = licenseMock.createLicense({ license: { type: 'gold', mode: 'gold' }
const packagePolicyClient =
createPackagePolicyServiceMock() as jest.Mocked<PackagePolicyServiceInterface>;

describe('handlers', () => {
describe('TrustedApps API Handlers', () => {
beforeEach(() => {
packagePolicyClient.getByIDs.mockReset();
});
Expand Down Expand Up @@ -195,6 +195,7 @@ describe('handlers', () => {
const mockResponse = httpServerMock.createResponseFactory();

exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null);
exceptionsListClient.getExceptionListItem.mockResolvedValue(null);

await deleteTrustedAppHandler(
createHandlerContextMock(),
Expand Down Expand Up @@ -582,7 +583,7 @@ describe('handlers', () => {
});

it('should return 404 if trusted app does not exist', async () => {
exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
exceptionsListClient.getExceptionListItem.mockResolvedValue(null);

await updateHandler(
createHandlerContextMock(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const exceptionListItemToTrustedApp = (
const grouped = entriesToConditionEntriesMap(exceptionListItem.entries);

return {
id: exceptionListItem.id,
id: exceptionListItem.item_id,
version: exceptionListItem._version || '',
name: exceptionListItem.name,
description: exceptionListItem.description,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,10 @@ const TRUSTED_APP: TrustedApp = {
],
};

describe('service', () => {
describe('TrustedApps service', () => {
beforeEach(() => {
exceptionsListClient.deleteExceptionListItem.mockReset();
exceptionsListClient.getExceptionListItem.mockReset();
exceptionsListClient.createExceptionListItem.mockReset();
exceptionsListClient.findExceptionListItem.mockReset();
exceptionsListClient.createTrustedAppsList.mockReset();
Expand All @@ -96,6 +97,7 @@ describe('service', () => {

describe('deleteTrustedApp', () => {
it('should delete existing trusted app', async () => {
exceptionsListClient.getExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);
exceptionsListClient.deleteExceptionListItem.mockResolvedValue(EXCEPTION_LIST_ITEM);

expect(await deleteTrustedApp(exceptionsListClient, { id: '123' })).toBeUndefined();
Expand All @@ -107,6 +109,7 @@ describe('service', () => {
});

it('should throw for non existing trusted app', async () => {
exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
exceptionsListClient.deleteExceptionListItem.mockResolvedValue(null);

await expect(deleteTrustedApp(exceptionsListClient, { id: '123' })).rejects.toBeInstanceOf(
Expand Down Expand Up @@ -393,7 +396,7 @@ describe('service', () => {
});

it('should throw a Not Found error if trusted app is not found prior to making update', async () => {
exceptionsListClient.getExceptionListItem.mockResolvedValueOnce(null);
exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
await expect(
updateTrustedApp(
exceptionsListClient,
Expand Down Expand Up @@ -489,5 +492,22 @@ describe('service', () => {
TrustedAppNotFoundError
);
});

it('should try to find trusted app by `itemId` and then by `id`', async () => {
exceptionsListClient.getExceptionListItem.mockResolvedValue(null);
await getTrustedApp(exceptionsListClient, '123').catch(() => Promise.resolve());

expect(exceptionsListClient.getExceptionListItem).toHaveBeenCalledTimes(2);
expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(1, {
itemId: '123',
id: undefined,
namespaceType: 'agnostic',
});
expect(exceptionsListClient.getExceptionListItem).toHaveBeenNthCalledWith(2, {
itemId: undefined,
id: '123',
namespaceType: 'agnostic',
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import {
DeleteTrustedAppsRequestParams,
GetOneTrustedAppResponse,
GetTrustedAppsListRequest,
GetTrustedAppsSummaryResponse,
GetTrustedAppsListResponse,
GetTrustedAppsSummaryRequest,
GetTrustedAppsSummaryResponse,
PostTrustedAppCreateRequest,
PostTrustedAppCreateResponse,
PutTrustedAppUpdateRequest,
PutTrustedAppUpdateResponse,
GetTrustedAppsSummaryRequest,
TrustedApp,
} from '../../../../common/endpoint/types';

Expand All @@ -33,8 +33,8 @@ import {
} from './mapping';
import {
TrustedAppNotFoundError,
TrustedAppVersionConflictError,
TrustedAppPolicyNotExistsError,
TrustedAppVersionConflictError,
} from './errors';
import { PackagePolicyServiceInterface } from '../../../../../fleet/server';
import { PackagePolicy } from '../../../../../fleet/common';
Expand Down Expand Up @@ -87,30 +87,61 @@ const isUserTryingToModifyEffectScopeWithoutPermissions = (
}
};

export const deleteTrustedApp = async (
/**
* Attempts to first fine the ExceptionItem using `item_id` and if not found, then a second attempt wil be done
* against the Saved Object `id`.
* @param exceptionsListClient
* @param id
*/
export const findTrustedAppExceptionItemByIdOrItemId = async (
exceptionsListClient: ExceptionListClient,
{ id }: DeleteTrustedAppsRequestParams
) => {
const exceptionListItem = await exceptionsListClient.deleteExceptionListItem({
id,
id: string
): Promise<ExceptionListItemSchema | null> => {
const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({
itemId: id,
id: undefined,
namespaceType: 'agnostic',
});

if (trustedAppExceptionItem) {
return trustedAppExceptionItem;
}

return exceptionsListClient.getExceptionListItem({
itemId: undefined,
id,
namespaceType: 'agnostic',
});
};

if (!exceptionListItem) {
export const deleteTrustedApp = async (
exceptionsListClient: ExceptionListClient,
{ id }: DeleteTrustedAppsRequestParams
): Promise<void> => {
const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
exceptionsListClient,
id
);

if (!trustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
}

await exceptionsListClient.deleteExceptionListItem({
id: trustedAppExceptionItem.id,
itemId: undefined,
namespaceType: 'agnostic',
});
};

export const getTrustedApp = async (
exceptionsListClient: ExceptionListClient,
id: string
): Promise<GetOneTrustedAppResponse> => {
const trustedAppExceptionItem = await exceptionsListClient.getExceptionListItem({
itemId: '',
id,
namespaceType: 'agnostic',
});
const trustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
exceptionsListClient,
id
);

if (!trustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
Expand Down Expand Up @@ -189,19 +220,18 @@ export const updateTrustedApp = async (
updatedTrustedApp: PutTrustedAppUpdateRequest,
isAtLeastPlatinum: boolean
): Promise<PutTrustedAppUpdateResponse> => {
const currentTrustedApp = await exceptionsListClient.getExceptionListItem({
itemId: '',
id,
namespaceType: 'agnostic',
});
const currentTrustedAppExceptionItem = await findTrustedAppExceptionItemByIdOrItemId(
exceptionsListClient,
id
);

if (!currentTrustedApp) {
if (!currentTrustedAppExceptionItem) {
throw new TrustedAppNotFoundError(id);
}

if (
isUserTryingToModifyEffectScopeWithoutPermissions(
exceptionListItemToTrustedApp(currentTrustedApp),
exceptionListItemToTrustedApp(currentTrustedAppExceptionItem),
updatedTrustedApp,
isAtLeastPlatinum
)
Expand All @@ -226,7 +256,10 @@ export const updateTrustedApp = async (

try {
updatedTrustedAppExceptionItem = await exceptionsListClient.updateExceptionListItem(
updatedTrustedAppToUpdateExceptionListItemOptions(currentTrustedApp, updatedTrustedApp)
updatedTrustedAppToUpdateExceptionListItemOptions(
currentTrustedAppExceptionItem,
updatedTrustedApp
)
);
} catch (e) {
if (e?.output?.statusCode === 409) {
Expand Down

0 comments on commit 65786cf

Please sign in to comment.