diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index 4b8d0e5e7..1132292db 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -3636,6 +3636,8 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout var ACTION_USER_UPDATE = 'user.update'; var ACTION_USER_TRANSFER = 'user.transfer'; + var ACTION_USER_DIRECTORY_PROFILE_CONFIG_UPDATE = 'userdirectory.profileconfig.update'; + var ACTION_MAIL_LOCATION = 'mail.location'; var ACTION_MAIL_ENABLED = 'mail.enabled'; var ACTION_MAIL_DISABLED = 'mail.disabled'; @@ -3982,6 +3984,9 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout case ACTION_USER_LOGOUT: return 'User ' + (data.user ? data.user.username : data.userId) + ' logged out'; + case ACTION_USER_DIRECTORY_PROFILE_CONFIG_UPDATE: + return 'User directory profile config updated. Mandatory 2FA: ' + (data.config.mandatory2FA) + ' Lock profiles: ' + (data.config.lockUserProfiles); + case ACTION_DYNDNS_UPDATE: { details = data.errorMessage ? 'Error updating DNS. ' : 'Updated DNS. '; if (data.fromIpv4 !== data.toIpv4) details += 'From IPv4 ' + data.fromIpv4 + ' to ' + data.toIpv4 + '. '; diff --git a/dashboard/src/views/eventlog.js b/dashboard/src/views/eventlog.js index c669fbad3..4e0abc111 100644 --- a/dashboard/src/views/eventlog.js +++ b/dashboard/src/views/eventlog.js @@ -72,6 +72,7 @@ angular.module('Application').controller('EventLogController', ['$scope', '$loca { name: 'user.remove', value: 'user.remove' }, { name: 'user.transfer', value: 'user.transfer' }, { name: 'user.update', value: 'user.update' }, + { name: 'userdirectory.profileconfig.update', value: 'userdirectory.profileconfig.update '}, { name: 'volume.add', value: 'volume.add' }, { name: 'volume.update', value: 'volume.update' }, { name: 'volume.remove', value: 'volume.update' }, diff --git a/src/eventlog.js b/src/eventlog.js index 9585b8b29..44575aebe 100644 --- a/src/eventlog.js +++ b/src/eventlog.js @@ -80,6 +80,8 @@ exports = module.exports = { ACTION_USER_UPDATE: 'user.update', ACTION_USER_TRANSFER: 'user.transfer', + ACTION_USER_DIRECTORY_PROFILE_CONFIG_UPDATE: 'userdirectory.profileconfig.update', + ACTION_VOLUME_ADD: 'volume.add', ACTION_VOLUME_UPDATE: 'volume.update', ACTION_VOLUME_REMOUNT: 'volume.remount', @@ -169,7 +171,7 @@ async function listPaged(actions, search, page, perPage) { assert.strictEqual(typeof page, 'number'); assert.strictEqual(typeof perPage, 'number'); - let data = []; + const data = []; let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog`; if (actions.length || search) query += ' WHERE'; @@ -206,7 +208,7 @@ async function cleanup(options) { ]; let query = `SELECT ${EVENTLOG_FIELDS} FROM eventlog WHERE creationTime <= ? AND (`; - let data = [ creationTime ]; + const data = [ creationTime ]; actions.forEach(function (action, i) { query += ' action = ? '; data.push(action); diff --git a/src/routes/user-directory.js b/src/routes/user-directory.js index c167e1e05..861d1611d 100644 --- a/src/routes/user-directory.js +++ b/src/routes/user-directory.js @@ -6,6 +6,7 @@ exports = module.exports = { }; const assert = require('assert'), + AuditSource = require('../auditsource.js'), BoxError = require('../boxerror.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, @@ -25,7 +26,7 @@ async function setProfileConfig(req, res, next) { if (typeof req.body.lockUserProfiles !== 'boolean') return next(new HttpError(400, 'lockUserProfiles is required')); if (typeof req.body.mandatory2FA !== 'boolean') return next(new HttpError(400, 'mandatory2FA is required')); - const [error] = await safe(userDirectory.setProfileConfig(req.body)); + const [error] = await safe(userDirectory.setProfileConfig(req.body, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(200, {})); diff --git a/src/test/user-directory-test.js b/src/test/user-directory-test.js index 62e511da4..5f9a83e5d 100644 --- a/src/test/user-directory-test.js +++ b/src/test/user-directory-test.js @@ -11,7 +11,7 @@ const common = require('./common.js'), userDirectory = require('../user-directory.js'); describe('User Directory', function () { - const { setup, cleanup, admin } = common; + const { setup, cleanup, admin, auditSource } = common; before(setup); after(cleanup); @@ -28,7 +28,7 @@ describe('User Directory', function () { let result = await tokens.listByUserId(admin.id); expect(result.length).to.be(1); // just confirm the token was really added! - await userDirectory.setProfileConfig({ mandatory2FA: true, lockUserProfiles: true }); + await userDirectory.setProfileConfig({ mandatory2FA: true, lockUserProfiles: true }, auditSource); result = await tokens.listByUserId(admin.id); expect(result.length).to.be(0); // should have been removed by mandatory 2fa setting change }); diff --git a/src/user-directory.js b/src/user-directory.js index c46451278..83e6e2d2e 100644 --- a/src/user-directory.js +++ b/src/user-directory.js @@ -9,6 +9,7 @@ const assert = require('assert'), BoxError = require('./boxerror.js'), constants = require('./constants.js'), debug = require('debug')('box:user-directory'), + eventlog = require('./eventlog.js'), oidc = require('./oidc.js'), settings = require('./settings.js'), tokens = require('./tokens.js'), @@ -19,14 +20,17 @@ async function getProfileConfig() { return value || { lockUserProfiles: false, mandatory2FA: false }; } -async function setProfileConfig(profileConfig) { +async function setProfileConfig(profileConfig, auditSource) { assert.strictEqual(typeof profileConfig, 'object'); + assert(auditSource && typeof auditSource === 'object'); if (constants.DEMO) throw new BoxError(BoxError.BAD_STATE, 'Not allowed in demo mode'); const oldConfig = await getProfileConfig(); await settings.setJson(settings.PROFILE_CONFIG_KEY, profileConfig); + await eventlog.add(eventlog.ACTION_USER_DIRECTORY_PROFILE_CONFIG_UPDATE, auditSource, { oldConfig, config: profileConfig }); + if (profileConfig.mandatory2FA && !oldConfig.mandatory2FA) { debug('setProfileConfig: logging out non-2FA users to enforce 2FA');