Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
1773c79
Add isolation_ips_whitelist to the exception list endpoint types
academo Sep 6, 2021
71c6bf4
Add menu item for host isolation exceptions
academo Sep 13, 2021
ae332c9
Rename list id for host isolation exceptions
academo Sep 13, 2021
406f6b0
Add boilerplate code for stores and middlewares
academo Sep 14, 2021
819f97a
Basic middleware setup for host isolation exceptions
academo Sep 14, 2021
e353289
WIP list excepted ips
academo Sep 15, 2021
5207d59
Fix entries types for loading date
academo Sep 15, 2021
1d6037e
Broken TS
academo Sep 16, 2021
c470ac1
Fix TS errors related to AsyncResourceState problems with actions
academo Sep 16, 2021
216ffb1
Reducer WIP
academo Sep 20, 2021
0ce0223
Add selectors to load data on the list display
academo Sep 20, 2021
8497e6c
Showing the list of cards now, pagination not working
academo Sep 20, 2021
7496082
Added working pagination
academo Sep 21, 2021
8f44539
Implement search functionality
academo Sep 21, 2021
81522cd
Dispatch loading and error states
academo Sep 21, 2021
03041b2
remove todo comment
academo Sep 21, 2021
76ec46e
Clean code for PR
academo Sep 21, 2021
dc8a8a5
Fix unit tests
academo Sep 21, 2021
84eecf7
Fix Exceptions Enum
academo Sep 21, 2021
d1e79f3
Add a feature flag for the isolation exceptions menu
academo Sep 21, 2021
43c3a61
Fix test failing due to feature flag
academo Sep 22, 2021
4169d44
Merge remote-tracking branch 'upstream/master' into feature/host_isol…
academo Sep 22, 2021
4d3fcab
Add middleware and reducers tests
academo Sep 22, 2021
c398b11
Replace ItemExceptionCard by ArtifactCard
academo Sep 23, 2021
5310cf2
Remove experimental flag
academo Sep 23, 2021
f6b9af5
Revert "Fix test failing due to feature flag"
academo Sep 23, 2021
fb63309
Fix loading state for middleware
academo Sep 23, 2021
01fc428
Add empty state and test ids
academo Sep 23, 2021
021679a
Add unit tests for host isolation exceptions lists component
academo Sep 23, 2021
a768a2e
Merge remote-tracking branch 'upstream/master' into feature/host_isol…
academo Sep 27, 2021
954d11d
Remove outdated comment
academo Sep 27, 2021
ff04de3
PR review fixes
academo Sep 27, 2021
b0ead25
Cast variable instead of result
academo Sep 27, 2021
971ce34
Merge remote-tracking branch 'upstream/master' into feature/host_isol…
academo Sep 28, 2021
445385d
Refactor function to use a cached promise
academo Sep 28, 2021
96050b8
Merge remote-tracking branch 'upstream/master' into feature/host_isol…
academo Sep 29, 2021
bc52d56
Add delete item for host isolation exception items
academo Sep 29, 2021
8342e61
Add return type to functions
academo Sep 30, 2021
3500199
add space between tests
academo Sep 30, 2021
b4db7b1
Replace ts-ignore with ts-expect-error
academo Sep 30, 2021
c233d14
Merge remote-tracking branch 'upstream/master' into feature/host_isol…
academo Sep 30, 2021
23665a5
Merge branch 'feature/host_isolation_ips_whitelist' into feature/host…
academo Sep 30, 2021
21a5195
Add tests for host isolation exceptions delete middleware
academo Sep 30, 2021
b82ac4a
Add tests for delete modal react component
academo Sep 30, 2021
a6f6278
Merge remote-tracking branch 'upstream/master' into feature/host-isol…
academo Sep 30, 2021
777409d
Fix translation issue
academo Sep 30, 2021
6fccc2b
Merge remote-tracking branch 'upstream/master' into feature/host-isol…
academo Oct 1, 2021
e8a3ee3
Merge branch 'master' into feature/host-isolation-exceptions-delete
kibanamachine Oct 1, 2021
d4750dd
Update x-pack/plugins/security_solution/public/management/pages/host_…
academo Oct 1, 2021
4d61098
Merge branch 'master' into feature/host-isolation-exceptions-delete
kibanamachine Oct 4, 2021
146d1d3
Merge branch 'master' into feature/host-isolation-exceptions-delete
kibanamachine Oct 4, 2021
5e6c908
Merge remote-tracking branch 'upstream/master' into feature/host-isol…
academo Oct 5, 2021
04fa1e7
Fix minor issues from PR
academo Oct 5, 2021
5c46368
Fix eslint
academo Oct 5, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,13 @@ export async function getHostIsolationExceptionItems({
});
return entries;
}

