From 2e8b55a69c427e59faa42eadc2889892b8aa8f3a Mon Sep 17 00:00:00 2001 From: Douglas Fabris Date: Thu, 22 Oct 2020 16:03:32 -0300 Subject: [PATCH] [NEW] Audits search by User (#19275) Co-authored-by: Martin --- client/reactAdapters.js | 2 +- .../auditing/client/templates/audit/audit.js | 2 +- ee/app/auditing/server/methods.js | 64 +++++++++++++++++-- ee/client/audit/AuditPage.js | 35 +++++++--- ee/client/audit/DateRangePicker.js | 2 +- ee/client/audit/RoomAutoComplete.js | 2 +- 6 files changed, 88 insertions(+), 19 deletions(-) diff --git a/client/reactAdapters.js b/client/reactAdapters.js index 2c7b129e2ed..c0ea0d07a17 100644 --- a/client/reactAdapters.js +++ b/client/reactAdapters.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]) { diff --git a/ee/app/auditing/client/templates/audit/audit.js b/ee/app/auditing/client/templates/audit/audit.js index 8ff72329cc8..62c4c5faf4f 100644 --- a/ee/app/auditing/client/templates/audit/audit.js +++ b/ee/app/auditing/client/templates/audit/audit.js @@ -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, diff --git a/ee/app/auditing/server/methods.js b/ee/app/auditing/server/methods.js index 68cd31705a8..9406f7d30f2 100644 --- a/ee/app/auditing/server/methods.js +++ b/ee/app/auditing/server/methods.js @@ -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); diff --git a/ee/client/audit/AuditPage.js b/ee/client/audit/AuditPage.js index a434968e737..754df69f93e 100644 --- a/ee/client/audit/AuditPage.js +++ b/ee/client/audit/AuditPage.js @@ -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 - {t('Others')} + {t('Channels')} + {t('Users')} {t('Direct_Messages')} {t('Omnichannel')} @@ -133,9 +143,9 @@ const AuditPage = () => { - + {type === '' && - {t('room_name')} + {t('Channel_name')} @@ -143,6 +153,15 @@ const AuditPage = () => { {errors.rid} } } + {type === 'u' && + {t('Users')} + + + + {errors.users && + {errors.users} + } + } {type === 'd' && {t('Users')} @@ -174,10 +193,10 @@ const AuditPage = () => { } + + + - - - diff --git a/ee/client/audit/DateRangePicker.js b/ee/client/audit/DateRangePicker.js index 16b64373102..7bb3972ff87 100644 --- a/ee/client/audit/DateRangePicker.js +++ b/ee/client/audit/DateRangePicker.js @@ -113,7 +113,7 @@ const DateRangePicker = ({ onChange = () => {}, ...props }) => { return - + ; diff --git a/ee/client/audit/RoomAutoComplete.js b/ee/client/audit/RoomAutoComplete.js index f65b9c673e8..0aefc825f18 100644 --- a/ee/client/audit/RoomAutoComplete.js +++ b/ee/client/audit/RoomAutoComplete.js @@ -23,7 +23,7 @@ const RoomAutoComplete = React.memo((props) => { renderSelected={({ value, label, - }) => <> {label.name}} + }) => <> {label?.name}} renderItem={({ value, label, ...props }) =>