Making use of the resourceId in the middleware package.

This commit is contained in:
mike12345567 2021-02-08 17:52:22 +00:00
parent 2eb095fecd
commit 7d8cdafc60
3 changed files with 71 additions and 12 deletions

View File

@ -1,10 +1,11 @@
const { const {
BUILTIN_ROLE_IDS, BUILTIN_ROLE_IDS,
getUserPermissionIds, getUserPermissions,
} = require("../utilities/security/roles") } = require("../utilities/security/roles")
const { const {
PermissionTypes, PermissionTypes,
doesHavePermission, doesHaveResourcePermission,
doesHaveBasePermission,
} = require("../utilities/security/permissions") } = require("../utilities/security/permissions")
const env = require("../environment") const env = require("../environment")
const { isAPIKeyValid } = require("../utilities/security/apikey") const { isAPIKeyValid } = require("../utilities/security/apikey")
@ -14,6 +15,10 @@ const ADMIN_ROLES = [BUILTIN_ROLE_IDS.ADMIN, BUILTIN_ROLE_IDS.BUILDER]
const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|")) const LOCAL_PASS = new RegExp(["webhooks/trigger", "webhooks/schema"].join("|"))
function hasResource(ctx) {
return ctx.resourceId != null
}
module.exports = (permType, permLevel = null) => async (ctx, next) => { module.exports = (permType, permLevel = null) => async (ctx, next) => {
// webhooks can pass locally // webhooks can pass locally
if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) { if (!env.CLOUD && LOCAL_PASS.test(ctx.request.url)) {
@ -47,7 +52,10 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => {
} }
const role = ctx.user.role const role = ctx.user.role
const permissions = await getUserPermissionIds(ctx.appId, role._id) const { basePermissions, permissions } = await getUserPermissions(
ctx.appId,
role._id
)
if (ADMIN_ROLES.indexOf(role._id) !== -1) { if (ADMIN_ROLES.indexOf(role._id) !== -1) {
return next() return next()
} }
@ -56,7 +64,14 @@ module.exports = (permType, permLevel = null) => async (ctx, next) => {
ctx.throw(403, "Not Authorized") ctx.throw(403, "Not Authorized")
} }
if (!doesHavePermission(permType, permLevel, permissions)) { if (
hasResource(ctx) &&
doesHaveResourcePermission(permissions, permLevel, ctx)
) {
return next()
}
if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
ctx.throw(403, "User does not have permission") ctx.throw(403, "User does not have permission")
} }

View File

@ -97,7 +97,35 @@ exports.BUILTIN_PERMISSIONS = {
}, },
} }
exports.doesHavePermission = (permType, permLevel, permissionIds) => { exports.doesHaveResourcePermission = (
permissions,
permLevel,
{ resourceId, subResourceId }
) => {
// set foundSub to not subResourceId, incase there is no subResource
let foundMain = false,
foundSub = !subResourceId
for (let [resource, level] of Object.entries(permissions)) {
const levels = getAllowedLevels(level)
if (resource === resourceId && levels.indexOf(permLevel) !== -1) {
foundMain = true
}
if (
subResourceId &&
resource === subResourceId &&
levels.indexOf(permLevel) !== -1
) {
foundSub = true
}
// this will escape if foundMain only when no sub resource
if (foundMain && foundSub) {
break
}
}
return foundMain && foundSub
}
exports.doesHaveBasePermission = (permType, permLevel, permissionIds) => {
const builtins = Object.values(exports.BUILTIN_PERMISSIONS) const builtins = Object.values(exports.BUILTIN_PERMISSIONS)
let permissions = flatten( let permissions = flatten(
builtins builtins

View File

@ -1,6 +1,10 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { cloneDeep } = require("lodash/fp") const { cloneDeep } = require("lodash/fp")
const { BUILTIN_PERMISSION_IDS } = require("./permissions") const {
BUILTIN_PERMISSION_IDS,
higherPermission,
doesHaveBasePermission,
} = require("./permissions")
const BUILTIN_IDS = { const BUILTIN_IDS = {
ADMIN: "ADMIN", ADMIN: "ADMIN",
@ -127,14 +131,26 @@ exports.getUserRoleHierarchy = async (appId, userRoleId) => {
* Get all of the user permissions which could be found across the role hierarchy * Get all of the user permissions which could be found across the role hierarchy
* @param appId The ID of the application from which roles should be obtained. * @param appId The ID of the application from which roles should be obtained.
* @param userRoleId The user's role ID, this can be found in their access token. * @param userRoleId The user's role ID, this can be found in their access token.
* @returns {Promise<string[]>} A list of permission IDs these should all be unique. * @returns {Promise<{basePermissions: string[], permissions: Object}>} the base
* permission IDs as well as any custom resource permissions.
*/ */
exports.getUserPermissionIds = async (appId, userRoleId) => { exports.getUserPermissions = async (appId, userRoleId) => {
return [ const rolesHierarchy = await getAllUserRoles(appId, userRoleId)
...new Set( const basePermissions = [
(await getAllUserRoles(appId, userRoleId)).map(role => role.permissionId) ...new Set(rolesHierarchy.map(role => role.permissionId)),
),
] ]
const permissions = {}
for (let role of rolesHierarchy) {
if (role.permissions) {
for (let [resource, level] of Object.entries(role.permissions)) {
permissions[resource] = higherPermission(permissions[resource], level)
}
}
}
return {
basePermissions,
permissions,
}
} }
class AccessController { class AccessController {