diff --git a/packages/backend-core/src/security/permissions.ts b/packages/backend-core/src/security/permissions.ts index 42189bba0c..6cacc12dd6 100644 --- a/packages/backend-core/src/security/permissions.ts +++ b/packages/backend-core/src/security/permissions.ts @@ -24,7 +24,7 @@ export enum PermissionType { QUERY = "query", } -class Permission { +export class Permission { type: PermissionType level: PermissionLevel @@ -34,7 +34,7 @@ class Permission { } } -function levelToNumber(perm: PermissionLevel) { +export function levelToNumber(perm: PermissionLevel) { switch (perm) { // not everything has execute privileges case PermissionLevel.EXECUTE: @@ -55,7 +55,7 @@ function levelToNumber(perm: PermissionLevel) { * @param {string} userPermLevel The permission level of the user. * @return {string[]} All the permission levels this user is allowed to carry out. */ -function getAllowedLevels(userPermLevel: PermissionLevel) { +export function getAllowedLevels(userPermLevel: PermissionLevel): string[] { switch (userPermLevel) { case PermissionLevel.EXECUTE: return [PermissionLevel.EXECUTE] @@ -64,9 +64,9 @@ function getAllowedLevels(userPermLevel: PermissionLevel) { case PermissionLevel.WRITE: case PermissionLevel.ADMIN: return [ + PermissionLevel.EXECUTE, PermissionLevel.READ, PermissionLevel.WRITE, - PermissionLevel.EXECUTE, ] default: return [] @@ -81,7 +81,7 @@ export enum BuiltinPermissionID { POWER = "power", } -const BUILTIN_PERMISSIONS = { +export const BUILTIN_PERMISSIONS = { PUBLIC: { _id: BuiltinPermissionID.PUBLIC, name: "Public", diff --git a/packages/backend-core/src/security/tests/permissions.spec.ts b/packages/backend-core/src/security/tests/permissions.spec.ts new file mode 100644 index 0000000000..caf8bb29a6 --- /dev/null +++ b/packages/backend-core/src/security/tests/permissions.spec.ts @@ -0,0 +1,145 @@ +import { cloneDeep } from "lodash" +import * as permissions from "../permissions" +import { BUILTIN_ROLE_IDS } from "../roles" + +describe("levelToNumber", () => { + it("should return 0 for EXECUTE", () => { + expect(permissions.levelToNumber(permissions.PermissionLevel.EXECUTE)).toBe( + 0 + ) + }) + + it("should return 1 for READ", () => { + expect(permissions.levelToNumber(permissions.PermissionLevel.READ)).toBe(1) + }) + + it("should return 2 for WRITE", () => { + expect(permissions.levelToNumber(permissions.PermissionLevel.WRITE)).toBe(2) + }) + + it("should return 3 for ADMIN", () => { + expect(permissions.levelToNumber(permissions.PermissionLevel.ADMIN)).toBe(3) + }) + + it("should return -1 for an unknown permission level", () => { + expect( + permissions.levelToNumber("unknown" as permissions.PermissionLevel) + ).toBe(-1) + }) +}) +describe("getAllowedLevels", () => { + it('should return ["execute"] for EXECUTE', () => { + expect( + permissions.getAllowedLevels(permissions.PermissionLevel.EXECUTE) + ).toEqual([permissions.PermissionLevel.EXECUTE]) + }) + + it('should return ["execute", "read"] for READ', () => { + expect( + permissions.getAllowedLevels(permissions.PermissionLevel.READ) + ).toEqual([ + permissions.PermissionLevel.EXECUTE, + permissions.PermissionLevel.READ, + ]) + }) + + it('should return ["execute", "read", "write"] for WRITE', () => { + expect( + permissions.getAllowedLevels(permissions.PermissionLevel.WRITE) + ).toEqual([ + permissions.PermissionLevel.EXECUTE, + permissions.PermissionLevel.READ, + permissions.PermissionLevel.WRITE, + ]) + }) + + it('should return ["execute", "read", "write"] for ADMIN', () => { + expect( + permissions.getAllowedLevels(permissions.PermissionLevel.ADMIN) + ).toEqual([ + permissions.PermissionLevel.EXECUTE, + permissions.PermissionLevel.READ, + permissions.PermissionLevel.WRITE, + ]) + }) + + it("should return [] for an unknown permission level", () => { + expect( + permissions.getAllowedLevels("unknown" as permissions.PermissionLevel) + ).toEqual([]) + }) +}) + +describe("doesHaveBasePermission", () => { + it("should return true if base permission has the required level", () => { + const permType = permissions.PermissionType.USER + const permLevel = permissions.PermissionLevel.READ + const rolesHierarchy = [ + { + roleId: BUILTIN_ROLE_IDS.ADMIN, + permissionId: permissions.BuiltinPermissionID.ADMIN, + }, + ] + expect( + permissions.doesHaveBasePermission(permType, permLevel, rolesHierarchy) + ).toBe(true) + }) + + it("should return false if base permission does not have the required level", () => { + const permType = permissions.PermissionType.APP + const permLevel = permissions.PermissionLevel.READ + const rolesHierarchy = [ + { + roleId: BUILTIN_ROLE_IDS.PUBLIC, + permissionId: permissions.BuiltinPermissionID.PUBLIC, + }, + ] + expect( + permissions.doesHaveBasePermission(permType, permLevel, rolesHierarchy) + ).toBe(false) + }) +}) + +describe("isPermissionLevelHigherThanRead", () => { + it("should return true if level is higher than read", () => { + expect( + permissions.isPermissionLevelHigherThanRead( + permissions.PermissionLevel.WRITE + ) + ).toBe(true) + }) + + it("should return false if level is read or lower", () => { + expect( + permissions.isPermissionLevelHigherThanRead( + permissions.PermissionLevel.READ + ) + ).toBe(false) + }) +}) + +describe("getBuiltinPermissions", () => { + it("returns a clone of the builtin permissions", () => { + const builtins = permissions.getBuiltinPermissions() + expect(builtins).toEqual(cloneDeep(permissions.BUILTIN_PERMISSIONS)) + expect(builtins).not.toBe(permissions.BUILTIN_PERMISSIONS) + }) +}) + +describe("getBuiltinPermissionByID", () => { + it("returns correct permission object for valid ID", () => { + const expectedPermission = { + _id: permissions.BuiltinPermissionID.PUBLIC, + name: "Public", + permissions: [ + new permissions.Permission( + permissions.PermissionType.WEBHOOK, + permissions.PermissionLevel.EXECUTE + ), + ], + } + expect(permissions.getBuiltinPermissionByID("public")).toEqual( + expectedPermission + ) + }) +})