[NEW] Audits search by User (#19275)

Co-authored-by: Martin <martin.schoeler@rocket.chat>
pull/19340/head^2
Douglas Fabris 5 years ago committed by GitHub
parent 526674ac3e
commit 2e8b55a69c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      client/reactAdapters.js
  2. 2
      ee/app/auditing/client/templates/audit/audit.js
  3. 64
      ee/app/auditing/server/methods.js
  4. 35
      ee/client/audit/AuditPage.js
  5. 2
      ee/client/audit/DateRangePicker.js
  6. 2
      ee/client/audit/RoomAutoComplete.js

@ -122,7 +122,7 @@ export const createTemplateForComponent = (
name,
importFn,
{
renderContainerView = () => HTML.DIV(), // eslint-disable-line new-cap
renderContainerView = () => HTML.DIV({ style: 'height: 100%;' }), // eslint-disable-line new-cap
} = {},
) => {
if (Template[name]) {

@ -13,7 +13,7 @@ const loadMessages = async function({ rid, users, startDate, endDate = new Date(
this.loading = this.loading || new ReactiveVar(true);
try {
this.loading.set(true);
const messages = await call('auditGetMessages', { rid, users, startDate, endDate, msg, type, visitor, agent });
const messages = type === 'l' ? await call('auditGetOmnichannelMessages', { rid, users, startDate, endDate, msg, type, visitor, agent }) : await call('auditGetMessages', { rid, users, startDate, endDate, msg, type, visitor, agent });
this.messagesContext.set({
...messageContext({ rid }),
messages,

@ -5,11 +5,16 @@ import { DDPRateLimiter } from 'meteor/ddp-rate-limiter';
import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
import AuditLog from './auditLog';
import { LivechatRooms, Rooms, Messages } from '../../../../app/models/server';
import { LivechatRooms, Rooms, Messages, Users } from '../../../../app/models/server';
import { hasAllPermission } from '../../../../app/authorization/server';
const getValue = (room) => room && { rids: [room._id], name: room.name };
const getUsersIdFromUserName = (usersName) => {
const user = usersName && Users.findByUsername({ $in: usersName });
return user.map((userId) => userId._id);
};
const getRoomInfoByAuditParams = ({ type, roomId, users, visitor, agent }) => {
if (roomId) {
return getValue(Rooms.findOne({ _id: roomId }));
@ -20,13 +25,14 @@ const getRoomInfoByAuditParams = ({ type, roomId, users, visitor, agent }) => {
}
if (type === 'l') {
console.warning('Deprecation Warning! This method will be removed in the next version (4.0.0)');
const rooms = LivechatRooms.findByVisitorIdAndAgentId(visitor, agent, { fields: { _id: 1 } }).fetch();
return rooms && rooms.length && { rids: rooms.map(({ _id }) => _id), name: TAPi18n.__('Omnichannel') };
}
};
Meteor.methods({
auditGetMessages({ rid: roomId, startDate, endDate, users, msg, type, visitor, agent }) {
auditGetOmnichannelMessages({ startDate, endDate, users, msg, type, visitor, agent }) {
check(startDate, Date);
check(endDate, Date);
@ -35,12 +41,10 @@ Meteor.methods({
throw new Meteor.Error('Not allowed');
}
const roomInfo = getRoomInfoByAuditParams({ type, roomId, users, visitor, agent });
if (!roomInfo) {
throw new Meteor.Error('Room doesn`t exist');
}
const rooms = LivechatRooms.findByVisitorIdAndAgentId(visitor, agent !== 'all' && agent, { fields: { _id: 1 } }).fetch();
const roomsData = rooms && rooms.length && { rids: rooms.map(({ _id }) => _id), name: TAPi18n.__('Omnichannel') };
const { rids, name } = roomInfo;
const { rids, name } = roomsData;
const query = {
rid: { $in: rids },
@ -62,6 +66,52 @@ Meteor.methods({
return messages;
},
auditGetMessages({ rid: roomId, startDate, endDate, users, msg, type, visitor, agent }) {
check(startDate, Date);
check(endDate, Date);
const user = Meteor.user();
if (!hasAllPermission(user._id, 'can-audit')) {
throw new Meteor.Error('Not allowed');
}
let rids;
let name;
const query = {
ts: {
$gt: startDate,
$lt: endDate,
},
};
if (type === 'u') {
const usersId = getUsersIdFromUserName(users);
query['u._id'] = { $in: usersId };
} else {
const roomInfo = getRoomInfoByAuditParams({ type, roomId, users, visitor, agent });
if (!roomInfo) {
throw new Meteor.Error('Room doesn`t exist');
}
rids = roomInfo.rids;
name = roomInfo.name;
query.rid = { $in: rids };
}
if (msg) {
const regex = new RegExp(s.trim(s.escapeRegExp(msg)), 'i');
query.msg = regex;
}
const messages = Messages.find(query).fetch();
// Once the filter is applied, messages will be shown and a log containing all filters will be saved for further auditing.
AuditLog.insert({ ts: new Date(), results: messages.length, u: user, fields: { msg, users, rids, room: name, startDate, endDate, type, visitor, agent } });
return messages;
},
auditGetAuditions({ startDate, endDate }) {
check(startDate, Date);
check(endDate, Date);

@ -57,6 +57,10 @@ const AuditPage = () => {
} = handlers;
const useHandleType = (type) => useMutableCallback(() => {
handleVisitor('');
handleAgent();
handleRid('');
handleUsers([]);
handleType(type);
});
@ -73,7 +77,7 @@ const AuditPage = () => {
const apply = useMutableCallback(() => {
if (!rid && type === '') {
return setErrors({
rid: t('The_field_is_required', t('room_name')),
rid: t('The_field_is_required', t('Channel_name')),
});
}
@ -90,17 +94,22 @@ const AuditPage = () => {
errors.agent = t('The_field_is_required', t('Agent'));
}
if (visitor === '') {
errors.visitor = t('The_field_is_required', t('Visitor'));
}
if (errors.visitor || errors.agent) {
return setErrors(errors);
}
}
setErrors({});
setData.current({
msg,
type,
startDate: new Date(startDate),
endDate: new Date(endDate),
endDate: new Date(`${ endDate }T23:59:00`),
visitor,
agent,
users,
@ -111,7 +120,8 @@ const AuditPage = () => {
return <Page>
<Page.Header title={t('Message_auditing')} />
<Tabs>
<Tabs.Item selected={type === ''} onClick={useHandleType('')}>{t('Others')}</Tabs.Item>
<Tabs.Item selected={type === ''} onClick={useHandleType('')}>{t('Channels')}</Tabs.Item>
<Tabs.Item selected={type === 'u'} onClick={useHandleType('u')}>{t('Users')}</Tabs.Item>
<Tabs.Item selected={type === 'd'} onClick={useHandleType('d')}>{t('Direct_Messages')}</Tabs.Item>
<Tabs.Item selected={type === 'l'} onClick={useHandleType('l')}>{t('Omnichannel')}</Tabs.Item>
</Tabs>
@ -133,9 +143,9 @@ const AuditPage = () => {
</Field>
</Margins>
</Box>
<Box display='flex' flexDirection='row'>
<Box display='flex' flexDirection='row' alignItems='flex-end'>
{type === '' && <Field>
<Field.Label>{t('room_name')}</Field.Label>
<Field.Label>{t('Channel_name')}</Field.Label>
<Field.Row>
<RoomAutoComplete error={errors.rid} value={rid} onChange={handleRid} placeholder={t('Channel_Name_Placeholder')}/>
</Field.Row>
@ -143,6 +153,15 @@ const AuditPage = () => {
{errors.rid}
</Field.Error>}
</Field>}
{type === 'u' && <Field>
<Field.Label>{t('Users')}</Field.Label>
<Field.Row>
<UserAutoCompleteMultiple error={errors.users} value={users} onChange={onChangeUsers} placeholder={t('Username_Placeholder')}/>
</Field.Row>
{errors.users && <Field.Error>
{errors.users}
</Field.Error>}
</Field>}
{type === 'd' && <Field>
<Field.Label>{t('Users')}</Field.Label>
<Field.Row>
@ -174,10 +193,10 @@ const AuditPage = () => {
</Field>
</Margins>
</Box>}
<ButtonGroup mis='x8' align='end'>
<Button primary onClick={apply}>{t('Apply')}</Button>
</ButtonGroup>
</Box>
<ButtonGroup mis='x8' align='end'>
<Button primary onClick={apply}>{t('Apply')}</Button>
</ButtonGroup>
<Result setDataRef={setData} />
</Margins>
</Page.ScrollableContentWithShadow>

@ -113,7 +113,7 @@ const DateRangePicker = ({ onChange = () => {}, ...props }) => {
return <Box mi='neg-x4' {...props}>
<Margins inline='x4'>
<InputBox type='date' onChange={handleStart} max={todayDate} value={start} flexGrow={1} h='x20'/>
<InputBox type='date' onChange={handleEnd} min={start} max={todayDate} value={end} flexGrow={1} h='x20'/>
<InputBox type='date' onChange={handleEnd} max={todayDate} min={start} value={end} flexGrow={1} h='x20'/>
<Menu options={options} alignSelf='center'/>
</Margins>
</Box>;

@ -23,7 +23,7 @@ const RoomAutoComplete = React.memo((props) => {
renderSelected={({
value,
label,
}) => <><RoomAvatar size='x20' room={{ _id: value, ...label }} /> {label.name}</>}
}) => <><RoomAvatar size='x20' room={{ _id: value, ...label }} /> {label?.name}</>}
renderItem={({ value, label, ...props }) => <Option key={value} {...props} label={label.name} avatar={<Avatar value={value} {...label} />} />}
options={ options }
/>;

Loading…
Cancel
Save