From dc25bd7de72aa574767876341e5792733c2ee0e0 Mon Sep 17 00:00:00 2001 From: Jannik Schönartz Date: Sun, 1 Dec 2019 15:03:55 +0000 Subject: [server/log] Add logging to all modules Logging with snapshots: Client: create / edit / delete / added to group / removed from group Group: create / edit / delete / added to group / removed from group Logging without snapshot: Wake-on-lan: wakup Ipxe-Builder: build / clear / cancel / script save IP-Ranges: create / edit / delete Logging: with info in description: User: create / edit / delete / grant role / revoke role Event: create / edit / delete Permission-Manager-Role: create / edit / delete Registration-Hook: create / delete / edit / change order Ipxe Configuration: create / delete / edit Backend: create / edit / delete --- server/api/users.js | 251 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 233 insertions(+), 18 deletions(-) (limited to 'server/api/users.js') diff --git a/server/api/users.js b/server/api/users.js index 33ad3d3..d69c776 100644 --- a/server/api/users.js +++ b/server/api/users.js @@ -5,6 +5,7 @@ var express = require('express') const { decorateApp } = require('@awaitjs/express') var router = decorateApp(express.Router()) var authentication = require(path.join(__appdir, 'lib', 'authentication')) +const log = require(path.join(__appdir, 'lib', 'log')) // ############################################################################ // ########################### GET requests ################################# @@ -39,12 +40,44 @@ router.postAsync('/roles', async (req, res) => { const userIds = req.body.users const roleIds = req.body.roles - const users = await db.user.findAll({ where: { id: userIds } }) + const users = await db.user.findAll({ where: { id: userIds }, include: ['roles'] }) + const userDb = await db.user.findOne({ where: { id: req.user.id } }) if (users) { if (req.query.delete !== undefined && req.query.delete !== 'false') { - users.forEach(user => { user.removeRoles(roleIds) }) + for (let index in users) { + const user = users[index] + const roles = user.roles + const count = await user.removeRoles(roleIds) + let roleString = 'role' + if (count > 1) roleString += 's' + log({ + category: 'USER_REVOKE_ROLE', + description: '[' + user.id + '] ' + user.name + ': Successfully removed ' + count + ' ' + roleString + '.\n' + + 'ID: ' + user.id + '\n' + + 'Name: ' + user.name + '\n' + + 'Removed Roles: ' + roleIds.filter(y => { return roles.map(x => x.id).includes(y) }), + userDb, + userId: req.user.id + }) + } } else { - users.forEach(user => { user.addRoles(roleIds) }) + for (let index in users) { + const user = users[index] + const count = await user.addRoles(roleIds) + if (count.length > 0) { + let roleString = 'role' + if (count[0].length > 1) roleString += 's' + log({ + category: 'USER_GRANT_ROLE', + description: '[' + user.id + '] ' + user.name + ': Successfully added ' + count[0].length + ' ' + roleString + '.\n' + + 'ID: ' + user.id + '\n' + + 'Name: ' + user.name + '\n' + + 'Added Roles: ' + count[0].map(x => x.roleId), + userDb, + userId: req.user.id + }) + } + } } res.status(200).end() } else { @@ -59,24 +92,116 @@ router.postAsync(['/', '/:id'], async (req, res) => { // TODO: Check for permission to delete / create / update user } + // Delete request if (req.query.delete !== undefined && req.query.delete !== 'false') { - const count = await db.user.destroy({ where: { id: body.ids } }) - return res.send({ count }) + const user = await db.user.findOne({ where: { id: req.user.id } }) + + // Only need to log batch request if there is more than one user to delete. + if (req.body.ids.length > 1) { + await log({ + category: 'USER_BATCH_DELETE', + description: 'User batch deletion of ' + req.body.ids.length + ' users initiated by user.', + user, + userId: req.user.id + }) + } + + let deletionCounter = 0 + let selfdeletion = false + + // Delete every user on its own, to get a better log + for (let index in req.body.ids) { + // We can't set the userId in the log if we delete ourselfs. + if (req.body.ids[index] === req.user.id) selfdeletion = true + const userDb = await db.user.findOne({ where: { id: req.body.ids[index] } }) + const count = await db.user.destroy({ where: { id: req.body.ids[index] } }) + if (count !== 1) { + await log({ + category: 'ERROR_USER_DELETE', + description: '[' + userDb.id + '] ' + userDb.username + ': User could not be deleted.\n' + + 'ID: ' + userDb.id + '\n' + + 'Username: ' + userDb.username + '\n' + + 'Name: ' + userDb.name + '\n' + + 'E-Mail: ' + userDb.email + '\n', + user, + userId: selfdeletion ? undefined : req.user.id + }) + } else { + await log({ + category: 'USER_DELETE', + description: '[' + userDb.id + '] ' + userDb.username + ': User successfully deleted.\n' + + 'ID: ' + userDb.id + '\n' + + 'Username: ' + userDb.username + '\n' + + 'Name: ' + userDb.name + '\n' + + 'E-Mail: ' + userDb.email + '\n', + user, + userId: selfdeletion ? undefined : req.user.id + }) + deletionCounter++ + } + } + if (req.body.ids.length > 1) { + log({ + category: 'USER_BATCH_DELETE', + description: deletionCounter + '/' + req.body.ids.length + ' users successfully deleted.', + user, + userId: selfdeletion ? undefined : req.user.id + }) + } + + return res.send({ deletionCounter }) } if (req.params.id === undefined) { const result = await authentication.signup(body) const code = result.code + const user = await db.user.findOne({ where: { id: result.id } }) + + // Create a log entry for the user creation + if (code === 200) { + log({ + category: 'USER_CREATE', + description: '[' + user.id + '] ' + user.username + ': User successfully created.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + } else { + log({ + category: 'ERROR_USER_CREATE', + description: '[' + code + '][' + result.error + '] ' + result.message + '\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + } + delete result.code return res.status(code).send(result) } else { const id = req.params.id === 'current' ? req.user.id : req.params.id + let user = await db.user.findOne({ where: { id: id } }) let email = req.body.email - if (!authentication.validateEmail(req.body.email)) return res.status(500).send({ error: 'EMAIL_INVALID', message: 'The provided email is invalid.' }) - - let user - user = await db.user.findOne({ where: { id: id } }) + if (!authentication.validateEmail(req.body.email)) { + log({ + category: 'ERROR_USER_EDIT', + description: '[' + user.id + '] ' + user.username + ': User could not be updated. The E-Mail is invalid.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + return res.status(500).send({ + error: 'EMAIL_INVALID', + message: 'The provided email is invalid.' + }) + } if (user) { let userinfo = { @@ -87,20 +212,74 @@ router.postAsync(['/', '/:id'], async (req, res) => { // Check if the username is set and if it's valid. let username = body.username if (username && req.params.id !== 'current') { - if (!authentication.validateUsername(username)) return res.status(400).send({ error: 'INVALID_USERNAME', message: 'Username does not fullfill the requirements. (No whitespaces)' }) + if (!authentication.validateUsername(username)) { + log({ + category: 'ERROR_USER_EDIT', + description: '[' + user.id + '] ' + user.username + ': User could not be updated. The username does not fullfull the requirements. (No whitespaces)\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + 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: { [db.Op.not]: id } } }) - if (userDb) return res.status(400).send({ error: 'USER_ALREADY_EXISTS', message: 'The provided username already exists.' }) + if (userDb) { + log({ + category: 'ERROR_USER_EDIT', + description: '[' + user.id + '] ' + user.username + ': User could not be updated. The username already exists.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + return res.status(400).send({ error: 'USER_ALREADY_EXISTS', message: 'The provided username already exists.' }) + } userinfo.username = username } // Update the user. await user.update(userinfo) + log({ + category: 'USER_EDIT', + description: '[' + user.id + '] ' + user.username + ': User successfully updated.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) if (body.password) { const result = await authentication.changePassword(id, body.password, body.passwordCurrent) const code = result.code delete result.code + + if (code === 200) { + log({ + category: 'USER_EDIT_PASSWORD', + description: '[' + user.id + '] ' + user.username + ': Password successfully updated.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + } else { + log({ + category: 'ERROR_USER_EDIT_PASSWORD', + description: '[' + user.id + '] ' + user.username + ': Password could not be updated. Code ' + code + '\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + userId: req.user.id + }) + } + res.status(code).send(result) } } @@ -118,22 +297,58 @@ router.postAsync('/:id/password', async (req, res) => { const result = await authentication.changePassword(id, body.password, body.passwordCurrent) const code = result.code delete result.code + + if (code === 200) { + log({ + category: 'USER_EDIT_PASSWORD', + description: '[' + id + '] Password changed.', + userId: req.user.id + }) + } else { + log({ + category: 'ERROR_USER_EDIT_PASSWORD', + description: '[' + id + '] Password could not be changed. Code ' + code, + userId: req.user.id + }) + } res.status(code).send(result) } else res.status(400).send({ error: 'PASSWORD_MISSING', message: 'This service requires the current and the new password.' }) }) // Function for deleting a single user -router.delete('/:id/', (req, res) => { - // Check if the user has the permission for chaning those userdata. Else return. +router.deleteAsync('/:id/', async (req, res) => { if (req.params.id !== 'current') { - return res.status(500).end() + // Check if the user has the permission for changing those userdata. Else return. + // return res.status(500).end() } const id = req.params.id === 'current' ? req.user.id : req.params.id - + const user = await db.user.findOne({ where: { id: id } }) // Every user can delete his own account. - db.user.destroy({ where: { id } }).then(() => { - res.status(200).end() - }) + const count = await db.user.destroy({ where: { id } }) + if (count !== 1) { + log({ + category: 'ERROR_USER_DELETE', + description: '[' + user.id + '] ' + user.username + ': User could not be deleted.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + user, + userId: req.user.id + }) + } else { + log({ + category: 'USER_DELETE', + description: '[' + user.id + '] ' + user.username + ': User successfully deleted.\n' + + 'ID: ' + user.id + '\n' + + 'Username: ' + user.username + '\n' + + 'Name: ' + user.name + '\n' + + 'E-Mail: ' + user.email + '\n', + user, + userId: req.params.id === 'current' ? undefined : req.user.id + }) + } + res.status(200).end() }) // ############################################################################ -- cgit v1.2.3-55-g7522