budibase/packages/server/src/middleware/authorized.js

107 lines
3.1 KiB
JavaScript
Raw Normal View History

const {
getUserRoleHierarchy,
getRequiredResourceRole,
BUILTIN_ROLE_IDS,
} = require("@budibase/backend-core/roles")
const {
PermissionTypes,
doesHaveBasePermission,
} = require("@budibase/backend-core/permissions")
const builderMiddleware = require("./builder")
const { isWebhookEndpoint } = require("./utils")
2022-01-25 23:54:50 +01:00
const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
const { getAppId } = require("@budibase/backend-core/context")
function hasResource(ctx) {
return ctx.resourceId != null
}
2022-01-25 23:54:50 +01:00
const csrf = buildCsrfMiddleware()
/**
* Apply authorization to the requested resource:
* - If this is a builder resource the user must be a builder.
* - Builders can access all resources.
* - Otherwise the user must have the required role.
*/
const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
// check if this is a builder api and the user is not a builder
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
const isBuilderApi = permType === PermissionTypes.BUILDER
if (isBuilderApi && !isBuilder) {
return ctx.throw(403, "Not Authorized")
}
// check for resource authorization
if (!isBuilder) {
await checkAuthorizedResource(ctx, resourceRoles, permType, permLevel)
}
}
const checkAuthorizedResource = async (
ctx,
resourceRoles,
permType,
permLevel
) => {
// get the user's roles
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
const userRoles = await getUserRoleHierarchy(roleId, {
2022-01-25 23:54:50 +01:00
idOnly: false,
})
const permError = "User does not have permission"
// check if the user has the required role
if (resourceRoles.length > 0) {
// deny access if the user doesn't have the required resource role
const found = userRoles.find(role => resourceRoles.indexOf(role._id) !== -1)
if (!found) {
ctx.throw(403, permError)
}
// fallback to the base permissions when no resource roles are found
} else if (!doesHaveBasePermission(permType, permLevel, userRoles)) {
ctx.throw(403, permError)
}
}
2021-06-15 20:39:40 +02:00
module.exports =
(permType, permLevel = null) =>
async (ctx, next) => {
// webhooks don't need authentication, each webhook unique
// also internal requests (between services) don't need authorized
if (isWebhookEndpoint(ctx) || ctx.internal) {
2021-06-15 20:39:40 +02:00
return next()
}
2020-10-12 12:57:37 +02:00
2021-06-15 20:39:40 +02:00
if (!ctx.user) {
return ctx.throw(403, "No user info found")
}
2020-06-18 17:59:31 +02:00
2021-06-15 20:39:40 +02:00
// check general builder stuff, this middleware is a good way
// to find API endpoints which are builder focused
await builderMiddleware(ctx, permType)
2020-05-27 18:23:01 +02:00
2022-01-25 23:54:50 +01:00
// get the resource roles
let resourceRoles = []
const appId = getAppId()
if (appId && hasResource(ctx)) {
resourceRoles = await getRequiredResourceRole(permLevel, ctx)
2021-06-15 20:39:40 +02:00
}
2022-01-25 23:54:50 +01:00
// if the resource is public, proceed
const isPublicResource = resourceRoles.includes(BUILTIN_ROLE_IDS.PUBLIC)
if (isPublicResource) {
return next()
2021-06-15 20:39:40 +02:00
}
2020-05-27 18:23:01 +02:00
2022-01-25 23:54:50 +01:00
// check authenticated
if (!ctx.isAuthenticated) {
return ctx.throw(403, "Session not authenticated")
2021-06-15 20:39:40 +02:00
}
2022-01-25 23:54:50 +01:00
// check authorized
await checkAuthorized(ctx, resourceRoles, permType, permLevel)
// csrf protection
return csrf(ctx, next)
2021-06-15 20:39:40 +02:00
}