From bb2892cbc2efcc69df325bdde7e9595a4c3c4e91 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 14:21:25 +0100 Subject: [PATCH 1/6] Getting client library loading in Webstorm debug, as well as adding accessible roles endpoint. --- .../backend-core/src/security/permissions.ts | 9 +++++++-- .../client/src/components/ClientApp.svelte | 2 ++ packages/client/src/stores/index.js | 13 ++++++------ packages/client/src/stores/roles.js | 20 +++++++++++++++++++ packages/frontend-core/src/api/roles.js | 9 +++++++++ packages/server/src/api/controllers/role.ts | 14 +++++++++---- .../src/api/controllers/static/index.ts | 15 +++++++++++--- packages/server/src/api/routes/role.ts | 7 +++++++ packages/server/src/utilities/centralPath.ts | 4 ++-- 9 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 packages/client/src/stores/roles.js diff --git a/packages/backend-core/src/security/permissions.ts b/packages/backend-core/src/security/permissions.ts index 13083534b1..539bbaef27 100644 --- a/packages/backend-core/src/security/permissions.ts +++ b/packages/backend-core/src/security/permissions.ts @@ -1,8 +1,9 @@ -import { PermissionType, PermissionLevel } from "@budibase/types" -export { PermissionType, PermissionLevel } from "@budibase/types" +import { PermissionLevel, PermissionType } from "@budibase/types" import flatten from "lodash/flatten" import cloneDeep from "lodash/fp/cloneDeep" +export { PermissionType, PermissionLevel } from "@budibase/types" + export type RoleHierarchy = { permissionId: string }[] @@ -78,6 +79,7 @@ export const BUILTIN_PERMISSIONS = { permissions: [ new Permission(PermissionType.QUERY, PermissionLevel.READ), new Permission(PermissionType.TABLE, PermissionLevel.READ), + new Permission(PermissionType.APP, PermissionLevel.READ), ], }, WRITE: { @@ -88,6 +90,7 @@ export const BUILTIN_PERMISSIONS = { new Permission(PermissionType.TABLE, PermissionLevel.WRITE), new Permission(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new Permission(PermissionType.APP, PermissionLevel.READ), ], }, POWER: { @@ -99,6 +102,7 @@ export const BUILTIN_PERMISSIONS = { new Permission(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), new Permission(PermissionType.WEBHOOK, PermissionLevel.READ), new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new Permission(PermissionType.APP, PermissionLevel.READ), ], }, ADMIN: { @@ -111,6 +115,7 @@ export const BUILTIN_PERMISSIONS = { new Permission(PermissionType.WEBHOOK, PermissionLevel.READ), new Permission(PermissionType.QUERY, PermissionLevel.ADMIN), new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new Permission(PermissionType.APP, PermissionLevel.READ), ], }, } diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 4efa8af4e6..2db1a20485 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -15,6 +15,7 @@ builderStore, themeStore, appStore, + roleStore, devToolsStore, devToolsEnabled, } from "stores" @@ -84,6 +85,7 @@ onMount(async () => { await initialise() await authStore.actions.fetchUser() + await roleStore.actions.fetchAccessibleRoles() dataLoaded = true if (get(builderStore).inBuilder) { builderStore.actions.notifyLoaded() diff --git a/packages/client/src/stores/index.js b/packages/client/src/stores/index.js index 9530792353..1b1b9d46bc 100644 --- a/packages/client/src/stores/index.js +++ b/packages/client/src/stores/index.js @@ -11,12 +11,13 @@ export { stateStore } from "./state" export { themeStore } from "./theme" export { devToolsStore } from "./devTools" export { componentStore } from "./components" -export { uploadStore } from "./uploads.js" -export { rowSelectionStore } from "./rowSelection.js" -export { blockStore } from "./blocks.js" +export { uploadStore } from "./uploads" +export { rowSelectionStore } from "./rowSelection" +export { blockStore } from "./blocks" export { environmentStore } from "./environment" -export { eventStore } from "./events.js" -export { orgStore } from "./org.js" +export { eventStore } from "./events" +export { orgStore } from "./org" +export { roleStore } from "./roles" export { dndStore, dndIndex, @@ -25,7 +26,7 @@ export { dndIsNewComponent, dndIsDragging, } from "./dnd" -export { sidePanelStore } from "./sidePanel.js" +export { sidePanelStore } from "./sidePanel" // Context stores are layered and duplicated, so it is not a singleton export { createContextStore } from "./context" diff --git a/packages/client/src/stores/roles.js b/packages/client/src/stores/roles.js new file mode 100644 index 0000000000..afb194969d --- /dev/null +++ b/packages/client/src/stores/roles.js @@ -0,0 +1,20 @@ +import { API } from "api" +import { writable } from "svelte/store" + +const createRoleStore = () => { + const store = writable([]) + + // Fetches the user object if someone is logged in and has reloaded the page + const fetchAccessibleRoles = async () => { + const accessible = await API.getAccessibleRoles() + // Use the app self if present, otherwise fallback to the global self + store.set(accessible || []) + } + + return { + subscribe: store.subscribe, + actions: { fetchAccessibleRoles }, + } +} + +export const roleStore = createRoleStore() diff --git a/packages/frontend-core/src/api/roles.js b/packages/frontend-core/src/api/roles.js index 0b8a866a00..16bb048d01 100644 --- a/packages/frontend-core/src/api/roles.js +++ b/packages/frontend-core/src/api/roles.js @@ -38,4 +38,13 @@ export const buildRoleEndpoints = API => ({ url: `/api/global/roles/${appId}`, }) }, + + /** + * For the logging in user and current app - retrieves accessible roles. + */ + getAccessibleRoles: async () => { + return await API.get({ + url: `/api/roles/accessible`, + }) + }, }) diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts index 5ab1ad3502..7094481934 100644 --- a/packages/server/src/api/controllers/role.ts +++ b/packages/server/src/api/controllers/role.ts @@ -1,6 +1,6 @@ -import { roles, context, events, db as dbCore } from "@budibase/backend-core" +import { context, db as dbCore, events, roles } from "@budibase/backend-core" import { getUserMetadataParams, InternalTables } from "../../db/utils" -import { UserCtx, Database, UserRoles, Role } from "@budibase/types" +import { Database, Role, UserCtx, UserRoles } from "@budibase/types" import sdk from "../../sdk" const UpdateRolesOptions = { @@ -94,7 +94,6 @@ export async function save(ctx: UserCtx) { ) role._rev = result.rev ctx.body = role - ctx.message = `Role '${role.name}' created successfully.` } export async function destroy(ctx: UserCtx) { @@ -129,5 +128,12 @@ export async function destroy(ctx: UserCtx) { role.version ) ctx.message = `Role ${ctx.params.roleId} deleted successfully` - ctx.status = 200 +} + +export async function accessible(ctx: UserCtx) { + const user = ctx.user + if (!user || !user.roleId) { + ctx.throw(400, "User does not have a valid role") + } + ctx.body = await roles.getUserRoleHierarchy(user.roleId!) } diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 3ea07bcc20..9c30b8a6a6 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -107,6 +107,11 @@ export const serveApp = async function (ctx: any) { //Public Settings const { config } = await configs.getSettingsConfigDoc() const branding = await pro.branding.getBrandingConfig(config) + // incase running direct from TS + let appHbsPath = join(__dirname, "app.hbs") + if (!fs.existsSync(appHbsPath)) { + appHbsPath = join(__dirname, "templates", "app.hbs") + } let db try { @@ -138,7 +143,7 @@ export const serveApp = async function (ctx: any) { ? objectStore.getGlobalFileUrl("settings", "logoUrl") : "", }) - const appHbs = loadHandlebarsFile(`${__dirname}/app.hbs`) + const appHbs = loadHandlebarsFile(appHbsPath) ctx.body = await processString(appHbs, { head, body: html, @@ -166,7 +171,7 @@ export const serveApp = async function (ctx: any) { : "", }) - const appHbs = loadHandlebarsFile(`${__dirname}/app.hbs`) + const appHbs = loadHandlebarsFile(appHbsPath) ctx.body = await processString(appHbs, { head, body: html, @@ -193,8 +198,12 @@ export const serveBuilderPreview = async function (ctx: any) { } export const serveClientLibrary = async function (ctx: any) { + let rootPath = join(NODE_MODULES_PATH, "@budibase", "client", "dist") + if (!fs.existsSync(rootPath)) { + rootPath = join(__dirname, "..", "..", "..", "..", "client") + } return send(ctx, "budibase-client.js", { - root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"), + root: rootPath, }) } diff --git a/packages/server/src/api/routes/role.ts b/packages/server/src/api/routes/role.ts index 816105c710..f88de41b36 100644 --- a/packages/server/src/api/routes/role.ts +++ b/packages/server/src/api/routes/role.ts @@ -3,10 +3,17 @@ import * as controller from "../controllers/role" import authorized from "../../middleware/authorized" import { permissions } from "@budibase/backend-core" import { roleValidator } from "./utils/validators" +import { PermissionLevel, PermissionType } from "@budibase/types" const router: Router = new Router() router + // retrieve a list of the roles a user can access + .get( + "/api/roles/accessible", + authorized(PermissionType.APP, PermissionLevel.READ), + controller.accessible + ) .post( "/api/roles", authorized(permissions.BUILDER), diff --git a/packages/server/src/utilities/centralPath.ts b/packages/server/src/utilities/centralPath.ts index b9c0a8aedf..bd0578c7ce 100644 --- a/packages/server/src/utilities/centralPath.ts +++ b/packages/server/src/utilities/centralPath.ts @@ -8,7 +8,7 @@ import path from "path" * @param args Any number of string arguments to add to a path * @returns {string} The final path ready to use */ -export function join(...args: any) { +export function join(...args: string[]) { return path.join(...args) } @@ -17,6 +17,6 @@ export function join(...args: any) { * @param args Any number of string arguments to add to a path * @returns {string} The final path ready to use */ -export function resolve(...args: any) { +export function resolve(...args: string[]) { return path.resolve(...args) } From e9161780468747f0790cbb68ab17f26c1aecf2da Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 15:46:04 +0100 Subject: [PATCH 2/6] Adding test case for new endpoint and covering public, builder and normal roles. --- packages/backend-core/src/security/roles.ts | 7 ++- .../client/src/components/app/Layout.svelte | 12 +++-- packages/client/src/sdk.js | 2 + packages/server/src/api/controllers/role.ts | 15 +++++-- .../src/api/controllers/static/index.ts | 5 ++- packages/server/src/api/routes/role.ts | 8 +--- .../server/src/api/routes/tests/role.spec.js | 44 +++++++++++++++---- .../src/tests/utilities/TestConfiguration.ts | 9 ++++ 8 files changed, 73 insertions(+), 29 deletions(-) diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index e87df2e9c9..3497ecc972 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -253,7 +253,10 @@ export function checkForRoleResourceArray( * Given an app ID this will retrieve all of the roles that are currently within that app. * @return {Promise} An array of the role objects that were found. */ -export async function getAllRoles(appId?: string): Promise { +export async function getAllRoles( + appId?: string, + opts?: { idOnly: boolean } +): Promise { if (appId) { return doWithDB(appId, internal) } else { @@ -308,7 +311,7 @@ export async function getAllRoles(appId?: string): Promise { ) } } - return roles + return opts?.idOnly ? roles.map(role => role._id) : roles } } diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index e557874edb..5a0acd25be 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -4,15 +4,14 @@ import { Heading, Icon, clickOutside } from "@budibase/bbui" import { FieldTypes } from "constants" import active from "svelte-spa-router/active" - import { RoleUtils } from "@budibase/frontend-core" const sdk = getContext("sdk") const { routeStore, + roleStore, styleable, linkable, builderStore, - currentRole, sidePanelStore, } = sdk const component = getContext("component") @@ -61,7 +60,8 @@ }) setContext("layout", store) - $: validLinks = getValidLinks(links, $currentRole) + $: accessibleRoles = $roleStore + $: validLinks = getValidLinks(links) $: typeClass = NavigationClasses[navigation] || NavigationClasses.None $: navWidthClass = WidthClasses[navWidth || width] || WidthClasses.Large $: pageWidthClass = WidthClasses[pageWidth || width] || WidthClasses.Large @@ -99,14 +99,12 @@ } } - const getValidLinks = (allLinks, role) => { + const getValidLinks = allLinks => { // Strip links missing required info let validLinks = (allLinks || []).filter(link => link.text && link.url) - // Filter to only links allowed by the current role - const priority = RoleUtils.getRolePriority(role) return validLinks.filter(link => { - return !link.roleId || RoleUtils.getRolePriority(link.roleId) <= priority + return accessibleRoles?.find(roleId => roleId === link.roleId) }) } diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js index 237334ca57..1d77f3e2da 100644 --- a/packages/client/src/sdk.js +++ b/packages/client/src/sdk.js @@ -13,6 +13,7 @@ import { sidePanelStore, dndIsDragging, confirmationStore, + roleStore, } from "stores" import { styleable } from "utils/styleable" import { linkable } from "utils/linkable" @@ -39,6 +40,7 @@ export default { dndIsDragging, currentRole, confirmationStore, + roleStore, styleable, linkable, getAction, diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts index 7094481934..e72517afc3 100644 --- a/packages/server/src/api/controllers/role.ts +++ b/packages/server/src/api/controllers/role.ts @@ -1,6 +1,7 @@ import { context, db as dbCore, events, roles } from "@budibase/backend-core" import { getUserMetadataParams, InternalTables } from "../../db/utils" import { Database, Role, UserCtx, UserRoles } from "@budibase/types" +import { sdk as sharedSdk } from "@budibase/shared-core" import sdk from "../../sdk" const UpdateRolesOptions = { @@ -128,12 +129,18 @@ export async function destroy(ctx: UserCtx) { role.version ) ctx.message = `Role ${ctx.params.roleId} deleted successfully` + ctx.status = 200 } export async function accessible(ctx: UserCtx) { - const user = ctx.user - if (!user || !user.roleId) { - ctx.throw(400, "User does not have a valid role") + let roleId = ctx.user?.roleId + if (!roleId) { + roleId = roles.BUILTIN_ROLE_IDS.PUBLIC + } + if (ctx.user && sharedSdk.users.isAdminOrBuilder(ctx.user)) { + const appId = context.getAppId() + ctx.body = await roles.getAllRoles(appId, { idOnly: true }) + } else { + ctx.body = await roles.getUserRoleHierarchy(roleId!, { idOnly: true }) } - ctx.body = await roles.getUserRoleHierarchy(user.roleId!) } diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 9c30b8a6a6..bbf9dd34f5 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -199,8 +199,9 @@ export const serveBuilderPreview = async function (ctx: any) { export const serveClientLibrary = async function (ctx: any) { let rootPath = join(NODE_MODULES_PATH, "@budibase", "client", "dist") - if (!fs.existsSync(rootPath)) { - rootPath = join(__dirname, "..", "..", "..", "..", "client") + // incase running from TS directly + if (env.isDev() && !fs.existsSync(rootPath)) { + rootPath = join(require.resolve("@budibase/client"), "..") } return send(ctx, "budibase-client.js", { root: rootPath, diff --git a/packages/server/src/api/routes/role.ts b/packages/server/src/api/routes/role.ts index f88de41b36..595779bf11 100644 --- a/packages/server/src/api/routes/role.ts +++ b/packages/server/src/api/routes/role.ts @@ -3,17 +3,13 @@ import * as controller from "../controllers/role" import authorized from "../../middleware/authorized" import { permissions } from "@budibase/backend-core" import { roleValidator } from "./utils/validators" -import { PermissionLevel, PermissionType } from "@budibase/types" const router: Router = new Router() router // retrieve a list of the roles a user can access - .get( - "/api/roles/accessible", - authorized(PermissionType.APP, PermissionLevel.READ), - controller.accessible - ) + // needs to be public for public screens + .get("/api/roles/accessible", controller.accessible) .post( "/api/roles", authorized(permissions.BUILDER), diff --git a/packages/server/src/api/routes/tests/role.spec.js b/packages/server/src/api/routes/tests/role.spec.js index a6418c2277..c8e383d5ed 100644 --- a/packages/server/src/api/routes/tests/role.spec.js +++ b/packages/server/src/api/routes/tests/role.spec.js @@ -15,7 +15,7 @@ describe("/roles", () => { await config.init() }) - const createRole = async (role) => { + const createRole = async role => { if (!role) { role = basicRole() } @@ -33,9 +33,6 @@ describe("/roles", () => { const role = basicRole() const res = await createRole(role) - expect(res.res.statusMessage).toEqual( - `Role '${role.name}' created successfully.` - ) expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() expect(events.role.updated).not.toBeCalled() @@ -51,9 +48,6 @@ describe("/roles", () => { jest.clearAllMocks() res = await createRole(res.body) - expect(res.res.statusMessage).toEqual( - `Role '${role.name}' created successfully.` - ) expect(res.body._id).toBeDefined() expect(res.body._rev).toBeDefined() expect(events.role.created).not.toBeCalled() @@ -99,7 +93,11 @@ describe("/roles", () => { it("should be able to get the role with a permission added", async () => { const table = await config.createTable() - await config.api.permission.set({ roleId: BUILTIN_ROLE_IDS.POWER, resourceId: table._id, level: PermissionLevel.READ }) + await config.api.permission.set({ + roleId: BUILTIN_ROLE_IDS.POWER, + resourceId: table._id, + level: PermissionLevel.READ, + }) const res = await request .get(`/api/roles`) .set(config.defaultHeaders()) @@ -131,4 +129,34 @@ describe("/roles", () => { expect(events.role.deleted).toBeCalledWith(customRole) }) }) + + describe("accessible", () => { + it("should be able to fetch accessible roles (with builder)", async () => { + const res = await request + .get("/api/roles/accessible") + .set(config.defaultHeaders()) + .expect(200) + expect(res.body.length).toBe(5) + expect(typeof res.body[0]).toBe("string") + }) + + it("should be able to fetch accessible roles (basic user)", async () => { + const res = await request + .get("/api/roles/accessible") + .set(await config.basicRoleHeaders()) + .expect(200) + expect(res.body.length).toBe(2) + expect(res.body[0]).toBe("BASIC") + expect(res.body[1]).toBe("PUBLIC") + }) + + it("should be able to fetch accessible roles (no user)", async () => { + const res = await request + .get("/api/roles/accessible") + .set(config.publicHeaders()) + .expect(200) + expect(res.body.length).toBe(1) + expect(res.body[0]).toBe("PUBLIC") + }) + }) }) diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 799e6f34e9..21b6463ce7 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -425,6 +425,15 @@ class TestConfiguration { return headers } + async basicRoleHeaders() { + return await this.roleHeaders({ + email: this.defaultUserValues.email, + builder: false, + prodApp: true, + roleId: roles.BUILTIN_ROLE_IDS.BASIC, + }) + } + async roleHeaders({ email = this.defaultUserValues.email, roleId = roles.BUILTIN_ROLE_IDS.ADMIN, From 31103d6f25852bcc2870c010c2cf36b7271eee8a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 15:51:39 +0100 Subject: [PATCH 3/6] Fixing comment. --- packages/frontend-core/src/api/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/api/roles.js b/packages/frontend-core/src/api/roles.js index 16bb048d01..80b8028c12 100644 --- a/packages/frontend-core/src/api/roles.js +++ b/packages/frontend-core/src/api/roles.js @@ -40,7 +40,7 @@ export const buildRoleEndpoints = API => ({ }, /** - * For the logging in user and current app - retrieves accessible roles. + * For the logged in user and current app - retrieves accessible roles. */ getAccessibleRoles: async () => { return await API.get({ From 395969e0f06233dc0b9c6c685107c6f2134b20d1 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 16:24:12 +0100 Subject: [PATCH 4/6] Fixing build issue,. --- packages/backend-core/src/security/roles.ts | 34 ++++++++++--------- packages/server/src/api/controllers/role.ts | 4 +-- .../server/src/api/controllers/routing.ts | 4 +-- packages/server/src/middleware/authorized.ts | 4 +-- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/packages/backend-core/src/security/roles.ts b/packages/backend-core/src/security/roles.ts index 3497ecc972..24279e6b5c 100644 --- a/packages/backend-core/src/security/roles.ts +++ b/packages/backend-core/src/security/roles.ts @@ -215,21 +215,23 @@ async function getAllUserRoles(userRoleId?: string): Promise { return roles } +export async function getUserRoleIdHierarchy( + userRoleId?: string +): Promise { + const roles = await getUserRoleHierarchy(userRoleId) + return roles.map(role => role._id!) +} + /** * 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. * @param {string} userRoleId The user's role ID, this can be found in their access token. - * @param {object} opts Various options, such as whether to only retrieve the IDs (default true). - * @returns {Promise} returns an ordered array of the roles, with the first being their + * @returns {Promise} returns an ordered array of the roles, with the first being their * highest level of access and the last being the lowest level. */ -export async function getUserRoleHierarchy( - userRoleId?: string, - opts = { idOnly: true } -) { +export async function getUserRoleHierarchy(userRoleId?: string) { // special case, if they don't have a role then they are a public user - const roles = await getAllUserRoles(userRoleId) - return opts.idOnly ? roles.map(role => role._id) : roles + return getAllUserRoles(userRoleId) } // this function checks that the provided permissions are in an array format @@ -249,14 +251,16 @@ export function checkForRoleResourceArray( return rolePerms } +export async function getAllRoleIds(appId?: string) { + const roles = await getAllRoles(appId) + return roles.map(role => role._id) +} + /** * Given an app ID this will retrieve all of the roles that are currently within that app. * @return {Promise} An array of the role objects that were found. */ -export async function getAllRoles( - appId?: string, - opts?: { idOnly: boolean } -): Promise { +export async function getAllRoles(appId?: string): Promise { if (appId) { return doWithDB(appId, internal) } else { @@ -311,7 +315,7 @@ export async function getAllRoles( ) } } - return opts?.idOnly ? roles.map(role => role._id) : roles + return roles } } @@ -335,9 +339,7 @@ export class AccessController { } let roleIds = userRoleId ? this.userHierarchies[userRoleId] : null if (!roleIds && userRoleId) { - roleIds = (await getUserRoleHierarchy(userRoleId, { - idOnly: true, - })) as string[] + roleIds = await getUserRoleIdHierarchy(userRoleId) this.userHierarchies[userRoleId] = roleIds } diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts index e72517afc3..ed23009706 100644 --- a/packages/server/src/api/controllers/role.ts +++ b/packages/server/src/api/controllers/role.ts @@ -139,8 +139,8 @@ export async function accessible(ctx: UserCtx) { } if (ctx.user && sharedSdk.users.isAdminOrBuilder(ctx.user)) { const appId = context.getAppId() - ctx.body = await roles.getAllRoles(appId, { idOnly: true }) + ctx.body = await roles.getAllRoleIds(appId) } else { - ctx.body = await roles.getUserRoleHierarchy(roleId!, { idOnly: true }) + ctx.body = await roles.getUserRoleIdHierarchy(roleId!) } } diff --git a/packages/server/src/api/controllers/routing.ts b/packages/server/src/api/controllers/routing.ts index 1bfd289637..f356a1cd59 100644 --- a/packages/server/src/api/controllers/routing.ts +++ b/packages/server/src/api/controllers/routing.ts @@ -63,9 +63,7 @@ export async function fetch(ctx: UserCtx) { export async function clientFetch(ctx: UserCtx) { const routing = await getRoutingStructure() let roleId = ctx.user?.role?._id - const roleIds = (await roles.getUserRoleHierarchy(roleId, { - idOnly: true, - })) as string[] + const roleIds = await roles.getUserRoleIdHierarchy(roleId) for (let topLevel of Object.values(routing.routes) as any) { for (let subpathKey of Object.keys(topLevel.subpaths)) { let found = false diff --git a/packages/server/src/middleware/authorized.ts b/packages/server/src/middleware/authorized.ts index b2ffeacaf8..8d148b04bf 100644 --- a/packages/server/src/middleware/authorized.ts +++ b/packages/server/src/middleware/authorized.ts @@ -55,9 +55,7 @@ const checkAuthorizedResource = async ( ) => { // get the user's roles const roleId = ctx.roleId || roles.BUILTIN_ROLE_IDS.PUBLIC - const userRoles = (await roles.getUserRoleHierarchy(roleId, { - idOnly: false, - })) as Role[] + const userRoles = await roles.getUserRoleIdHierarchy(roleId) const permError = "User does not have permission" // check if the user has the required role if (resourceRoles.length > 0) { From e3469ed38d9550fd7acb9b3f3219c17833dcfa28 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 16:52:20 +0100 Subject: [PATCH 5/6] Fixing dev-tools handling of custom roles and build issue - PR comments. --- .../client/src/components/ClientApp.svelte | 1 - .../client/src/components/app/Layout.svelte | 7 +-- .../components/devtools/DevToolsHeader.svelte | 55 +++++++++++-------- packages/client/src/stores/roles.js | 4 ++ packages/server/src/middleware/authorized.ts | 2 +- 5 files changed, 39 insertions(+), 30 deletions(-) diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 2db1a20485..912fc60fcd 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -85,7 +85,6 @@ onMount(async () => { await initialise() await authStore.actions.fetchUser() - await roleStore.actions.fetchAccessibleRoles() dataLoaded = true if (get(builderStore).inBuilder) { builderStore.actions.notifyLoaded() diff --git a/packages/client/src/components/app/Layout.svelte b/packages/client/src/components/app/Layout.svelte index 5a0acd25be..e482e6b336 100644 --- a/packages/client/src/components/app/Layout.svelte +++ b/packages/client/src/components/app/Layout.svelte @@ -60,8 +60,7 @@ }) setContext("layout", store) - $: accessibleRoles = $roleStore - $: validLinks = getValidLinks(links) + $: validLinks = getValidLinks(links, $roleStore) $: typeClass = NavigationClasses[navigation] || NavigationClasses.None $: navWidthClass = WidthClasses[navWidth || width] || WidthClasses.Large $: pageWidthClass = WidthClasses[pageWidth || width] || WidthClasses.Large @@ -99,12 +98,12 @@ } } - const getValidLinks = allLinks => { + const getValidLinks = (allLinks, userRoleHierarchy) => { // Strip links missing required info let validLinks = (allLinks || []).filter(link => link.text && link.url) // Filter to only links allowed by the current role return validLinks.filter(link => { - return accessibleRoles?.find(roleId => roleId === link.roleId) + return userRoleHierarchy?.find(roleId => roleId === link.roleId) }) } diff --git a/packages/client/src/components/devtools/DevToolsHeader.svelte b/packages/client/src/components/devtools/DevToolsHeader.svelte index a60cec8ce1..a15e8351a5 100644 --- a/packages/client/src/components/devtools/DevToolsHeader.svelte +++ b/packages/client/src/components/devtools/DevToolsHeader.svelte @@ -1,32 +1,39 @@
@@ -34,7 +41,7 @@