From e3cee37527762f2e604e9d6b4234735fe0af8c5b Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Tue, 19 Nov 2019 09:53:00 +0100 Subject: [PATCH] Move autocreation logic into external ldap --- src/externalldap.js | 100 ++++++++++++++++++++++++++++++-------------- src/users.js | 39 +++-------------- 2 files changed, 74 insertions(+), 65 deletions(-) diff --git a/src/externalldap.js b/src/externalldap.js index a1f73e06d..b71e214ff 100644 --- a/src/externalldap.js +++ b/src/externalldap.js @@ -3,6 +3,7 @@ exports = module.exports = { search: search, verifyPassword: verifyPassword, + createAndVerifyUserIfNotExist: createAndVerifyUserIfNotExist, testConfig: testConfig, startSyncer: startSyncer, @@ -34,6 +35,25 @@ function removePrivateFields(ldapConfig) { return ldapConfig; } +function translateUser(ldapConfig, ldapUser) { + assert.strictEqual(typeof ldapConfig, 'object'); + + return { + username: ldapUser[ldapConfig.usernameField], + email: ldapUser.mail, + displayName: ldapUser.cn // user.giveName + ' ' + user.sn + }; +} + +function validUserRequirements(user) { + if (!user.username || !user.email || !user.displayName) { + debug(`[LDAP user empty username/email/displayName] username=${user.username} email=${user.email} displayName=${user.displayName}`); + return false; + } else { + return true; + } +} + // performs service bind if required function getClient(externalLdapConfig, callback) { assert.strictEqual(typeof externalLdapConfig, 'object'); @@ -151,19 +171,46 @@ function search(identifier, callback) { if (error) return callback(error); // translate ldap properties to ours - let users = ldapUsers.map(function (u) { - return { - username: u[externalLdapConfig.usernameField], - email: u.mail, - displayName: u.cn // user.giveName + ' ' + user.sn - }; - }); + let users = ldapUsers.map(function (u) { return translateUser(externalLdapConfig, u); }); callback(null, users); }); }); } +function createAndVerifyUserIfNotExist(identifier, password, callback) { + assert.strictEqual(typeof identifier, 'string'); + assert.strictEqual(typeof password, 'string'); + assert.strictEqual(typeof callback, 'function'); + + settings.getExternalLdapConfig(function (error, externalLdapConfig) { + if (error) return callback(error); + if (externalLdapConfig.provider === 'noop') return callback(new BoxError(BoxError.BAD_STATE, 'not enabled')); + if (!externalLdapConfig.autoCreate) return callback(new BoxError(BoxError.BAD_STATE, 'auto create not enabled')); + + ldapSearch(externalLdapConfig, { filter: `${externalLdapConfig.usernameField}=${identifier}` }, function (error, users) { + if (error) return callback(error); + if (users.length === 0) return callback(new BoxError(BoxError.NOT_FOUND)); + if (users.length > 1) return callback(new BoxError(BoxError.CONFLICT)); + + let user = translateUser(externalLdapConfig, users[0]); + if (!validUserRequirements(user)) return callback(new BoxError(BoxError.BAD_FIELD)); + + users.create(user.username, null /* password */, user.email, user.displayName, { source: 'ldap' }, auditSource.EXTERNAL_LDAP_AUTO_CREATE, function (error, user) { + if (error) { + console.error('Failed to auto create user', user.username, error); + return callback(new BoxError(BoxError.INTERNAL_ERROR)); + } + + verifyPassword(user, password, function (error) { + if (error) return callback(error); + callback(null, user); + }); + }); + }); + }); +} + function verifyPassword(user, password, callback) { assert.strictEqual(typeof user, 'object'); assert.strictEqual(typeof password, 'string'); @@ -178,18 +225,12 @@ function verifyPassword(user, password, callback) { if (ldapUsers.length === 0) return callback(new BoxError(BoxError.NOT_FOUND)); if (ldapUsers.length > 1) return callback(new BoxError(BoxError.CONFLICT)); - const userDn = ldapUsers[0].dn; let client = ldap.createClient({ url: externalLdapConfig.url }); - - client.bind(userDn, password, function (error) { + client.bind(ldapUsers[0].dn, password, function (error) { if (error instanceof ldap.InvalidCredentialsError) return callback(new BoxError(BoxError.INVALID_CREDENTIALS)); if (error) return callback(new BoxError(BoxError.EXTERNAL_ERROR, error)); - callback(null, { - username: ldapUsers[0][externalLdapConfig.usernameField], - email: ldapUsers[0].mail, - displayName: ldapUsers[0].cn // user.giveName + ' ' + user.sn - }); + callback(null, translateUser(externalLdapConfig, ldapUsers[0])); }); }); }); @@ -233,46 +274,41 @@ function sync(progressCallback, callback) { // we ignore all errors here and just log them for now async.eachSeries(ldapUsers, function (user, iteratorCallback) { - const username = user[externalLdapConfig.usernameField]; - const email = user.mail; - const displayName = user.cn; // user.giveName + ' ' + user.sn + user = translateUser(externalLdapConfig, user); - if (!username || !email || !displayName) { - debug(`[empty username/email/displayName] username=${username} email=${email} displayName=${displayName} usernameField=${externalLdapConfig.usernameField}`); - return iteratorCallback(); - } + if (!validUserRequirements(user)) return iteratorCallback(); percent += step; - progressCallback({ percent, message: `Syncing... ${username}` }); + progressCallback({ percent, message: `Syncing... ${user.username}` }); - users.getByUsername(username, function (error, result) { + users.getByUsername(user.username, function (error, result) { if (error && error.reason !== BoxError.NOT_FOUND) { - debug(`Could not find user with username ${username}: ${error.message}`); + debug(`Could not find user with username ${user.username}: ${error.message}`); return iteratorCallback(); } if (error) { - debug(`[adding user] username=${username} email=${email} displayName=${displayName}`); + debug(`[adding user] username=${user.username} email=${user.email} displayName=${user.displayName}`); - users.create(username, null /* password */, email, displayName, { source: 'ldap' }, auditSource.EXTERNAL_LDAP_TASK, function (error) { + users.create(user.username, null /* password */, user.email, user.displayName, { source: 'ldap' }, auditSource.EXTERNAL_LDAP_TASK, function (error) { if (error) console.error('Failed to create user', user, error); iteratorCallback(); }); } else if (result.source !== 'ldap') { - debug(`[conflicting user] username=${username} email=${email} displayName=${displayName}`); + debug(`[conflicting user] username=${user.username} email=${user.email} displayName=${user.displayName}`); iteratorCallback(); - } else if (result.email !== email || result.displayName !== displayName) { - debug(`[updating user] username=${username} email=${email} displayName=${displayName}`); + } else if (result.email !== user.email || result.displayName !== user.displayName) { + debug(`[updating user] username=${user.username} email=${user.email} displayName=${user.displayName}`); - users.update(result.id, { email: email, fallbackEmail: email, displayName: displayName }, auditSource.EXTERNAL_LDAP_TASK, function (error) { + users.update(result.id, { email: user.email, fallbackEmail: user.email, displayName: user.displayName }, auditSource.EXTERNAL_LDAP_TASK, function (error) { if (error) debug('Failed to update user', user, error); iteratorCallback(); }); } else { // user known and up-to-date - debug(`[up-to-date user] username=${username} email=${email} displayName=${displayName}`); + debug(`[up-to-date user] username=${user.username} email=${user.email} displayName=${user.displayName}`); iteratorCallback(); } diff --git a/src/users.js b/src/users.js index 53f7ba227..8cd135ed8 100644 --- a/src/users.js +++ b/src/users.js @@ -241,44 +241,17 @@ function verify(userId, password, callback) { }); } -// TODO should be a setting probably has to move to externalldap.js -let autocreate = true; - -function createAndVerifyIfNotExist(identifier, password, callback) { +function createAndVerifyUserIfNotExist(identifier, password, callback) { assert.strictEqual(typeof identifier, 'string'); assert.strictEqual(typeof password, 'string'); assert.strictEqual(typeof callback, 'function'); - if (!autocreate) return callback(new BoxError(BoxError.NOT_FOUND)); - - externalLdap.search(identifier, function (error, users) { + externalLdap.createAndVerifyUserIfNotExist(identifier, password, function (error, result) { if (error && error.reason === BoxError.BAD_STATE) return callback(new BoxError(BoxError.NOT_FOUND)); + if (error && error.reason === BoxError.BAD_FIELD) return callback(new BoxError(BoxError.NOT_FOUND)); if (error) return callback(error); - if (users.length === 0) return callback(new BoxError(BoxError.NOT_FOUND)); - if (users.length > 1) return callback(new BoxError(BoxError.CONFLICT)); - const username = users[0].username; - const email = users[0].email; - const displayName = users[0].displayName; - - if (!username || !email || !displayName) { - debug(`[LDAP user empty username/email/displayName] username=${username} email=${email} displayName=${displayName}`); - return callback(new BoxError(BoxError.BAD_FIELD)); - } - - // first validate to only create on first successful login - externalLdap.verifyPassword({ username: username }, password, function (error) { - if (error) return callback(error); - - create(username, null /* password */, email, displayName, { source: 'ldap' }, auditSource.EXTERNAL_LDAP_AUTO_CREATE, function (error, user) { - if (error) { - console.error('Failed to auto create user', user.username, error); - return callback(new BoxError(BoxError.INTERNAL_ERROR)); - } - - callback(null, user); - }); - }); + callback(null, result); }); } @@ -288,7 +261,7 @@ function verifyWithUsername(username, password, callback) { assert.strictEqual(typeof callback, 'function'); userdb.getByUsername(username.toLowerCase(), function (error, user) { - if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyIfNotExist(username.toLowerCase(), password, callback); + if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyUserIfNotExist(username.toLowerCase(), password, callback); if (error) return callback(error); verify(user.id, password, callback); @@ -301,7 +274,7 @@ function verifyWithEmail(email, password, callback) { assert.strictEqual(typeof callback, 'function'); userdb.getByEmail(email.toLowerCase(), function (error, user) { - if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyIfNotExist(email.toLowerCase(), password, callback); + if (error && error.reason === BoxError.NOT_FOUND) return createAndVerifyUserIfNotExist(email.toLowerCase(), password, callback); if (error) return callback(error); verify(user.id, password, callback);