diff --git a/modules/account/back/models/samba-config.js b/modules/account/back/models/samba-config.js index 7f7ad8ca5..b604b4220 100644 --- a/modules/account/back/models/samba-config.js +++ b/modules/account/back/models/samba-config.js @@ -172,190 +172,105 @@ module.exports = Self => { async syncRoles() { let $ = app.models; - let { - client, - accountConfig - } = this; + // let { + // client, + // accountConfig + // } = this; // Prepare data + try { + // OBTENER ROLES + let roles = (await $.VnRole.find({ + fields: ['id', 'name', 'description'], + order: 'modified DESC' + })); + let rolesMap = roles.map(role => role.name); - let roles = await $.VnRole.find({ - fields: ['id', 'name', 'description'] - }); - let roleRoles = await $.RoleRole.find({ - fields: ['role', 'inheritsFrom'] - }); - let roleMap = toMap(roleRoles, e => { - return {key: e.inheritsFrom, val: e.role}; - }); + // OBTENER SAMBA ROLES + let sambaCurrentRoles = await sambaTool('group', ['list']); - let accounts = await $.Account.find({ - fields: ['id'], - include: { - relation: 'user', - scope: { + // Encontrar elementos a eliminar + const rolesToDelete = differences(sambaCurrentRoles, rolesMap); + + // Encontrar elementos a insertar + const rolesToInsert = differences(rolesMap, sambaCurrentRoles); + + // Encontrar elementos a actualizar + const rolesToUpdate = differences(rolesMap, [...rolesToDelete, ...rolesToInsert]); + + // OBTENER USUARIOS Y SUS ROLES + if ( + rolesToDelete.length > 0 || + rolesToInsert.length > 0 || + rolesToUpdate.length > 0) { + let users = await $.VnUser.find({ + include: { + relation: 'role', + scope: {fields: ['name'], + where: {'name': {nin: rolesToDelete}} + } + }, fields: ['name', 'roleFk'], - where: {active: true} + // where: {'active': true} + }); + let usersMap = toMap(users, user => { + let role = user.role(); + if (!role) { + console.info(`User ${user.name} has not valid role`); + return; + } + return {key: role.name, val: user.name}; + }); + usersMap.set('group1', ['employee']); + if (rolesToDelete.length > 0) { + // PROCEDIMIENTO PARA ELIMINAR USUARIOS ASOCIADOS AL ROL + let usersToDelete = rolesToDelete.flatMap(role => { + const exist = usersMap.get(role); + + if (exist) { + return usersMap.get(role)?.map( + a => sambaTool('group', ['removemembers', role, a]) + ); + } else return []; + } + ); + const resultsUserDelete = await Promise.all(usersToDelete); + + // PROCEDIMIENTO PARA ELIMINAR ROLES + const resultsRoleDelete = await Promise.all( + rolesToDelete.map(role => sambaTool('group', ['delete', role])) + ); + } + + if (rolesToInsert.length > 0) { + // PROCEDIMIENTO PARA INSERTAR ROLES + const resultsRoleInsert = await Promise.all( + rolesToInsert.map(role => sambaTool('group', ['add', role])) + ); + + // PROCEDIMIENTO PARA INSERTAR USUARIOS ASOCIADOS AL ROL + let usersToInsert = rolesToInsert.flatMap(role => usersMap.get(role).map( + a => sambaTool('group', ['addmembers', role, a]) + ) + ); + const resultsUserInsert = await Promise.all(usersToInsert); + } + + if (rolesToUpdate.length > 0) { + let promises = []; + for await (const role of rolesToUpdate) { + const users = await sambaTool('group', ['listmembers', role]); + const usersToDelete = differences(users, usersMap.get(role)); + promises.push(usersToDelete.map(user => sambaTool('group', ['removemembers', user.name]))); + const usersToInsert = differences(usersMap.get(role), users); + promises.push(usersToInsert.map(user => sambaTool('group', ['addmembers', user.name]))); + + await Promise.all(promises); + } } } - }); - let accountMap = toMap(accounts, e => { - let user = e.user(); - if (!user) return; - return {key: user.roleFk, val: user.name}; - }); - - // Delete roles - - let opts = { - scope: 'sub', - attributes: ['dn'], - filter: 'objectClass=posixGroup' - }; - let reqs = []; - await client.searchForeach(this.groupDn, opts, object => { - if (shouldSync) - reqs.push(client.del(object.dn)); - }); - await Promise.all(reqs); - - // Recreate roles - - reqs = []; - for (let role of roles) { - let newEntry = { - objectClass: ['top', 'posixGroup'], - cn: role.name, - description: role.description, - gidNumber: accountConfig.idBase + role.id - }; - - let memberUid = []; - for (let 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},${this.groupDn}`; - if (shouldSync) - reqs.push(client.add(dn, newEntry)); - } - await Promise.all(reqs); - }, - async syncRole(role) { - let $ = app.models; - let { - client, - accountConfig - } = this; - - // Prepare data - - let roles = await $.VnRole.find({ - fields: ['id', 'name', 'description'], - where: { - name: role - } - }); - let roleRoles = await $.RoleRole.find({ - fields: ['role', 'inheritsFrom'] - }); - let roleMap = toMap(roleRoles, e => { - return {key: e.inheritsFrom, val: e.role}; - }); - - let accounts = await $.Account.find({ - fields: ['id'], - include: { - relation: 'user', - scope: { - fields: ['name', 'roleFk'], - where: {active: true} - } - } - }); - let accountMap = toMap(accounts, e => { - let user = e.user(); - if (!user) return; - return {key: user.roleFk, val: user.name}; - }); - - // Delete roles - - let opts = { - scope: 'sub', - attributes: ['dn'], - filter: 'objectClass=posixGroup' - }; - let reqs = []; - await client.searchForeach(this.groupDn, opts, object => { - if (shouldSync) - reqs.push(client.del(object.dn)); - }); - await Promise.all(reqs); - - // Recreate roles - - reqs = []; - for (let role of roles) { - let newEntry = { - objectClass: ['top', 'posixGroup'], - cn: role.name, - description: role.description, - gidNumber: accountConfig.idBase + role.id - }; - - let memberUid = []; - for (let 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},${this.groupDn}`; - if (shouldSync) - reqs.push(client.add(dn, newEntry)); - } - await Promise.all(reqs); - }, - async syncRoleX(roleName, info) { - let vnRoleArgs = { - fields: ['id', 'name', 'description'], - - }; - let role = null; - let roles = []; - if (roleName) { - vnRoleArgs.where = { - name: roleName - }; - role = await $.VnRole.find(vnRoleArgs); - } else roles = await $.VnRole.find(vnRoleArgs); - let roleRoleArgs = { - fields: ['role', 'inheritsFrom'], - - }; - if (role) roleRoleArgs.where = {'role.id': roles[0].id}; - let roleRoles = await $.RoleRole.find(roleRoleArgs); - let roleMap = toMap(roleRoles, e => { - return {key: e.inheritsFrom, val: e.role}; - }); - - let currentGroupList = await this.sambaTool('group', ['list']); - if (info.disableGroup || info.enableGroup) { - if (currentGroupList.includes(roleName)) - await this.sambaTool('group', ['modify', roleName, `--is-visible=${info.enableGroup ? 'yes' : 'no'}`]); - } else if (info.removeGroup) - await this.sambaTool('group', ['delete']); - - else if (info.recreateGroups) { - for (const role of roleMap) - await this.sambaTool('group', ['add', roleName, `--description=${role.description}`]); + } catch (error) { + console.error(error); } } diff --git a/modules/account/back/models/specs/samba-config.spec.js b/modules/account/back/models/specs/samba-config.spec.js index 80a7b949d..74a00f0b9 100644 --- a/modules/account/back/models/specs/samba-config.spec.js +++ b/modules/account/back/models/specs/samba-config.spec.js @@ -1,33 +1,10 @@ // const execFile = require('child_process').execFile; const app = require('vn-loopback/server/server'); const SAMBA_TOOL = 'samba-tool'; -// const RoleControlFlags = { -// ACCOUNTDISABLE: 0x2 -// }; fdescribe('Samba config', () => { - // const employeeId = 1; - // const developerId = 9; - // const sysadminId = 66; - // const itBossId = 104; - // const rootId = 100; - // const clarkKent = 1103; - - // const roles = { - - // itBoss: {id: 104, value: 'itBoss'} - // }; - it('SyncRoles', async() => { await syncRoles(); }); - - // it('With role as argument', async() => { - // syncRole(roles.itBoss.value); - // }); - - // it('No role as argument', async() => { - - // }); }); async function sambaTool(command, args = []) { // const allArgs = [command].concat( @@ -41,17 +18,22 @@ async function sambaTool(command, args = []) { case 'group': if (args[0] === 'list') return ['employee', 'customer', 'agency', 'group1']; - else if (args[0] === 'add') { + else if (['listmembers'].includes(args[0])) { return new Promise(resolve => { - resolve(`${SAMBA_TOOL} add ${args[1]}`); + resolve(['user1']); }); - } else if (['delete', 'listmembers'].includes(args[0])) { + } else if (['delete', 'add'].includes(args[0])) { return new Promise(resolve => { - resolve(`${SAMBA_TOOL} ${args[0]} ${args[1]}`); + resolve(`${SAMBA_TOOL} ${command} ${args[0]} ${args[1]}`); }); } else if (['addmembers', 'removemembers'].includes(args[0])) { return new Promise(resolve => { - resolve(`${SAMBA_TOOL} ${args[0]} ${args[1]} ${args[2]}`); + resolve(`${SAMBA_TOOL} ${command} ${args[0]} ${args[1]} ${args[2]}`); + }); + } else if (args[0] === 'modify') { + return new Promise(resolve => { + // samba-tool group modify group_name --description description + resolve(`${SAMBA_TOOL} ${command} ${args[0]} ${args[1]} ${args[2]} ${args[3]}`); }); } break; @@ -79,23 +61,36 @@ async function syncRoles() { // OBTENER SAMBA ROLES let sambaCurrentRoles = await sambaTool('group', ['list']); expect(sambaCurrentRoles.length).toEqual(4); + // Encontrar elementos para eliminados - const rolesToDelete = sambaCurrentRoles.filter(item => !rolesMap.includes(item)); + const rolesToDelete = differences(sambaCurrentRoles, rolesMap); + // const rolesToDelete = sambaCurrentRoles.filter(item => !rolesMap.includes(item)); expect(rolesToDelete.length).toEqual(1); + // Encontrar elementos insertar - const rolesToInsert = rolesMap.filter(item => !sambaCurrentRoles.includes(item)); + const rolesToInsert = differences(rolesMap, sambaCurrentRoles); + // const rolesToInsert = rolesMap.filter(item => !sambaCurrentRoles.includes(item)); expect(rolesToInsert.length).toEqual(2); + + // Encontrar elementos actualizar + const rolesToUpdate = differences(rolesMap, [...rolesToDelete, ...rolesToInsert]); + // const rolesToInsert = rolesMap.filter(item => !sambaCurrentRoles.includes(item)); + expect(rolesToUpdate.length).toEqual(3); + // OBTENER USUARIOS Y SUS ROLES - if (rolesToDelete.length > 0 || rolesToInsert.length > 0) { + if ( + rolesToDelete.length > 0 || + rolesToInsert.length > 0 || + rolesToUpdate.length > 0) { let users = await $.VnUser.find({ include: { relation: 'role', scope: {fields: ['name'], - where: {'name': {inq: rolesToInsert}} + where: {'name': {nin: rolesToDelete}} } }, fields: ['name', 'roleFk'], - where: {'active': true} + // where: {'active': true} }); /* let accounts = await $.Account.find({ fields: ['id'], @@ -115,11 +110,18 @@ async function syncRoles() { });*/ let usersMap = toMap(users, user => { let role = user.role(); - if (!role) return; + if (!role) { + console.info(`User ${user.name} has not valid role`); + return; + } return {key: role.name, val: user.name}; }); usersMap.set('group1', ['employee']); - console.log(usersMap); + // console.log(usersMap); + // const sumOfLengths = [...usersMap.values()].reduce((accumulator, currentArray) => { + // return accumulator + currentArray.length; + // }, 0); + // console.assert(sumOfLengths === users.length+1); /* toMap(accounts, e => { let user = e.user(); if (!user) return; @@ -148,33 +150,61 @@ async function syncRoles() { ); const resultsUserDelete = await Promise.all(usersToDelete); expect(resultsUserDelete.length).toEqual(1); - expect(resultsUserDelete[0]).toEqual('samba-tool removemembers group1 employee'); + expect(resultsUserDelete[0]).toEqual('samba-tool group removemembers group1 employee'); // PROCEDIMIENTO PARA ELIMINAR ROLES const resultsRoleDelete = await Promise.all( rolesToDelete.map(role => sambaTool('group', ['delete', role])) ); expect(resultsRoleDelete.length).toEqual(1); - expect(resultsRoleDelete[0]).toEqual('samba-tool delete group1'); + expect(resultsRoleDelete[0]).toEqual('samba-tool group delete group1'); } if (rolesToInsert.length > 0) { - // PROCEDIMIENTO PARA INSERTAR USUARIOS ASOCIADOS AL ROL + // PROCEDIMIENTO PARA INSERTAR ROLES const resultsRoleInsert = await Promise.all( - rolesToInsert.map(role => sambaTool('group', ['add', role])) + rolesToInsert.map(role => sambaTool('group', ['add', role])), ); expect(resultsRoleInsert.length).toEqual(2); - expect(resultsRoleInsert[0]).toEqual('samba-tool add administrative'); - expect(resultsRoleInsert[1]).toEqual('samba-tool add guest'); - - let usersToInsert = rolesToInsert.flatMap(role => usersMap.get(role).map( - a => sambaTool('group', ['addmembers', role, a]) - ) + expect(resultsRoleInsert[0]).toEqual('samba-tool group add administrative'); + expect(resultsRoleInsert[1]).toEqual('samba-tool group add guest'); + const rolesToUpdate = await Promise.all( + rolesToInsert.map( + role => + sambaTool('group', + ['modify', role, '--description', `"${roles.find(r => r.name === role).description}"`] + ) + ) ); + expect(rolesToUpdate.length).toEqual(2); + expect(rolesToUpdate[0]).toEqual('samba-tool group modify administrative --description "Tareas relacionadas con la contabilidad"'); + expect(rolesToUpdate[1]).toEqual('samba-tool group modify guest --description "Privilegios para usuarios sin cuenta"'); + + // PROCEDIMIENTO PARA AÑADIR DESCRIPCION AL ROLE + + let usersToInsert = rolesToInsert.flatMap(role => + usersMap.get(role).map( + a => sambaTool('group', ['addmembers', role, a]) + ) + ); + // PROCEDIMIENTO PARA INSERTAR USUARIOS ASOCIADOS AL ROL const resultsUserInsert = await Promise.all(usersToInsert); expect(resultsUserInsert.length).toEqual(2); - expect(resultsUserInsert[0]).toEqual('samba-tool addmembers administrative administrative'); - expect(resultsUserInsert[1]).toEqual('samba-tool addmembers guest guest'); - // PROCEDIMIENTO PARA INSERTAR ROLES + expect(resultsUserInsert[0]).toEqual('samba-tool group addmembers administrative administrative'); + expect(resultsUserInsert[1]).toEqual('samba-tool group addmembers guest guest'); + } + + if (rolesToUpdate.length > 0) { + let promises = []; + for await (const role of rolesToUpdate) { + const users = await sambaTool('group', ['listmembers', role]); + const usersToDelete = differences(users, usersMap.get(role)); + promises.push(usersToDelete.map(user => sambaTool('group', ['removemembers', role, user]))); + const usersToInsert = differences(usersMap.get(role), users); + promises.push(...usersToInsert.map(user => sambaTool('group', ['addmembers', role, user]))); + + const result = await Promise.all(promises); + console.log(result); + } } } // let roleRoles = await $.RoleRole.find({ @@ -202,3 +232,46 @@ function toMap(array, fn) { } return map; } +function binarySearch(array, value) { + let first = 0; + + let last = array.length - 1; + + while (first <= last) { + const index = Math.floor((first + last) / 2); + + const middle = array[index]; + + if (middle === value) + return index; // Elemento encontrado, devuelve la posición. + + if (middle < value) + first = index + 1; + else + last = index - 1; + } + + return -1; // Elemento no encontrado. +} + +function differences(array1, array2) { + const differences = []; + + // Ordena ambos arrays + + const sortedArray1 = array1.slice().sort(); + const sortedArray2 = array2.slice().sort(); + + for (const value of sortedArray1) { + // Busca el elemento en el array ordenado utilizando búsqueda binaria + + const result = binarySearch(sortedArray2, value); + + // Si el elemento no se encuentra, agrégalo a la lista de diferencias + + if (result === -1) + differences.push(value); + } + + return differences; +}