From 1ee0e6c1d7484930387438b7ebb15340595b5383 Mon Sep 17 00:00:00 2001 From: Christian Hofmaier Date: Sun, 24 Mar 2019 16:02:18 +0000 Subject: [eventmanager] Add module and functionality - list to show all events, buttons to create/delete events - can add groups/clients to event - can add blacklist to event --- server/api/configloader.js | 230 ++++++++++++++------- server/api/events.js | 86 ++++++++ server/api/roles.js | 18 +- server/migrations/20190226151600-create-event.js | 31 +++ .../20190226152330-create-client_x_event.js | 33 +++ .../20190226152400-create-group_x_event.js | 33 +++ server/models/client.js | 2 + server/models/event.js | 26 +++ server/models/group.js | 3 + 9 files changed, 379 insertions(+), 83 deletions(-) create mode 100644 server/api/events.js create mode 100644 server/migrations/20190226151600-create-event.js create mode 100644 server/migrations/20190226152330-create-client_x_event.js create mode 100644 server/migrations/20190226152400-create-group_x_event.js create mode 100644 server/models/event.js (limited to 'server') diff --git a/server/api/configloader.js b/server/api/configloader.js index 2422693..0ede64f 100644 --- a/server/api/configloader.js +++ b/server/api/configloader.js @@ -1,70 +1,125 @@ /* global __appdir */ -const path = require('path') +var path = require('path') var db = require(path.join(__appdir, 'lib', 'sequelize')) var express = require('express') -var noAuthRouter = express.Router() +const { decorateApp } = require('@awaitjs/express') +var noAuthRouter = decorateApp(express.Router()) // if client in db -> load script (default if none is found), else load registration script -noAuthRouter.get('/:uuid', (req, res) => { +noAuthRouter.getAsync('/:uuid', async (req, res) => { const uuid = req.params.uuid res.setHeader('content-type', 'text/plain') - db.client.findOne({ where: { uuid: uuid }, include: ['groups'] }).then(client => { - if (client !== null) { - // Check for registration hooks. - if (client.registrationState !== null) { - // client is in registration state, load scripts - db.registrationhook.findOne({ where: { id: client.registrationState } }).then(hook => { - if (hook.type === 'IPXE') res.send(hook.script) - else if (hook.type === 'BASH') res.sendFile(path.join(__appdir, 'ipxe', 'minilinux.ipxe')) - }) + var client = await db.client.findOne({ where: { uuid: uuid }, include: ['groups', 'events'] }) + if (client !== null) { + // 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 } }) + if (hook.type === 'IPXE') res.send(hook.script) + else if (hook.type === 'BASH') res.sendFile(path.join(__appdir, 'ipxe', 'minilinux.ipxe')) + return + } + + var events = [] + var blacklist = [] + var importantEvents = [] + var groupIds = [] + var result + + for (let i = 0; i < client.events.length; i++) { + if (client.events[i].important) importantEvents.push(client.events[i]) + if (client.events[i].client_x_event.blacklist) blacklist.push(client.events[i].id) + else events.push(client.events[i]) + } + + importantEvents = importantEvents.filter(e => !blacklist.includes(e.id)) + importantEvents = importantEvents.map(e => e.config) + importantEvents = importantEvents.filter(c => c !== null) + importantEvents = importantEvents.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos }) + if (importantEvents.length === 1) { + result = await createIpxeScript(importantEvents[0]) + res.send(result) + return + } + if (importantEvents.length > 1) { + result = await createDynamicMenu(importantEvents) + res.send(result) + return + } + + events = events.filter(e => !blacklist.includes(e.id)) + events = events.map(e => e.config) + events = events.filter(c => c !== null) + events = events.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos }) + + groupIds = client.groups.map(x => x.id) + + var response = await fetchParentConfigs(groupIds, blacklist) + if (response.ids.length > 0) { + // Check if there is an important event + if (response.type === 'important') { + if (response.ids.length === 1) { + result = await createIpxeScript(response.ids[0]) + res.send(result) + return + } + if (response.ids.length > 1) { + result = await createDynamicMenu(response.ids) + res.send(result) + return + } + } + // No important event, use client event if existent + if (events.length === 1) { + result = await createIpxeScript(events[0]) + res.send(result) return } - - // client is in db - if (client.configId !== null) { - // client has a config - createIpxeScript(client.configId).then(response => { - res.send(response) - }) + if (events.length > 1) { + result = await createDynamicMenu(events) + res.send(result) return } - // client has no config, check parent groups - var parentIds = [] - var configIds = [] - for (var i = 0; i < client.groups.length; i++) { - // gather parent ids - parentIds.push(client.groups[i].id) + // No client event, use events of lowest parents + if (response.type === 'event') { + if (response.ids.length === 1) { + result = await createIpxeScript(response.ids[0]) + res.send(result) + return + } + if (response.ids.length > 1) { + result = await createDynamicMenu(response.ids) + res.send(result) + return + } + } + // No events, use client config + if (client.configId !== null) { + result = await createIpxeScript(client.configId) + res.send(result) + return } - if (parentIds.length !== 0) { - // client has a parent to look for - checkGroupsForConfigs(parentIds).then(response => { - configIds = response - if (configIds.length === 1) { - // only one parent has a config, load it - createIpxeScript(configIds[0]).then(response => { - res.send(response) - }) - return - } else if (configIds.length > 1) { - // multiple parents have a config, make dynamic menu - createDynamicMenu(configIds).then(response => { - res.send(response) - }) - return - } - // no parent has a config, load default - res.sendFile(path.join(__appdir, 'ipxe', 'default.ipxe')) - }) - } else { - // no config given, load default - res.sendFile(path.join(__appdir, 'ipxe', 'default.ipxe')) + // No client config, use configs of lowest parents + if (response.type === 'config') { + if (response.ids.length === 1) { + result = await createIpxeScript(response.ids[0]) + res.send(result) + return + } + if (response.ids.length > 1) { + result = await createDynamicMenu(response.ids) + res.send(result) + return + } } - } else { - // client not known, start registration - res.sendFile(path.join(__appdir, 'ipxe', 'registration.ipxe')) } - }) + // No config found, use default config + res.sendFile(path.join(__appdir, 'ipxe', 'default.ipxe')) + } else { + // client not known, start registration + res.sendFile(path.join(__appdir, 'ipxe', 'registration.ipxe')) + } }) // load config by given id @@ -77,32 +132,54 @@ noAuthRouter.get('/getconfig/:configId', (req, res) => { }) }) -// recursive iteration through the layers of parents until a config is found or no parents left -function checkGroupsForConfigs (groupIds) { +async function fetchParentConfigs(groupIds, blacklist) { + if (groupIds.length === 0) return {'ids': [], 'type': ''} + + var importantEvents = [] + var events = [] + var configs = [] var parentIds = [] - var configIds = [] - if (groupIds.length === 0) { - return configIds - } - return db.group.findAll({ where: { id: groupIds }, include: ['parents'] }).then(groups => { - groups.forEach(group => { - group.parents.forEach(parent => { - if (!parentIds.includes(parent.id)) { - parentIds.push(parent.id) - } - }) - if (group.configId !== null && !configIds.includes(group.configId)) { - configIds.push(group.configId) - } - }) - if (configIds.length !== 0 || parentIds.length === 0) { - return configIds - } else { - return checkGroupsForConfigs(parentIds).then(response => { - return response - }) + var newBlacklist = blacklist.slice(0) + + var groups = await db.group.findAll({ where: { id: groupIds }, include: ['parents', 'events'] }) + for (let i = 0; i < groups.length; i++) { + configs.push(groups[i].configId) + // groups[i].map(g => g.parents.map(p => p.id)) + for (let j = 0; j < groups[i].parents.length; j++) { + parentIds.push(groups[i].parents[j].id) } - }) + for (let j = 0; j < groups[i].events.length; j++) { + if (groups[i].events[j].important) importantEvents.push(groups[i].events[j]) + if (groups[i].events[j].group_x_event.blacklist) newBlacklist.push(groups[i].events[j].id) + else events.push(groups[i].events[j]) + } + } + + importantEvents = importantEvents.filter(e => !newBlacklist.includes(e.id)) + importantEvents = importantEvents.map(e => e.config) + importantEvents = importantEvents.filter(c => c !== null) + importantEvents = importantEvents.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos }) + if (importantEvents.length > 0) return {'ids': importantEvents, 'type': 'important'} + + var response = await fetchParentConfigs(parentIds, newBlacklist) + + if (response.type === 'important') return response + + events = events.filter(e => !newBlacklist.includes(e.id)) + events = events.map(e => e.config) + events = events.filter(c => c !== null) + events = events.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos }) + if (events.length > 0) return {'ids': events, 'type': 'event'} + + if (response.type === 'event') return response + + configs = configs.filter(function (elem, pos, arr) { return arr.indexOf(elem) === pos }) + configs = configs.filter(c => c !== null) + if (configs.length > 0) return {'ids': configs, 'type': 'config'} + + if (response.type === 'config') return response + + return {'ids': [], 'type': ''} } // create the config script from database @@ -143,6 +220,7 @@ function createIpxeScript (id) { } // create dynamic menu to load the different given configs for a client +// TODO: Hardcoded Chain Port?! function createDynamicMenu (ids) { return db.config.findAll({ where: { id: ids } }).then(configs => { var script = '' diff --git a/server/api/events.js b/server/api/events.js new file mode 100644 index 0000000..96bf15e --- /dev/null +++ b/server/api/events.js @@ -0,0 +1,86 @@ +/* global __appdir */ +var path = require('path') +var db = require(path.join(__appdir, 'lib', 'sequelize')) +var groupUtil = require(path.join(__appdir, 'lib', 'grouputil')) +var express = require('express') +const { decorateApp } = require('@awaitjs/express') +var router = decorateApp(express.Router()) + +// ############################################################################ +// ########################### GET requests ################################# + +/* + * @return: Returns event of given id. + */ +router.getAsync('/:id', async (req, res) => { + var event = await db.event.findOne({ where: { id: req.params.id }, include: ['clients', 'groups'] }) + if (event) res.send(event) + else res.status(404).end() +}) + +/* + * @return: Returns a list of all events in the database. + */ +router.getAsync('', async (req, res) => { + var events = await db.event.findAll({ include: ['clients', 'groups'] }) + if (events) res.status(200).send(events) + else res.status(404).end() +}) + +// ############################################################################ +// ########################## POST requests ################################# + +router.postAsync('/blacklist', async (req, res) => { + console.log('body: ' + req.body.groups) + if (req.body.groups) { + var blacklist = await groupUtil.getAllChildren(req.body.groups) + res.send(blacklist) + } + else res.status(404).end() +}) + +// Create, Update or Delete POST +router.postAsync(['', '/:id'], async (req, res) => { + if (req.query.delete !== undefined && req.query.delete !== 'false') { + await db.event.destroy({ where: { id: req.params.id || req.body.ids } }).then(count => { res.send({ count }) }) + } else { + var promises = [] + var promisesBlacklist = [] + var eventDb + if (req.body.config.length !== 1) req.body.config = null + if (req.body.times.length === 0) req.body.times = null + if (req.params.id > 0) { + // Update existing role + eventDb = await db.event.findOne({ where: { id: req.params.id } }) + console.log('Blacklist: ' + req.body.blacklistGroups) + console.log('Groups: ' + req.body.groups) + if (eventDb !== null) { + promises.push(eventDb.update({ name: req.body.name, description: req.body.description, config: req.body.config || null, times: req.body.times, important: req.body.important })) + promises.push(eventDb.setGroups(req.body.groups, { through: { blacklist: 0 } })) + promises.push(eventDb.setClients(req.body.clients, { through: { blacklist: 0 } })) + await Promise.all(promises) + promisesBlacklist.push(eventDb.addGroups(req.body.blacklistGroups, { through: { blacklist: 1 } })) + promisesBlacklist.push(eventDb.addClients(req.body.blacklistClients, { through: { blacklist: 1 } })) + await Promise.all(promisesBlacklist) + res.send({ id: req.params.id }) + } else { + res.status(404).end() + } + } else { + // Create new role + eventDb = await db.event.create({ name: req.body.name, description: req.body.description, config: req.body.config || null, times: req.body.times, important: req.body.important }) + promises.push(eventDb.setGroups(req.body.groups, { through: { blacklist: 0 } })) + promises.push(eventDb.setClients(req.body.clients, { through: { blacklist: 0 } })) + await Promise.all(promises) + promisesBlacklist.push(eventDb.addGroups(req.body.blacklistGroups, { through: { blacklist: 1 } })) + promisesBlacklist.push(eventDb.addClients(req.body.blacklistClients, { through: { blacklist: 1 } })) + await Promise.all(promisesBlacklist) + res.send({ id: req.body.id }) + } + } +}) + +// ############################################################################ +// ############################################################################ + +module.exports.router = router diff --git a/server/api/roles.js b/server/api/roles.js index d90d1bd..4d75bfb 100644 --- a/server/api/roles.js +++ b/server/api/roles.js @@ -59,13 +59,17 @@ router.postAsync(['', '/:id'], async (req, res) => { res.send({ id: req.body.id }) } else { // Update existing role - roleDb = await db.role.findOne({ where: { id: req.body.id } }) - promises.push(roleDb.update({ name: req.body.name, descr: req.body.descr })) - promises.push(roleDb.setPermissions(req.body.permissions)) - promises.push(roleDb.setGroups(req.body.groups, { through: { blacklist: 0 } })) - promises.push(roleDb.addGroups(req.body.blacklist, { through: { blacklist: 1 } })) - await Promise.all(promises) - res.send({ id: req.body.id }) + roleDb = await db.role.findOne({ where: { id: req.params.id } }) + if (roleDb !== null) { + promises.push(roleDb.update({ name: req.body.name, descr: req.body.descr })) + promises.push(roleDb.setPermissions(req.body.permissions)) + promises.push(roleDb.setGroups(req.body.groups, { through: { blacklist: 0 } })) + promises.push(roleDb.addGroups(req.body.blacklist, { through: { blacklist: 1 } })) + await Promise.all(promises) + res.send({ id: req.params.id }) + } else { + res.status(404).end() + } } } }) diff --git a/server/migrations/20190226151600-create-event.js b/server/migrations/20190226151600-create-event.js new file mode 100644 index 0000000..59cab86 --- /dev/null +++ b/server/migrations/20190226151600-create-event.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('events', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + description: { + type: Sequelize.STRING(2048) + }, + config: { + type: Sequelize.INTEGER + }, + times: { + type: Sequelize.STRING(4096) + }, + important: { + type: Sequelize.BOOLEAN + } + }) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('permissions') + } +} diff --git a/server/migrations/20190226152330-create-client_x_event.js b/server/migrations/20190226152330-create-client_x_event.js new file mode 100644 index 0000000..1d6952e --- /dev/null +++ b/server/migrations/20190226152330-create-client_x_event.js @@ -0,0 +1,33 @@ +'use strict' +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('client_x_event', { + clientId: { + primaryKey: true, + allowNull: false, + type: Sequelize.INTEGER, + onDelete: 'cascade', + references: { + model: 'clients', + key: 'id' + } + }, + eventId: { + primaryKey: true, + allowNull: false, + type: Sequelize.INTEGER, + onDelete: 'cascade', + references: { + model: 'events', + key: 'id' + } + }, + blacklist: { + type: Sequelize.BOOLEAN + } + }) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('client_x_event') + } +} diff --git a/server/migrations/20190226152400-create-group_x_event.js b/server/migrations/20190226152400-create-group_x_event.js new file mode 100644 index 0000000..8c438de --- /dev/null +++ b/server/migrations/20190226152400-create-group_x_event.js @@ -0,0 +1,33 @@ +'use strict' +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('group_x_event', { + groupId: { + primaryKey: true, + allowNull: false, + type: Sequelize.INTEGER, + onDelete: 'cascade', + references: { + model: 'groups', + key: 'id' + } + }, + eventId: { + primaryKey: true, + allowNull: false, + type: Sequelize.INTEGER, + onDelete: 'cascade', + references: { + model: 'events', + key: 'id' + } + }, + blacklist: { + type: Sequelize.BOOLEAN + } + }) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('group_x_event') + } +} diff --git a/server/models/client.js b/server/models/client.js index 6483573..502f668 100644 --- a/server/models/client.js +++ b/server/models/client.js @@ -20,6 +20,8 @@ module.exports = (sequelize, DataTypes) => { var GroupXClient = sequelize.define('group_x_client', {}, { timestamps: false, freezeTableName: true }) client.belongsToMany(models.group, { as: 'groups', through: GroupXClient, foreignKey: 'clientId', otherKey: 'groupId' }) client.belongsTo(models.config) + var ClientXEvent = sequelize.define('client_x_event', { blacklist: DataTypes.BOOLEAN }, { timestamps: false, freezeTableName: true }) + client.belongsToMany(models.event, { as: 'events', through: ClientXEvent, foreignKey: 'clientId', otherKey: 'eventId' }) } return client } diff --git a/server/models/event.js b/server/models/event.js new file mode 100644 index 0000000..11a35e7 --- /dev/null +++ b/server/models/event.js @@ -0,0 +1,26 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + var event = sequelize.define('event', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: DataTypes.INTEGER + }, + name: DataTypes.STRING, + description: DataTypes.STRING(2048), + config: DataTypes.INTEGER, + times: DataTypes.STRING(4096), + important: DataTypes.BOOLEAN + }, { + timestamps: false + }) + event.associate = function (models) { + var ClientXEvent = sequelize.define('client_x_event', { blacklist: DataTypes.BOOLEAN }, { timestamps: false, freezeTableName: true }) + event.belongsToMany(models.client, { as: 'clients', through: ClientXEvent, foreignKey: 'eventId', otherKey: 'clientId' }) + + var GroupXEvent = sequelize.define('group_x_event', { blacklist: DataTypes.BOOLEAN }, { timestamps: false, freezeTableName: true }) + event.belongsToMany(models.group, { as: 'groups', through: GroupXEvent, foreignKey: 'eventId', otherKey: 'groupId' }) + } + return event +} diff --git a/server/models/group.js b/server/models/group.js index 30fc78d..6ff9034 100644 --- a/server/models/group.js +++ b/server/models/group.js @@ -15,11 +15,14 @@ module.exports = (sequelize, DataTypes) => { group.associate = function (models) { var GroupXGroup = sequelize.define('group_x_group', {}, { timestamps: false, freezeTableName: true }) var GroupXClient = sequelize.define('group_x_client', {}, { timestamps: false, freezeTableName: true }) + var GroupXEvent = sequelize.define('group_x_event', { blacklist: DataTypes.BOOLEAN }, { timestamps: false, freezeTableName: true }) group.belongsToMany(group, { as: 'parents', through: GroupXGroup, foreignKey: 'childId', otherKey: 'parentId' }) group.belongsToMany(group, { as: 'subgroups', through: GroupXGroup, foreignKey: 'parentId', otherKey: 'childId' }) group.belongsToMany(models.client, { as: 'clients', through: GroupXClient, foreignKey: 'groupId', otherKey: 'clientId' }) + group.belongsToMany(models.event, { as: 'events', through: GroupXEvent, foreignKey: 'groupId', otherKey: 'eventId' }) group.belongsTo(models.config) group.hasMany(models.iprange, { as: 'ipranges' }) + } return group } -- cgit v1.2.3-55-g7522