export async function deleteHostIsolationExceptionItems(http: HttpStart, id: string) {
await ensureHostIsolationExceptionsListExists(http);
return http.delete<ExceptionListItemSchema>(EXCEPTION_LIST_ITEM_URL, {
query: {
id,
namespace_type: 'agnostic',
},
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* 2.0.
*/

import { ExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import { Action } from 'redux';
import { HostIsolationExceptionsPageState } from '../types';

Expand All @@ -13,4 +14,19 @@ export type HostIsolationExceptionsPageDataChanged =
payload: HostIsolationExceptionsPageState['entries'];
};

export type HostIsolationExceptionsPageAction = HostIsolationExceptionsPageDataChanged;
export type HostIsolationExceptionsDeleteItem = Action<'hostIsolationExceptionsMarkToDelete'> & {
payload?: ExceptionListItemSchema;
};

export type HostIsolationExceptionsSubmitDelete = Action<'hostIsolationExceptionsSubmitDelete'>;

export type HostIsolationExceptionsDeleteStatusChanged =
Action<'hostIsolationExceptionsDeleteStatusChanged'> & {
payload: HostIsolationExceptionsPageState['deletion']['status'];
};

export type HostIsolationExceptionsPageAction =
| HostIsolationExceptionsPageDataChanged
| HostIsolationExceptionsDeleteItem
| HostIsolationExceptionsSubmitDelete
| HostIsolationExceptionsDeleteStatusChanged;
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ export const initialHostIsolationExceptionsPageState = (): HostIsolationExceptio
page_size: MANAGEMENT_DEFAULT_PAGE_SIZE,
filter: '',
},
deletion: {
item: undefined,
status: createUninitialisedResourceState(),
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ import {
createSpyMiddleware,
MiddlewareActionSpyHelper,
} from '../../../../common/store/test_utils';
import { isFailedResourceState, isLoadedResourceState } from '../../../state';
import { getHostIsolationExceptionItems } from '../service';
import {
isFailedResourceState,
isLoadedResourceState,
isLoadingResourceState,
} from '../../../state';
import { getHostIsolationExceptionItems, deleteHostIsolationExceptionItems } from '../service';
import { HostIsolationExceptionsPageState } from '../types';
import { initialHostIsolationExceptionsPageState } from './builders';
import { createHostIsolationExceptionsPageMiddleware } from './middleware';
Expand All @@ -24,6 +28,7 @@ import { getListFetchError } from './selector';

jest.mock('../service');
const getHostIsolationExceptionItemsMock = getHostIsolationExceptionItems as jest.Mock;
const deleteHostIsolationExceptionItemsMock = deleteHostIsolationExceptionItems as jest.Mock;

const fakeCoreStart = coreMock.createStart({ basePath: '/mock' });

Expand Down Expand Up @@ -139,4 +144,69 @@ describe('Host isolation exceptions middleware', () => {
});
});
});

