const ldap = require('../../util/ldapjs-extra'); module.exports = Self => { Self.remoteMethod('sync', { description: 'Synchronizes the user with the other user databases', http: { path: `/sync`, verb: 'PATCH' } }); Self.sync = async function() { let $ = Self.app.models; let ldapConfig = await $.LdapConfig.findOne({ fields: ['host', 'rdn', 'password', 'groupDn'] }); let accountConfig = await $.AccountConfig.findOne({ fields: ['idBase'] }); if (!ldapConfig) return; // Connect let client = ldap.createClient({ url: `ldap://${ldapConfig.host}:389` }); let ldapPassword = Buffer .from(ldapConfig.password, 'base64') .toString('ascii'); await client.bind(ldapConfig.rdn, ldapPassword); let err; try { // Delete roles let opts = { scope: 'sub', attributes: ['dn'], filter: 'objectClass=posixGroup' }; res = await client.search(ldapConfig.groupDn, opts); let reqs = []; await new Promise((resolve, reject) => { res.on('error', err => { if (err.name === 'NoSuchObjectError') err = new Error(`Object '${ldapConfig.groupDn}' does not exist`); reject(err); }); res.on('searchEntry', e => { reqs.push(client.del(e.object.dn)); }); res.on('end', resolve); }); await Promise.all(reqs); // Recreate roles let roles = await $.Role.find({ fields: ['id', 'name'] }); let roleRoles = await $.RoleRole.find({ fields: ['role', 'inheritsFrom'] }); let roleMap = toMap(roleRoles, e => { return {key: e.inheritsFrom, val: e.role}; }); let accounts = await $.UserAccount.find({ fields: ['id'], include: { relation: 'user', scope: { fields: ['name', 'roleFk'], where: {active: true} } } }); let accountMap = toMap(accounts, e => { let user = e.user(); return {key: user.roleFk, val: user.name}; }); reqs = []; for (let role of roles) { let newEntry = { objectClass: ['top', 'posixGroup'], cn: role.name, gidNumber: accountConfig.idBase + role.id }; let memberUid = []; for (subrole of roleMap.get(role.id) || []) memberUid = memberUid.concat(accountMap.get(subrole) || []); if (memberUid.length) { memberUid.sort((a, b) => a.localeCompare(b)); newEntry.memberUid = memberUid; } let dn = `cn=${role.name},${ldapConfig.groupDn}`; reqs.push(client.add(dn, newEntry)); } await Promise.all(reqs); } catch (e) { err = e; } // FIXME: Cannot disconnect, hangs on undind() call // await client.unbind(); if (err) throw err; }; }; function toMap(array, fn) { let map = new Map(); for (let item of array) { let keyVal = fn(item); let key = keyVal.key; if (!map.has(key)) map.set(key, []); map.get(key).push(keyVal.val); } return map; }