/* global __appdir */ var path = require('path') var db = require(path.join(__appdir, 'lib', 'sequelize')) 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')) // Permission check middleware router.all(['', '/:x'], async (req, res, next) => { // User is allowed to edit his own information even without any permissions. let currentInfo = false if (req.params.x && req.params.x === 'current') currentInfo = true switch (req.method) { case 'GET': if (!await req.user.hasPermission('users.view') && !currentInfo) return res.status(403).send({ error: 'Missing permission', permission: 'users.view' }) break case 'POST': case 'DELETE': if (!await req.user.hasPermission('users.edit') && !currentInfo) return res.status(403).send({ error: 'Missing permission', permission: 'users.edit' }) break default: return res.status(400).send() } next() }) // ############################################################################ // ########################### GET requests ################################# /* * @return: Returns a list of all users in the database and their given roles. */ router.getAsync('', async (req, res) => { const users = await db.user.findAll({ attributes: { exclude: ['password'] }, include: ['roles'], order: [['name', 'ASC']] }) res.status(200).send(users) }) /* * @return: Returns information about a specific user. */ router.getAsync('/:id', async (req, res) => { const id = req.params.id === 'current' ? req.user.id : req.params.id const user = await db.user.findOne({ where: { id }, attributes: { exclude: ['password'] } }) if (user) { res.status(200).send(user) } else { res.status(404).end() } }) // ############################################################################ // ########################## POST requests ################################# // Post request for adding roles to users. 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 }, include: ['roles'] }) const userDb = await db.user.findOne({ where: { id: req.user.id } }) if (users) { if (req.query.delete !== undefined && req.query.delete !== 'false') { 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 + '] ' + 'Successfully removed ' + count + ' ' + roleString + ' from' + user.name + '.\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 { 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 { res.status(404).end() } }) // Post request for creating / editing new user accounts. router.postAsync(['/', '/:id'], async (req, res) => { const body = req.body // Delete request if (req.query.delete !== undefined && req.query.delete !== 'false') { 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 }) } // Create new user 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 { // Edit user 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)) { 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 = { name: body.name, email: email } // 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)) { 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) { 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) } } res.status(200).end() } }) // Post request for changing the password. router.postAsync('/:id/password', async (req, res) => { const id = req.params.id === 'current' ? req.user.id : req.params.id const body = req.body // Check if passwords are set. if (body.passwordCurrent && body.password) { if (body.passwordCurrent === body.password) return res.status(500).send({ error: 'PASSWORD_ERROR', message: 'The provided password must be different than the old 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: '[' + 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.deleteAsync('/:id/', async (req, res) => { 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. 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() }) // ############################################################################ // ############################################################################ module.exports.router = router