describe('When deleting an item from host isolation exceptions', () => {
beforeEach(() => {
deleteHostIsolationExceptionItemsMock.mockClear();
deleteHostIsolationExceptionItemsMock.mockReturnValue(undefined);
getHostIsolationExceptionItemsMock.mockClear();
getHostIsolationExceptionItemsMock.mockImplementation(getFoundExceptionListItemSchemaMock);
store.dispatch({
type: 'hostIsolationExceptionsMarkToDelete',
payload: {
id: '1',
},
});
});

it('should call the delete exception API when a delete is submitted and advertise a loading status', async () => {
const waiter = Promise.all([
// delete loading action
spyMiddleware.waitForAction('hostIsolationExceptionsDeleteStatusChanged', {
validate({ payload }) {
return isLoadingResourceState(payload);
},
}),
// delete finished action
spyMiddleware.waitForAction('hostIsolationExceptionsDeleteStatusChanged', {
validate({ payload }) {
return isLoadedResourceState(payload);
},
}),
]);
store.dispatch({
type: 'hostIsolationExceptionsSubmitDelete',
});
await waiter;
expect(deleteHostIsolationExceptionItemsMock).toHaveBeenLastCalledWith(
fakeCoreStart.http,
'1'
);
});

it('should dispatch a failure if the API returns an error', async () => {
deleteHostIsolationExceptionItemsMock.mockRejectedValue({
body: { message: 'error message', statusCode: 500, error: 'Internal Server Error' },
});
store.dispatch({
type: 'hostIsolationExceptionsSubmitDelete',
});
await spyMiddleware.waitForAction('hostIsolationExceptionsDeleteStatusChanged', {
validate({ payload }) {
return isFailedResourceState(payload);
},
});
});

it('should reload the host isolation exception lists after delete', async () => {
store.dispatch({
type: 'hostIsolationExceptionsSubmitDelete',
});
await spyMiddleware.waitForAction('hostIsolationExceptionsPageDataChanged', {
validate({ payload }) {
return isLoadingResourceState(payload);
},
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* 2.0.
*/

import { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import { CoreStart, HttpStart } from 'kibana/public';
import {
ExceptionListItemSchema,
FoundExceptionListItemSchema,
} from '@kbn/securitysolution-io-ts-list-types';
import { CoreStart, HttpSetup, HttpStart } from 'kibana/public';
import { matchPath } from 'react-router-dom';
import { AppLocation, Immutable } from '../../../../../common/endpoint/types';
import { ImmutableMiddleware, ImmutableMiddlewareAPI } from '../../../../common/store';
Expand All @@ -17,9 +20,9 @@ import {
createFailedResourceState,
createLoadedResourceState,
} from '../../../state/async_resource_builders';
import { getHostIsolationExceptionItems } from '../service';
import { deleteHostIsolationExceptionItems, getHostIsolationExceptionItems } from '../service';
import { HostIsolationExceptionsPageState } from '../types';
import { getCurrentListPageDataState, getCurrentLocation } from './selector';
import { getCurrentListPageDataState, getCurrentLocation, getItemToDelete } from './selector';

export const SEARCHABLE_FIELDS: Readonly<string[]> = [`name`, `description`, `entries.value`];

Expand All @@ -36,6 +39,9 @@ export const createHostIsolationExceptionsPageMiddleware = (
if (action.type === 'userChangedUrl' && isHostIsolationExceptionsPage(action.payload)) {
loadHostIsolationExceptionsList(store, coreStart.http);
}
if (action.type === 'hostIsolationExceptionsSubmitDelete') {
deleteHostIsolationExceptionsItem(store, coreStart.http);
}
};
};

Expand Down Expand Up @@ -88,3 +94,37 @@ function isHostIsolationExceptionsPage(location: Immutable<AppLocation>) {
}) !== null
);
}

async function deleteHostIsolationExceptionsItem(
store: ImmutableMiddlewareAPI<HostIsolationExceptionsPageState, AppAction>,
http: HttpSetup
) {
const { dispatch } = store;
const itemToDelete = getItemToDelete(store.getState());
if (itemToDelete === undefined) {
return;
}
try {
dispatch({
type: 'hostIsolationExceptionsDeleteStatusChanged',
payload: {
type: 'LoadingResourceState',
// @ts-expect-error-next-line will be fixed with when AsyncResourceState is refactored (#830)
previousState: store.getState().deletion.status,
},
});

await deleteHostIsolationExceptionItems(http, itemToDelete.id);

dispatch({
type: 'hostIsolationExceptionsDeleteStatusChanged',
payload: createLoadedResourceState(itemToDelete),
});
loadHostIsolationExceptionsList(store, http);
} catch (error) {
dispatch({
type: 'hostIsolationExceptionsDeleteStatusChanged',
payload: createFailedResourceState<ExceptionListItemSchema>(error.body ?? error),
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { HostIsolationExceptionsPageState } from '../types';
import { initialHostIsolationExceptionsPageState } from './builders';
import { MANAGEMENT_ROUTING_HOST_ISOLATION_EXCEPTIONS_PATH } from '../../../common/constants';
import { UserChangedUrl } from '../../../../common/store/routing/action';
import { createUninitialisedResourceState } from '../../../state';

type StateReducer = ImmutableReducer<HostIsolationExceptionsPageState, AppAction>;
type CaseReducer<T extends AppAction> = (
Expand Down Expand Up @@ -45,6 +46,23 @@ export const hostIsolationExceptionsPageReducer: StateReducer = (
}
case 'userChangedUrl':
return userChangedUrl(state, action);
case 'hostIsolationExceptionsMarkToDelete': {
return {
...state,
deletion: {
item: action.payload,
status: createUninitialisedResourceState(),
},
};
}
case 'hostIsolationExceptionsDeleteStatusChanged':
return {
...state,
deletion: {
...state.deletion,
status: action.payload,
},
};
}
return state;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import {
getLastLoadedResourceState,
isFailedResourceState,
isLoadedResourceState,
isLoadingResourceState,
} from '../../../state/async_resource_state';
import { HostIsolationExceptionsPageState } from '../types';
Expand Down Expand Up @@ -73,3 +74,37 @@ export const getListFetchError: HostIsolationExceptionsSelector<
export const getCurrentLocation: HostIsolationExceptionsSelector<StoreState['location']> = (
state
) => state.location;

export const getDeletionState: HostIsolationExceptionsSelector<StoreState['deletion']> =
createSelector(getCurrentListPageState, (listState) => listState.deletion);

export const showDeleteModal: HostIsolationExceptionsSelector<boolean> = createSelector(
getDeletionState,
({ item }) => {
return Boolean(item);
}
);

export const getItemToDelete: HostIsolationExceptionsSelector<StoreState['deletion']['item']> =
createSelector(getDeletionState, ({ item }) => item);

export const isDeletionInProgress: HostIsolationExceptionsSelector<boolean> = createSelector(
getDeletionState,
({ status }) => {
return isLoadingResourceState(status);
}
);

export const wasDeletionSuccessful: HostIsolationExceptionsSelector<boolean> = createSelector(
getDeletionState,
({ status }) => {
return isLoadedResourceState(status);
}
);

export const getDeleteError: HostIsolationExceptionsSelector<ServerApiError | undefined> =
createSelector(getDeletionState, ({ status }) => {
if (isFailedResourceState(status)) {
return status.error;
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
* 2.0.
*/

import type { FoundExceptionListItemSchema } from '@kbn/securitysolution-io-ts-list-types';
import type {
ExceptionListItemSchema,
FoundExceptionListItemSchema,
} from '@kbn/securitysolution-io-ts-list-types';
import { AsyncResourceState } from '../../state/async_resource_state';

export interface HostIsolationExceptionsPageLocation {
Expand All @@ -20,4 +23,8 @@ export interface HostIsolationExceptionsPageLocation {
export interface HostIsolationExceptionsPageState {
entries: AsyncResourceState<FoundExceptionListItemSchema>;
location: HostIsolationExceptionsPageLocation;
deletion: {
item?: ExceptionListItemSchema;
status: AsyncResourceState<ExceptionListItemSchema>;
};
}
Loading