summaryrefslogblamecommitdiffstats
path: root/server/api/groups.js
blob: 64351cf4029a51a8d3cabc1ad758e64646f58197 (plain) (tree)
1
2
3
4
5
6
7
8
9
                     

                                                           
                                                                      
                                                                
                                  
                                                   
                                            
                                                                        
                                                                                               
                                                      
 



                                                                               



                                                                                 





                                                                      
                                                                                                                                            
                

                                              

                                                          
        
                                                                                                                    

                                      
                                                           
     
                                     




                                                                                             
          
                                          
   

  

                                                                               
 

                                                                       
                                                                                                       











































                                                                                                   

             



                                                  





                                                   
                                   
                                                                                             
                                                                       








                                                    
            
                                               
     









                                                                                              
          









                                                                                        





                                                                                                                                                                  



                                                     





                                                                                                                                                                 
         



                                                 
       
     
                               
                                                             
   
  
 
                                                        
                                                                     
                                                                        
 
              


                                                                      
                                                                         














































                                                                                                                                   
            














































                                                                                                                               
     
          
                                                  

   
 
                                                      
                                                                     

                                                                        

                                                                      
                      
                                                                         















































                                                                                                                                 
            
















































                                                                                                                             






                                                                

          
                                                  

   
 

                                                                               
 
                                                
                                                                     
                                                                  
                                                                        








                                                                     
  
 

                                                                               
 
                              
/* 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