summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJannik Schönartz2019-03-07 20:20:25 +0100
committerJannik Schönartz2019-03-07 20:20:25 +0100
commit0999302d99156200ff174d5ec56d6831c8afd332 (patch)
tree56dfcf175c3d5b15dd44eb8d156e4448caaa5455
parent[server/ipranges] Forgot to commit the lib. ¯\_(ツ)_/¯ (diff)
downloadbas-0999302d99156200ff174d5ec56d6831c8afd332.tar.gz
bas-0999302d99156200ff174d5ec56d6831c8afd332.tar.xz
bas-0999302d99156200ff174d5ec56d6831c8afd332.zip
[server] New clients are automaticly added to the groups of the fitting subranges
Add conflict models Sequelize string operators depricated fix IPv4 is now saved as decimal in the database Add host to config instead of hardcoding Rename ip.js lib to iphelper.js
-rw-r--r--server/api/groups.js14
-rw-r--r--server/api/ipxe.js2
-rw-r--r--server/api/registration.js110
-rw-r--r--server/api/users.js2
-rw-r--r--server/config/config.template.json1
-rw-r--r--server/lib/external-backends/backendhelper.js21
-rw-r--r--server/lib/external-backends/backends/idoit-backend.js4
-rw-r--r--server/lib/ip.js21
-rw-r--r--server/lib/iphelper.js62
-rw-r--r--server/lib/permissions/index.js2
-rw-r--r--server/lib/permissions/permissionutil.js2
-rw-r--r--server/lib/sequelize.js8
-rw-r--r--server/migrations/20190307051433-create-conflict.js19
-rw-r--r--server/migrations/20190307051633-create-conflict_x_object.js25
-rw-r--r--server/models/conflict.js23
15 files changed, 213 insertions, 103 deletions
diff --git a/server/api/groups.js b/server/api/groups.js
index 6dbddc7..9a17c92 100644
--- a/server/api/groups.js
+++ b/server/api/groups.js
@@ -2,7 +2,7 @@
var path = require('path')
var db = require(path.join(__appdir, 'lib', 'sequelize'))
var groupUtil = require(path.join(__appdir, 'lib', 'grouputil'))
-const ip = require(path.join(__appdir, 'lib', 'ip'))
+const ipHelper = require(path.join(__appdir, 'lib', 'iphelper'))
var express = require('express')
const { decorateApp } = require('@awaitjs/express')
var router = decorateApp(express.Router())
@@ -22,8 +22,8 @@ router.getAsync('/:id', async (req, res) => {
if (group) {
// Convert ipranges in readable strings.
group.ipranges.forEach(iprange => {
- iprange.startIp = ip.toString(iprange.startIp)
- iprange.endIp = ip.toString(iprange.endIp)
+ iprange.startIp = ipHelper.toIPv4(iprange.startIp)
+ iprange.endIp = ipHelper.toIPv4(iprange.endIp)
})
if (all) res.status(200).send({ ...group.get({ plain: true }), ...await groupUtil.getAllChildren([group]) })
else res.status(200).send(group)
@@ -68,12 +68,10 @@ router.postAsync(['', '/:id'], async (req, res) => {
}
// Update existing ipranges and create the new ones
req.body.ipranges.forEach(iprange => {
- if (!iprange.startIp || !iprange.endIp) return
-
// Convert valid ip addresses to integer values.
- if (!ip.isIpv4(iprange.startIp) || !ip.isIpv4(iprange.endIp)) return
- iprange.startIp = ip.toInt(iprange.startIp)
- iprange.endIp = ip.toInt(iprange.endIp)
+ 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))
diff --git a/server/api/ipxe.js b/server/api/ipxe.js
index 3a6270f..6367246 100644
--- a/server/api/ipxe.js
+++ b/server/api/ipxe.js
@@ -31,7 +31,7 @@ router.get('/:version/log', async (req, res) => {
res.setHeader('content-type', 'text/plain')
const filepath = path.join(__appdir, 'ipxe', 'log_' + req.params.version + '.txt')
res.sendFile(filepath, err => {
- if (err) {
+ if (err) {
res.end()
}
})
diff --git a/server/api/registration.js b/server/api/registration.js
index 2da1431..2a26c4f 100644
--- a/server/api/registration.js
+++ b/server/api/registration.js
@@ -1,40 +1,18 @@
/* global __appdir */
var path = require('path')
var express = require('express')
-var router = express.Router()
-var noAuthRouter = express.Router()
+const { decorateApp } = require('@awaitjs/express')
+var router = decorateApp(express.Router())
+var noAuthRouter = decorateApp(express.Router())
var db = require(path.join(__appdir, 'lib', 'sequelize'))
-const ExternalBackends = require(path.join(__appdir, 'lib', 'external-backends'))
const backendHelper = require(path.join(__appdir, 'lib', 'external-backends', 'backendhelper'))
+const ipHelper = require(path.join(__appdir, 'lib', 'iphelper'))
+const config = require(path.join(__appdir, 'config', 'config'))
+const url = config.https.host + ':' + config.https.port
// GET requests.
/*
- * TODO: CURRENTLY TEST FUNCTION
- */
-noAuthRouter.get('/', (req, res) => {
- // backendHelper.deleteClients().then(r => {
- // res.send(r)
- // })
-
- /*
- 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)
- })
- }) */
- db.backend.findOne({ where: { id: 3 } }).then(result => {
- const b = new ExternalBackends()
- const instance = b.getInstance(result.type)
- instance.uploadTpm(result.credentials, 99696, 'I-123-d12', null).then(result => {
- res.status(200).send(result)
- })
- })
-})
-
-/*
* Returns all registration hooks sorted by sortValue.
*
* @return: List of registration hooks
@@ -50,7 +28,7 @@ router.get('/hooks', (req, res) => {
/*
* Reorders the registration hooks based on an array of hook ids.
*/
-router.post('/hookorder', async (req, res) => {
+router.postAsync('/hookorder', async (req, res) => {
var idSortvalueMap = {}
req.body.ids.forEach((id, index) => {
idSortvalueMap[id] = index
@@ -64,7 +42,7 @@ router.post('/hookorder', async (req, res) => {
res.end()
})
-router.post(['/hooks', '/hooks/:id'], async (req, res) => {
+router.postAsync(['/hooks', '/hooks/:id'], async (req, res) => {
var item = {
name: req.body.name,
description: req.body.description,
@@ -130,7 +108,7 @@ noAuthRouter.post('/group', (req, res) => {
/*
* Adds the client to the database and set parents if a parent was selected. Calls addClient for all external-backends.
*/
-noAuthRouter.post('/add', (req, res) => {
+noAuthRouter.postAsync('/add', async (req, res) => {
const feedback = req.body.feedback
const mac = req.body.mac
const uuid = req.body.uuid
@@ -138,40 +116,44 @@ noAuthRouter.post('/add', (req, res) => {
var name = req.body.name
const parentId = req.body.id
const purpose = req.body.purpose
+ let parentIds = []
if (!name) name = 'Client_' + uuid
- db.client.findOne({ where: { uuid: uuid } }).then(client => {
- if (client) res.send(`#!ipxe\nchain https://bas.intra.uni-freiburg.de/api/configloader/\${uuid}`)
- else {
- var groupids = []
- if (parentId) groupids = [parentId]
- getNextHookScript(groupids).then(resId => {
- db.client.create({ name: name, description: 'Client', ip: ip, mac: mac, uuid: uuid, registrationState: resId }).then(newClient => {
- if (parentId) {
- newClient.addGroup(parentId)
- }
-
- // Add the client to the backends.
- var c = { title: name, uuid: uuid, network: { mac: mac, ip: ip } }
- if (parentId) c.parentId = parentId
- if (purpose) c.purpose = purpose
- backendHelper.addClient(c).then(result => {
- if (feedback) res.send(result)
- result.forEach(response => {
- // If the object was created we need to make the objectid / external id mapping.
- if (response.success) {
- db.backend.findOne({ where: { id: response.backendId }, include: ['mappedClients'] }).then(backend => {
- backend.addMappedClients(newClient, { through: { externalId: response.id, externalType: response.type } })
- })
- }
- })
- })
- if (!feedback) res.send(`#!ipxe\nchain https://bas.intra.uni-freiburg.de/api/configloader/\${uuid}`)
- })
- })
+ const client = await db.client.findOne({ where: { uuid: uuid } })
+
+ if (client) return res.send(`#!ipxe\nchain https://` + url + `/api/configloader/\${uuid}`)
+ // Else
+ var groupids = []
+ if (parentId) groupids = [parentId]
+ const resId = await getNextHookScript(groupids)
+ const newClient = await db.client.create({ name: name, description: 'Client', ip: ip, mac: mac, uuid: uuid, registrationState: resId })
+ if (parentId) {
+ newClient.addGroup(parentId)
+ parentIds.push(parentId)
+ } else {
+ // Filtered list with all group we will add the client
+ parentIds = await ipHelper.getGroups(ip)
+ parentIds.forEach(pid => { newClient.addGroup(pid) })
+ // TODO: check if there are multiple groups which are mapped to a backend if so --> merge conflict
+ }
+
+ // Add the client to the backends.
+ var c = { title: name, uuid: uuid, network: { mac: mac, ip: ip } }
+ if (parentIds.length > 0) c.parents = parentIds
+ // if (parentId) c.parentId = parentId
+ if (purpose) c.purpose = purpose
+ const result = await backendHelper.addClient(c)
+ if (feedback) res.send(result)
+
+ for (let response of result) {
+ // If the object was created we need to make the objectid / external id mapping.
+ if (response.success) {
+ const backend = await db.backend.findOne({ where: { id: response.backendId }, include: ['mappedClients'] })
+ backend.addMappedClients(newClient, { through: { externalId: response.id, externalType: response.type } })
}
- })
+ }
+ if (!feedback) res.send(`#!ipxe\nchain https://` + url + `/api/configloader/\${uuid}`)
})
noAuthRouter.post('/:uuid/update', (req, res) => {
@@ -345,8 +327,8 @@ function getNextHookScript (groupids, sortvalue) {
// Gets the list of all groupids inclusive the recursive parents.
return getRecursiveParents(groupids).then(gids => {
// Get the list of all hooks where the parent is null or those who fullfill the group dependency.
- var options = { where: { '$groups.id$': { $or: [null, gids] } }, include: ['groups'], order: [['sortvalue', 'ASC']] }
- if (sortvalue !== undefined) options.where.sortvalue = { $gt: sortvalue }
+ var options = { where: { '$groups.id$': { [db.Op.or]: [null, gids] } }, include: ['groups'], order: [['sortvalue', 'ASC']] }
+ if (sortvalue !== undefined) options.where.sortvalue = { [db.Op.gt]: sortvalue }
return db.registrationhook.findAll(options).then(result => {
var resID = null
if (result.length >= 1) resID = result[0].id
@@ -386,7 +368,7 @@ function getRecursiveParents (groupIds) {
* Used by the manual registration.
*/
function buildIpxeMenu (id, name, groups, parents) {
- var basUrl = 'https://bas.intra.uni-freiburg.de'
+ var basUrl = 'https://' + url
var script = '#!ipxe\r\n'
// script = script.concat(`console --picture \${img} --x 800 --y 600 || shell\r\n`)
diff --git a/server/api/users.js b/server/api/users.js
index c4d9a2f..744ffc6 100644
--- a/server/api/users.js
+++ b/server/api/users.js
@@ -89,7 +89,7 @@ router.postAsync(['/', '/:id'], async (req, res) => {
if (!authentication.validateUsername(username)) return res.status(400).send({ error: 'INVALID_USERNAME', message: 'Username does not fullfill the requirements. (No whitespaces)' })
// Check if the username already exists.
- let userDb = await db.user.findOne({ where: { username: username, id: { $not: id } } })
+ let userDb = await db.user.findOne({ where: { username: username, id: { [db.Op.not]: id } } })
if (userDb) return res.status(400).send({ error: 'USER_ALREADY_EXISTS', message: 'The provided username already exists.' })
userinfo.username = username
}
diff --git a/server/config/config.template.json b/server/config/config.template.json
index 75e0c4f..a79376e 100644
--- a/server/config/config.template.json
+++ b/server/config/config.template.json
@@ -1,5 +1,6 @@
{
"https": {
+ "host": "<HOSTNAME>",
"port": "<HTTPS_PORT>"
}
}
diff --git a/server/lib/external-backends/backendhelper.js b/server/lib/external-backends/backendhelper.js
index dc4324c..752ccf7 100644
--- a/server/lib/external-backends/backendhelper.js
+++ b/server/lib/external-backends/backendhelper.js
@@ -28,9 +28,24 @@ module.exports = {
}
// Convert the parent group id to the external backend parentId.
- if (client.parentId) {
- var element = backend.mappedGroups.find(x => x.id === parseInt(client.parentId))
- if (element) tmpClient['parentId'] = element.backend_x_group.externalId
+ // if (client.parentId) {
+ if (client.parents) {
+ var elements = backend.mappedGroups.filter(x => client.parents.includes(x.id))
+ if (elements.length > 1) {
+ // TODO ADD MERGE CONFLICT
+ const conflict = await db.conflict.create({ description: 'Multiple parents found' })
+
+ // Add backend to the conflict.
+ conflict.createObject({ objectType: 'BACKEND', objectId: backend.id })
+
+ // Add the groups to the conflict.
+ for (let element of elements) {
+ conflict.createObject({ objectType: 'GROUP', objectId: element.id })
+ }
+ } else if (elements.length === 1) tmpClient['parentId'] = elements[0].backend_x_group.externalId
+
+ // var element = backend.mappedGroups.find(x => x.id === parseInt(client.parentId))
+ // if (element) tmpClient['parentId'] = element.backend_x_group.externalId
}
var addClient = await instance.addClient(backend.credentials, tmpClient)
diff --git a/server/lib/external-backends/backends/idoit-backend.js b/server/lib/external-backends/backends/idoit-backend.js
index 81a955b..43ab434 100644
--- a/server/lib/external-backends/backends/idoit-backend.js
+++ b/server/lib/external-backends/backends/idoit-backend.js
@@ -659,9 +659,9 @@ class IdoitBackend extends ExternalBackends {
}
}
try {
- var requestUpdate = await axios.post(c.url, bodies, config)
+ var requestUpdate = await axios.post(c.url, bodies, config)
} catch (error) {
- console.log(error)
+ console.log(error)
}
// 10 is the idoit object id for clients.
var result = {
diff --git a/server/lib/ip.js b/server/lib/ip.js
deleted file mode 100644
index 87c2eb5..0000000
--- a/server/lib/ip.js
+++ /dev/null
@@ -1,21 +0,0 @@
-module.exports = { toInt, toString, isIpv4 }
-
-// Takes an ip address and converts it to an integer.
-function toInt(ipString) {
- const ipArray = ipString.split('.')
- if (ipArray.length !== 4) return false
-
- let result = parseInt(ipArray[0]) * 256**3 + parseInt(ipArray[1]) * 256**2 + parseInt(ipArray[2]) * 256 + parseInt(ipArray[3])
- return result
-}
-
-// Converts an integer value in a human readable typical ip address.
-function toString(ipInt) {
- return [(ipInt>>24)&0xff, (ipInt>>16)&0xff, (ipInt>>8)&0xff, ipInt&0xff].join('.');
-}
-
-// Sanity check for an ipv4.
-function isIpv4(ipString) {
- const re = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
- return re.test(ipString)
-} \ No newline at end of file
diff --git a/server/lib/iphelper.js b/server/lib/iphelper.js
new file mode 100644
index 0000000..5c40313
--- /dev/null
+++ b/server/lib/iphelper.js
@@ -0,0 +1,62 @@
+/* global __appdir */
+var path = require('path')
+var db = require(path.join(__appdir, 'lib', 'sequelize'))
+module.exports = { toDecimal, toString, toIPv4, getGroups }
+
+// Finds the groups where the ip fits best in the subnets.
+async function getGroups (ipString) {
+ const ipInt = toDecimal(ipString)
+
+ let fittingIpRanges = await db.iprange.findAll({ where: { startIp: { [db.Op.lte]: ipInt }, endIp: { [db.Op.gte]: ipInt } } })
+ fittingIpRanges = fittingIpRanges.map(x => x.groupId)
+
+ // List with all groups mapped so that groups[id] = [parents]
+ let groups = await db.group.findAll({ include: ['parents'] })
+ let parentMap = {}
+ groups.forEach(group => {
+ parentMap[group.id] = group.parents
+ })
+
+ // foreach fittingIpRanges go through groups and eliminate parents in the fittingIpRanges
+ let result = fittingIpRanges
+ fittingIpRanges.forEach(groupid => {
+ result = eliminateParents(groupid, parentMap, result)
+ })
+
+ return result
+}
+
+function eliminateParents (groupId, groupMap, eliminateArray, alreadyChecked = []) {
+ // Check for cycles and return if there is one.
+ if (alreadyChecked.includes(groupId)) return eliminateArray
+ alreadyChecked.push(groupId)
+
+ const parents = groupMap[groupId]
+ parents.forEach(parent => {
+ const index = eliminateArray.indexOf(parent.id)
+ if (index !== -1) eliminateArray.splice(index, 1)
+ if (groupMap[parent.id].length > 0) eliminateArray = eliminateParents(parent.id, groupMap, eliminateArray, alreadyChecked)
+ })
+ return eliminateArray
+}
+
+// Takes an ip address and converts it to an integer.
+function toDecimal (ipString) {
+ if (isIPv4(ipString)) return false
+ const ipArray = ipString.split('.')
+ if (ipArray.length !== 4) return false
+
+ let result = parseInt(ipArray[0]) * 256 ** 3 + parseInt(ipArray[1]) * 256 ** 2 + parseInt(ipArray[2]) * 256 + parseInt(ipArray[3])
+ return result
+}
+
+// Converts an integer value in a human readable typical ip address.
+function toIPv4 (ipInt) {
+ return [(ipInt >> 24) & 0xff, (ipInt >> 16) & 0xff, (ipInt >> 8) & 0xff, ipInt & 0xff].join('.')
+}
+
+// Sanity check for an ipv4.
+function isIPv4 (ipString) {
+ const re = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
+ return re.test(ipString)
+}
diff --git a/server/lib/permissions/index.js b/server/lib/permissions/index.js
index 79ce6b1..a0af9d4 100644
--- a/server/lib/permissions/index.js
+++ b/server/lib/permissions/index.js
@@ -19,7 +19,7 @@ function updatePermissionDatabase () {
// Delete entries from Database which are not in the permission.json
db.permission.destroy(
- { where: { $not: { name: permissionNames } } }
+ { where: { [db.Op.not]: { name: permissionNames } } }
)
}
diff --git a/server/lib/permissions/permissionutil.js b/server/lib/permissions/permissionutil.js
index 532af6c..8e91af9 100644
--- a/server/lib/permissions/permissionutil.js
+++ b/server/lib/permissions/permissionutil.js
@@ -31,7 +31,7 @@ async function hasPermission (userid, permissionName) {
var user
if (permission[1] === '*') {
user = await db.user.findOne({
- where: { id: userid, '$roles.permissions.name$': { $like: permission[0] + '%' } },
+ where: { id: userid, '$roles.permissions.name$': { [db.Op.like]: permission[0] + '%' } },
include: [{ as: 'roles', model: db.role, include: ['permissions'] }]
})
} else {
diff --git a/server/lib/sequelize.js b/server/lib/sequelize.js
index 3624723..b6b99dd 100644
--- a/server/lib/sequelize.js
+++ b/server/lib/sequelize.js
@@ -7,7 +7,12 @@ var Sequelize = require('sequelize')
var config = require(path.join(__appdir, 'config', 'database'))
var db = {}
-var sequelize = new Sequelize(config.database, config.username, config.password, config)
+var sequelize = new Sequelize(config.database, config.username, config.password, {
+ host: config.host,
+ dialect: config.dialect,
+ operatorsAliases: false
+})
+const Op = Sequelize.Op
fs
.readdirSync(path.join(__dirname, '/../models'))
@@ -24,5 +29,6 @@ Object.keys(db).forEach(modelName => {
db.sequelize = sequelize
db.Sequelize = Sequelize
+db.Op = Op
module.exports = db
diff --git a/server/migrations/20190307051433-create-conflict.js b/server/migrations/20190307051433-create-conflict.js
new file mode 100644
index 0000000..c5630dc
--- /dev/null
+++ b/server/migrations/20190307051433-create-conflict.js
@@ -0,0 +1,19 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('conflicts', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ description: {
+ type: Sequelize.STRING
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('conflicts')
+ }
+}
diff --git a/server/migrations/20190307051633-create-conflict_x_object.js b/server/migrations/20190307051633-create-conflict_x_object.js
new file mode 100644
index 0000000..bead43f
--- /dev/null
+++ b/server/migrations/20190307051633-create-conflict_x_object.js
@@ -0,0 +1,25 @@
+'use strict'
+module.exports = {
+ up: (queryInterface, Sequelize) => {
+ return queryInterface.createTable('conflict_x_object', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: Sequelize.INTEGER
+ },
+ conflictId: {
+ type: Sequelize.INTEGER
+ },
+ objectType: {
+ type: Sequelize.STRING
+ },
+ objectId: {
+ type: Sequelize.INTEGER
+ }
+ })
+ },
+ down: (queryInterface, Sequelize) => {
+ return queryInterface.dropTable('conflict_x_object')
+ }
+}
diff --git a/server/models/conflict.js b/server/models/conflict.js
new file mode 100644
index 0000000..5769710
--- /dev/null
+++ b/server/models/conflict.js
@@ -0,0 +1,23 @@
+'use strict'
+module.exports = (sequelize, DataTypes) => {
+ var conflict = sequelize.define('conflict', {
+ id: {
+ allowNull: false,
+ autoIncrement: true,
+ primaryKey: true,
+ type: DataTypes.INTEGER
+ },
+ description: DataTypes.STRING(2048)
+ }, {
+ timestamps: false
+ })
+ conflict.associate = function (models) {
+ var ConflictXObject = sequelize.define('conflict_x_object', {
+ objectType: DataTypes.STRING,
+ objectId: DataTypes.INTEGER
+ }, { timestamps: false, freezeTableName: true })
+ conflict.hasMany(ConflictXObject, { as: 'objects' })
+ }
+
+ return conflict
+}