From bb2892cbc2efcc69df325bdde7e9595a4c3c4e91 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 27 Sep 2023 14:21:25 +0100 Subject: [PATCH] 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) }