Skip to content

Commit

Permalink
Merge pull request mendersoftware#4421 from mzedel/men-7034
Browse files Browse the repository at this point in the history
MEN-7034 - device information in auditlog entries
  • Loading branch information
mzedel authored May 28, 2024
2 parents 8a3d06b + 281741d commit d5e36c6
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 75 deletions.
26 changes: 2 additions & 24 deletions src/js/components/auditlogs/__snapshots__/auditlogs.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ exports[`Auditlogs Component renders correctly 1`] = `
Change type
</h5>
<div
class="MuiAutocomplete-root MuiAutocomplete-hasClearIcon MuiAutocomplete-hasPopupIcon css-clttge-MuiAutocomplete-root"
class="MuiAutocomplete-root MuiAutocomplete-hasPopupIcon css-clttge-MuiAutocomplete-root"
name="type"
>
<div
Expand All @@ -137,28 +137,6 @@ exports[`Auditlogs Component renders correctly 1`] = `
<div
class="MuiAutocomplete-endAdornment css-p1olib-MuiAutocomplete-endAdornment"
>
<button
aria-label="Clear"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeMedium MuiAutocomplete-clearIndicator css-1qk9ba-MuiButtonBase-root-MuiIconButton-root-MuiAutocomplete-clearIndicator"
tabindex="-1"
title="Clear"
type="button"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root MuiSvgIcon-fontSizeSmall css-1cwsc1r-MuiSvgIcon-root"
data-testid="CloseIcon"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
/>
</svg>
<span
class="MuiTouchRipple-root css-8je8zh-MuiTouchRipple-root"
/>
</button>
<button
aria-label="Open"
class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeMedium MuiAutocomplete-popupIndicator css-1xk1zqq-MuiButtonBase-root-MuiIconButton-root-MuiAutocomplete-popupIndicator"
Expand Down Expand Up @@ -679,7 +657,7 @@ exports[`Auditlogs Component renders correctly 1`] = `
</div>
</div>
<div
class="loaderContainer"
class="loaderContainer shrunk"
>
<div
class="small loader"
Expand Down
32 changes: 23 additions & 9 deletions src/js/components/auditlogs/auditlogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ import { getUserList } from '../../actions/userActions';
import { BEGINNING_OF_TIME, BENEFITS, SORTING_OPTIONS, TIMEOUTS } from '../../constants/appConstants';
import { AUDIT_LOGS_TYPES } from '../../constants/organizationConstants';
import { createDownload, getISOStringBoundaries } from '../../helpers';
import { getCurrentSession, getGroupNames, getTenantCapabilities, getUserCapabilities } from '../../selectors';
import {
getAuditLog,
getAuditLogEntry,
getAuditLogSelectionState,
getCurrentSession,
getGroupNames,
getTenantCapabilities,
getUserCapabilities
} from '../../selectors';
import { useLocationParams } from '../../utils/liststatehook';
import EnterpriseNotification, { DefaultUpgradeNotification } from '../common/enterpriseNotification';
import { ControlledAutoComplete } from '../common/forms/autocomplete';
Expand Down Expand Up @@ -57,7 +65,7 @@ const useStyles = makeStyles()(theme => ({
upgradeNote: { marginTop: '5vh', placeSelf: 'center' }
}));

const getOptionLabel = option => option.title || option.email || option;
const getOptionLabel = option => option.title ?? option.email ?? option;

const renderOption = (props, option) => <li {...props}>{getOptionLabel(option)}</li>;

Expand All @@ -83,9 +91,10 @@ export const AuditLogs = props => {
const [locationParams, setLocationParams] = useLocationParams('auditlogs', { today, tonight, defaults: locationDefaults });
const { classes } = useStyles();
const dispatch = useDispatch();
const events = useSelector(state => state.organization.auditlog.events);
const events = useSelector(getAuditLog);
const eventItem = useSelector(getAuditLogEntry);
const groups = useSelector(getGroupNames);
const selectionState = useSelector(state => state.organization.auditlog.selectionState);
const selectionState = useSelector(getAuditLogSelectionState);
const userCapabilities = useSelector(getUserCapabilities);
const tenantCapabilities = useSelector(getTenantCapabilities);
const users = useSelector(state => state.users.byId);
Expand All @@ -95,7 +104,7 @@ export const AuditLogs = props => {
const [dirtyField, setDirtyField] = useState('');
const { token } = useSelector(getCurrentSession);

const { detail, isLoading, perPage, endDate, user, sort, startDate, total, type = '' } = selectionState;
const { detail, isLoading, perPage, endDate, user, sort, startDate, total, type } = selectionState;

useEffect(() => {
if (!hasAuditlogs || !isInitialized.current) {
Expand All @@ -121,7 +130,6 @@ export const AuditLogs = props => {

const initAuditlogState = useCallback(
(result, state) => {
isInitialized.current = true;
const { detail, endDate, startDate, type, user } = state;
const resultList = result ? Object.values(result.events) : [];
if (resultList.length && startDate === today) {
Expand All @@ -136,6 +144,8 @@ export const AuditLogs = props => {
field = field || (state.startDate !== today ? 'startDate' : field);
setDirtyField(field);
}, TIMEOUTS.debounceDefault);
// the timeout here is slightly longer than the debounce in the filter component, otherwise the population of the filters with the url state would trigger a reset to page 1
setTimeout(() => (isInitialized.current = true), TIMEOUTS.oneSecond + TIMEOUTS.debounceDefault);
},
[dispatch, today, tonight]
);
Expand All @@ -158,8 +168,8 @@ export const AuditLogs = props => {
let field = endDate !== tonight ? 'endDate' : '';
field = field || (startDate !== today ? 'startDate' : field);
setDirtyField(field);
isInitialized.current = true;
dispatch(setAuditlogsState(state));
// the timeout here is slightly longer than the debounce in the filter component, otherwise the population of the filters with the url state would trigger a reset to page 1
dispatch(setAuditlogsState(state)).then(() => setTimeout(() => (isInitialized.current = true), TIMEOUTS.oneSecond + TIMEOUTS.debounceDefault));
return;
}
dispatch(
Expand Down Expand Up @@ -189,6 +199,9 @@ export const AuditLogs = props => {

const onFiltersChange = useCallback(
({ endDate, detail, startDate, user, type }) => {
if (!isInitialized.current) {
return;
}
const selectedUser = Object.values(users).find(item => isUserOptionEqualToValue(item, user));
dispatch(setAuditlogsState({ page: 1, detail, startDate, endDate, user: selectedUser, type }));
},
Expand All @@ -213,7 +226,7 @@ export const AuditLogs = props => {
<ClickFilter disabled={!hasAuditlogs}>
<Filters
initialValues={{ startDate, endDate, user, type, detail }}
defaultValues={{ startDate: today, endDate: tonight, user: '', type: '', detail: '' }}
defaultValues={{ startDate: today, endDate: tonight, user: '', type: null, detail: '' }}
fieldResetTrigger={detailsReset}
dirtyField={dirtyField}
clearDirty={setDirtyField}
Expand Down Expand Up @@ -275,6 +288,7 @@ export const AuditLogs = props => {
<AuditLogsList
{...props}
items={events}
eventItem={eventItem}
loading={isLoading}
onChangePage={onChangePagination}
onChangeRowsPerPage={newPerPage => onChangePagination(1, newPerPage)}
Expand Down
24 changes: 13 additions & 11 deletions src/js/components/auditlogs/auditlogslist.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { useMemo } from 'react';
import React from 'react';
import { Link } from 'react-router-dom';

import { ArrowRightAlt as ArrowRightAltIcon, Sort as SortIcon } from '@mui/icons-material';
Expand Down Expand Up @@ -128,20 +128,22 @@ const auditLogColumns = [
{ title: 'Time', sortable: true, render: TimeWrapper }
];

export const AuditLogsList = ({ items, loading, onChangePage, onChangeRowsPerPage, onChangeSorting, selectionState, setAuditlogsState, userCapabilities }) => {
const { page, perPage, selectedId, sort = {}, total: count } = selectionState;
export const AuditLogsList = ({
eventItem,
items,
loading,
onChangePage,
onChangeRowsPerPage,
onChangeSorting,
selectionState,
setAuditlogsState,
userCapabilities
}) => {
const { page, perPage, sort = {}, total: count } = selectionState;

const onIssueSelection = selectedIssue =>
setAuditlogsState({ selectedId: selectedIssue ? btoa(`${selectedIssue.action}|${selectedIssue.time}`) : undefined });

const eventItem = useMemo(() => {
if (!selectedId) {
return;
}
const [eventAction, eventTime] = atob(selectedId).split('|');
return items.find(item => item.action === eventAction && item.time === eventTime);
}, [items, selectedId]);

return (
!!items.length && (
<div className="fadeIn deploy-table-contain auditlogs-list">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ exports[`PortForward Component renders correctly 1`] = `
class="flexbox "
>
<div
title="-"
title="raspotifyInstaller"
>
-
raspotifyInstaller
</div>
</div>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,9 +542,9 @@ exports[`TerminalSession Component renders correctly 1`] = `
class="flexbox "
>
<div
title="-"
title="raspotifyInstaller"
>
-
raspotifyInstaller
</div>
</div>
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@ import { useDispatch, useSelector } from 'react-redux';
import { useTheme } from '@mui/material/styles';

import { getDeviceById } from '../../../actions/deviceActions';
import { getDeviceById as getDeviceByIdSelector, getIdAttribute, getUserCapabilities } from '../../../selectors';
import { getAuditlogDevice, getIdAttribute, getUserCapabilities } from '../../../selectors';
import Loader from '../../common/loader';
import DeviceDetails, { DetailInformation } from './devicedetails';

export const DeviceConfiguration = ({ item, onClose }) => {
const { object = {} } = item;
const { canReadDevices } = useSelector(getUserCapabilities);
const device = useSelector(state => getDeviceByIdSelector(state, object.id));
const device = useSelector(getAuditlogDevice);
const { attribute: idAttribute } = useSelector(getIdAttribute);
const dispatch = useDispatch();

const theme = useTheme();
useEffect(() => {
const { object } = item;
if (!device.id && canReadDevices) {
if (canReadDevices) {
dispatch(getDeviceById(object.id));
}
}, [canReadDevices, device.id, dispatch, item]);
}, [canReadDevices, dispatch, object.id]);

if (canReadDevices && !device.id) {
return <Loader show={true} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,28 @@ describe('DeviceConfiguration Component', () => {
it('renders correctly', async () => {
const { baseElement } = render(
<DeviceConfiguration
item={{ ...defaultState.organization.auditlog.events[2], object: { id: defaultState.devices.byId.a1.id }, change: '{"something":"here"}' }}
item={{
...defaultState.organization.auditlog.events[2],
object: { ...defaultState.organization.auditlog.events[2].object, id: defaultState.devices.byId.a1.id },
change: '{"something":"here"}'
}}
onClose={jest.fn}
/>
/>,
{
preloadedState: {
...defaultState,
organization: {
...defaultState.organization,
auditlog: {
...defaultState.organization.auditlog,
selectionState: {
...defaultState.organization.auditlog.selectionState,
selectedId: btoa(`${defaultState.organization.auditlog.events[2].action}|${defaultState.organization.auditlog.events[2].time}`)
}
}
}
}
}
);

const view = baseElement.firstChild.firstChild;
Expand Down
9 changes: 4 additions & 5 deletions src/js/components/auditlogs/eventdetails/filetransfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { useTheme } from '@mui/material/styles';

import { getDeviceById } from '../../../actions/deviceActions';
import { getDeviceById as getDeviceByIdSelector, getIdAttribute, getUserCapabilities } from '../../../selectors';
import { getAuditlogDevice, getIdAttribute, getUserCapabilities } from '../../../selectors';
import Loader from '../../common/loader';
import DeviceDetails, { DetailInformation } from './devicedetails';

Expand All @@ -28,17 +28,16 @@ export const FileTransfer = ({ item, onClose }) => {
meta: { path = [] },
object = {}
} = item;
const device = useSelector(state => getDeviceByIdSelector(state, object.id));
const device = useSelector(getAuditlogDevice);
const { canReadDevices } = useSelector(getUserCapabilities);
const { attribute: idAttribute } = useSelector(getIdAttribute);
const theme = useTheme();

useEffect(() => {
const { object } = item;
if (!device && canReadDevices) {
if (canReadDevices) {
dispatch(getDeviceById(object.id));
}
}, [canReadDevices, device, dispatch, item]);
}, [canReadDevices, dispatch, object.id]);

if (canReadDevices && !device) {
return <Loader show={true} />;
Expand Down
16 changes: 15 additions & 1 deletion src/js/components/auditlogs/eventdetails/filetransfer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,21 @@ import FileTransfer from './filetransfer';

describe('FileTransfer Component', () => {
it('renders correctly', async () => {
const { baseElement } = render(<FileTransfer item={{ ...defaultState.organization.auditlog.events[2], meta: { path: ['/dev/null'] } }} />);
const { baseElement } = render(<FileTransfer item={{ ...defaultState.organization.auditlog.events[2], meta: { path: ['/dev/null'] } }} />, {
preloadedState: {
...defaultState,
organization: {
...defaultState.organization,
auditlog: {
...defaultState.organization.auditlog,
selectionState: {
...defaultState.organization.auditlog.selectionState,
selectedId: btoa(`${defaultState.organization.auditlog.events[2].action}|${defaultState.organization.auditlog.events[2].time}`)
}
}
}
}
});

const view = baseElement.firstChild.firstChild;
expect(view).toMatchSnapshot();
Expand Down
11 changes: 5 additions & 6 deletions src/js/components/auditlogs/eventdetails/portforward.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import moment from 'moment';
import momentDurationFormatSetup from 'moment-duration-format';

import { getDeviceById, getSessionDetails } from '../../../actions/deviceActions';
import { getDeviceById as getDeviceByIdSelector, getIdAttribute, getUserCapabilities } from '../../../selectors';
import { getAuditlogDevice, getIdAttribute, getUserCapabilities } from '../../../selectors';
import Loader from '../../common/loader';
import Time from '../../common/time';
import DeviceDetails, { DetailInformation } from './devicedetails';
Expand All @@ -31,20 +31,19 @@ export const PortForward = ({ item, onClose }) => {
const theme = useTheme();
const [sessionDetails, setSessionDetails] = useState();
const dispatch = useDispatch();
const { object = {} } = item;
const { action, actor, meta, object = {}, time } = item;
const { canReadDevices } = useSelector(getUserCapabilities);
const device = useSelector(state => getDeviceByIdSelector(state, object.id));
const device = useSelector(getAuditlogDevice);
const { attribute: idAttribute } = useSelector(getIdAttribute);

useEffect(() => {
const { action, actor, meta, object, time } = item;
if (canReadDevices && !device) {
if (canReadDevices) {
dispatch(getDeviceById(object.id));
}
dispatch(
getSessionDetails(meta.session_id[0], object.id, actor.id, action.startsWith('open') ? time : undefined, action.startsWith('close') ? time : undefined)
).then(setSessionDetails);
}, [canReadDevices, device, dispatch, item]);
}, [action, actor.id, canReadDevices, dispatch, meta.session_id, object.id, time]);

if (!sessionDetails || (canReadDevices && !device)) {
return <Loader show={true} />;
Expand Down
16 changes: 15 additions & 1 deletion src/js/components/auditlogs/eventdetails/portforward.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,21 @@ describe('PortForward Component', () => {
it('renders correctly', async () => {
const sessionSpy = jest.spyOn(DeviceActions, 'getSessionDetails');
const ui = <PortForward item={defaultState.organization.auditlog.events[2]} />;
const { baseElement, rerender } = render(ui);
const { baseElement, rerender } = render(ui, {
preloadedState: {
...defaultState,
organization: {
...defaultState.organization,
auditlog: {
...defaultState.organization.auditlog,
selectionState: {
...defaultState.organization.auditlog.selectionState,
selectedId: btoa(`${defaultState.organization.auditlog.events[2].action}|${defaultState.organization.auditlog.events[2].time}`)
}
}
}
}
});
await act(async () => {});
await waitFor(() => rerender(ui));
await act(async () => {
Expand Down
Loading

0 comments on commit d5e36c6

Please sign in to comment.