module.exports = function (app) { var Response = require('../lib/httpResponse.js'); var User = require('mongoose').model('User'); var acl = require('../lib/auth').acl; var jwtRefreshSecret = require('../lib/auth').jwtRefreshSecret; var jwt = require('jsonwebtoken'); var _ = require('lodash'); var passwordpolicy = require('../lib/passwordpolicy'); // Check token validity app.get( '/api/users/checktoken', acl.hasPermission('validtoken'), function (req, res) { Response.Ok(res, req.cookies['token']); }, ); // Refresh token app.get('/api/users/refreshtoken', function (req, res) { var userAgent = req.headers['user-agent']; var token = req.cookies['refreshToken']; User.updateRefreshToken(token, userAgent) .then(msg => { res.cookie('token', `JWT ${msg.token}`, { secure: true, httpOnly: true, }); res.cookie('refreshToken', msg.refreshToken, { secure: true, httpOnly: true, path: '/api/users/refreshtoken', }); Response.Ok(res, msg); }) .catch(err => { if (err.fn === 'Unauthorized') { res.clearCookie('token'); res.clearCookie('refreshToken'); } Response.Internal(res, err); }); }); // Remove token cookie app.delete('/api/users/refreshtoken', function (req, res) { var token = req.cookies['refreshToken']; try { var decoded = jwt.verify(token, jwtRefreshSecret); } catch (err) { res.clearCookie('token'); res.clearCookie('refreshToken'); if (err.name === 'TokenExpiredError') Response.Unauthorized(res, 'Expired refreshToken'); else Response.Unauthorized(res, 'Invalid refreshToken'); return; } User.removeSession(decoded.userId, decoded.sessionId) .then(msg => { res.clearCookie('token'); res.clearCookie('refreshToken'); Response.Ok(res, msg); }) .catch(err => Response.Internal(res, err)); }); // Authenticate user -> return JWT token app.post('/api/users/token', function (req, res) { if (!req.body.password || !req.body.username) { Response.BadParameters(res, 'Required parameters: username, password'); return; } // Validate types if ( typeof req.body.password !== 'string' || typeof req.body.username !== 'string' || (req.body.totpToken && typeof req.body.totpToken !== 'string') ) { Response.BadParameters(res, 'Parameters must be of type String'); return; } var user = new User(); //Required params user.username = req.body.username; user.password = req.body.password; //Optional params if (req.body.totpToken) user.totpToken = req.body.totpToken; user .getToken(req.headers['user-agent']) .then(msg => { res.cookie('token', `JWT ${msg.token}`, { secure: true, httpOnly: true, sameSite: 'None', }); res.cookie('refreshToken', msg.refreshToken, { secure: true, httpOnly: true, path: '/api/users/refreshtoken', sameSite: 'None', }); Response.Ok(res, msg); }) .catch(err => Response.Internal(res, err)); }); // Check if there are any existing users for creating first user app.get('/api/users/init', function (req, res) { User.getAll() .then(msg => Response.Ok(res, msg.length === 0)) .catch(err => Response.Internal(res, err)); }); // Get all users app.get('/api/users', acl.hasPermission('users:read'), function (req, res) { User.getAll() .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }); // Get all reviewers app.get( '/api/users/reviewers', acl.hasPermission('users:read'), function (req, res) { User.getAll() .then(users => { var reviewers = []; users.forEach(user => { if ( acl.isAllowed(user.role, 'audits:review') || acl.isAllowed(user.role, 'audits:review-all') ) { reviewers.push(user); } }); Response.Ok(res, reviewers); }) .catch(err => Response.Internal(res, err)); }, ); // Get user self app.get( '/api/users/me', acl.hasPermission('validtoken'), function (req, res) { User.getByUsername(req.decodedToken.username) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); //get TOTP Qrcode URL app.get( '/api/users/totp', acl.hasPermission('validtoken'), function (req, res) { User.getTotpQrcode(req.decodedToken.username) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); //setup TOTP app.post( '/api/users/totp', acl.hasPermission('validtoken'), function (req, res) { if (!req.body.totpToken || !req.body.totpSecret) { Response.BadParameters(res, 'Missing some required parameters'); return; } User.setupTotp( req.body.totpToken, req.body.totpSecret, req.decodedToken.username, ) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); //cancel TOTP app.delete( '/api/users/totp', acl.hasPermission('validtoken'), function (req, res) { if (!req.body.totpToken) { Response.BadParameters(res, 'Missing some required parameters'); return; } User.cancelTotp(req.body.totpToken, req.decodedToken.username) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); // Get user by username app.get( '/api/users/:username', acl.hasPermission('users:read'), function (req, res) { User.getByUsername(req.params.username) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); // Create user app.post( '/api/users', acl.hasPermission('users:create'), function (req, res) { if ( !req.body.username || !req.body.password || !req.body.firstname || !req.body.lastname ) { Response.BadParameters(res, 'Missing some required parameters'); return; } if (passwordpolicy.strongPassword(req.body.password) !== true) { Response.BadParameters( res, 'Password does not match the password policy', ); return; } var user = {}; //Required params user.username = req.body.username; user.password = req.body.password; user.firstname = req.body.firstname; user.lastname = req.body.lastname; //Optionals params user.role = req.body.role || 'user'; if (req.body.email) user.email = req.body.email; if (req.body.phone) user.phone = req.body.phone; User.create(user) .then(msg => Response.Created(res, 'User created successfully')) .catch(err => Response.Internal(res, err)); }, ); // Create First User app.post('/api/users/init', function (req, res) { if ( !req.body.username || !req.body.password || !req.body.firstname || !req.body.lastname ) { Response.BadParameters(res, 'Missing some required parameters'); return; } if (passwordpolicy.strongPassword(req.body.password) !== true) { Response.BadParameters( res, 'Password does not match the password policy', ); return; } var user = {}; //Required params user.username = req.body.username; user.password = req.body.password; user.firstname = req.body.firstname; user.lastname = req.body.lastname; user.role = 'admin'; User.getAll() .then(users => { if (users.length === 0) User.create(user) .then(msg => { var newUser = new User(); //Required params newUser.username = req.body.username; newUser.password = req.body.password; newUser .getToken(req.headers['user-agent']) .then(msg => { res.cookie('token', `JWT ${msg.token}`, { secure: true, httpOnly: true, }); res.cookie('refreshToken', msg.refreshToken, { secure: true, httpOnly: true, path: '/api/users/refreshtoken', }); Response.Created(res, msg); }) .catch(err => Response.Internal(res, err)); }) .catch(err => Response.Internal(res, err)); else Response.Forbidden(res, 'Already Initialized'); }) .catch(err => Response.Internal(res, err)); }); // Update my profile app.put( '/api/users/me', acl.hasPermission('validtoken'), function (req, res) { if ( !req.body.currentPassword || (req.body.newPassword && !req.body.confirmPassword) || (req.body.confirmPassword && !req.body.newPassword) ) { Response.BadParameters(res, 'Missing some required parameters'); return; } if ( req.body.newPassword && passwordpolicy.strongPassword(req.body.newPassword) !== true ) { Response.BadParameters( res, 'New Password does not match the password policy', ); return; } if ( req.body.newPassword && req.body.confirmPassword && req.body.newPassword !== req.body.confirmPassword ) { Response.BadParameters(res, 'New password validation failed'); return; } var user = {}; // Required params user.password = req.body.currentPassword; // Optionals params if (req.body.username) user.username = req.body.username; if (req.body.newPassword) user.newPassword = req.body.newPassword; if (req.body.firstname) user.firstname = req.body.firstname; if (req.body.lastname) user.lastname = req.body.lastname; if (!_.isNil(req.body.email)) user.email = req.body.email; if (!_.isNil(req.body.phone)) user.phone = req.body.phone; User.updateProfile(req.decodedToken.username, user) .then(msg => { res.cookie('token', msg.token, { secure: true, httpOnly: true }); Response.Ok(res, msg); }) .catch(err => Response.Internal(res, err)); }, ); // Update any user (admin only) app.put( '/api/users/:id', acl.hasPermission('users:update'), function (req, res) { if ( req.body.password && !passwordpolicy.strongPassword(req.body.password) ) { Response.BadParameters( res, 'New Password does not match the password policy', ); return; } var user = {}; // Optionals params if (req.body.username) user.username = req.body.username; if (req.body.password) user.password = req.body.password; if (req.body.firstname) user.firstname = req.body.firstname; if (req.body.lastname) user.lastname = req.body.lastname; if (!_.isNil(req.body.email)) user.email = req.body.email; if (!_.isNil(req.body.phone)) user.phone = req.body.phone; if (req.body.role) user.role = req.body.role; if (typeof req.body.totpEnabled === 'boolean') user.totpEnabled = req.body.totpEnabled; if (typeof req.body.enabled === 'boolean') user.enabled = req.body.enabled; User.updateUser(req.params.id, user) .then(msg => Response.Ok(res, msg)) .catch(err => Response.Internal(res, err)); }, ); };