UNPKG

linagora-rse

Version:
305 lines (261 loc) 7.9 kB
'use strict'; const q = require('q'); const util = require('util'); const _ = require('lodash'); const esnConfig = require('../../core')['esn-config']; const pubsub = require('../../core/pubsub').local; const logger = require('../logger'); const authToken = require('../auth/token'); const extend = require('extend'); const mongoose = require('mongoose'); const trim = require('trim'); const User = mongoose.model('User'); const emailAddresses = require('email-addresses'); const CONSTANTS = require('./constants'); const moderation = require('./moderation'); const coreAvailability = require('../availability'); const utils = require('./utils'); const TYPE = CONSTANTS.TYPE; function getUserTemplate(callback) { esnConfig('user').get(callback); } function extendUserTemplate(template, data) { extend(template, data); } function recordUser(userData, callback) { const userAsModel = userData instanceof User ? userData : new User(userData); checkEmailsAvailability(userAsModel.emails).then(unavailableEmails => { if (unavailableEmails.length > 0) { return callback(new Error(`Emails already in use: ${unavailableEmails.join(', ')}`)); } userAsModel.save(function(err, resp) { if (!err) { pubsub.topic(CONSTANTS.EVENTS.userCreated).publish(resp); logger.info('User provisioned in datastore:', userAsModel.emails.join(',')); } else { logger.warn('Error while trying to provision user in database:', err.message); } callback(err, resp); }); }, callback); } function provisionUser(data, callback) { getUserTemplate(function(err, user) { if (err) { return callback(err); } extendUserTemplate(user, data); recordUser(user, callback); }); } function findByEmail(email, callback) { User.findOne(buildFindByEmailQuery(email), callback); } function findUsersByEmail(email, callback) { User.find(buildFindByEmailQuery(email), callback); } function buildFindByEmailQuery(email) { if (util.isArray(email)) { return { $or: email.map(function(e) { return { accounts: { $elemMatch: { emails: trim(e).toLowerCase() } } }; }) }; } return { accounts: { $elemMatch: { emails: trim(email).toLowerCase() } } }; } function get(uuid, callback) { User.findOne({_id: uuid}, callback); } function list(callback) { User.find(callback); } function listByCursor() { return User.find().cursor(); } function update(user, callback) { user.save((err, savedUser, rowAffected) => { if (!err && rowAffected > 0) { pubsub.topic(CONSTANTS.EVENTS.userUpdated).publish(savedUser); } callback(err, savedUser); }); } function updateProfile(user, profile, callback) { if (!user || !profile) { return callback(new Error('User and profile are required')); } var id = user._id || user; User.findOneAndUpdate({ _id: id }, { $set: profile || {} }, { new: true }, function(err, user) { if (!err) { pubsub.topic(CONSTANTS.EVENTS.userUpdated).publish(user); } callback(err, user); }); } function removeAccountById(user, accountId, callback) { var accountIndex = -1; user.accounts.forEach(function(account, index) { if (account.data && account.data.id === accountId) { accountIndex = index; } if (index === user.accounts.length - 1) { if (accountIndex === -1) { return callback(new Error('Invalid account id: ' + accountId)); } else { user.accounts.splice(accountIndex, 1); user.markModified('accounts'); return user.save(callback); } } }); } function belongsToCompany(user, company, callback) { if (!user || !company) { return callback(new Error('User and company are required.')); } var hasCompany = user.emails.some(function(email) { var domain = emailAddresses.parseOneAddress(email).domain.toLowerCase(); var domainWithoutSuffix = domain.split('.')[0].toLowerCase(); return domain === company.toLowerCase() || domainWithoutSuffix === company.toLowerCase(); }); return callback(null, hasCompany); } function getCompanies(user, callback) { if (!user) { return callback(new Error('User is required.')); } var companies = user.emails.map(function(email) { var parsedEmail = emailAddresses.parseOneAddress(email); return parsedEmail.domain.split('.')[0]; }); return callback(null, companies); } function getNewToken(user, ttl, callback) { authToken.getNewToken({ttl: ttl, user: user._id, user_type: TYPE}, callback); } function find(query, callback) { User.findOne(query, callback); } function init() { moderation.init(); coreAvailability.email.addChecker({ name: 'user', check(email) { return q.denodeify(findByEmail)(email).then(user => !user); } }); } /** * Translate external payload to OpenPaaS user. This is used by provision modules, * such as converting LDAP user to OpenPaaS user * * @param {Object} baseUser The base user object to be extended * @param {Object} payload The payload used to convert to OP user * @return {Object} The OpenPaaS user object */ function translate(baseUser, payload) { const userEmail = payload.username; // we use email as username to authenticate const domainId = payload.domainId; const payloadUser = payload.user; const mapping = payload.mapping; const outputUser = baseUser || {}; // provision domain if (!outputUser.domains) { outputUser.domains = []; } if (domainId) { const domain = _.find(outputUser.domains, domain => String(domain.domain_id) === String(domainId)); if (!domain) { outputUser.domains.push({ domain_id: domainId }); } } // provision email account if (!outputUser.accounts) { outputUser.accounts = []; } let emailAccount = _.find(outputUser.accounts, { type: 'email' }); if (!emailAccount) { emailAccount = { type: 'email', hosted: true, emails: [] }; outputUser.accounts.push(emailAccount); } if (emailAccount.emails.indexOf(userEmail) === -1) { emailAccount.emails.push(userEmail); } // provision other fields basing on mapping _.forEach(mapping, (value, key) => { if (key === 'email') { const email = payloadUser[value]; if (emailAccount.emails.indexOf(email) === -1) { emailAccount.emails.push(email); } } else { outputUser[key] = payloadUser[value]; } }); return outputUser; } function checkEmailsAvailability(emails) { return q.all( emails.map(email => coreAvailability.email.isAvailable(email) .then(result => ({ email, available: result.available })) )) .then(results => results.filter(result => !result.available).map(result => result.email) ); } function updateStates(userId, states, callback) { if (!userId || !states) { return callback(new Error('User id and states are required')); } User.findOneAndUpdate({ _id: userId }, { $set: { states } }, { new: true }, (err, user) => { if (!err) { pubsub.topic(CONSTANTS.EVENTS.userUpdated).publish(user); } callback(err); }); } module.exports = { getDisplayName: utils.getDisplayName, TYPE: TYPE, recordUser: recordUser, provisionUser: provisionUser, translate, findByEmail: findByEmail, findUsersByEmail: findUsersByEmail, get: get, list: list, listByCursor, update: update, updateProfile: updateProfile, updateStates, removeAccountById: removeAccountById, belongsToCompany: belongsToCompany, getCompanies: getCompanies, getNewToken: getNewToken, find: find, init: init, moderation: moderation, domain: require('./domain'), follow: require('./follow'), login: require('./login'), denormalize: require('./denormalize'), states: require('./states') };