summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorUdo Walter2019-10-24 03:30:38 +0200
committerUdo Walter2019-10-24 03:30:38 +0200
commitc618fad9b41fc2cdc4d5e258f7a9b5ec36588678 (patch)
tree61de61850b7bf793a91dfe298728fd819eac4cb5 /server
parent[server/registration] Fix ipxe url bug and add delay before chaining (diff)
downloadbas-c618fad9b41fc2cdc4d5e258f7a9b5ec36588678.tar.gz
bas-c618fad9b41fc2cdc4d5e258f7a9b5ec36588678.tar.xz
bas-c618fad9b41fc2cdc4d5e258f7a9b5ec36588678.zip
[configloader] new confighelper and test api to get a priority list of loaded configs
Diffstat (limited to 'server')
-rw-r--r--server/api/configloader.js14
-rw-r--r--server/lib/confighelper.js227
-rw-r--r--server/lib/eventhelper.js22
3 files changed, 250 insertions, 13 deletions
diff --git a/server/api/configloader.js b/server/api/configloader.js
index 6b69a49..4059e5b 100644
--- a/server/api/configloader.js
+++ b/server/api/configloader.js
@@ -6,6 +6,20 @@ const { decorateApp } = require('@awaitjs/express')
var noAuthRouter = decorateApp(express.Router())
const config = require(path.join(__appdir, 'config', 'config'))
const url = config.https.host + ':' + config.https.port
+const configHelper = require(path.join(__appdir, 'lib', 'confighelper'))
+
+
+// if client in db -> load script (default if none is found), else load registration script
+noAuthRouter.getAsync('/test/:uuid', async (req, res) => {
+ const list = req.query.list !== undefined && req.query.list !== 'false'
+ const config = await configHelper.getConfig(req.params.uuid, list)
+ if (!list) {
+ res.set('Content-Type', 'text/plain')
+ res.send(config.script)
+ } else {
+ res.send(config)
+ }
+})
// if client in db -> load script (default if none is found), else load registration script
noAuthRouter.getAsync('/:uuid', async (req, res) => {
diff --git a/server/lib/confighelper.js b/server/lib/confighelper.js
new file mode 100644
index 0000000..2771596
--- /dev/null
+++ b/server/lib/confighelper.js
@@ -0,0 +1,227 @@
+/* global __appdir */
+const path = require('path')
+const fs = require('fs')
+const db = require(path.join(__appdir, 'lib', 'sequelize'))
+const eventHelper = require(path.join(__appdir, 'lib', 'eventhelper'))
+const config = require(path.join(__appdir, 'config', 'config'))
+const url = config.https.host + ':' + config.https.port
+
+
+async function getConfig (uuid, list) {
+ const client = await db.client.findOne({ where: { uuid: uuid }, include: ['groups', 'events'] })
+ let configPath = []
+
+ // client not known, start registration
+ if (client === null) {
+ let config = { id: 'REGISTRATION', name: 'Registration Script' }
+ if (!list) {
+ config.script = fs.readFileSync(path.join(__appdir, 'ipxe', 'registration.ipxe'), 'utf8')
+ return config
+ }
+ configPath.push(config)
+ return configPath
+ }
+
+ // Client is in db, check for registration hooks.
+ if (client.registrationState !== null) {
+ // client is in registration state, load scripts
+ var hook = await db.registrationhook.findOne({ where: { id: client.registrationState } })
+ let config = { id: 'HOOK', name: 'hook.name', hookId: hook.id, hookType: hook.type }
+ if (!list) {
+ if (hook.type === 'IPXE') {
+ config.script = hook.script
+ } else if (hook.type === 'BASH') {
+ config.script = fs.readFileSync(path.join(__appdir, 'ipxe', 'minilinux.ipxe'), 'utf8')
+ }
+ return config
+ }
+ configPath.push(config)
+ }
+
+ // Check for event or direct configs
+ let importantList = []
+ let eventList = []
+ let configList = []
+ await _checkParentConfigs(client.id, importantList, eventList, configList, !list)
+
+ if (!list) {
+ if (importantList.length) return importantList[0]
+ else if (eventList.length) return eventList[0]
+ else if (configList.length) return configList[0]
+ } else {
+ configPath = configPath.concat(importantList, eventList, configList)
+ }
+
+ // No config found, use default config
+ let config = await _prepareConfig({ id: 'DEFAULT', source: { type: 'DEFAULT' } })
+ if (!list) return config
+
+ configPath.push(config)
+ return configPath
+}
+
+
+async function _checkParentConfigs (ids, importantList, eventList, configList, breakOnImportant, blackList = []) {
+ let events = []
+ let configs = []
+
+ if (Array.isArray(ids)) {
+ events = await db.event.findAll({ where: { '$groups.id$': ids }, include: ['groups', 'config'] })
+ let groups = await db.group.findAll({ where: { id: ids }, include: ['config'] })
+ groups.forEach(group => {
+ if (group.configId) configs.push({ id: group.configId, source: { type: 'GROUP', id: group.id, name: group.name } })
+ })
+ } else {
+ events = await db.event.findAll({ where: { '$clients.id$': ids }, include: ['clients', 'config'] })
+ let client = await db.client.findOne({ where: { id: ids }, include: ['config'] })
+ if (client && client.configId) configs = [{ id: client.configId, source: { type: 'CLIENT', id: client.id, name: client.name } }]
+ }
+
+ let importants = []
+ let nonImportants = []
+
+ events.forEach(event => {
+ let eventTimes = JSON.parse(event.times)
+ if (!eventHelper.isActive(eventTimes)) return
+
+ if ((event.groups && event.groups.some(group => group.group_x_event.blacklist))
+ || (event.clients && event.clients.length && event.clients[0].client_x_event.blacklist)) {
+ return blackList.push(event.id)
+ }
+
+ if (blackList.includes(event.id) || !event.configId) return
+
+ let config = {
+ id: event.configId,
+ source: {
+ type: event.important ? 'IMPORTANT_EVENT' : 'EVENT',
+ id: event.id,
+ name: event.name
+ }
+ }
+
+ if (event.groups) config.source.groups = event.groups.map(x => ({ id: x.id, name: x.name }))
+
+ if (event.important) importants.push(config)
+ else nonImportants.push(config)
+ })
+
+ if (importants.length > 1) importantList.push(await _createDynamicMenu(importants))
+ else if (importants.length === 1) importantList.push(await _prepareConfig(importants[0]))
+
+ if (breakOnImportant && importantList.length) return
+
+ if (nonImportants.length > 1) eventList.push(await _createDynamicMenu(nonImportants))
+ else if (nonImportants.length === 1) eventList.push(await _prepareConfig(nonImportants[0]))
+
+ if (configs.length > 1) configList.push(await _createDynamicMenu(configs))
+ else if (configs.length === 1) configList.push(await _prepareConfig(configs[0]))
+
+ let parents = []
+ if (Array.isArray(ids)) {
+ parents = await db.group.findAll({ where: { '$subgroups.id$': ids }, include: ['subgroups'] })
+ } else {
+ parents = await db.group.findAll({ where: { '$clients.id$': ids }, include: ['clients'] })
+ }
+ if (parents.length) await _checkParentConfigs(parents.map(x => x.id), importantList, eventList, configList, breakOnImportant, blackList)
+}
+
+
+// create the config script from database
+async function _prepareConfig (configInfo, noScript) {
+ let id = configInfo.id
+
+ // If no id is given, find the default config
+ const where = {}
+ if (id === 'DEFAULT') where.isDefault = true
+ else where.id = id
+
+ const config = await db.config.findOne({ where, include: ['entries'], order: [[db.config.associations.entries, db.config.associations.entries.through, 'sortValue', 'ASC']] })
+ if (!config) return configInfo
+
+ const result = { id: config.id, name: config.name, source: configInfo.source }
+
+ if (noScript) return result
+ if (config.script !== null && config.script !== '') {
+ result.script = config.script
+ return result
+ }
+ var script = ''
+ var menuscript = ''
+ script += '#!ipxe\r\n\r\n'
+ script += ':start\r\n'
+ script += 'menu ' + config.name + '\r\n'
+ config.entries.forEach(entry => {
+ script += 'item'
+ if (entry.config_x_entry.keyBind !== null) {
+ script += ' --key ' + entry.config_x_entry.keyBind
+ }
+ script += ' menuentry' + entry.id + ' '
+ if (entry.config_x_entry.customName !== null && entry.config_x_entry.customName !== '') {
+ script += entry.config_x_entry.customName
+ } else {
+ script += entry.name
+ }
+ script += '\r\n'
+ menuscript += ':' + 'menuentry' + entry.id + '\r\n'
+ menuscript += entry.script + '\r\n\r\n'
+ })
+ script += 'choose '
+ if (config.defaultEntry !== null && config.timeout !== null) {
+ script += '--default menuentry' + config.defaultEntry + ' --timeout ' + config.timeout + ' '
+ }
+ script += `target && goto \${target}\r\n\r\n`
+ script += menuscript
+
+ result.script = script
+ return result
+}
+
+
+// create dynamic menu to load the different given configs for a client
+async function _createDynamicMenu (configInfos, noScript) {
+ const ids = []
+ const idSourceMap = {}
+
+ configInfos.forEach(configInfo => {
+ ids.push(configInfo.id)
+ idSourceMap[configInfo.id] = configInfo.source
+ })
+
+ const configs = await db.config.findAll({ where: { id: ids } })
+ const result = {
+ merged: true,
+ id: JSON.stringify(configs.map(x => x.id)),
+ configs: configs.map(x => ({ id: x.id, name: x.name, source: idSourceMap[x.id] }))
+ }
+ if (noScript) return result
+
+ var script = ''
+ var menuscript = ''
+ var defaultentry = ''
+ script += '#!ipxe\r\n\r\n'
+ script += ':start\r\n'
+ script += 'menu ' + 'Choose one configuration to boot' + '\r\n'
+ configs.forEach(config => {
+ script += 'item '
+ script += ' menuentry' + config.id + ' '
+ script += config.name
+ script += '\r\n'
+
+ // Last script processed is default script
+ defaultentry = 'menuentry' + config.id
+
+ menuscript += ':' + 'menuentry' + config.id + '\r\n'
+ menuscript += 'chain ' + 'https://' + url + '/api/configloader/getconfig/' + config.id + '\r\n\r\n'
+ })
+ script += 'choose --default ' + defaultentry + ' '
+ script += '--timeout 15 '
+ script += `target && goto \${target}\r\n\r\n`
+ script += menuscript
+
+ result.script = script
+ return result
+}
+
+
+module.exports = { getConfig } \ No newline at end of file
diff --git a/server/lib/eventhelper.js b/server/lib/eventhelper.js
index 46213e6..e5d54f3 100644
--- a/server/lib/eventhelper.js
+++ b/server/lib/eventhelper.js
@@ -20,15 +20,14 @@ class Schedule {
const splitStartTime = splitStart[1].split(':')
const rule = {
dtstart: new Date(event.start + 'Z'),
- until: new Date(event.start + 'Z'),
+ until: new Date(event.end + 'Z'),
interval: event.interval,
- byhour: splitStartTime[0],
- byminute: splitStartTime[1]
+ byhour: [splitStartTime[0]],
+ byminute: [splitStartTime[1]],
+ byweekday: event.dayMap.reduce((arr, v, i) => { if (v) arr.push(rruleWeekdays[i]); return arr }, []),
+ bymonth: event.monthMap.reduce((arr, v, i) => { if (v) arr.push(i + 1); return arr }, [])
}
- rule.byweekday = event.dayMap.reduce((arr, v, i) => { if (v) arr.push(rruleWeekdays[i]); return arr }, [])
- rule.bymonth = event.monthMap.reduce((arr, v, i) => { if (v) arr.push(i + 1); return arr }, [])
-
if (event.intervalType === 'month') rule.freq = RRule.MONTHLY
else if (event.intervalType === 'week') rule.freq = RRule.WEEKLY
else rule.freq = RRule.DAILY
@@ -56,13 +55,10 @@ class Schedule {
}
function isActive (event) {
- if (event.repetitive) {
- const schedule = new Schedule(event)
- return schedule.isActive(new Date())
- } else {
- const now = formatDate(new Date())
- return event.start <= now && now <= event.end
- }
+ const now = formatDate(new Date())
+ if (now < event.start || event.end < now) return false
+ if (event.repetitive) return (new Schedule(event)).isValid(now)
+ else return true
}
function formatDate (date) {