summaryrefslogblamecommitdiffstats
path: root/server/api/registrations.js
blob: 3c5c8d8eb3547ca2de59b211cc5759a918b910f6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                          



                                                         
                                                                                 



                
                                

                               
                                                           

                                               
                                                               

                                  









                              
                                                     
  
                                            
   

                                           
                  
                                                              


                                                                                                
                                                       






                                                                                                         
                                                                         





                             
 


                                                                            





                                         









                                                                                                                                      


    






                                                                      
                                                                                    












                                                                                                             




                                        
 













                                                                                                                                                                                                 























                                                                                                     
                                          
 
  





















                                                                                                   







                                                                      
                           


                                                                                     









                                                                 

         
                     

                                                                          



                             

                                                                                    

    




                                                                                                                      
                                                         

                           


                                                                                                                           

             
 


                            
                    



                                                                                                                  

                    
                   

                                                                                                                                                    
   

              

                                                    

                                              

                                                      

               


































                                                                            
/* global __appdir */
var path = require('path')
var express = require('express')
var router = express.Router()
var noAuthRouter = express.Router()
var db = require(path.join(__appdir, 'lib', 'sequelize'))
const ExternalBackends = require(path.join(__appdir, 'lib', 'external-backends'))

// GET requests.

/*
 * TODO: CURRENTLY TEST FUNCTION
 */
router.get('/', (req, res) => {
  db.backend.findOne({ where: { id: 1 } }).then(result => {
    const b = new ExternalBackends()
    const instance = b.getInstance(result.type)
    instance.getClient(result.credentials, {}).then(result => {
      res.status(200).send(result)
    })
  })
})

module.exports.router = router

// GET requests.

// POST requests.

/*
 * Returns all root parents or all childs of a group.
 *
 * @return: Ipxe menu with a list of groups.
 */
noAuthRouter.post('/group', (req, res) => {
  const id = req.body.id
  var parents = []
  if (req.body.parents) parents = JSON.parse(req.body.parents)
  if (id === '0') {
    db.group.findAll({ where: { '$parents.id$': null }, include: ['parents'] }).then(groups => {
      if (groups) {
        res.send(buildIpxeMenu(id, 'Root', groups, []))
      } else {
        res.status(404).end()
      }
    })
  } else {
    db.group.findOne({ where: { id: id }, include: ['parents', 'subgroups', 'clients'] }).then(group => {
      if (group) {
        res.send(buildIpxeMenu(id, group.name, group.subgroups, parents))
      } else {
        res.status(404).end()
      }
    })
  }
})

/*
 * Adds the client in the database and set parents if a parent was selected.
 */
noAuthRouter.post('/add', (req, res) => {
  const mac = req.body.mac
  const uuid = req.body.uuid
  const ip = req.body.ip
  const name = req.body.name
  const parentId = req.body.id
  db.client.findOne({ where: { uuid: uuid } }).then(client => {
    if (client) res.status(200).send('#!ipxe\r\necho Client already exists\r\necho Press any key to continue ...\r\nread x\r\nreboot')
    else {
      db.client.create({ name: name, ip: ip, mac: mac, uuid: uuid }).then(client => {
        if (parentId) {
          client.addGroup(parentId)
        }
        res.send('#!ipxe\r\nreboot')
      })
    }
  })
})

/*
 * Open api method for setting the registration state of a given uuid.
 */
noAuthRouter.post('/:uuid/state', (req, res) => {
  const uuid = req.params.uuid
  const state = parseInt(req.body.state)

  db.client.findOne({ where: { uuid: uuid }, include: ['groups'] }).then(client => {
    // Client not found handling
    if (client === null) {
      res.status(404).send({ status: 'INVALID_UUID', error: 'There is no client with the provided UUID.' })
      return
    }

    // Check if the finished script id (state) matches the current state of the client.
    if (client.registrationState !== state) {
      res.status(400).send({ status: 'INVALID_SCRIPT', error: 'This script should not have been executed.' })
      return
    }

    // If it matches, search for the next script and update the clients registrationState.
    // Get all group id's of the client.
    var groupids = []
    client.groups.forEach(g => {
      groupids = [...groupids, g.id]
    })

    // Get the sort value of the current hook.
    db.registrationhook.findOne({ where: { id: client.registrationState } }).then(hook => {
      // Gets the list of all groupids inclusive the recursive parents.
      getRecursiveParents(groupids).then(gids => {
        // Get the list of all hooks where the parent is null or those who fullfill the group dependency.
        db.registrationhook.findAll({ where: { '$groups.id$': { $or: [null, gids] }, sortvalue: { $gt: hook.sortvalue } }, include: ['groups'], order: [['sortvalue', 'ASC']] }).then(result => {
          // Update the client's registration state
          client.updateAttributes({
            registrationState: result[0].id
          })
          res.send({ status: 'SUCCESS' })
        })
      })
    })
  })
})

/*
 * Returns the next bash script for the minilinux. Else empty script. (Empty = bash will make reboot)
 */
