split routes and model code into user-directory.js

This commit is contained in:
Girish Ramakrishnan
2024-06-12 10:27:59 +02:00
parent e1f87161a8
commit b4e7e394c3
10 changed files with 121 additions and 80 deletions

View File

@@ -32,7 +32,7 @@ const apps = require('./apps.js'),
settings = require('./settings.js'),
system = require('./system.js'),
tasks = require('./tasks.js'),
users = require('./users.js');
userDirectory = require('./user-directory.js');
async function getLocation() {
const domain = await settings.get(settings.DASHBOARD_DOMAIN_KEY);
@@ -56,7 +56,7 @@ async function clearLocation() {
async function getConfig() {
const ubuntuVersion = await system.getUbuntuVersion();
const profileConfig = await users.getProfileConfig();
const profileConfig = await userDirectory.getProfileConfig();
const externalLdapConfig = await externalLdap.getConfig();
const { fqdn:adminFqdn, domain:adminDomain } = await getLocation();

View File

@@ -32,6 +32,7 @@ exports = module.exports = {
tasks: require('./tasks.js'),
tokens: require('./tokens.js'),
updater: require('./updater.js'),
userDirectory: require('./user-directory.js'),
users: require('./users.js'),
volumes: require('./volumes.js'),
wellknown: require('./wellknown.js')

View File

@@ -27,12 +27,13 @@ const assert = require('assert'),
path = require('path'),
paths = require('../paths.js'),
safe = require('safetydance'),
userDirectory = require('../user-directory.js'),
users = require('../users.js');
async function canEditProfile(req, res, next) {
assert.strictEqual(typeof req.user, 'object');
const [error, profileConfig] = await safe(users.getProfileConfig());
const [error, profileConfig] = await safe(userDirectory.getProfileConfig());
if (error) return next(BoxError.toHttpError(error));
if (profileConfig.lockUserProfiles) return next(new HttpError(403, 'admin has disallowed users from editing profiles'));

View File

@@ -0,0 +1,32 @@
'use strict';
exports = module.exports = {
getProfileConfig,
setProfileConfig
};
const assert = require('assert'),
BoxError = require('../boxerror.js'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
safe = require('safetydance'),
userDirectory = require('../user-directory.js');
async function getProfileConfig(req, res, next) {
const [error, directoryConfig] = await safe(userDirectory.getProfileConfig());
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(200, directoryConfig));
}
async function setProfileConfig(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
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));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(200, {}));
}

View File

@@ -18,9 +18,6 @@ exports = module.exports = {
getPasswordResetLink,
sendPasswordResetEmail,
setProfileConfig,
getProfileConfig,
getInviteLink,
sendInviteEmail,
@@ -275,22 +272,3 @@ async function sendInviteEmail(req, res, next) {
next(new HttpSuccess(202, {}));
}
async function getProfileConfig(req, res, next) {
const [error, directoryConfig] = await safe(users.getProfileConfig());
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(200, directoryConfig));
}
async function setProfileConfig(req, res, next) {
assert.strictEqual(typeof req.body, 'object');
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(users.setProfileConfig(req.body));
if (error) return next(BoxError.toHttpError(error));
next(new HttpSuccess(200, {}));
}

View File

@@ -215,8 +215,8 @@ async function initializeExpressSync() {
router.del ('/api/v1/groups/:groupId', token, authorizeUserManager, routes.groups.load, routes.groups.remove);
// User directory
router.get ('/api/v1/user_directory/profile_config', token, authorizeAdmin, routes.users.getProfileConfig);
router.post('/api/v1/user_directory/profile_config', json, token, authorizeAdmin, routes.users.setProfileConfig);
router.get ('/api/v1/user_directory/profile_config', token, authorizeAdmin, routes.userDirectory.getProfileConfig);
router.post('/api/v1/user_directory/profile_config', json, token, authorizeAdmin, routes.userDirectory.setProfileConfig);
// External LDAP
router.get ('/api/v1/external_ldap/config', token, authorizeAdmin, routes.externalLdap.getConfig);

View File

@@ -0,0 +1,36 @@
/* global it:false */
/* global describe:false */
/* global before:false */
/* global after:false */
'use strict';
const common = require('./common.js'),
expect = require('expect.js'),
tokens = require('../tokens.js'),
userDirectory = require('../user-directory.js');
describe('User Directory', function () {
const { setup, cleanup, admin } = common;
before(setup);
after(cleanup);
describe('profile config', function () {
it('can get default profile config', async function () {
const profileConfig = await userDirectory.getProfileConfig();
expect(profileConfig.lockUserProfiles).to.be(false);
expect(profileConfig.mandatory2FA).to.be(false);
});
it('can set default profile config', async function () {
await tokens.add({ name: 'token1', identifier: admin.id, clientId: tokens.ID_WEBADMIN, expires: Number.MAX_SAFE_INTEGER, lastUsedTime: null });
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 });
result = await tokens.listByUserId(admin.id);
expect(result.length).to.be(0); // should have been removed by mandatory 2fa setting change
});
});
});

View File

@@ -10,7 +10,6 @@ const BoxError = require('../boxerror.js'),
expect = require('expect.js'),
safe = require('safetydance'),
speakeasy = require('speakeasy'),
tokens = require('../tokens.js'),
users = require('../users.js'),
_ = require('underscore');
@@ -199,7 +198,7 @@ describe('User', function () {
});
it('can listPaged (search)', async function () {
let results = await users.listPaged(admin.email.slice(0, 8), null, 1, 1);
const results = await users.listPaged(admin.email.slice(0, 8), null, 1, 1);
expect(results.length).to.be(1);
checkUser(results[0], admin);
});
@@ -632,24 +631,6 @@ describe('User', function () {
it('can re-create user after user was removed', createOwner);
});
describe('profile config', function () {
it('can get default profile config', async function () {
const profileConfig = await users.getProfileConfig();
expect(profileConfig.lockUserProfiles).to.be(false);
expect(profileConfig.mandatory2FA).to.be(false);
});
it('can set default profile config', async function () {
await tokens.add({ name: 'token1', identifier: admin.id, clientId: tokens.ID_WEBADMIN, expires: Number.MAX_SAFE_INTEGER, lastUsedTime: null });
let result = await tokens.listByUserId(admin.id);
expect(result.length).to.be(1); // just confirm the token was really added!
await users.setProfileConfig({ mandatory2FA: true, lockUserProfiles: true });
result = await tokens.listByUserId(admin.id);
expect(result.length).to.be(0); // should have been removed by mandatory 2fa setting change
});
});
describe('parseDisplayName', function () {
it('parses names', function () {
const names = [

41
src/user-directory.js Normal file
View File

@@ -0,0 +1,41 @@
'use strict';
exports = module.exports = {
getProfileConfig,
setProfileConfig
};
const assert = require('assert'),
BoxError = require('./boxerror.js'),
constants = require('./constants.js'),
debug = require('debug')('box:user-directory'),
oidc = require('./oidc.js'),
settings = require('./settings.js'),
tokens = require('./tokens.js'),
users = require('./users.js');
async function getProfileConfig() {
const value = await settings.getJson(settings.PROFILE_CONFIG_KEY);
return value || { lockUserProfiles: false, mandatory2FA: false };
}
async function setProfileConfig(profileConfig) {
assert.strictEqual(typeof profileConfig, '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);
if (profileConfig.mandatory2FA && !oldConfig.mandatory2FA) {
debug('setProfileConfig: logging out non-2FA users to enforce 2FA');
const allUsers = await users.list();
for (const user of allUsers) {
if (user.twoFactorAuthenticationEnabled) continue;
await tokens.delByUserIdAndType(user.id, tokens.ID_WEBADMIN);
await oidc.revokeByUserId(user.id);
}
}
}

View File

@@ -51,9 +51,6 @@ exports = module.exports = {
getBackgroundImage,
setBackgroundImage,
getProfileConfig,
setProfileConfig,
resetSource,
parseDisplayName,
@@ -91,15 +88,15 @@ const appPasswords = require('./apppasswords.js'),
mail = require('./mail.js'),
mailer = require('./mailer.js'),
mysql = require('mysql'),
oidc = require('../oidc.js'),
qrcode = require('qrcode'),
safe = require('safetydance'),
settings = require('./settings.js'),
speakeasy = require('speakeasy'),
tokens = require('./tokens.js'),
translation = require('./translation.js'),
uuid = require('uuid'),
uaParser = require('ua-parser-js'),
userDirectory = require('./user-directory.js'),
uuid = require('uuid'),
superagent = require('superagent'),
util = require('util'),
validator = require('validator'),
@@ -826,7 +823,7 @@ async function getInviteLink(user, auditSource) {
if (user.source) throw new BoxError(BoxError.CONFLICT, 'User is from an external directory');
if (!user.inviteToken) throw new BoxError(BoxError.BAD_STATE, 'User already used invite link');
const directoryConfig = await getProfileConfig();
const directoryConfig = await userDirectory.getProfileConfig();
const { fqdn:dashboardFqdn } = await dashboard.getLocation();
let inviteLink = `https://${dashboardFqdn}/setupaccount.html?inviteToken=${user.inviteToken}&email=${encodeURIComponent(user.email)}`;
@@ -854,7 +851,7 @@ async function setupAccount(user, data, auditSource) {
assert.strictEqual(typeof data, 'object');
assert(auditSource && typeof auditSource === 'object');
const profileConfig = await getProfileConfig();
const profileConfig = await userDirectory.getProfileConfig();
const tmp = { inviteToken: '' };
@@ -972,32 +969,6 @@ async function setBackgroundImage(id, backgroundImage) {
if (result.length === 0) throw new BoxError(BoxError.NOT_FOUND, 'User not found');
}
async function getProfileConfig() {
const value = await settings.getJson(settings.PROFILE_CONFIG_KEY);
return value || { lockUserProfiles: false, mandatory2FA: false };
}
async function setProfileConfig(profileConfig) {
assert.strictEqual(typeof profileConfig, '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);
if (profileConfig.mandatory2FA && !oldConfig.mandatory2FA) {
debug('setProfileConfig: logging out non-2FA users to enforce 2FA');
const allUsers = await list();
for (const user of allUsers) {
if (!user.twoFactorAuthenticationEnabled) {
await tokens.delByUserIdAndType(user.id, tokens.ID_WEBADMIN);
await oidc.revokeByUserId(user.id);
}
}
}
}
async function resetSource() {
await database.query('UPDATE users SET source = ?', [ '' ]);
}