// Dynamic generation of JWT Secret if not exist (different for each environnment) var fs = require('fs'); var env = process.env.NODE_ENV || 'dev'; var config = require('../config/config.json'); if (!config[env].jwtSecret) { config[env].jwtSecret = require('crypto').randomBytes(32).toString('hex'); var configString = JSON.stringify(config, null, 4); fs.writeFileSync(`${__basedir}/config/config.json`, configString); } if (!config[env].jwtRefreshSecret) { config[env].jwtRefreshSecret = require('crypto') .randomBytes(32) .toString('hex'); var configString = JSON.stringify(config, null, 4); fs.writeFileSync(`${__basedir}/config/config.json`, configString); } var jwtSecret = config[env].jwtSecret; exports.jwtSecret = jwtSecret; var jwtRefreshSecret = config[env].jwtRefreshSecret; exports.jwtRefreshSecret = jwtRefreshSecret; /* ROLES LOGIC role_name: { allows: [], inherits: [] } allows: allowed permissions to access | use * for all inherits: inherits other users "allows" */ const builtInRoles = { user: { allows: [ // Audits 'audits:create', 'audits:read', 'audits:update', 'audits:delete', // Images 'images:create', 'images:read', // Clients 'clients:create', 'clients:read', 'clients:update', 'clients:delete', // Companies 'companies:create', 'companies:read', 'companies:update', 'companies:delete', // Languages 'languages:read', // Audit Types 'audit-types:read', // Vulnerability Types 'vulnerability-types:read', // Vulnerability Categories 'vulnerability-categories:read', // Sections Data 'sections:read', // Templates 'templates:read', // Users 'users:read', // Roles 'roles:read', // Vulnerabilities 'vulnerabilities:read', 'vulnerability-updates:create', // Custom Fields 'custom-fields:read', // Settings 'settings:read-public', // Classify 'classify:all', // Check CWE Update 'check-update:all', // Update CWE Model 'update-model:all', ], }, admin: { allows: '*', }, }; try { var customRoles = require('../config/roles.json'); } catch (error) { var customRoles = []; } var roles = { ...customRoles, ...builtInRoles }; class ACL { constructor(roles) { if (typeof roles !== 'object') { throw new TypeError('Expected an object as input'); } this.roles = roles; } isAllowed(role, permission) { // Check if role exists if (!this.roles[role] && !this.roles['user']) { return false; } let $role = this.roles[role] || this.roles['user']; // Default to user role in case of inexistant role // Check if role is allowed with permission if ( $role.allows && ($role.allows === '*' || $role.allows.indexOf(permission) !== -1) ) { return true; } // Check if there is inheritance if (!$role.inherits || $role.inherits.length < 1) { return false; } // Recursive check childs until true or false return $role.inherits.some(role => this.isAllowed(role, permission)); } hasPermission(permission) { var Response = require('./httpResponse'); var jwt = require('jsonwebtoken'); return (req, res, next) => { if (!req.cookies['token']) { Response.Unauthorized(res, 'No token provided'); return; } var cookie = req.cookies['token'].split(' '); if (cookie.length !== 2 || cookie[0] !== 'JWT') { Response.Unauthorized(res, 'Bad token type'); return; } var token = cookie[1]; jwt.verify(token, jwtSecret, (err, decoded) => { if (err) { if (err.name === 'TokenExpiredError') Response.Unauthorized(res, 'Expired token'); else Response.Unauthorized(res, 'Invalid token'); return; } if ( permission === 'validtoken' || this.isAllowed(decoded.role, permission) ) { req.decodedToken = decoded; return next(); } else { Response.Forbidden(res, 'Insufficient privileges'); return; } }); }; } buildRoles(role) { var currentRole = this.roles[role] || this.roles['user']; // Default to user role in case of inexistant role var result = currentRole.allows || []; if (currentRole.inherits) { currentRole.inherits.forEach(element => { result = [...new Set([...result, ...this.buildRoles(element)])]; }); } return result; } getRoles(role) { var result = this.buildRoles(role); if (result.includes('*')) return '*'; return result; } } exports.acl = new ACL(roles);