2024-10-17 11:37:41 +02:00
|
|
|
import semver from "semver"
|
2022-11-17 15:59:18 +01:00
|
|
|
import { BuiltinPermissionID, PermissionLevel } from "./permissions"
|
2023-11-20 16:19:31 +01:00
|
|
|
import {
|
|
|
|
prefixRoleID,
|
|
|
|
getRoleParams,
|
|
|
|
DocumentType,
|
|
|
|
SEPARATOR,
|
|
|
|
doWithDB,
|
|
|
|
} from "../db"
|
2022-11-08 19:25:37 +01:00
|
|
|
import { getAppDB } from "../context"
|
2024-10-17 11:37:41 +02:00
|
|
|
import {
|
|
|
|
Screen,
|
|
|
|
Role as RoleDoc,
|
|
|
|
RoleUIMetadata,
|
|
|
|
Database,
|
|
|
|
App,
|
|
|
|
} from "@budibase/types"
|
2023-07-25 12:45:19 +02:00
|
|
|
import cloneDeep from "lodash/fp/cloneDeep"
|
2024-09-12 16:40:37 +02:00
|
|
|
import { RoleColor } from "@budibase/shared-core"
|
2020-12-02 14:20:56 +01:00
|
|
|
|
2022-11-08 21:01:01 +01:00
|
|
|
export const BUILTIN_ROLE_IDS = {
|
2020-12-02 14:20:56 +01:00
|
|
|
ADMIN: "ADMIN",
|
2020-12-08 13:20:37 +01:00
|
|
|
POWER: "POWER",
|
2020-12-02 14:20:56 +01:00
|
|
|
BASIC: "BASIC",
|
|
|
|
PUBLIC: "PUBLIC",
|
2022-11-08 21:01:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const BUILTIN_IDS = {
|
|
|
|
...BUILTIN_ROLE_IDS,
|
2022-11-08 19:25:37 +01:00
|
|
|
BUILDER: "BUILDER",
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2023-06-27 13:45:00 +02:00
|
|
|
export const RoleIDVersion = {
|
2023-06-22 19:02:35 +02:00
|
|
|
// original version, with a UUID based ID
|
2023-06-27 13:45:00 +02:00
|
|
|
UUID: undefined,
|
2023-06-22 19:02:35 +02:00
|
|
|
// new version - with name based ID
|
2023-06-27 13:45:00 +02:00
|
|
|
NAME: "name",
|
2023-06-22 19:02:35 +02:00
|
|
|
}
|
|
|
|
|
2022-11-22 14:56:01 +01:00
|
|
|
export class Role implements RoleDoc {
|
2022-11-08 19:25:37 +01:00
|
|
|
_id: string
|
2022-11-22 17:52:25 +01:00
|
|
|
_rev?: string
|
2022-11-08 19:25:37 +01:00
|
|
|
name: string
|
2022-11-22 14:56:01 +01:00
|
|
|
permissionId: string
|
2022-11-08 19:25:37 +01:00
|
|
|
inherits?: string
|
2023-06-27 13:45:00 +02:00
|
|
|
version?: string
|
2024-09-11 19:06:05 +02:00
|
|
|
permissions: Record<string, PermissionLevel[]> = {}
|
2024-09-12 16:40:37 +02:00
|
|
|
uiMetadata?: RoleUIMetadata
|
2020-12-02 18:08:25 +01:00
|
|
|
|
2024-09-13 15:14:36 +02:00
|
|
|
constructor(
|
|
|
|
id: string,
|
|
|
|
name: string,
|
|
|
|
permissionId: string,
|
|
|
|
uiMetadata?: RoleUIMetadata
|
|
|
|
) {
|
2022-11-08 19:25:37 +01:00
|
|
|
this._id = id
|
2024-09-13 15:14:36 +02:00
|
|
|
this.name = name
|
2024-09-12 16:40:37 +02:00
|
|
|
this.uiMetadata = uiMetadata
|
2022-11-08 19:25:37 +01:00
|
|
|
this.permissionId = permissionId
|
2023-06-22 19:02:35 +02:00
|
|
|
// version for managing the ID - removing the role_ when responding
|
2023-06-27 13:45:00 +02:00
|
|
|
this.version = RoleIDVersion.NAME
|
2022-11-08 19:25:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
addInheritance(inherits: string) {
|
|
|
|
this.inherits = inherits
|
|
|
|
return this
|
|
|
|
}
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2021-02-12 21:34:54 +01:00
|
|
|
const BUILTIN_ROLES = {
|
2024-09-13 15:14:36 +02:00
|
|
|
ADMIN: new Role(
|
|
|
|
BUILTIN_IDS.ADMIN,
|
|
|
|
BUILTIN_IDS.ADMIN,
|
|
|
|
BuiltinPermissionID.ADMIN,
|
|
|
|
{
|
|
|
|
displayName: "App admin",
|
|
|
|
description: "Can do everything",
|
|
|
|
color: RoleColor.ADMIN,
|
|
|
|
}
|
|
|
|
).addInheritance(BUILTIN_IDS.POWER),
|
|
|
|
POWER: new Role(
|
|
|
|
BUILTIN_IDS.POWER,
|
|
|
|
BUILTIN_IDS.POWER,
|
|
|
|
BuiltinPermissionID.POWER,
|
|
|
|
{
|
|
|
|
displayName: "App power user",
|
|
|
|
description: "An app user with more access",
|
|
|
|
color: RoleColor.POWER,
|
|
|
|
}
|
|
|
|
).addInheritance(BUILTIN_IDS.BASIC),
|
|
|
|
BASIC: new Role(
|
|
|
|
BUILTIN_IDS.BASIC,
|
|
|
|
BUILTIN_IDS.BASIC,
|
|
|
|
BuiltinPermissionID.WRITE,
|
|
|
|
{
|
|
|
|
displayName: "App user",
|
|
|
|
description: "Any logged in user",
|
|
|
|
color: RoleColor.BASIC,
|
|
|
|
}
|
|
|
|
).addInheritance(BUILTIN_IDS.PUBLIC),
|
|
|
|
PUBLIC: new Role(
|
|
|
|
BUILTIN_IDS.PUBLIC,
|
|
|
|
BUILTIN_IDS.PUBLIC,
|
|
|
|
BuiltinPermissionID.PUBLIC,
|
|
|
|
{
|
|
|
|
displayName: "Public user",
|
|
|
|
description: "Accessible to anyone",
|
|
|
|
color: RoleColor.PUBLIC,
|
|
|
|
}
|
|
|
|
),
|
|
|
|
BUILDER: new Role(
|
|
|
|
BUILTIN_IDS.BUILDER,
|
|
|
|
BUILTIN_IDS.BUILDER,
|
|
|
|
BuiltinPermissionID.ADMIN,
|
|
|
|
{
|
|
|
|
displayName: "Builder user",
|
|
|
|
description: "Users that can edit this app",
|
|
|
|
color: RoleColor.BUILDER,
|
|
|
|
}
|
|
|
|
),
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2022-11-22 14:56:01 +01:00
|
|
|
export function getBuiltinRoles(): { [key: string]: RoleDoc } {
|
2021-02-12 21:34:54 +01:00
|
|
|
return cloneDeep(BUILTIN_ROLES)
|
|
|
|
}
|
|
|
|
|
2024-03-04 17:42:41 +01:00
|
|
|
export function isBuiltin(role: string) {
|
|
|
|
return getBuiltinRole(role) !== undefined
|
|
|
|
}
|
2020-12-02 14:20:56 +01:00
|
|
|
|
2024-03-04 17:42:41 +01:00
|
|
|
export function getBuiltinRole(roleId: string): Role | undefined {
|
|
|
|
const role = Object.values(BUILTIN_ROLES).find(role =>
|
|
|
|
roleId.includes(role._id)
|
|
|
|
)
|
|
|
|
if (!role) {
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
return cloneDeep(role)
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2021-02-11 14:38:07 +01:00
|
|
|
/**
|
2021-02-11 19:13:09 +01:00
|
|
|
* Works through the inheritance ranks to see how far up the builtin stack this ID is.
|
2021-02-11 14:38:07 +01:00
|
|
|
*/
|
2024-03-05 18:35:04 +01:00
|
|
|
export function builtinRoleToNumber(id: string) {
|
2022-11-08 19:25:37 +01:00
|
|
|
const builtins = getBuiltinRoles()
|
2022-09-22 19:27:43 +02:00
|
|
|
const MAX = Object.values(builtins).length + 1
|
2021-02-11 19:13:09 +01:00
|
|
|
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
|
|
|
|
return MAX
|
|
|
|
}
|
2021-02-12 21:41:30 +01:00
|
|
|
let role = builtins[id],
|
2021-02-11 19:13:09 +01:00
|
|
|
count = 0
|
|
|
|
do {
|
|
|
|
if (!role) {
|
|
|
|
break
|
2021-02-11 14:38:07 +01:00
|
|
|
}
|
2022-11-22 14:56:01 +01:00
|
|
|
role = builtins[role.inherits!]
|
2021-02-11 19:13:09 +01:00
|
|
|
count++
|
|
|
|
} while (role !== null)
|
|
|
|
return count
|
|
|
|
}
|
|
|
|
|
2022-09-22 19:27:43 +02:00
|
|
|
/**
|
|
|
|
* Converts any role to a number, but has to be async to get the roles from db.
|
|
|
|
*/
|
2024-03-04 17:42:41 +01:00
|
|
|
export async function roleToNumber(id: string) {
|
2022-11-08 19:25:37 +01:00
|
|
|
if (isBuiltin(id)) {
|
|
|
|
return builtinRoleToNumber(id)
|
2022-09-22 19:27:43 +02:00
|
|
|
}
|
2023-10-27 19:03:06 +02:00
|
|
|
const hierarchy = (await getUserRoleHierarchy(id, {
|
|
|
|
defaultPublic: true,
|
|
|
|
})) as RoleDoc[]
|
2022-09-22 19:27:43 +02:00
|
|
|
for (let role of hierarchy) {
|
2024-03-04 17:42:41 +01:00
|
|
|
if (role?.inherits && isBuiltin(role.inherits)) {
|
2022-11-08 19:25:37 +01:00
|
|
|
return builtinRoleToNumber(role.inherits) + 1
|
2022-09-22 19:27:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2021-02-11 19:13:09 +01:00
|
|
|
/**
|
|
|
|
* Returns whichever builtin roleID is lower.
|
|
|
|
*/
|
2022-11-22 14:56:01 +01:00
|
|
|
export function lowerBuiltinRoleID(roleId1?: string, roleId2?: string): string {
|
2021-02-11 19:13:09 +01:00
|
|
|
if (!roleId1) {
|
2022-11-22 14:56:01 +01:00
|
|
|
return roleId2 as string
|
2021-02-11 19:13:09 +01:00
|
|
|
}
|
|
|
|
if (!roleId2) {
|
2022-11-22 14:56:01 +01:00
|
|
|
return roleId1 as string
|
2021-02-11 14:38:07 +01:00
|
|
|
}
|
2022-11-08 19:25:37 +01:00
|
|
|
return builtinRoleToNumber(roleId1) > builtinRoleToNumber(roleId2)
|
2021-02-12 10:55:37 +01:00
|
|
|
? roleId2
|
|
|
|
: roleId1
|
2021-02-11 14:38:07 +01:00
|
|
|
}
|
|
|
|
|
2020-12-02 14:20:56 +01:00
|
|
|
/**
|
|
|
|
* Gets the role object, this is mainly useful for two purposes, to check if the level exists and
|
|
|
|
* to check if the role inherits any others.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @param roleId The level ID to lookup.
|
|
|
|
* @param opts options for the function, like whether to halt errors, instead return public.
|
|
|
|
* @returns The role object, which may contain an "inherits" property.
|
2020-12-02 14:20:56 +01:00
|
|
|
*/
|
2023-06-12 19:39:30 +02:00
|
|
|
export async function getRole(
|
2024-03-04 17:42:41 +01:00
|
|
|
roleId: string,
|
2023-06-12 19:39:30 +02:00
|
|
|
opts?: { defaultPublic?: boolean }
|
2024-03-04 17:42:41 +01:00
|
|
|
): Promise<RoleDoc> {
|
2021-02-05 16:58:25 +01:00
|
|
|
// built in roles mostly come from the in-code implementation,
|
|
|
|
// but can be extended by a doc stored about them (e.g. permissions)
|
2024-03-04 17:42:41 +01:00
|
|
|
let role: RoleDoc | undefined = getBuiltinRole(roleId)
|
|
|
|
if (!role) {
|
2023-06-22 19:02:35 +02:00
|
|
|
// make sure has the prefix (if it has it then it won't be added)
|
2023-06-27 15:56:24 +02:00
|
|
|
roleId = prefixRoleID(roleId)
|
2021-02-05 16:58:25 +01:00
|
|
|
}
|
|
|
|
try {
|
2022-01-27 19:18:31 +01:00
|
|
|
const db = getAppDB()
|
2024-03-04 17:42:41 +01:00
|
|
|
const dbRole = await db.get<RoleDoc>(getDBRoleID(roleId))
|
|
|
|
role = Object.assign(role || {}, dbRole)
|
2021-02-09 14:01:45 +01:00
|
|
|
// finalise the ID
|
2024-03-04 17:42:41 +01:00
|
|
|
role._id = getExternalRoleID(role._id!, role.version)
|
2023-10-27 19:04:28 +02:00
|
|
|
} catch (err) {
|
2023-06-13 15:45:33 +02:00
|
|
|
if (!isBuiltin(roleId) && opts?.defaultPublic) {
|
2023-06-12 19:39:30 +02:00
|
|
|
return cloneDeep(BUILTIN_ROLES.PUBLIC)
|
|
|
|
}
|
2021-02-05 16:58:25 +01:00
|
|
|
// only throw an error if there is no role at all
|
2024-03-05 15:40:29 +01:00
|
|
|
if (!role || Object.keys(role).length === 0) {
|
2021-02-05 16:58:25 +01:00
|
|
|
throw err
|
|
|
|
}
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
return role
|
|
|
|
}
|
|
|
|
|
2020-12-02 18:08:25 +01:00
|
|
|
/**
|
|
|
|
* Simple function to get all the roles based on the top level user role ID.
|
|
|
|
*/
|
2023-10-27 19:03:06 +02:00
|
|
|
async function getAllUserRoles(
|
2024-03-04 17:42:41 +01:00
|
|
|
userRoleId: string,
|
2023-10-27 19:03:06 +02:00
|
|
|
opts?: { defaultPublic?: boolean }
|
|
|
|
): Promise<RoleDoc[]> {
|
2022-02-04 18:34:39 +01:00
|
|
|
// admins have access to all roles
|
|
|
|
if (userRoleId === BUILTIN_IDS.ADMIN) {
|
2022-11-08 19:25:37 +01:00
|
|
|
return getAllRoles()
|
2020-12-02 18:08:25 +01:00
|
|
|
}
|
2023-10-27 19:03:06 +02:00
|
|
|
let currentRole = await getRole(userRoleId, opts)
|
2020-12-02 18:08:25 +01:00
|
|
|
let roles = currentRole ? [currentRole] : []
|
|
|
|
let roleIds = [userRoleId]
|
|
|
|
// get all the inherited roles
|
|
|
|
while (
|
|
|
|
currentRole &&
|
|
|
|
currentRole.inherits &&
|
|
|
|
roleIds.indexOf(currentRole.inherits) === -1
|
2021-05-14 17:32:51 +02:00
|
|
|
) {
|
2020-12-02 18:08:25 +01:00
|
|
|
roleIds.push(currentRole.inherits)
|
2022-11-08 19:25:37 +01:00
|
|
|
currentRole = await getRole(currentRole.inherits)
|
2022-11-10 18:38:26 +01:00
|
|
|
if (currentRole) {
|
|
|
|
roles.push(currentRole)
|
|
|
|
}
|
2020-12-02 18:08:25 +01:00
|
|
|
}
|
|
|
|
return roles
|
|
|
|
}
|
|
|
|
|
2023-09-27 17:24:12 +02:00
|
|
|
export async function getUserRoleIdHierarchy(
|
2024-03-04 17:42:41 +01:00
|
|
|
userRoleId: string
|
2023-09-27 17:24:12 +02:00
|
|
|
): Promise<string[]> {
|
|
|
|
const roles = await getUserRoleHierarchy(userRoleId)
|
|
|
|
return roles.map(role => role._id!)
|
|
|
|
}
|
|
|
|
|
2020-12-02 14:20:56 +01:00
|
|
|
/**
|
|
|
|
* Returns an ordered array of the user's inherited role IDs, this can be used
|
|
|
|
* to determine if a user can access something that requires a specific role.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @param userRoleId The user's role ID, this can be found in their access token.
|
2023-10-27 19:03:06 +02:00
|
|
|
* @param opts optional - if want to default to public use this.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @returns returns an ordered array of the roles, with the first being their
|
2020-12-02 14:20:56 +01:00
|
|
|
* highest level of access and the last being the lowest level.
|
|
|
|
*/
|
2023-10-27 19:03:06 +02:00
|
|
|
export async function getUserRoleHierarchy(
|
2024-03-04 17:42:41 +01:00
|
|
|
userRoleId: string,
|
2023-10-27 19:03:06 +02:00
|
|
|
opts?: { defaultPublic?: boolean }
|
|
|
|
) {
|
2020-12-02 14:20:56 +01:00
|
|
|
// special case, if they don't have a role then they are a public user
|
2023-10-27 19:03:06 +02:00
|
|
|
return getAllUserRoles(userRoleId, opts)
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2022-03-28 17:34:50 +02:00
|
|
|
// this function checks that the provided permissions are in an array format
|
|
|
|
// some templates/older apps will use a simple string instead of array for roles
|
|
|
|
// convert the string to an array using the theory that write is higher than read
|
2022-11-08 19:25:37 +01:00
|
|
|
export function checkForRoleResourceArray(
|
2024-09-11 19:06:05 +02:00
|
|
|
rolePerms: Record<string, PermissionLevel[]>,
|
2022-11-08 19:25:37 +01:00
|
|
|
resourceId: string
|
2024-09-11 19:06:05 +02:00
|
|
|
): Record<string, PermissionLevel[]> {
|
2022-03-28 17:34:50 +02:00
|
|
|
if (rolePerms && !Array.isArray(rolePerms[resourceId])) {
|
2022-11-08 19:25:37 +01:00
|
|
|
const permLevel = rolePerms[resourceId] as any
|
2022-03-28 17:34:50 +02:00
|
|
|
rolePerms[resourceId] = [permLevel]
|
2022-11-17 15:59:18 +01:00
|
|
|
if (permLevel === PermissionLevel.WRITE) {
|
|
|
|
rolePerms[resourceId].push(PermissionLevel.READ)
|
2022-03-28 17:34:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return rolePerms
|
|
|
|
}
|
|
|
|
|
2024-03-04 17:42:41 +01:00
|
|
|
export async function getAllRoleIds(appId: string): Promise<string[]> {
|
2023-09-27 17:24:12 +02:00
|
|
|
const roles = await getAllRoles(appId)
|
2024-03-04 17:42:41 +01:00
|
|
|
return roles.map(role => role._id!)
|
2023-09-27 17:24:12 +02:00
|
|
|
}
|
|
|
|
|
2021-05-14 17:31:07 +02:00
|
|
|
/**
|
|
|
|
* Given an app ID this will retrieve all of the roles that are currently within that app.
|
2023-10-17 17:46:32 +02:00
|
|
|
* @return An array of the role objects that were found.
|
2021-05-14 17:31:07 +02:00
|
|
|
*/
|
2023-09-27 17:24:12 +02:00
|
|
|
export async function getAllRoles(appId?: string): Promise<RoleDoc[]> {
|
2022-04-19 20:42:52 +02:00
|
|
|
if (appId) {
|
|
|
|
return doWithDB(appId, internal)
|
|
|
|
} else {
|
2022-08-02 17:01:38 +02:00
|
|
|
let appDB
|
|
|
|
try {
|
|
|
|
appDB = getAppDB()
|
|
|
|
} catch (error) {
|
|
|
|
// We don't have any apps, so we'll just use the built-in roles
|
|
|
|
}
|
|
|
|
return internal(appDB)
|
2021-05-14 17:31:07 +02:00
|
|
|
}
|
2024-10-17 11:37:41 +02:00
|
|
|
async function internal(db: Database | undefined) {
|
2022-11-08 19:25:37 +01:00
|
|
|
let roles: RoleDoc[] = []
|
2022-08-02 17:01:38 +02:00
|
|
|
if (db) {
|
|
|
|
const body = await db.allDocs(
|
|
|
|
getRoleParams(null, {
|
|
|
|
include_docs: true,
|
|
|
|
})
|
|
|
|
)
|
2022-11-08 19:25:37 +01:00
|
|
|
roles = body.rows.map((row: any) => row.doc)
|
2023-06-22 19:02:35 +02:00
|
|
|
roles.forEach(
|
|
|
|
role => (role._id = getExternalRoleID(role._id!, role.version))
|
|
|
|
)
|
2022-08-02 17:01:38 +02:00
|
|
|
}
|
2022-11-08 19:25:37 +01:00
|
|
|
const builtinRoles = getBuiltinRoles()
|
2022-04-19 20:42:52 +02:00
|
|
|
|
2024-10-17 11:37:41 +02:00
|
|
|
// exclude internal roles like builder
|
|
|
|
let externalBuiltinRoles = []
|
|
|
|
|
|
|
|
if (db && !(await shouldIncludePowerRole(db))) {
|
|
|
|
externalBuiltinRoles = [
|
|
|
|
BUILTIN_IDS.ADMIN,
|
|
|
|
BUILTIN_IDS.POWER,
|
|
|
|
BUILTIN_IDS.BASIC,
|
|
|
|
BUILTIN_IDS.PUBLIC,
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
externalBuiltinRoles = [
|
|
|
|
BUILTIN_IDS.ADMIN,
|
|
|
|
BUILTIN_IDS.BASIC,
|
|
|
|
BUILTIN_IDS.PUBLIC,
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2022-04-19 20:42:52 +02:00
|
|
|
// need to combine builtin with any DB record of them (for sake of permissions)
|
2024-10-17 11:37:41 +02:00
|
|
|
for (let builtinRoleId of externalBuiltinRoles) {
|
2022-04-19 20:42:52 +02:00
|
|
|
const builtinRole = builtinRoles[builtinRoleId]
|
|
|
|
const dbBuiltin = roles.filter(
|
2023-06-22 19:02:35 +02:00
|
|
|
dbRole =>
|
|
|
|
getExternalRoleID(dbRole._id!, dbRole.version) === builtinRoleId
|
2022-04-19 20:42:52 +02:00
|
|
|
)[0]
|
|
|
|
if (dbBuiltin == null) {
|
|
|
|
roles.push(builtinRole || builtinRoles.BASIC)
|
|
|
|
} else {
|
|
|
|
// remove role and all back after combining with the builtin
|
|
|
|
roles = roles.filter(role => role._id !== dbBuiltin._id)
|
2023-06-22 19:02:35 +02:00
|
|
|
dbBuiltin._id = getExternalRoleID(dbBuiltin._id!, dbBuiltin.version)
|
2022-04-19 20:42:52 +02:00
|
|
|
roles.push(Object.assign(builtinRole, dbBuiltin))
|
|
|
|
}
|
2022-03-28 17:34:50 +02:00
|
|
|
}
|
2022-04-19 20:42:52 +02:00
|
|
|
// check permissions
|
|
|
|
for (let role of roles) {
|
|
|
|
if (!role.permissions) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for (let resourceId of Object.keys(role.permissions)) {
|
2022-11-08 19:25:37 +01:00
|
|
|
role.permissions = checkForRoleResourceArray(
|
2022-04-19 20:42:52 +02:00
|
|
|
role.permissions,
|
|
|
|
resourceId
|
|
|
|
)
|
|
|
|
}
|
2022-03-28 17:34:50 +02:00
|
|
|
}
|
2023-09-27 17:24:12 +02:00
|
|
|
return roles
|
2022-03-28 17:34:50 +02:00
|
|
|
}
|
2021-05-14 17:31:07 +02:00
|
|
|
}
|
|
|
|
|
2024-10-17 11:37:41 +02:00
|
|
|
async function shouldIncludePowerRole(db: Database) {
|
|
|
|
const app = await db.get<App>(DocumentType.APP_METADATA)
|
|
|
|
const { creationVersion } = app
|
|
|
|
if (semver.gte(creationVersion || "", "3.0.0")) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2022-11-08 19:25:37 +01:00
|
|
|
export class AccessController {
|
|
|
|
userHierarchies: { [key: string]: string[] }
|
2022-01-27 19:18:31 +01:00
|
|
|
constructor() {
|
2020-12-02 14:20:56 +01:00
|
|
|
this.userHierarchies = {}
|
|
|
|
}
|
|
|
|
|
2022-11-08 19:25:37 +01:00
|
|
|
async hasAccess(tryingRoleId?: string, userRoleId?: string) {
|
2020-12-02 14:20:56 +01:00
|
|
|
// special cases, the screen has no role, the roles are the same or the user
|
|
|
|
// is currently in the builder
|
|
|
|
if (
|
|
|
|
tryingRoleId == null ||
|
|
|
|
tryingRoleId === "" ||
|
|
|
|
tryingRoleId === userRoleId ||
|
2021-03-10 12:47:39 +01:00
|
|
|
tryingRoleId === BUILTIN_IDS.BUILDER ||
|
|
|
|
userRoleId === BUILTIN_IDS.BUILDER
|
2020-12-02 14:20:56 +01:00
|
|
|
) {
|
|
|
|
return true
|
|
|
|
}
|
2022-11-08 19:25:37 +01:00
|
|
|
let roleIds = userRoleId ? this.userHierarchies[userRoleId] : null
|
|
|
|
if (!roleIds && userRoleId) {
|
2023-09-27 17:24:12 +02:00
|
|
|
roleIds = await getUserRoleIdHierarchy(userRoleId)
|
2020-12-04 15:01:02 +01:00
|
|
|
this.userHierarchies[userRoleId] = roleIds
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2022-11-08 19:25:37 +01:00
|
|
|
return roleIds?.indexOf(tryingRoleId) !== -1
|
2020-12-02 14:20:56 +01:00
|
|
|
}
|
|
|
|
|
2022-11-08 19:25:37 +01:00
|
|
|
async checkScreensAccess(screens: Screen[], userRoleId: string) {
|
2020-12-02 14:20:56 +01:00
|
|
|
let accessibleScreens = []
|
|
|
|
// don't want to handle this with Promise.all as this would mean all custom roles would be
|
|
|
|
// retrieved at same time, it is likely a custom role will be re-used and therefore want
|
|
|
|
// to work in sync for performance save
|
|
|
|
for (let screen of screens) {
|
|
|
|
const accessible = await this.checkScreenAccess(screen, userRoleId)
|
|
|
|
if (accessible) {
|
|
|
|
accessibleScreens.push(accessible)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return accessibleScreens
|
|
|
|
}
|
|
|
|
|
2022-11-08 19:25:37 +01:00
|
|
|
async checkScreenAccess(screen: Screen, userRoleId: string) {
|
|
|
|
const roleId = screen && screen.routing ? screen.routing.roleId : undefined
|
2020-12-02 14:20:56 +01:00
|
|
|
if (await this.hasAccess(roleId, userRoleId)) {
|
|
|
|
return screen
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 14:01:45 +01:00
|
|
|
/**
|
|
|
|
* Adds the "role_" for builtin role IDs which are to be written to the DB (for permissions).
|
|
|
|
*/
|
2023-06-22 19:02:35 +02:00
|
|
|
export function getDBRoleID(roleName: string) {
|
|
|
|
if (roleName?.startsWith(DocumentType.ROLE)) {
|
|
|
|
return roleName
|
2021-02-09 14:01:45 +01:00
|
|
|
}
|
2023-06-27 15:56:24 +02:00
|
|
|
return prefixRoleID(roleName)
|
2021-02-09 14:01:45 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove the "role_" from builtin role IDs that have been written to the DB (for permissions).
|
|
|
|
*/
|
2023-06-27 13:45:00 +02:00
|
|
|
export function getExternalRoleID(roleId: string, version?: string) {
|
2022-11-08 19:25:37 +01:00
|
|
|
// for built-in roles we want to remove the DB role ID element (role_)
|
2023-06-22 19:02:35 +02:00
|
|
|
if (
|
2023-09-06 01:34:39 +02:00
|
|
|
roleId.startsWith(DocumentType.ROLE) &&
|
|
|
|
(isBuiltin(roleId) || version === RoleIDVersion.NAME)
|
2023-06-22 19:02:35 +02:00
|
|
|
) {
|
2024-10-10 17:13:32 +02:00
|
|
|
const parts = roleId.split(SEPARATOR)
|
|
|
|
parts.shift()
|
|
|
|
return parts.join(SEPARATOR)
|
2021-02-09 14:01:45 +01:00
|
|
|
}
|
|
|
|
return roleId
|
|
|
|
}
|