Fix unable to open host or user detail pages when their names conflict with tabs (#137719)

* Fix Unable to open user detail pages when user names conflict with tabs

* Fix Unable to open host detail pages when host names conflict with tabs

* Preserve tab name on user and host compatibility redirect
This commit is contained in:
Pablo Machado 2022-08-04 14:39:47 +02:00 committed by GitHub
parent c3a55d1aa8
commit 4c690bb3a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 65 additions and 31 deletions

View file

@ -146,7 +146,7 @@ describe('ml conditional links', () => {
visitWithoutDateRange(mlHostSingleHostNullKqlQuery);
cy.url().should(
'include',
'/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
'/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
);
});
@ -154,7 +154,7 @@ describe('ml conditional links', () => {
visitWithoutDateRange(mlHostSingleHostKqlQueryVariable);
cy.url().should(
'include',
'/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
'/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
);
});
@ -162,7 +162,7 @@ describe('ml conditional links', () => {
visitWithoutDateRange(mlHostSingleHostKqlQuery);
cy.url().should(
'include',
'/app/security/hosts/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
'/app/security/hosts/name/siem-windows/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!(%27auditbeat-*%27)))&query=(language:kuery,query:%27(process.name:%20%22conhost.exe%22%20or%20process.name:%20%22sc.exe%22)%27)&timerange=(global:(linkTo:!(timeline),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)),timeline:(linkTo:!(global),timerange:(from:%272019-06-06T06:00:00.000Z%27,kind:absolute,to:%272019-06-07T05:59:59.999Z%27)))'
);
});

View file

@ -255,7 +255,7 @@ describe('url state', () => {
cy.get(ANOMALIES_TAB).should(
'have.attr',
'href',
"/app/security/hosts/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')"
"/app/security/hosts/name/siem-kibana/anomalies?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')"
);
cy.get(BREADCRUMBS)
@ -270,7 +270,7 @@ describe('url state', () => {
.should(
'have.attr',
'href',
`/app/security/hosts/siem-kibana?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))`
`/app/security/hosts/name/siem-kibana?sourcerer=(default:(id:security-solution-default,selectedPatterns:!('auditbeat-*')))&query=(language:kuery,query:'agent.type:%20%22auditbeat%22%20')&timerange=(global:(linkTo:!(timeline),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')),timeline:(linkTo:!(global),timerange:(from:'2019-08-01T20:03:29.186Z',kind:absolute,to:'2023-01-01T21:33:29.186Z')))`
);
});

View file

@ -15,10 +15,10 @@ export const getTabsOnHostsUrl = (tabName: HostsTableType, search?: string) =>
`/${tabName}${appendSearch(search)}`;
export const getHostDetailsUrl = (detailName: string, search?: string) =>
`/${detailName}${appendSearch(search)}`;
`/name/${detailName}${appendSearch(search)}`;
export const getTabsOnHostDetailsUrl = (
detailName: string,
tabName: HostsTableType,
search?: string
) => `/${detailName}/${tabName}${appendSearch(search)}`;
) => `/name/${detailName}/${tabName}${appendSearch(search)}`;

View file

@ -9,10 +9,10 @@ import type { UsersTableType } from '../../../users/store/model';
import { appendSearch } from './helpers';
export const getUsersDetailsUrl = (detailName: string, search?: string) =>
`/${detailName}${appendSearch(search)}`;
`/name/${detailName}${appendSearch(search)}`;
export const getTabsOnUsersDetailsUrl = (
detailName: string,
tabName: UsersTableType,
search?: string
) => `/${detailName}/${tabName}${appendSearch(search)}`;
) => `/name/${detailName}/${tabName}${appendSearch(search)}`;

View file

@ -60,13 +60,13 @@ describe('Custom Links', () => {
describe('HostDetailsLink', () => {
test('should render valid link to Host Details with hostName as the display text', () => {
const wrapper = mount(<HostDetailsLink hostName={hostName} />);
expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`);
expect(wrapper.find('EuiLink').prop('href')).toEqual(`/name/${encodeURIComponent(hostName)}`);
expect(wrapper.text()).toEqual(hostName);
});
test('should render valid link to Host Details with child text as the display text', () => {
const wrapper = mount(<HostDetailsLink hostName={hostName}>{hostName}</HostDetailsLink>);
expect(wrapper.find('EuiLink').prop('href')).toEqual(`/${encodeURIComponent(hostName)}`);
expect(wrapper.find('EuiLink').prop('href')).toEqual(`/name/${encodeURIComponent(hostName)}`);
expect(wrapper.text()).toEqual(hostName);
});
});

View file

@ -88,7 +88,9 @@ export const MlHostConditionalContainer = React.memo(() => {
});
return (
<Redirect to={`${HOSTS_PATH}/${hostName}/${HostsTableType.anomalies}?${reEncoded}`} />
<Redirect
to={`${HOSTS_PATH}/name/${hostName}/${HostsTableType.anomalies}?${reEncoded}`}
/>
);
}
}}

View file

@ -240,7 +240,7 @@ describe('Navigation Breadcrumbs', () => {
hostsBreadcrumbs,
{
text: 'siem-kibana',
href: 'securitySolutionUI/hosts/siem-kibana',
href: 'securitySolutionUI/hosts/name/siem-kibana',
},
{ text: 'Authentications', href: '' },
]);
@ -458,7 +458,7 @@ describe('Navigation Breadcrumbs', () => {
}),
expect.objectContaining({
text: 'siem-kibana',
href: 'securitySolutionUI/hosts/siem-kibana',
href: 'securitySolutionUI/hosts/name/siem-kibana',
onClick: expect.any(Function),
}),
{
@ -556,7 +556,7 @@ describe('Navigation Breadcrumbs', () => {
hostsBreadcrumbs,
{
text: 'siem-kibana',
href: 'securitySolutionUI/hosts/siem-kibana',
href: 'securitySolutionUI/hosts/name/siem-kibana',
},
{ text: 'Authentications', href: '' },
]);
@ -787,7 +787,7 @@ describe('Navigation Breadcrumbs', () => {
}),
expect.objectContaining({
text: 'siem-kibana',
href: 'securitySolutionUI/hosts/siem-kibana',
href: 'securitySolutionUI/hosts/name/siem-kibana',
onClick: expect.any(Function),
}),
{

View file

@ -89,7 +89,7 @@ describe('Table Navigation', () => {
`EuiTab[data-test-subj="navigation-${HostsTableType.authentications}"]`
);
expect(firstTab.props().href).toBe(
`/app/securitySolutionUI/hosts/siem-window/authentications${SEARCH_QUERY}`
`/app/securitySolutionUI/hosts/name/siem-window/authentications${SEARCH_QUERY}`
);
});

View file

@ -97,7 +97,7 @@ describe('body', () => {
test(`it should pass expected object properties to ${componentName}`, () => {
const wrapper = mount(
<TestProviders>
<MemoryRouter initialEntries={[`/hosts/host-1/${path}`]}>
<MemoryRouter initialEntries={[`/hosts/name/host-1/${path}`]}>
<HostDetailsTabs
isInitializing={false}
detailName={'host-1'}

View file

@ -12,7 +12,7 @@ import { HostsTableType } from '../../store/model';
import { HOSTS_PATH } from '../../../../common/constants';
const getTabsOnHostDetailsUrl = (hostName: string, tabName: HostsTableType) =>
`${HOSTS_PATH}/${hostName}/${tabName}`;
`${HOSTS_PATH}/name/${hostName}/${tabName}`;
export const navTabsHostDetails = ({
hasMlUserPermissions,

View file

@ -55,7 +55,7 @@ export const HostsContainer = React.memo(() => (
/>
)}
/>
<Route
<Route // Redirect to the first tab when tabName is not present.
path={hostDetailsPagePath}
render={({
match: {
@ -65,14 +65,29 @@ export const HostsContainer = React.memo(() => (
}) => (
<Redirect
to={{
pathname: `${HOSTS_PATH}/${detailName}/${HostsTableType.authentications}`,
pathname: `${HOSTS_PATH}/name/${detailName}/${HostsTableType.authentications}`,
search,
}}
/>
)}
/>
<Route
<Route // Compatibility redirect for the old user detail path.
path={`${HOSTS_PATH}/:detailName/:tabName?`}
render={({
match: {
params: { detailName, tabName = HostsTableType.authentications },
},
location: { search = '' },
}) => (
<Redirect
to={{
pathname: `${HOSTS_PATH}/name/${detailName}/${tabName}`,
search,
}}
/>
)}
/>
<Route // Redirect to the first tab when tabName is not present.
path={HOSTS_PATH}
render={({ location: { search = '' } }) => (
<Redirect to={{ pathname: `${HOSTS_PATH}/${HostsTableType.hosts}`, search }} />

View file

@ -13,7 +13,7 @@ import type { GlobalTimeArgs } from '../../common/containers/use_global_time';
import type { InputsModelId } from '../../common/store/inputs/constants';
import { HOSTS_PATH } from '../../../common/constants';
export const hostDetailsPagePath = `${HOSTS_PATH}/:detailName`;
export const hostDetailsPagePath = `${HOSTS_PATH}/name/:detailName`;
export type HostsTabsProps = GlobalTimeArgs & {
filterQuery: string;

View file

@ -146,7 +146,7 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1
<a
class="euiLink emotion-euiLink-primary"
data-test-subj="host-details-button"
href="securitySolutionUI/hosts/raspberrypi"
href="securitySolutionUI/hosts/name/raspberrypi"
rel="noreferrer"
>
raspberrypi
@ -201,7 +201,7 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot
<a
class="euiLink emotion-euiLink-primary"
data-test-subj="host-details-button"
href="securitySolutionUI/hosts/raspberrypi"
href="securitySolutionUI/hosts/name/raspberrypi"
rel="noreferrer"
>
raspberrypi

View file

@ -8,7 +8,7 @@
import { USERS_PATH } from '../../../common/constants';
import { UsersTableType } from '../store/model';
export const usersDetailsPagePath = `${USERS_PATH}/:detailName`;
export const usersDetailsPagePath = `${USERS_PATH}/name/:detailName`;
export const usersTabPath = `${USERS_PATH}/:tabName(${UsersTableType.allUsers}|${UsersTableType.authentications}|${UsersTableType.anomalies}|${UsersTableType.risk}|${UsersTableType.events}|)`;

View file

@ -12,7 +12,7 @@ import { UsersTableType } from '../../store/model';
import { USERS_PATH } from '../../../../common/constants';
const getTabsOnUsersDetailsUrl = (userName: string, tabName: UsersTableType) =>
`${USERS_PATH}/${userName}/${tabName}`;
`${USERS_PATH}/name/${userName}/${tabName}`;
export const navTabsUsersDetails = (
userName: string,

View file

@ -34,7 +34,7 @@ export const UsersContainer = React.memo(() => {
/>
)}
/>
<Route
<Route // Redirect to the first tab when tabName is not present.
path={usersDetailsPagePath}
render={({
match: {
@ -44,13 +44,30 @@ export const UsersContainer = React.memo(() => {
}) => (
<Redirect
to={{
pathname: `${USERS_PATH}/${detailName}/${UsersTableType.authentications}`,
pathname: `${USERS_PATH}/name/${detailName}/${UsersTableType.authentications}`,
search,
}}
/>
)}
/>
<Route
<Route // Compatibility redirect for the old user detail path.
path={`${USERS_PATH}/:detailName/:tabName?`}
render={({
match: {
params: { detailName, tabName = UsersTableType.authentications },
},
location: { search = '' },
}) => (
<Redirect
to={{
pathname: `${USERS_PATH}/name/${detailName}/${tabName}`,
search,
}}
/>
)}
/>
<Route // Redirect to the first tab when tabName is not present.
path={USERS_PATH}
render={({ location: { search = '' } }) => (
<Redirect to={{ pathname: `${USERS_PATH}/${UsersTableType.allUsers}`, search }} />