2023-09-01 17:03:33 +02:00
|
|
|
import { context, db, env, roles } from "@budibase/backend-core"
|
2023-08-31 10:36:17 +02:00
|
|
|
import { features } from "@budibase/pro"
|
2023-08-21 16:56:40 +02:00
|
|
|
import {
|
|
|
|
DocumentType,
|
|
|
|
PermissionLevel,
|
2023-09-01 11:33:59 +02:00
|
|
|
PlanType,
|
2023-08-31 13:01:17 +02:00
|
|
|
Role,
|
2023-08-21 16:56:40 +02:00
|
|
|
VirtualDocumentType,
|
|
|
|
} from "@budibase/types"
|
2023-08-31 13:01:17 +02:00
|
|
|
import {
|
|
|
|
extractViewInfoFromID,
|
|
|
|
getRoleParams,
|
|
|
|
isViewID,
|
|
|
|
} from "../../../db/utils"
|
2023-08-31 10:36:17 +02:00
|
|
|
import {
|
|
|
|
CURRENTLY_SUPPORTED_LEVELS,
|
|
|
|
getBasePermissions,
|
|
|
|
} from "../../../utilities/security"
|
2023-09-01 17:03:33 +02:00
|
|
|
import sdk from "../../../sdk"
|
|
|
|
import { isV2 } from "../views"
|
2023-08-21 16:56:40 +02:00
|
|
|
|
|
|
|
type ResourceActionAllowedResult =
|
|
|
|
| { allowed: true }
|
|
|
|
| {
|
|
|
|
allowed: false
|
|
|
|
level: PermissionLevel
|
|
|
|
resourceType: DocumentType | VirtualDocumentType
|
|
|
|
}
|
|
|
|
|
|
|
|
export async function resourceActionAllowed({
|
|
|
|
resourceId,
|
|
|
|
level,
|
|
|
|
}: {
|
|
|
|
resourceId: string
|
|
|
|
level: PermissionLevel
|
|
|
|
}): Promise<ResourceActionAllowedResult> {
|
|
|
|
if (!isViewID(resourceId)) {
|
|
|
|
return { allowed: true }
|
|
|
|
}
|
|
|
|
|
2023-08-22 10:27:06 +02:00
|
|
|
if (await features.isViewPermissionEnabled()) {
|
|
|
|
return { allowed: true }
|
|
|
|
}
|
|
|
|
|
2023-08-21 16:56:40 +02:00
|
|
|
return {
|
|
|
|
allowed: false,
|
|
|
|
level,
|
|
|
|
resourceType: VirtualDocumentType.VIEW,
|
|
|
|
}
|
|
|
|
}
|
2023-08-31 10:36:17 +02:00
|
|
|
|
2023-09-01 09:50:55 +02:00
|
|
|
enum PermissionSource {
|
|
|
|
EXPLICIT = "EXPLICIT",
|
|
|
|
INHERITED = "INHERITED",
|
|
|
|
BASE = "BASE",
|
2023-09-01 09:40:29 +02:00
|
|
|
}
|
|
|
|
|
2023-08-31 13:01:17 +02:00
|
|
|
type ResourcePermissions = Record<
|
|
|
|
string,
|
2023-09-01 09:50:55 +02:00
|
|
|
{ role: string; type: PermissionSource }
|
2023-08-31 13:01:17 +02:00
|
|
|
>
|
|
|
|
|
2023-09-01 09:50:55 +02:00
|
|
|
export async function getInheritablePermissions(
|
|
|
|
resourceId: string
|
|
|
|
): Promise<ResourcePermissions | undefined> {
|
2023-09-01 11:33:59 +02:00
|
|
|
if (isViewID(resourceId)) {
|
2023-09-01 09:50:55 +02:00
|
|
|
return await getResourcePerms(extractViewInfoFromID(resourceId).tableId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-01 11:33:59 +02:00
|
|
|
export async function allowsExplicitPermissions(resourceId: string) {
|
|
|
|
if (isViewID(resourceId)) {
|
|
|
|
const allowed = await features.isViewPermissionEnabled()
|
|
|
|
const minPlan = !allowed
|
|
|
|
? env.SELF_HOSTED
|
|
|
|
? PlanType.BUSINESS
|
|
|
|
: PlanType.PREMIUM
|
|
|
|
: undefined
|
|
|
|
|
|
|
|
return {
|
|
|
|
allowed,
|
|
|
|
minPlan,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return { allowed: true }
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:01:17 +02:00
|
|
|
export async function getResourcePerms(
|
|
|
|
resourceId: string
|
|
|
|
): Promise<ResourcePermissions> {
|
2023-08-31 10:36:17 +02:00
|
|
|
const db = context.getAppDB()
|
|
|
|
const body = await db.allDocs(
|
|
|
|
getRoleParams(null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2023-08-31 13:01:17 +02:00
|
|
|
const rolesList = body.rows.map<Role>(row => row.doc)
|
2023-09-01 09:40:29 +02:00
|
|
|
let permissions: ResourcePermissions = {}
|
2023-08-31 13:01:17 +02:00
|
|
|
|
2023-09-01 09:50:55 +02:00
|
|
|
const permsToInherit = await getInheritablePermissions(resourceId)
|
2023-08-31 13:01:17 +02:00
|
|
|
|
2023-09-01 11:33:59 +02:00
|
|
|
const allowsExplicitPerm = (await allowsExplicitPermissions(resourceId))
|
|
|
|
.allowed
|
|
|
|
|
2023-08-31 10:36:17 +02:00
|
|
|
for (let level of CURRENTLY_SUPPORTED_LEVELS) {
|
|
|
|
// update the various roleIds in the resource permissions
|
|
|
|
for (let role of rolesList) {
|
2023-09-01 11:33:59 +02:00
|
|
|
const rolePerms = allowsExplicitPerm
|
|
|
|
? roles.checkForRoleResourceArray(role.permissions, resourceId)
|
|
|
|
: {}
|
2023-08-31 13:01:17 +02:00
|
|
|
if (rolePerms[resourceId]?.indexOf(level) > -1) {
|
|
|
|
permissions[level] = {
|
|
|
|
role: roles.getExternalRoleID(role._id!, role.version),
|
2023-09-01 09:50:55 +02:00
|
|
|
type: PermissionSource.EXPLICIT,
|
2023-08-31 13:01:17 +02:00
|
|
|
}
|
2023-09-01 10:52:06 +02:00
|
|
|
} else if (
|
|
|
|
!permissions[level] &&
|
|
|
|
permsToInherit &&
|
|
|
|
permsToInherit[level]
|
|
|
|
) {
|
2023-08-31 13:01:17 +02:00
|
|
|
permissions[level] = {
|
2023-09-01 09:40:29 +02:00
|
|
|
role: permsToInherit[level].role,
|
2023-09-01 09:50:55 +02:00
|
|
|
type: PermissionSource.INHERITED,
|
2023-08-31 13:01:17 +02:00
|
|
|
}
|
2023-08-31 10:36:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 13:01:17 +02:00
|
|
|
const basePermissions = Object.entries(
|
|
|
|
getBasePermissions(resourceId)
|
|
|
|
).reduce<ResourcePermissions>((p, [level, role]) => {
|
2023-09-01 09:50:55 +02:00
|
|
|
p[level] = { role, type: PermissionSource.BASE }
|
2023-08-31 13:01:17 +02:00
|
|
|
return p
|
|
|
|
}, {})
|
|
|
|
const result = Object.assign(basePermissions, permissions)
|
|
|
|
return result
|
2023-08-31 10:36:17 +02:00
|
|
|
}
|
2023-09-01 17:03:33 +02:00
|
|
|
|
2023-09-01 17:23:47 +02:00
|
|
|
export async function getDependantResources(
|
|
|
|
resourceId: string
|
|
|
|
): Promise<Record<string, number> | undefined> {
|
2023-09-01 17:03:33 +02:00
|
|
|
if (db.isTableId(resourceId)) {
|
2023-09-01 17:23:47 +02:00
|
|
|
const dependants: Record<string, Set<string>> = {}
|
2023-09-01 17:03:33 +02:00
|
|
|
|
|
|
|
const table = await sdk.tables.getTable(resourceId)
|
|
|
|
const views = Object.values(table.views || {})
|
|
|
|
|
|
|
|
for (const view of views) {
|
|
|
|
if (!isV2(view)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
const permissions = await getResourcePerms(view.id)
|
|
|
|
for (const [level, roleInfo] of Object.entries(permissions)) {
|
|
|
|
if (roleInfo.type === PermissionSource.INHERITED) {
|
2023-09-01 17:23:47 +02:00
|
|
|
dependants[VirtualDocumentType.VIEW] ??= new Set()
|
|
|
|
dependants[VirtualDocumentType.VIEW].add(view.id)
|
2023-09-01 17:03:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-01 17:23:47 +02:00
|
|
|
return Object.entries(dependants).reduce((p, [type, resources]) => {
|
|
|
|
p[type] = resources.size
|
|
|
|
return p
|
|
|
|
}, {} as Record<string, number>)
|
2023-09-01 17:03:33 +02:00
|
|
|
}
|
|
|
|
|
2023-09-01 17:23:47 +02:00
|
|
|
return
|
2023-09-01 17:03:33 +02:00
|
|
|
}
|