/* global __appdir */ const path = require('path') const db = require(path.join(__appdir, 'lib', 'sequelize')) const groupHelper = require(path.join(__appdir, 'lib', 'grouphelper')) const ipHelper = require(path.join(__appdir, 'lib', 'iphelper')) const express = require('express') const { decorateApp } = require('@awaitjs/express') const router = decorateApp(express.Router()) const HttpResponse = require(path.join(__appdir, 'lib', 'httpresponse')) const backendHelper = require(path.join(__appdir, 'lib', 'external-backends', 'backendhelper')) const log = require(path.join(__appdir, 'lib', 'log')) // ############################################################################ // ########################### GET requests ################################# router.getAsync('', async (req, res) => { const includePaths = req.query.path !== undefined && req.query.path !== 'false' const include = includePaths ? ['parents'] : [] let groups = await db.group.findAll({ order: [['name', 'ASC']], include }) if (includePaths) groups = await groupHelper.addPathsToGroups(groups, false) res.send(groups) }) router.getAsync('/:id', async (req, res) => { const all = req.query.all !== undefined && req.query.all !== 'false' if (req.params.id > 0) { let group = await db.group.findOne({ where: { id: req.params.id }, include: ['parents', 'ipranges', 'subgroups', 'clients', 'config'] }) if (group) { // Convert ipranges in readable strings. group.ipranges.forEach(iprange => { iprange.startIp = ipHelper.toIPv4(iprange.startIp) iprange.endIp = ipHelper.toIPv4(iprange.endIp) }) if (all) res.status(200).send({ ...group.get({ plain: true }), ...await groupHelper.getAllChildren([group]) }) else res.status(200).send(group) } else { return HttpResponse.notFound(req.params.id).send(res) } } else if (req.params.id === '0') { const [subgroups, clients] = await Promise.all([ db.group.findAll(all ? {} : { where: { '$parents.id$': null }, include: ['parents'] }), db.client.findAll(all ? {} : { where: { '$groups.id$': null }, include: ['groups'] }) ]) res.send({ id: 0, subgroups, clients }) } else { HttpResponse.invalidId(true).send(res) } }) // ############################################################################ // ########################## POST requests ################################# router.postAsync(['', '/:id'], async (req, res) => { if (req.query.delete !== undefined && req.query.delete !== 'false') { if (!Array.isArray(req.body.ids)) return HttpResponse.invalidBodyValue('ids', 'an array').send(res) const user = await db.user.findOne({ where: { id: req.user.id } }) // Only need to log batch request if there is more than one client to delete. if (req.body.ids.length > 1) { await log({ category: 'GROUP_BATCH_DELETE', description: 'Batch deletion of ' + req.body.ids.length + ' groups initiated by user.', user, userId: req.user.id }) } let deletionCounter = 0 // Delete every group on its own, to get a better log for (let index in req.body.ids) { const group = await db.group.findOne({ where: { id: req.body.ids[index] } }) const count = await db.group.destroy({ where: { id: req.body.ids[index] } }) if (count !== 1) { await log({ category: 'ERROR_GROUP_DELETE', description: 'Group could not be deleted.', group, user, userId: req.user.id }) } else { await log({ category: 'GROUP_DELETE', description: 'Group successfully deleted.', group, user, userId: req.user.id }) deletionCounter++ } } if (req.body.ids.length > 1) { log({ category: 'GROUP_BATCH_DELETE', description: deletionCounter + '/' + req.body.ids.length + ' groups successfully deleted.', user, userId: req.user.id }) } HttpResponse.successBatch('deleted', 'group', deletionCounter).send(res) } else { let group let action = 'updated' if (req.params.id === undefined) { group = await db.group.create(req.body.data) action = 'created' log({ category: 'GROUP_CREATE', description: 'Group successfully created.', groupId: group.id, userId: req.user.id }) } else if (req.params.id > 0) { group = await db.group.findOne({ where: { id: req.params.id }, include: ['ipranges'] }) if (!group) return HttpResponse.notFound(req.params.id).send(res) else { await group.update(req.body.data) log({ category: 'GROUP_EDIT', description: 'Group successfully edited.', groupId: group.id, userId: req.user.id }) } } else { return HttpResponse.invalidId().send(res) } const promises = [] // Set group parents if (Array.isArray(req.body.parentIds)) promises.push(group.setParents(req.body.parentIds)) // Update, create or destroy ipranges if (Array.isArray(req.body.ipranges)) { // Get the ipranges of the group an create a id -> iprange map of them const iprangeIdMap = {} if (group.ipranges) { group.ipranges.forEach(iprange => { iprangeIdMap[iprange.id] = iprange }) } // Update existing ipranges and create the new ones req.body.ipranges.forEach(iprange => { // Convert valid ip addresses to integer values. if (!ipHelper.isIPv4(iprange.startIp) || !ipHelper.isIPv4(iprange.endIp)) return iprange.startIp = ipHelper.toDecimal(iprange.startIp) iprange.endIp = ipHelper.toDecimal(iprange.endIp) if (iprange.id) { if (iprangeIdMap[iprange.id]) { promises.push(iprangeIdMap[iprange.id].update(iprange)) log({ category: 'IPRANGE_EDIT', description: '[' + iprange.id + '] IP range successfully edited from ' + ipHelper.toIPv4(iprange.startIp) + ' to ' + ipHelper.toIPv4(iprange.endIp), groupId: group.id, userId: req.user.id }) delete iprangeIdMap[iprange.id] } } else { promises.push(group.createIprange(iprange)) log({ category: 'IPRANGE_CREATE', description: '[' + iprange.id + '] IP range successfully created from ' + ipHelper.toIPv4(iprange.startIp) + ' to ' + ipHelper.toIPv4(iprange.endIp), groupId: group.id, userId: req.user.id }) } }) // Destroy the deleted ipranges for (let id in iprangeIdMap) { promises.push(iprangeIdMap[id].destroy()) } } await Promise.all(promises) HttpResponse.success(action, 'group', group.id).send(res) } }) router.postAsync('/:id/subgroups', async (req, res) => { if (!(req.params.id > 0)) return HttpResponse.invalidId().send(res) const group = await db.group.findOne({ where: { id: req.params.id } }) if (group) { let deletionCounter = 0 const user = await db.user.findOne({ where: { id: req.user.id } }) if (req.query.delete !== undefined && req.query.delete !== 'false') { // Remove method for subgroups if (req.body.ids.length > 1) { await log({ category: 'GROUP_BATCH_REMOVE_SUBGROUP', description: 'Group batch removal of ' + req.body.ids.length + ' subgroups initiated by user.', user, userId: req.user.id, group, groupId: group.id }) } for (let index in req.body.ids) { const count = await group.removeSubgroups(req.body.ids[index]) if (count !== 1) { await log({ category: 'ERROR_GROUP_REMOVE_SUBGROUP', description: 'Subgroup [' + req.body.ids[index] + '] could not be removed from group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) } else { await log({ category: 'GROUP_REMOVE_SUBGROUP', description: 'Subgroup [' + req.body.ids[index] + '] successfully removed from group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) deletionCounter++ } } if (req.body.ids.length > 1) { log({ category: 'GROUP_BATCH_REMOVE_SUBGROUP', description: deletionCounter + '/' + req.body.ids.length + ' subgroups successfully removed.', user, userId: req.user.id, group, groupId: group.id }) } HttpResponse.successBatch('removed', 'subgroup', deletionCounter).send(res) } else { // Add method for subgroups if (req.body.ids.length > 1) { await log({ category: 'GROUP_BATCH_ADD_SUBGROUP', description: 'Group batch addition of ' + req.body.ids.length + ' subgroups initiated by user.', user, userId: req.user.id, group, groupId: group.id }) } for (let index in req.body.ids) { const count = await group.addSubgroups(req.body.ids[index]) if (count.length !== 1) { await log({ category: 'ERROR_GROUP_ADD_SUBGROUP', description: 'Subgroup [' + req.body.ids[index] + '] could not be added to group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) } else { await log({ category: 'GROUP_ADD_SUBGROUP', description: 'Subgroup [' + req.body.ids[index] + '] successfully added to group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) deletionCounter++ } } if (req.body.ids.length > 1) { log({ category: 'GROUP_BATCH_ADD_SUBGROUP', description: deletionCounter + '/' + req.body.ids.length + ' subgroups successfully added.', user, userId: req.user.id, group, groupId: group.id }) } HttpResponse.successBatch('added', 'subgroup', deletionCounter).send(res) } } else { HttpResponse.notFound(req.params.id).send(res) } }) router.postAsync('/:id/clients', async (req, res) => { if (!(req.params.id > 0)) return HttpResponse.invalidId().send(res) const group = await db.group.findOne({ where: { id: req.params.id } }) if (group) { let deletionCounter = 0 const user = await db.user.findOne({ where: { id: req.user.id } }) let groupid = null if (req.query.delete !== undefined && req.query.delete !== 'false') { // Remove method for clients to groups if (req.body.ids.length > 1) { await log({ category: 'GROUP_BATCH_REMOVE_CLIENT', description: 'Group batch removal of ' + req.body.ids.length + ' clients initiated by user.', user, userId: req.user.id, group, groupId: group.id }) } for (let index in req.body.ids) { const count = await group.removeClients(req.body.ids[index]) if (count !== 1) { await log({ category: 'ERROR_GROUP_REMOVE_CLIENT', description: 'Client [' + req.body.ids[index] + '] could not be removed from group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) } else { await log({ category: 'GROUP_REMOVE_CLIENT', description: 'Client [' + req.body.ids[index] + '] successfully removed from group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id, clientId: req.body.ids[index] }) deletionCounter++ } } if (req.body.ids.length > 1) { log({ category: 'GROUP_BATCH_REMOVE_CLIENT', description: deletionCounter + '/' + req.body.ids.length + ' clients successfully deleted.', user, userId: req.user.id, group, groupId: group.id }) } HttpResponse.successBatch('removed', 'client', deletionCounter).send(res) } else { // Add method for clients to groups if (req.body.ids.length > 1) { await log({ category: 'GROUP_BATCH_ADD_CLIENT', description: 'Group batch addition of ' + req.body.ids.length + ' clients initiated by user.', user, userId: req.user.id, group, groupId: group.id }) } for (let index in req.body.ids) { const count = await group.addClients(req.body.ids[index]) if (count.length !== 1) { await log({ category: 'ERROR_GROUP_ADD_CLIENT', description: 'Client [' + req.body.ids[index] + '] could not be added to group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id }) } else { await log({ category: 'GROUP_ADD_CLIENT', description: 'Client [' + req.body.ids[index] + '] successfully added to group [' + group.id + '] ' + group.name, user, userId: req.user.id, group, groupId: group.id, clientId: req.body.ids[index] }) deletionCounter++ } } if (req.body.ids.length > 1) { log({ category: 'GROUP_BATCH_ADD_CLIENT', description: deletionCounter + '/' + req.body.ids.length + ' clients successfully added.', user, userId: req.user.id, group, groupId: group.id }) } HttpResponse.successBatch('added', 'client', deletionCounter).send(res) groupid = group.id } // Update each client backend for (let index in req.body.ids) { const id = req.body.ids[index] backendHelper.updateClient({ id: id, parents: [groupid] }) } } else { HttpResponse.notFound(req.params.id).send(res) } }) // ############################################################################ // ########################## DELETE requests ############################### router.deleteAsync('/:id', async (req, res) => { if (!(req.params.id > 0)) return HttpResponse.invalidId().send(res) const group = db.group.findOne({ where: { id: req.params.id } }) const count = await db.group.destroy({ where: { id: req.params.id } }) if (count) { log({ category: 'GROUP_DELETE', description: 'Group successfully deleted.', group, userId: req.user.id }) HttpResponse.success('deleted', 'group', req.params.id).send(res) } else HttpResponse.notFound(req.params.id).send(res) }) // ############################################################################ // ############################################################################ module.exports.router = router