summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--web/react/components/access_history_modal.jsx299
1 files changed, 279 insertions, 20 deletions
diff --git a/web/react/components/access_history_modal.jsx b/web/react/components/access_history_modal.jsx
index 2ad4d5b00..909639859 100644
--- a/web/react/components/access_history_modal.jsx
+++ b/web/react/components/access_history_modal.jsx
@@ -2,6 +2,7 @@
// See License.txt for license information.
var UserStore = require('../stores/user_store.jsx');
+var ChannelStore = require('../stores/channel_store.jsx');
var AsyncClient = require('../utils/async_client.jsx');
var LoadingScreen = require('./loading_screen.jsx');
var Utils = require('../utils/utils.jsx');
@@ -14,8 +15,10 @@ export default class AccessHistoryModal extends React.Component {
this.handleMoreInfo = this.handleMoreInfo.bind(this);
this.onHide = this.onHide.bind(this);
this.onShow = this.onShow.bind(this);
+ this.formatAuditInfo = this.formatAuditInfo.bind(this);
+ this.handleRevokedSession = this.handleRevokedSession.bind(this);
- let state = this.getStateFromStoresForAudits();
+ const state = this.getStateFromStoresForAudits();
state.moreInfo = [];
this.state = state;
@@ -52,23 +55,269 @@ export default class AccessHistoryModal extends React.Component {
newMoreInfo[index] = true;
this.setState({moreInfo: newMoreInfo});
}
- render() {
- var accessList = [];
- var currentHistoryDate = null;
+ handleRevokedSession(sessionId) {
+ return 'The session with id ' + sessionId + ' was revoked';
+ }
+ formatAuditInfo(currentAudit) {
+ const currentActionURL = currentAudit.action.replace(/\/api\/v[1-9]/, '');
- for (var i = 0; i < this.state.audits.length; i++) {
- var currentAudit = this.state.audits[i];
- var newHistoryDate = new Date(currentAudit.create_at);
- var newDate = null;
+ let currentAuditDesc = '';
+
+ if (currentActionURL.indexOf('/channels') === 0) {
+ const channelInfo = currentAudit.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 (currentActionURL) {
+ case '/channels/create':
+ currentAuditDesc = 'Created the ' + channelName + ' channel/group';
+ break;
+ case '/channels/create_direct':
+ currentAuditDesc = 'Established a direct message channel with ' + Utils.getDirectTeammate(channelObj.id).username;
+ break;
+ case '/channels/update':
+ currentAuditDesc = 'Updated the ' + channelName + ' channel/group name';
+ break;
+ case '/channels/update_desc':
+ currentAuditDesc = 'Updated the ' + channelName + ' channel/group description';
+ 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(currentActionURL)) {
+ currentAuditDesc = 'Deleted the channel/group with the URL ' + channelURL;
+ } else if (/\/channels\/[A-Za-z0-9]+\/add/.test(currentActionURL)) {
+ currentAuditDesc = 'Added ' + username + ' to the ' + channelName + ' channel/group';
+ } else if (/\/channels\/[A-Za-z0-9]+\/remove/.test(currentActionURL)) {
+ currentAuditDesc = 'Removed ' + username + ' from the ' + channelName + ' channel/group';
+ }
+
+ break;
+ }
+ } else if (currentActionURL.indexOf('/oauth') === 0) {
+ const oauthInfo = currentAudit.extra_info.split(' ');
+
+ switch (currentActionURL) {
+ case '/oauth/register':
+ const clientIdField = oauthInfo[0].split('=');
+
+ if (clientIdField[0] === 'client_id') {
+ currentAuditDesc = 'Attempted to register a new OAuth Application with ID ' + clientIdField[1];
+ }
+
+ break;
+ case '/oauth/allow':
+ if (oauthInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to allow a new OAuth service access';
+ } else if (oauthInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully gave a new OAuth service access';
+ } else if (oauthInfo[0] === 'fail - redirect_uri did not match registered callback') {
+ currentAuditDesc = 'Failed to allow a new OAuth service access - the redirect URI did not match the previously registered callback';
+ }
+
+ break;
+ case '/oauth/access_token':
+ if (oauthInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to get an OAuth access token';
+ } else if (oauthInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully added a new OAuth service';
+ } else {
+ const oauthTokenFailure = oauthInfo[0].split('-');
+
+ if (oauthTokenFailure[0].trim() === 'fail' && oauthTokenFailure[1]) {
+ currentAuditDesc = 'Failed to get an OAuth access token - ' + oauthTokenFailure[1].trim();
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ } else if (currentActionURL.indexOf('/users') === 0) {
+ const userInfo = currentAudit.extra_info.split(' ');
+
+ switch (currentActionURL) {
+ case '/users/login':
+ if (userInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to login';
+ } else if (userInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully logged in';
+ } else if (userInfo[0]) {
+ currentAuditDesc = 'FAILED login attempt';
+ }
+
+ break;
+ case '/users/revoke_session':
+ currentAuditDesc = this.handleRevokedSession(userInfo[0].split('=')[1]);
+ break;
+ case '/users/newimage':
+ currentAuditDesc = 'Updated your profile picture';
+ break;
+ case '/users/update':
+ currentAuditDesc = 'Updated the general settings of your account';
+ break;
+ case '/users/newpassword':
+ if (userInfo[0] === 'attempted') {
+ currentAuditDesc = 'Attempted to change password';
+ } else if (userInfo[0] === 'completed') {
+ currentAuditDesc = 'Successfully changed password';
+ } else if (userInfo[0] === 'failed - tried to update user password who was logged in through oauth') {
+ currentAuditDesc = 'Failed to change password - tried to update user password who was logged in through oauth';
+ }
+
+ break;
+ case '/users/update_roles':
+ const userRoles = userInfo[0].split('=')[1];
- if (!currentHistoryDate || currentHistoryDate.toLocaleDateString() !== newHistoryDate.toLocaleDateString()) {
- currentHistoryDate = newHistoryDate;
- newDate = (<div> {currentHistoryDate.toDateString()} </div>);
+ currentAuditDesc = 'Updated user role(s) to ';
+ if (userRoles.trim()) {
+ currentAuditDesc += userRoles;
+ } else {
+ currentAuditDesc += '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') {
+ currentAuditDesc = 'Account made active';
+ } else if (updateField === 'false') {
+ currentAuditDesc = 'Account made inactive';
+ }
+
+ const actingUserInfo = userInfo[1].split('=');
+ if (actingUserInfo[0] === 'session_user') {
+ const actingUser = UserStore.getProfile(actingUserInfo[1]);
+ const currentUser = UserStore.getCurrentUser();
+ if (currentUser && actingUser && (Utils.isAdmin(currentUser.roles) || Utils.isSystemAdmin(currentUser.roles))) {
+ currentAuditDesc += ' by ' + actingUser.username;
+ } else if (currentUser && actingUser) {
+ currentAuditDesc += ' by an admin';
+ }
+ }
+ } else if (updateType === 'session_id') {
+ currentAuditDesc = this.handleRevokedSession(updateField);
+ }
+
+ break;
+ case '/users/send_password_reset':
+ currentAuditDesc = 'Sent an email to ' + userInfo[0].split('=')[1] + ' to reset your password';
+ break;
+ case '/users/reset_password':
+ if (userInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to reset password';
+ } else if (userInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully reset password';
+ }
+
+ break;
+ case '/users/update_notify':
+ currentAuditDesc = 'Updated your global notification settings';
+ break;
+ default:
+ break;
}
+ } else if (currentActionURL.indexOf('/hooks') === 0) {
+ const webhookInfo = currentAudit.extra_info.split(' ');
+
+ switch (currentActionURL) {
+ case '/hooks/incoming/create':
+ if (webhookInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to create a webhook';
+ } else if (webhookInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully created a webhook';
+ } else if (webhookInfo[0] === 'fail - bad channel permissions') {
+ currentAuditDesc = 'Failed to create a webhook - bad channel permissions';
+ }
- if (!currentAudit.session_id && currentAudit.action.search('/users/login') !== -1) {
- currentAudit.session_id = 'N/A (Login attempt)';
+ break;
+ case '/hooks/incoming/delete':
+ if (webhookInfo[0] === 'attempt') {
+ currentAuditDesc = 'Attempted to delete a webhook';
+ } else if (webhookInfo[0] === 'success') {
+ currentAuditDesc = 'Successfully deleted a webhook';
+ } else if (webhookInfo[0] === 'fail - inappropriate conditions') {
+ currentAuditDesc = 'Failed to delete a webhook - inappropriate conditions';
+ }
+
+ break;
+ default:
+ break;
+ }
+ } else {
+ switch (currentActionURL) {
+ case '/logout':
+ currentAuditDesc = 'Logged out of your account';
+ break;
+ case '/verify_email':
+ currentAuditDesc = 'Sucessfully verified your email address';
+ break;
+ default:
+ break;
}
+ }
+
+ /* If all else fails... */
+ if (!currentAuditDesc) {
+ /* Currently not called anywhere */
+ if (currentAudit.extra_info.indexOf('revoked_all=') >= 0) {
+ currentAuditDesc = 'Revoked all current sessions for the team';
+ } else {
+ let currentActionDesc = '';
+ if (currentActionURL && currentActionURL.lastIndexOf('/') !== -1) {
+ currentActionDesc = currentActionURL.substring(currentActionURL.lastIndexOf('/') + 1).replace('_', ' ');
+ currentActionDesc = Utils.toTitleCase(currentActionDesc);
+ }
+
+ let currentExtraInfoDesc = '';
+ if (currentAudit.extra_info) {
+ currentExtraInfoDesc = currentAudit.extra_info;
+
+ if (currentExtraInfoDesc.indexOf('=') !== -1) {
+ currentExtraInfoDesc = currentExtraInfoDesc.substring(currentExtraInfoDesc.indexOf('=') + 1);
+ }
+ }
+ currentAuditDesc = currentActionDesc + ' ' + currentExtraInfoDesc;
+ }
+ }
+
+ const currentDate = new Date(currentAudit.create_at);
+ const currentAuditInfo = currentDate.toDateString() + ' - ' + currentDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'}) + ' | ' + currentAuditDesc;
+ return currentAuditInfo;
+ }
+ render() {
+ var accessList = [];
+
+ for (var i = 0; i < this.state.audits.length; i++) {
+ const currentAudit = this.state.audits[i];
+ const currentAuditInfo = this.formatAuditInfo(currentAudit);
var moreInfo = (
<a
@@ -76,15 +325,27 @@ export default class AccessHistoryModal extends React.Component {
className='theme'
onClick={this.handleMoreInfo.bind(this, i)}
>
- More info
+ {'More info'}
</a>
);
if (this.state.moreInfo[i]) {
+ if (!currentAudit.session_id) {
+ currentAudit.session_id = 'N/A';
+
+ if (currentAudit.action.search('/users/login') >= 0) {
+ if (currentAudit.extra_info === 'attempt') {
+ currentAudit.session_id += ' (Login attempt)';
+ } else {
+ currentAudit.session_id += ' (Login failure)';
+ }
+ }
+ }
+
moreInfo = (
<div>
+ <div>{'IP: ' + currentAudit.ip_address}</div>
<div>{'Session ID: ' + currentAudit.session_id}</div>
- <div>{'URL: ' + currentAudit.action.replace(/\/api\/v[1-9]/, '')}</div>
</div>
);
}
@@ -99,11 +360,9 @@ export default class AccessHistoryModal extends React.Component {
key={'accessHistoryEntryKey' + i}
className='access-history__table'
>
- <div className='access__date'>{newDate}</div>
<div className='access__report'>
- <div className='report__time'>{newHistoryDate.toLocaleTimeString(navigator.language, {hour: '2-digit', minute: '2-digit'})}</div>
+ <div className='report__time'>{currentAuditInfo}</div>
<div className='report__info'>
- <div>{'IP: ' + currentAudit.ip_address}</div>
{moreInfo}
</div>
{divider}
@@ -138,13 +397,13 @@ export default class AccessHistoryModal extends React.Component {
data-dismiss='modal'
aria-label='Close'
>
- <span aria-hidden='true'>&times;</span>
+ <span aria-hidden='true'>{'×'}</span>
</button>
<h4
className='modal-title'
id='myModalLabel'
>
- Access History
+ {'Access History'}
</h4>
</div>
<div