/* global __appdir */ const path = require('path') var db = require(path.join(__appdir, 'lib', 'sequelize')) var groupHelper = require(path.join(__appdir, 'lib', 'grouphelper')) module.exports = { exportFunctions } function exportFunctions (req, res, next) { req.user.hasPermission = permissionName => hasPermission(req.user.id, permissionName) req.user.getAllowedGroups = permissionName => getAllowedGroups(req.user.id, permissionName) req.user.hasPermissionForGroup = (permissionName, groupId) => hasPermissionForGroup(req.user.id, permissionName, groupId) req.user.getAllowedClients = permissionName => getAllowedClients(req.user.id, permissionName) req.user.hasPermissionForClient = (permissionName, clientId) => hasPermissionForClient(req.user.id, permissionName, clientId) req.user.getAllowedChilds = (permissionName, groupIds) => getAllowedChilds(req.user.id, permissionName, groupIds) next() } async function isSuperadmin (userid) { var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': 'superadmin' }, include: [{ as: 'roles', model: db.role, include: ['permissions'] }] }) return user !== null } // Check if the user has given permission. async function hasPermission (userid, permissionName) { var superAdmin = await isSuperadmin(userid) if (superAdmin) return true var permission = permissionName.split('.') // Wildcards var user if (permission[1] === '*') { user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': { [db.Op.like]: permission[0] + '%' } }, include: [{ as: 'roles', model: db.role, include: ['permissions'] }] }) } else { user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions'] }] }) } return user !== null } // Get a list of GroupIDs the user has the given permission for. 0 means for all groups. async function getAllowedGroups (userid, permissionName) { var superAdmin = await isSuperadmin(userid) if (superAdmin) return [0] var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions', 'groups'] }] }) // User doesn't have the permission if (user === null) return [] // User has permission, permission is not groupdependent else if (!user.roles[0].permissions[0].groupdependent) return [0] // User has permission, permission is groupdependent else { var result = [] for (let i = 0; i < user.roles.length; i++) { var whitelist = [] var blacklist = [] // Fill in white- and blacklist for (let j = 0; j < user.roles[i].groups.length; j++) { if (user.roles[i].groups[j].role_x_group.blacklist) { blacklist.push(user.roles[i].groups[j]) } else { whitelist.push(user.roles[i].groups[j]) } } // Get childs of whitelist groups, filtered by blacklist var whitelistChilds = await groupHelper.getAllChildren(whitelist, blacklist) result = result.concat(whitelist) result = result.concat(whitelistChilds.subgroups) } // Filter result for unique entries const filteredGroups = [] const groupMap = new Map() for (const item of result) { if (!groupMap.has(item.id)) { groupMap.set(item.id, true) filteredGroups.push(item) } } result = filteredGroups return result } } // Check if the user has a given permission for a given group. async function hasPermissionForGroup (userid, permissionName, groupId) { var superAdmin = await isSuperadmin(userid) if (superAdmin) return true var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions', 'groups'] }] }) // User doesn't have permission if (user === null) return false // User has permission, permission is not groupdependent else if (!user.roles[0].permissions[0].groupdependent) return true // User has permission, permission is groupdependent, check for group else { for (let i = 0; i < user.roles.length; i++) { var whitelist = [] var blacklist = [] var blacklistBreak = false // Fill in white- and blacklist for (let j = 0; j < user.roles[i].groups.length; j++) { if (user.roles[i].groups[j].role_x_group.blacklist) { // Shortcut, check next role if (user.roles[i].groups[j].id === groupId) { blacklistBreak = true break } blacklist.push(user.roles[i].groups[j].id) } else { // Shortcut if (user.roles[i].groups[j].id === groupId) return true whitelist.push(user.roles[i].groups[j].id) } } // Break by blacklist, do not check parents if (blacklistBreak) continue // Check parents for white-/blacklist entries let result = await checkParents(groupId, whitelist, blacklist) if (result) return true } return false } } // Get a list of ClientIDs the user has the given permission for. 0 means for all clients. async function getAllowedClients (userid, permissionName) { var superAdmin = await isSuperadmin(userid) if (superAdmin) return [0] var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions', { as: 'groups', model: db.group, include: ['clients'] }] }] }) // User doesn't have the permission if (user === null) return [] // User has permission, permission is not groupdependent else if (!user.roles[0].permissions[0].groupdependent) return [0] // User has permission, permission is groupdependent else { var result = [] for (let i = 0; i < user.roles.length; i++) { var whitelist = [] var blacklist = [] // Fill in white- and blacklist for (let j = 0; j < user.roles[i].groups.length; j++) { if (user.roles[i].groups[j].role_x_group.blacklist) { blacklist.push(user.roles[i].groups[j]) } else { whitelist.push(user.roles[i].groups[j]) } } // Get childs of whitelist groups, filtered by blacklist var whitelistChilds = await groupHelper.getAllChildren(whitelist, blacklist) result = result.concat(whitelistChilds.clients) } // Filter result for unique entries const filteredClients = [] const clientMap = new Map() for (const item of result) { if (!clientMap.has(item.id)) { clientMap.set(item.id, true) filteredClients.push(item) } } result = filteredClients return result } } // Check if the user has a given permission for a given client. async function hasPermissionForClient (userid, permissionName, clientId) { var superAdmin = await isSuperadmin(userid) if (superAdmin) return true var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions', { as: 'groups', model: db.group, include: ['clients'] }] }] }) // User doesn't have permission if (user === null) return false // User has permission, permission is not groupdependent else if (!user.roles[0].permissions[0].groupdependent) return true // User has permission, permission is groupdependent, check for client else { for (let i = 0; i < user.roles.length; i++) { var whitelist = [] var blacklist = [] var blacklistBreak = false var result = false // Fill in white- and blacklist for (let j = 0; j < user.roles[i].groups.length; j++) { var clients = user.roles[i].groups[j].clients.map(c => c.id) if (user.roles[i].groups[j].role_x_group.blacklist) { // Shortcut if (clients.includes(clientId)) { blacklistBreak = true break } blacklist.push(user.roles[i].groups[j].id) } else { // Remember it was found, check if client is in any blacklisted group on same layer tho. if (clients.includes(clientId)) result = true whitelist.push(user.roles[i].groups[j].id) } } if (blacklistBreak) continue // no blacklist shortcut used, but whitelist found if (result) return true // Get groups the client is assigned to var client = await db.client.findOne({ where: { id: clientId }, include: [{ as: 'groups', model: db.group }] }) var groupIds = client.groups.map(g => g.id) // Check parents for white-/blacklist entries. result = await checkParents(groupIds, whitelist, blacklist) if (result) return true } return false } } // Check if parents of groupIds are in the whitelist / blacklist // Whitelist returns true, blacklist or no parent in either list returns false async function checkParents (groupIds, whitelist, blacklist, knownGrps = []) { // No whitelist means the group can't be in one if (whitelist.length === 0) return false var result = false var parentIds = [] var groups = await db.group.findAll({ where: { id: groupIds }, include: ['parents'] }) for (let i = 0; i < groups.length; i++) { for (let j = 0; j < groups[i].parents.length; j++) { var id = groups[i].parents[j].id if (knownGrps.includes(id)) continue // Parent is blacklisted if (blacklist.includes(id)) return false // Parent is whitelisted, continue loop to see if another parent on SAME LAYER is blacklisted, as blacklisted > whitelisted if (whitelist.includes(id)) result = true parentIds.push(id) knownGrps.push(id) } } // A parent is whitelisted if (result) return true // No further parents found if (parentIds.length === 0) return false // Check next layer of parents result = await checkParents(parentIds, whitelist, blacklist, knownGrps) return result } async function getAllowedChilds (userid, permissionName, groupIds) { if (Number.isInteger(groupIds) && !Array.isArray(groupIds)) groupIds = [groupIds] var superAdmin = await isSuperadmin(userid) if (superAdmin) return groupHelper.getAllChildrenByIds(groupIds, {}, {}) var user = await db.user.findOne({ where: { id: userid, '$roles.permissions.name$': permissionName }, include: [{ as: 'roles', model: db.role, include: ['permissions', 'groups'] }] }) // User doesn't have the permission if (user === null) return {} // User has permission, permission is not groupdependent else if (!user.roles[0].permissions[0].groupdependent) return groupHelper.getAllChildrenByIds(groupIds, {}, {}) // User has permission, permission is groupdependent else { var subgroups = [] var clients = [] var groupList = [] for (let j = 0; j < groupIds.length; j++) { if (await hasPermissionForGroup(userid, permissionName, groupIds[j])) groupList.push(groupIds[j]) } var allChilds = await groupHelper.getAllChildrenByIds(groupIds, {}, {}) for (let i = 0; i < user.roles.length; i++) { var roleWhitelist = [] var roleBlacklistMap = {} var roleGroupList = groupList for (let j = 0; j < user.roles[i].groups.length; j++) { if (user.roles[i].groups[j].role_x_group.blacklist) { roleBlacklistMap[user.roles[i].groups[j].id] = true } else { roleWhitelist.push(user.roles[i].groups[j]) } } // TODO: This is just overwhelming inefficient. Even if whitelist contains only a few ids, if the function is called for the root, allChilds are 18k+ ids. for (let j = 0; j < roleWhitelist.length; j++) { for (let k = 0; k < allChilds.subgroups.length; k++) { if (allChilds.subgroups[k].id === roleWhitelist[j].id) { roleGroupList.push(roleWhitelist[j].id) subgroups = subgroups.concat(roleWhitelist[j]) } } } var childs = await groupHelper.getAllChildrenByIds(roleGroupList, roleBlacklistMap, {}) subgroups = subgroups.concat(childs.subgroups) clients = clients.concat(childs.clients) } // Filter result for unique entries const filteredGroups = [] const groupMap = new Map() for (const item of subgroups) { if (!groupMap.has(item.id)) { groupMap.set(item.id, true) filteredGroups.push(item) } } subgroups = filteredGroups const filteredClients = [] const clientMap = new Map() for (const item of clients) { if (!clientMap.has(item.id)) { clientMap.set(item.id, true) filteredClients.push(item) } } clients = filteredClients return { subgroups, clients } } }