noAuthRouter.get('/:uuid/nexthook', (req, res) => {
  const uuid = req.params.uuid
  db.client.findOne({ where: { uuid: uuid } }).then(client => {
    // Return 404 if the client doesn't exist or it has no registration state.
    if (client === null || client.registrationState === null) {
      res.status(404).send()
      return
    }
    db.registrationhook.findOne({ where: { id: client.registrationState } }).then(hook => {
      if (hook.type !== 'BASH') {
        res.send()
      } else {
        res.send(hook.script)
      }
    })
  })
})

module.exports.noAuthRouter = noAuthRouter

/*
 * groupids: Array of group ids to get the parents from.
 *
 * Returns a list of the grop ids and all recursive ids of their parents.
 */
function getRecursiveParents (groupIds) {
  var gids = []
  return db.group.findAll({ where: { id: groupIds }, include: ['parents'] }).then(groups => {
    groups.forEach(group => {
      group.parents.forEach(parent => {
        if (!groupIds.includes(parent.id) && !gids.includes(parent.id)) gids = [...gids, parent.id]
      })
    })
    if (gids.length === 0) return groupIds
    else {
      return getRecursiveParents(gids).then(r => {
        return groupIds.concat(r)
      })
    }
  })
}

/*
 * id: id of the current selected location.
 * name: Name of the current selected location
 * groups: List of group [{ id: <GROUP_ID>, name: <GROUP_NAME> }, ...]
 *
 * Build the ipxe menu out of the list of groups.
 * Used by the manual registration.
 */
function buildIpxeMenu (id, name, groups, parents) {
  var script = '#!ipxe\r\n'
  // script = script.concat(`console --picture \${img} --x 800 --y 600 || shell\r\n`)

  // Add parent to keep track of the path we clicked through.
  var parentId = 0
  var oldParents = parents.slice(0)
  if (parents.length > 0) {
    parentId = oldParents[oldParents.length - 1].id
    oldParents.length = oldParents.length - 1
  }
  parents.push({ id: id, name: toAscii(name) })
  script += `set space:hex 20:20\r\n`
  script += `set space \${space:string}\r\n`
  script += `set parents ` + JSON.stringify(parents) + '\r\n\r\n'

  // Menu
  var menuscript = ''
  script += ':start\r\n'
  script += 'menu Choose the group you want the client to be saved in\r\n'

  // Parent menu entries.
  var spacer = ''
  parents.forEach(parent => {
    script += 'item --gap ' + spacer + '[' + parent.id + '] ' + parent.name + '\r\n'
    spacer += `\${space}`
  })

  // Back button
  script += 'item --key b back ' + spacer + '..\r\n'
  menuscript += ':back\r\nparams\r\nparam id ' + parentId + '\r\nparam parents ' + JSON.stringify(oldParents) + '\r\n'
  menuscript += 'chain --replace https://bas.stfu-kthx.net:8888/api/registrations/group##params\r\n\r\n'

  // Group menu entries. First 1-9 are pressable via key?
  var counter = '1'
  groups.forEach(group => {
    script += 'item --key ' + counter + ' ' + counter + ' ' + spacer + '[' + group.id + '] ' + toAscii(group.name) + '\r\n'
    menuscript += ':' + counter + '\r\n' + 'params\r\nparam id ' + group.id + `\r\nparam parents \${parents}\r\n`
    menuscript += 'chain --replace https://bas.stfu-kthx.net:8888/api/registrations/group##params\r\n\r\n'
    counter++
  })

  // Menu seperator
  script += 'item --gap\r\n'

  // Add client menu
  script += 'item select Add client to ' + toAscii(name) + '\r\n'
  menuscript += `:select\r\necho Enter client name\r\nread clientname\r\nparams\r\nparam name \${clientname}\r\n`
  menuscript += 'param id ' + id + `\r\nparam mac \${net0/mac}\r\nparam uuid \${uuid}\r\nparam ip \${net0/ip}\r\n`
  menuscript += 'chain --replace https://bas.stfu-kthx.net:8888/api/registrations/add##params\r\n\r\n'

  // Goto start menu
  if (id !== '0') {
    script += 'item reset Go to start\r\n'
    menuscript += ':reset\r\nparams\r\nparam id ' + 0 + '\r\nchain --replace https://bas.stfu-kthx.net:8888/api/registrations/group##params\r\n\r\n'
  }

  // Exit menu
  script += 'item exit Exit manual registration\r\n'
  menuscript += ':exit\r\nexit 1\r\n\r\n'

  // Concat script + menuscript and return it.
  script += `choose target && goto \${target}\r\n\r\n`
  script += menuscript
  return script
}

function toAscii (string) {
  string = string.replace('ü', 'ue')
  string = string.replace('ö', 'oe')
  string = string.replace('ä', 'ae')
  return ascii(string)
}

/* eslint-disable */
var escapable = /[\\"\x00-\x1f\x7f-\uffff]/g
/* eslint-enable */
var meta = { // table of character substitutions
  '\b': '\\b',
  '\t': '\\t',
  '\n': '\\n',
  '\f': '\\f',
  '\r': '\\r',
  '"': '\\"',
  '\\': '\\\\'
}

function ascii (string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

  escapable.lastIndex = 0
  return escapable.test(string)
    ? string.replace(escapable, function (a) {
      var c = meta[a]
      return typeof c === 'string' ? c
        : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4)
    }) : string
}