// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. import UserStore from 'stores/user_store.jsx'; import ChannelStore from 'stores/channel_store.jsx'; import * as Utils from 'utils/utils.jsx'; import {intlShape, injectIntl, defineMessages, FormattedMessage, FormattedDate, FormattedTime} from 'react-intl'; const holders = defineMessages({ sessionRevoked: { id: 'audit_table.sessionRevoked', defaultMessage: 'The session with id {sessionId} was revoked' }, channelCreated: { id: 'audit_table.channelCreated', defaultMessage: 'Created the {channelName} channel' }, establishedDM: { id: 'audit_table.establishedDM', defaultMessage: 'Established a direct message channel with {username}' }, nameUpdated: { id: 'audit_table.nameUpdated', defaultMessage: 'Updated the {channelName} channel name' }, headerUpdated: { id: 'audit_table.headerUpdated', defaultMessage: 'Updated the {channelName} channel header' }, channelDeleted: { id: 'audit_table.channelDeleted', defaultMessage: 'Deleted the channel with the URL {url}' }, userAdded: { id: 'audit_table.userAdded', defaultMessage: 'Added {username} to the {channelName} channel' }, userRemoved: { id: 'audit_table.userRemoved', defaultMessage: 'Removed {username} to the {channelName} channel' }, attemptedRegisterApp: { id: 'audit_table.attemptedRegisterApp', defaultMessage: 'Attempted to register a new OAuth Application with ID {id}' }, attemptedAllowOAuthAccess: { id: 'audit_table.attemptedAllowOAuthAccess', defaultMessage: 'Attempted to allow a new OAuth service access' }, successfullOAuthAccess: { id: 'audit_table.successfullOAuthAccess', defaultMessage: 'Successfully gave a new OAuth service access' }, failedOAuthAccess: { id: 'audit_table.failedOAuthAccess', defaultMessage: 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback' }, attemptedOAuthToken: { id: 'audit_table.attemptedOAuthToken', defaultMessage: 'Attempted to get an OAuth access token' }, successfullOAuthToken: { id: 'audit_table.successfullOAuthToken', defaultMessage: 'Successfully added a new OAuth service' }, oauthTokenFailed: { id: 'audit_table.oauthTokenFailed', defaultMessage: 'Failed to get an OAuth access token - {token}' }, attemptedLogin: { id: 'audit_table.attemptedLogin', defaultMessage: 'Attempted to login' }, successfullLogin: { id: 'audit_table.successfullLogin', defaultMessage: 'Successfully logged in' }, failedLogin: { id: 'audit_table.failedLogin', defaultMessage: 'FAILED login attempt' }, updatePicture: { id: 'audit_table.updatePicture', defaultMessage: 'Updated your profile picture' }, updateGeneral: { id: 'audit_table.updateGeneral', defaultMessage: 'Updated the general settings of your account' }, attemptedPassword: { id: 'audit_table.attemptedPassword', defaultMessage: 'Attempted to change password' }, successfullPassword: { id: 'audit_table.successfullPassword', defaultMessage: 'Successfully changed password' }, failedPassword: { id: 'audit_table.failedPassword', defaultMessage: 'Failed to change password - tried to update user password who was logged in through OAuth' }, updatedRol: { id: 'audit_table.updatedRol', defaultMessage: 'Updated user role(s) to ' }, member: { id: 'audit_table.member', defaultMessage: 'member' }, accountActive: { id: 'audit_table.accountActive', defaultMessage: 'Account made active' }, accountInactive: { id: 'audit_table.accountInactive', defaultMessage: 'Account made inactive' }, by: { id: 'audit_table.by', defaultMessage: ' by {username}' }, byAdmin: { id: 'audit_table.byAdmin', defaultMessage: ' by an admin' }, sentEmail: { id: 'audit_table.sentEmail', defaultMessage: 'Sent an email to {email} to reset your password' }, attemptedReset: { id: 'audit_table.attemptedReset', defaultMessage: 'Attempted to reset password' }, successfullReset: { id: 'audit_table.successfullReset', defaultMessage: 'Successfully reset password' }, updateGlobalNotifications: { id: 'audit_table.updateGlobalNotifications', defaultMessage: 'Updated your global notification settings' }, attemptedWebhookCreate: { id: 'audit_table.attemptedWebhookCreate', defaultMessage: 'Attempted to create a webhook' }, succcessfullWebhookCreate: { id: 'audit_table.successfullWebhookCreate', defaultMessage: 'Successfully created a webhook' }, failedWebhookCreate: { id: 'audit_table.failedWebhookCreate', defaultMessage: 'Failed to create a webhook - bad channel permissions' }, attemptedWebhookDelete: { id: 'audit_table.attemptedWebhookDelete', defaultMessage: 'Attempted to delete a webhook' }, successfullWebhookDelete: { id: 'audit_table.successfullWebhookDelete', defaultMessage: 'Successfully deleted a webhook' }, failedWebhookDelete: { id: 'audit_table.failedWebhookDelete', defaultMessage: 'Failed to delete a webhook - inappropriate conditions' }, logout: { id: 'audit_table.logout', defaultMessage: 'Logged out of your account' }, verified: { id: 'audit_table.verified', defaultMessage: 'Sucessfully verified your email address' }, revokedAll: { id: 'audit_table.revokedAll', defaultMessage: 'Revoked all current sessions for the team' }, loginAttempt: { id: 'audit_table.loginAttempt', defaultMessage: ' (Login attempt)' }, loginFailure: { id: 'audit_table.loginFailure', defaultMessage: ' (Login failure)' }, attemptedLicenseAdd: { id: 'audit_table.attemptedLicenseAdd', defaultMessage: 'Attempted to add new license' }, successfullLicenseAdd: { id: 'audit_table.successfullLicenseAdd', defaultMessage: 'Successfully added new license' }, failedExpiredLicenseAdd: { id: 'audit_table.failedExpiredLicenseAdd', defaultMessage: 'Failed to add a new license as it has either expired or not yet been started' }, failedInvalidLicenseAdd: { id: 'audit_table.failedInvalidLicenseAdd', defaultMessage: 'Failed to add an invalid license' }, licenseRemoved: { id: 'audit_table.licenseRemoved', defaultMessage: 'Successfully removed a license' } }); import React from 'react'; class AuditTable extends React.Component { render() { var accessList = []; const {formatMessage} = this.props.intl; for (var i = 0; i < this.props.audits.length; i++) { const audit = this.props.audits[i]; const auditInfo = formatAuditInfo(audit, formatMessage); let uContent; if (this.props.showUserId) { var profile = UserStore.getProfile(auditInfo.userId); if (profile) { uContent = {profile.email}; } else { uContent = {auditInfo.userId}; } } let iContent; if (this.props.showIp) { iContent = {auditInfo.ip}; } let sContent; if (this.props.showSession) { sContent = {auditInfo.sessionId}; } const descStyle = {}; if (auditInfo.desc.toLowerCase().indexOf('fail') !== -1) { descStyle.color = 'red'; } accessList[i] = ( {auditInfo.timestamp} {uContent} {auditInfo.desc} {iContent} {sContent} ); } let userIdContent; if (this.props.showUserId) { userIdContent = ( ); } let ipContent; if (this.props.showIp) { ipContent = ( ); } let sessionContent; if (this.props.showSession) { sessionContent = ( ); } return ( {userIdContent} {ipContent} {sessionContent} {accessList}
); } } AuditTable.propTypes = { intl: intlShape.isRequired, audits: React.PropTypes.array.isRequired, showUserId: React.PropTypes.bool, showIp: React.PropTypes.bool, showSession: React.PropTypes.bool }; export default injectIntl(AuditTable); export function formatAuditInfo(audit, formatMessage) { const actionURL = audit.action.replace(/\/api\/v[1-9]/, ''); let auditDesc = ''; if (actionURL.indexOf('/channels') === 0) { const channelInfo = audit.extra_info.split(' '); const channelNameField = channelInfo[0].split('='); let channelURL = ''; let channelObj; let channelName = ''; if (channelNameField.indexOf('name') >= 0) { channelURL = channelNameField[channelNameField.indexOf('name') + 1]; channelObj = ChannelStore.getByName(channelURL); if (channelObj) { channelName = channelObj.display_name; } else { channelName = channelURL; } } switch (actionURL) { case '/channels/create': auditDesc = formatMessage(holders.channelCreated, {channelName}); break; case '/channels/create_direct': auditDesc = formatMessage(holders.establishedDM, {username: Utils.getDirectTeammate(channelObj.id).username}); break; case '/channels/update': auditDesc = formatMessage(holders.nameUpdated, {channelName}); break; case '/channels/update_desc': // support the old path case '/channels/update_header': auditDesc = formatMessage(holders.headerUpdated, {channelName}); break; default: { let userIdField = []; let userId = ''; let username = ''; if (channelInfo[1]) { userIdField = channelInfo[1].split('='); if (userIdField.indexOf('user_id') >= 0) { userId = userIdField[userIdField.indexOf('user_id') + 1]; username = UserStore.getProfile(userId).username; } } if (/\/channels\/[A-Za-z0-9]+\/delete/.test(actionURL)) { auditDesc = formatMessage(holders.channelDeleted, {url: channelURL}); } else if (/\/channels\/[A-Za-z0-9]+\/add/.test(actionURL)) { auditDesc = formatMessage(holders.userAdded, {username, channelName}); } else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(actionURL)) { auditDesc = formatMessage(holders.userRemoved, {username, channelName}); } break; } } } else if (actionURL.indexOf('/oauth') === 0) { const oauthInfo = audit.extra_info.split(' '); switch (actionURL) { case '/oauth/register': { const clientIdField = oauthInfo[0].split('='); if (clientIdField[0] === 'client_id') { auditDesc = formatMessage(holders.attemptedRegisterApp, {id: clientIdField[1]}); } break; } case '/oauth/allow': if (oauthInfo[0] === 'attempt') { auditDesc = formatMessage(holders.attemptedAllowOAuthAccess); } else if (oauthInfo[0] === 'success') { auditDesc = formatMessage(holders.successfullOAuthAccess); } else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') { auditDesc = formatMessage(holders.failedOAuthAccess); } break; case '/oauth/access_token': if (oauthInfo[0] === 'attempt') { auditDesc = formatMessage(holders.attemptedOAuthToken); } else if (oauthInfo[0] === 'success') { auditDesc = formatMessage(holders.successfullOAuthToken); } else { const oauthTokenFailure = oauthInfo[0].split('-'); if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) { auditDesc = formatMessage(oauthTokenFailure, {token: oauthTokenFailure[1].trim()}); } } break; default: break; } } else if (actionURL.indexOf('/users') === 0) { const userInfo = audit.extra_info.split(' '); switch (actionURL) { case '/users/login': if (userInfo[0] === 'attempt') { auditDesc = formatMessage(holders.attemptedLogin); } else if (userInfo[0] === 'success') { auditDesc = formatMessage(holders.successfullLogin); } else if (userInfo[0]) { auditDesc = formatMessage(holders.failedLogin); } break; case '/users/revoke_session': auditDesc = formatMessage(holders.sessionRevoked, {sessionId: userInfo[0].split('=')[1]}); break; case '/users/newimage': auditDesc = formatMessage(holders.updatePicture); break; case '/users/update': auditDesc = formatMessage(holders.updateGeneral); break; case '/users/newpassword': if (userInfo[0] === 'attempted') { auditDesc = formatMessage(holders.attemptedPassword); } else if (userInfo[0] === 'completed') { auditDesc = formatMessage(holders.successfullPassword); } else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') { auditDesc = formatMessage(holders.failedPassword); } break; case '/users/update_roles': { const userRoles = userInfo[0].split('=')[1]; auditDesc = formatMessage(holders.updatedRol); if (userRoles.trim()) { auditDesc += userRoles; } else { auditDesc += formatMessage(holders.member); } break; } case '/users/update_active': { const updateType = userInfo[0].split('=')[0]; const updateField = userInfo[0].split('=')[1]; /* Either describes account activation/deactivation or a revoked session as part of an account deactivation */ if (updateType === 'active') { if (updateField === 'true') { auditDesc = formatMessage(holders.accountActive); } else if (updateField === 'false') { auditDesc = formatMessage(holders.accountInactive); } const actingUserInfo = userInfo[1].split('='); if (actingUserInfo[0] === 'session_user') { const actingUser = UserStore.getProfile(actingUserInfo[1]); const user = UserStore.getCurrentUser(); if (user && actingUser && (Utils.isSystemAdmin(user.roles))) { auditDesc += formatMessage(holders.by, {username: actingUser.username}); } else if (user && actingUser) { auditDesc += formatMessage(holders.byAdmin); } } } else if (updateType === 'session_id') { auditDesc = formatMessage(holders.sessionRevoked, {sessionId: updateField}); } break; } case '/users/send_password_reset': auditDesc = formatMessage(holders.sentEmail, {email: userInfo[0].split('=')[1]}); break; case '/users/reset_password': if (userInfo[0] === 'attempt') { auditDesc = formatMessage(holders.attemptedReset); } else if (userInfo[0] === 'success') { auditDesc = formatMessage(holders.successfullReset); } break; case '/users/update_notify': auditDesc = formatMessage(holders.updateGlobalNotifications); break; default: break; } } else if (actionURL.indexOf('/hooks') === 0) { const webhookInfo = audit.extra_info; switch (actionURL) { case '/hooks/incoming/create': if (webhookInfo === 'attempt') { auditDesc = formatMessage(holders.attemptedWebhookCreate); } else if (webhookInfo === 'success') { auditDesc = formatMessage(holders.succcessfullWebhookCreate); } else if (webhookInfo === 'fail - bad channel permissions') { auditDesc = formatMessage(holders.failedWebhookCreate); } break; case '/hooks/incoming/delete': if (webhookInfo === 'attempt') { auditDesc = formatMessage(holders.attemptedWebhookDelete); } else if (webhookInfo === 'success') { auditDesc = formatMessage(holders.successfullWebhookDelete); } else if (webhookInfo === 'fail - inappropriate conditions') { auditDesc = formatMessage(holders.failedWebhookDelete); } break; default: break; } } else if (actionURL.indexOf('/license') === 0) { const licenseInfo = audit.extra_info; switch (actionURL) { case '/license/add': if (licenseInfo === 'attempt') { auditDesc = formatMessage(holders.attemptedLicenseAdd); } else if (licenseInfo === 'success') { auditDesc = formatMessage(holders.successfullLicenseAdd); } else if (licenseInfo === 'failed - expired or non-started license') { auditDesc = formatMessage(holders.failedExpiredLicenseAdd); } else if (licenseInfo === 'failed - invalid license') { auditDesc = formatMessage(holders.failedInvalidLicenseAdd); } break; case '/license/remove': auditDesc = formatMessage(holders.licenseRemoved); break; default: break; } } else if (actionURL.indexOf('/admin/download_compliance_report') === 0) { auditDesc = Utils.toTitleCase(audit.extra_info); } else { switch (actionURL) { case '/logout': auditDesc = formatMessage(holders.logout); break; case '/verify_email': auditDesc = formatMessage(holders.verified); break; default: break; } } /* If all else fails... */ if (!auditDesc) { /* Currently not called anywhere */ if (audit.extra_info.indexOf('revoked_all=') >= 0) { auditDesc = formatMessage(holders.revokedAll); } else { let actionDesc = ''; if (actionURL && actionURL.lastIndexOf('/') !== -1) { actionDesc = actionURL.substring(actionURL.lastIndexOf('/') + 1).replace('_', ' '); actionDesc = Utils.toTitleCase(actionDesc); } let extraInfoDesc = ''; if (audit.extra_info) { extraInfoDesc = audit.extra_info; if (extraInfoDesc.indexOf('=') !== -1) { extraInfoDesc = extraInfoDesc.substring(extraInfoDesc.indexOf('=') + 1); } } auditDesc = actionDesc + ' ' + extraInfoDesc; } } const date = new Date(audit.create_at); const auditInfo = {}; auditInfo.timestamp = (
); auditInfo.userId = audit.user_id; auditInfo.desc = auditDesc; auditInfo.ip = audit.ip_address; auditInfo.sessionId = audit.session_id; return auditInfo; }