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

[SIEM] Cases clean up Phase II #61750

Merged
merged 13 commits into from
Mar 31, 2020
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ const MySpinner = styled(EuiLoadingSpinner)`
`;

interface Props {
disabled?: boolean;
isLoading: boolean;
title: string | React.ReactNode;
onSubmit: (title: string) => void;
}

const EditableTitleComponent: React.FC<Props> = ({ onSubmit, isLoading, title }) => {
const EditableTitleComponent: React.FC<Props> = ({
disabled = false,
onSubmit,
isLoading,
title,
}) => {
const [editMode, setEditMode] = useState(false);
const [changedTitle, onTitleChange] = useState<string>(typeof title === 'string' ? title : '');

Expand Down Expand Up @@ -104,6 +110,7 @@ const EditableTitleComponent: React.FC<Props> = ({ onSubmit, isLoading, title })
{isLoading && <MySpinner />}
{!isLoading && (
<MyEuiButtonIcon
isDisabled={disabled}
aria-label={i18n.EDIT_TITLE_ARIA(title as string)}
iconType="pencil"
onClick={onClickEditIcon}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const useInsertTimeline = <T extends FormData>(form: FormHook<T>, fieldNa
});
const handleOnTimelineChange = useCallback(
(title: string, id: string | null) => {
const builtLink = `${basePath}/app/siem#/timelines?timeline=(id:${id},isOpen:!t)`;
const builtLink = `${basePath}/app/siem#/timelines?timeline=(id:'${id}',isOpen:!t)`;
const currentValue = form.getFormData()[fieldName];
const newValue: string = [
currentValue.slice(0, cursorPosition.start),
Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/siem/public/containers/case/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const getCases = async ({
signal,
}: FetchCasesProps): Promise<AllCases> => {
const query = {
reporters: filterOptions.reporters.map(r => r.username),
reporters: filterOptions.reporters.map(r => r.username ?? '').filter(r => r !== ''),
tags: filterOptions.tags,
...(filterOptions.status !== '' ? { status: filterOptions.status } : {}),
...(filterOptions.search.length > 0 ? { search: filterOptions.search } : {}),
Expand Down
2 changes: 1 addition & 1 deletion x-pack/legacy/plugins/siem/public/containers/case/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export enum SortFieldCase {
export interface ElasticUser {
readonly email?: string | null;
readonly fullName?: string | null;
readonly username: string;
readonly username?: string | null;
}

export interface FetchCasesProps extends ApiProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { useCallback, useEffect, useState } from 'react';

import { isEmpty } from 'lodash/fp';
import { User } from '../../../../../../plugins/case/common/api';
import { errorToToaster, useStateToaster } from '../../components/toasters';
import { getReporters } from './api';
Expand Down Expand Up @@ -44,9 +45,12 @@ export const useGetReporters = (): UseGetReporters => {
});
try {
const response = await getReporters(abortCtrl.signal);
const myReporters = response
.map(r => (r.full_name == null || isEmpty(r.full_name) ? r.username ?? '' : r.full_name))
.filter(u => !isEmpty(u));
if (!didCancel) {
setReporterState({
reporters: response.map(r => r.full_name ?? r.username ?? 'N/A'),
reporters: myReporters,
respReporters: response,
isLoading: false,
isError: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => {
createdAt,
createdBy: {
fullName: createdBy.fullName ?? null,
username: createdBy?.username,
username: createdBy?.username ?? '',
},
comments: comments
.filter(c => {
Expand All @@ -168,14 +168,14 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => {
createdAt: c.createdAt,
createdBy: {
fullName: c.createdBy.fullName ?? null,
username: c.createdBy.username,
username: c.createdBy.username ?? '',
},
updatedAt: c.updatedAt,
updatedBy:
c.updatedBy != null
? {
fullName: c.updatedBy.fullName ?? null,
username: c.updatedBy.username,
username: c.updatedBy.username ?? '',
}
: null,
})),
Expand All @@ -187,7 +187,7 @@ const formatServiceRequestData = (myCase: Case): ServiceConnectorCaseParams => {
updatedBy != null
? {
fullName: updatedBy.fullName ?? null,
username: updatedBy.username,
username: updatedBy.username ?? '',
}
: null,
};
Expand Down
34 changes: 31 additions & 3 deletions x-pack/legacy/plugins/siem/public/lib/kibana/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => {
let didCancel = false;
const fetchData = async () => {
try {
const response = await security.authc.getCurrentUser();
if (!didCancel) {
setUser(convertToCamelCase<AuthenticatedUser, AuthenticatedElasticUser>(response));
if (security != null) {
const response = await security.authc.getCurrentUser();
if (!didCancel) {
setUser(convertToCamelCase<AuthenticatedUser, AuthenticatedElasticUser>(response));
}
}
} catch (error) {
if (!didCancel) {
Expand All @@ -81,3 +83,29 @@ export const useCurrentUser = (): AuthenticatedElasticUser | null => {
}, []);
return user;
};

export interface UseGetUserSavedObjectPermissions {
crud: boolean;
read: boolean;
}

export const useGetUserSavedObjectPermissions = () => {
const [
savedObjectsPermissions,
setSavedObjectsPermissions,
] = useState<UseGetUserSavedObjectPermissions | null>(null);
const uiCapabilities = useKibana().services.application.capabilities;

useEffect(() => {
const capabilitiesCanUserCRUD: boolean =
typeof uiCapabilities.siem.crud === 'boolean' ? uiCapabilities.siem.crud : false;
const capabilitiesCanUserRead: boolean =
typeof uiCapabilities.siem.show === 'boolean' ? uiCapabilities.siem.show : false;
setSavedObjectsPermissions({
crud: capabilitiesCanUserCRUD,
read: capabilitiesCanUserRead,
});
}, [uiCapabilities]);

return savedObjectsPermissions;
};
36 changes: 27 additions & 9 deletions x-pack/legacy/plugins/siem/public/pages/case/case.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,34 @@
import React from 'react';

import { WrapperPage } from '../../components/wrapper_page';
import { AllCases } from './components/all_cases';
import { useGetUserSavedObjectPermissions } from '../../lib/kibana';
import { SpyRoute } from '../../utils/route/spy_routes';
import { AllCases } from './components/all_cases';

import { getSavedObjectReadOnly, CaseCallOut } from './components/callout';
import { CaseSavedObjectNoPermissions } from './saved_object_no_permissions';

const infoReadSavedObject = getSavedObjectReadOnly();

export const CasesPage = React.memo(() => {
const userPermissions = useGetUserSavedObjectPermissions();

export const CasesPage = React.memo(() => (
<>
<WrapperPage>
<AllCases />
</WrapperPage>
<SpyRoute />
</>
));
return userPermissions == null || userPermissions?.read ? (
<>
<WrapperPage>
{userPermissions != null && !userPermissions?.crud && userPermissions?.read && (
<CaseCallOut
title={infoReadSavedObject.title}
message={infoReadSavedObject.description}
/>
)}
<AllCases userCanCrud={userPermissions?.crud ?? false} />
</WrapperPage>
<SpyRoute />
</>
) : (
<CaseSavedObjectNoPermissions />
);
});

CasesPage.displayName = 'CasesPage';
28 changes: 21 additions & 7 deletions x-pack/legacy/plugins/siem/public/pages/case/case_details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,36 @@
*/

import React from 'react';
import { useParams } from 'react-router-dom';
import { useParams, Redirect } from 'react-router-dom';

import { CaseView } from './components/case_view';
import { useGetUrlSearch } from '../../components/navigation/use_get_url_search';
import { useGetUserSavedObjectPermissions } from '../../lib/kibana';
import { SpyRoute } from '../../utils/route/spy_routes';
import { getCaseUrl } from '../../components/link_to';
import { navTabs } from '../home/home_navigations';
import { CaseView } from './components/case_view';
import { getSavedObjectReadOnly, CaseCallOut } from './components/callout';

const infoReadSavedObject = getSavedObjectReadOnly();

export const CaseDetailsPage = React.memo(() => {
const userPermissions = useGetUserSavedObjectPermissions();
const { detailName: caseId } = useParams();
if (!caseId) {
return null;
const search = useGetUrlSearch(navTabs.case);

if (userPermissions != null && !userPermissions.read) {
return <Redirect to={getCaseUrl(search)} />;
}
return (

return caseId != null ? (
<>
<CaseView caseId={caseId} />
{userPermissions != null && !userPermissions?.crud && userPermissions?.read && (
<CaseCallOut title={infoReadSavedObject.title} message={infoReadSavedObject.description} />
)}
<CaseView caseId={caseId} userCanCrud={userPermissions?.crud ?? false} />
<SpyRoute />
</>
);
) : null;
});

CaseDetailsPage.displayName = 'CaseDetailsPage';
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ const renderStringField = (field: string, dataTestSubj: string) =>

export const getCasesColumns = (
actions: Array<DefaultItemIconButtonAction<Case>>,
filterStatus: string
filterStatus: string,
userCanCrud: boolean
): CasesColumns[] => [
{
name: i18n.NAME,
Expand Down Expand Up @@ -71,7 +72,7 @@ export const getCasesColumns = (
<>
<EuiAvatar
className="userAction__circle"
name={createdBy.fullName ? createdBy.fullName : createdBy.username}
name={createdBy.fullName ? createdBy.fullName : createdBy.username ?? ''}
size="s"
/>
<Spacer data-test-subj="case-table-column-createdBy">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ describe('AllCases', () => {
it('should render AllCases', () => {
const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
expect(
Expand Down Expand Up @@ -132,7 +132,7 @@ describe('AllCases', () => {
it('should tableHeaderSortButton AllCases', () => {
const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
wrapper
Expand All @@ -149,7 +149,7 @@ describe('AllCases', () => {
it('closes case when row action icon clicked', () => {
const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
wrapper
Expand Down Expand Up @@ -182,7 +182,7 @@ describe('AllCases', () => {

const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
wrapper
Expand Down Expand Up @@ -213,7 +213,7 @@ describe('AllCases', () => {

const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
wrapper
Expand All @@ -238,7 +238,7 @@ describe('AllCases', () => {

const wrapper = mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
wrapper
Expand All @@ -259,7 +259,7 @@ describe('AllCases', () => {

mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
expect(refetchCases).toBeCalled();
Expand All @@ -274,7 +274,7 @@ describe('AllCases', () => {

mount(
<TestProviders>
<AllCases />
<AllCases userCanCrud={true} />
</TestProviders>
);
expect(refetchCases).toBeCalled();
Expand Down
Loading