diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index 7fb4eefc8b..097d6e84da 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -44,7 +44,7 @@ export class Role implements RoleDoc { permissionId: string inherits?: string version?: string - permissions = {} + permissions: Record = {} displayName?: string color?: string description?: string @@ -274,9 +274,9 @@ export async function getUserRoleHierarchy( // 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 export function checkForRoleResourceArray( - rolePerms: { [key: string]: string[] }, + rolePerms: Record, resourceId: string -) { +): Record { if (rolePerms && !Array.isArray(rolePerms[resourceId])) { const permLevel = rolePerms[resourceId] as any rolePerms[resourceId] = [permLevel] diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index 14a450a4de..0629ebc967 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -63,7 +63,9 @@ async function updatePermissionOnRole( // resource from another role and then adding to the new role for (let role of dbRoles) { let updated = false - const rolePermissions = role.permissions ? role.permissions : {} + const rolePermissions: Record = role.permissions + ? role.permissions + : {} // make sure its an array, also handle migrating if ( !rolePermissions[resourceId] || @@ -71,7 +73,7 @@ async function updatePermissionOnRole( ) { rolePermissions[resourceId] = typeof rolePermissions[resourceId] === "string" - ? [rolePermissions[resourceId] as unknown as string] + ? [rolePermissions[resourceId] as unknown as PermissionLevel] : [] } // handle the removal/updating the role which has this permission first diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts index 9c7fb2859a..28e9cb2779 100644 --- a/packages/server/src/api/controllers/role.ts +++ b/packages/server/src/api/controllers/role.ts @@ -17,7 +17,7 @@ import { SaveRoleResponse, UserCtx, UserMetadata, - UserRoles, + DocumentType, } from "@budibase/types" import { sdk as sharedSdk } from "@budibase/shared-core" import sdk from "../../sdk" @@ -89,9 +89,9 @@ export async function save(ctx: UserCtx) { _id = dbCore.prefixRoleID(_id) } - let dbRole - if (!isCreate) { - dbRole = await db.get(_id) + let dbRole: Role | undefined + if (!isCreate && _id?.startsWith(DocumentType.ROLE)) { + dbRole = await db.get(_id) } if (dbRole && dbRole.name !== name && isNewVersion) { ctx.throw(400, "Cannot change custom role name") @@ -104,8 +104,13 @@ export async function save(ctx: UserCtx) { color || "var(--spectrum-global-color-static-magenta-400)", permissionId ).addInheritance(inherits) - if (ctx.request.body._rev) { - role._rev = ctx.request.body._rev + + if (dbRole?.permissions && !role.permissions) { + role.permissions = dbRole.permissions + } + const foundRev = ctx.request.body._rev || dbRole?._rev + if (foundRev) { + role._rev = foundRev } const result = await db.put(role) if (isCreate) { diff --git a/packages/server/src/api/routes/utils/validators.ts b/packages/server/src/api/routes/utils/validators.ts index dd943f1282..2b50f81868 100644 --- a/packages/server/src/api/routes/utils/validators.ts +++ b/packages/server/src/api/routes/utils/validators.ts @@ -200,7 +200,7 @@ export function webhookValidator() { export function roleValidator() { const permLevelArray = Object.values(permissions.PermissionLevel) - + const permissionString = Joi.string().valid(...permLevelArray) return auth.joiValidator.body( Joi.object({ _id: OPTIONAL_STRING, @@ -216,7 +216,13 @@ export function roleValidator() { .valid(...Object.values(permissions.BuiltinPermissionID)) .required(), permissions: Joi.object() - .pattern(/.*/, [Joi.string().valid(...permLevelArray)]) + .pattern( + /.*/, + Joi.alternatives().try( + Joi.array().items(permissionString), + permissionString + ) + ) .optional(), inherits: OPTIONAL_STRING, }).unknown(true) diff --git a/packages/server/src/sdk/app/permissions/index.ts b/packages/server/src/sdk/app/permissions/index.ts index 88058b508d..3ed2fe0a83 100644 --- a/packages/server/src/sdk/app/permissions/index.ts +++ b/packages/server/src/sdk/app/permissions/index.ts @@ -44,7 +44,7 @@ export async function getResourcePerms( role.permissions || {}, resourceId ) - if (rolePerms[resourceId]?.indexOf(level) > -1) { + if (rolePerms[resourceId]?.indexOf(level as PermissionLevel) > -1) { permissions[level] = { role: roles.getExternalRoleID(role._id!, role.version), type: PermissionSource.EXPLICIT, diff --git a/packages/types/src/documents/app/role.ts b/packages/types/src/documents/app/role.ts index 0169da90c2..29687ce6e1 100644 --- a/packages/types/src/documents/app/role.ts +++ b/packages/types/src/documents/app/role.ts @@ -1,9 +1,10 @@ import { Document } from "../document" +import { PermissionLevel } from "../../sdk" export interface Role extends Document { permissionId: string inherits?: string - permissions: { [key: string]: string[] } + permissions: Record version?: string name: string displayName?: string diff --git a/packages/types/src/documents/global/user.ts b/packages/types/src/documents/global/user.ts index af5c11374d..99e01fedf0 100644 --- a/packages/types/src/documents/global/user.ts +++ b/packages/types/src/documents/global/user.ts @@ -74,9 +74,8 @@ export enum UserStatus { INACTIVE = "inactive", } -export interface UserRoles { - [key: string]: string -} +// specifies a map of app ID to role ID +export type UserRoles = Record // UTILITY TYPES