diff --git a/packages/auth/src/security/roles.js b/packages/auth/src/security/roles.js index 53e1b90d73..baa8fc40dc 100644 --- a/packages/auth/src/security/roles.js +++ b/packages/auth/src/security/roles.js @@ -147,7 +147,7 @@ exports.getRole = async (appId, roleId) => { */ async function getAllUserRoles(appId, userRoleId) { if (!userRoleId) { - return [BUILTIN_IDS.PUBLIC] + return [BUILTIN_IDS.BASIC] } let currentRole = await exports.getRole(appId, userRoleId) let roles = currentRole ? [currentRole] : [] @@ -226,7 +226,7 @@ exports.getAllRoles = async appId => { dbRole => exports.getExternalRoleID(dbRole._id) === builtinRoleId )[0] if (dbBuiltin == null) { - roles.push(builtinRole) + roles.push(builtinRole || builtinRoles.BASIC) } else { // remove role and all back after combining with the builtin roles = roles.filter(role => role._id !== dbBuiltin._id) diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index 3e1080f2cd..77a8526a15 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -15,8 +15,12 @@ const dispatch = createEventDispatcher() - $: selected = $tab.title - $: selected = dispatch("select", selected) + $: { + if ($tab.title !== selected) { + selected = $tab.title + dispatch("select", selected) + } + } let top, left, width, height $: calculateIndicatorLength($tab) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 9746b43103..ac837978a9 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -137,6 +137,9 @@ export const getFrontendStore = () => { save: async screen => { const creatingNewScreen = screen._id === undefined const response = await api.post(`/api/screens`, screen) + if (response.status !== 200) { + return + } screen = await response.json() await store.actions.routing.fetch() diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte index f58b9f197f..8b7417c41f 100644 --- a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -10,8 +10,10 @@ let selectedRole = {} let errors = [] let builtInRoles = ["Admin", "Power", "Basic", "Public"] + // Don't allow editing of public role + $: editableRoles = $roles.filter(role => role._id !== "PUBLIC") $: selectedRoleId = selectedRole._id - $: otherRoles = $roles.filter(role => role._id !== selectedRoleId) + $: otherRoles = editableRoles.filter(role => role._id !== selectedRoleId) $: isCreating = selectedRoleId == null || selectedRoleId === "" const fetchBasePermissions = async () => { @@ -96,7 +98,7 @@ label="Role" value={selectedRoleId} on:change={changeRole} - options={$roles} + options={editableRoles} placeholder="Create new role" getOptionValue={role => role._id} getOptionLabel={role => role.name} diff --git a/packages/builder/src/components/design/AppPreview/iframeTemplate.js b/packages/builder/src/components/design/AppPreview/iframeTemplate.js index eb4f22f857..b659a041e4 100644 --- a/packages/builder/src/components/design/AppPreview/iframeTemplate.js +++ b/packages/builder/src/components/design/AppPreview/iframeTemplate.js @@ -27,8 +27,7 @@ export default ` align-items: stretch; } html.loaded { - box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.1); - + box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.1); } body { flex: 1 1 auto; diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte index 2a86afbf9a..1d2d772a50 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterEditor.svelte @@ -21,7 +21,7 @@ export let value = [] export let componentInstance let drawer - let tempValue = value + let tempValue = value || [] $: numFilters = Array.isArray(tempValue) ? tempValue.length @@ -31,15 +31,6 @@ $: schemaFields = Object.values(schema || {}) $: internalTable = dataSource?.type === "table" - // Reset value if value is wrong type for the datasource. - // Lucene editor needs an array, and simple editor needs an object. - $: { - if (!Array.isArray(value)) { - tempValue = [] - dispatch("change", []) - } - } - const saveFilter = async () => { dispatch("change", tempValue) notifications.success("Filters saved.") diff --git a/packages/builder/src/pages/builder/apps/index.svelte b/packages/builder/src/pages/builder/apps/index.svelte index 0296f2e5b7..69b6e770f6 100644 --- a/packages/builder/src/pages/builder/apps/index.svelte +++ b/packages/builder/src/pages/builder/apps/index.svelte @@ -28,12 +28,7 @@ onMount(async () => { await organisation.init() await apps.load() - // Skip the portal if you only have one app - if (!$auth.isBuilder && $apps.filter(publishedAppsOnly).length === 1) { - window.location = `/${publishedApps[0].prodId}` - } else { - loaded = true - } + loaded = true }) const publishedAppsOnly = app => app.status === AppStatus.DEPLOYED diff --git a/packages/builder/src/pages/builder/portal/manage/_layout.svelte b/packages/builder/src/pages/builder/portal/manage/_layout.svelte index 98ae140b25..e6c73bc596 100644 --- a/packages/builder/src/pages/builder/portal/manage/_layout.svelte +++ b/packages/builder/src/pages/builder/portal/manage/_layout.svelte @@ -9,8 +9,6 @@ $redirect("../") } } - - $: console.log($page) {#if $auth.isAdmin} diff --git a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte index 912506d0cf..8e029d73b8 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/[userId].svelte @@ -33,7 +33,7 @@ role: {}, } - $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "" + $: defaultRoleId = $userFetch?.data?.builder?.global ? "ADMIN" : "BASIC" // Merge the Apps list and the roles response to get something that makes sense for the table $: appList = Object.keys($apps?.data).map(id => { const role = $userFetch?.data?.roles?.[id] || defaultRoleId diff --git a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte index e881fa37d2..332be8e2d4 100644 --- a/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte +++ b/packages/builder/src/pages/builder/portal/manage/users/_components/UpdateRolesModal.svelte @@ -9,7 +9,9 @@ const dispatch = createEventDispatcher() const roles = app.roles - let options = roles.map(role => ({ value: role._id, label: role.name })) + let options = roles + .map(role => ({ value: role._id, label: role.name })) + .filter(role => role.value !== "PUBLIC") let selectedRole = user?.roles?.[app?._id] async function updateUserRoles() { diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js index fcdd9dbd5b..280b580164 100644 --- a/packages/client/src/api/api.js +++ b/packages/client/src/api/api.js @@ -43,10 +43,9 @@ const makeApiCall = async ({ method, url, body, json = true }) => { case 400: return handleError(`${url}: Bad Request`) case 403: - // reload the page incase the token has expired - if (!url.includes("self")) { - location.reload() - } + notificationStore.danger( + "Your session has expired, or you don't have permission to access that data" + ) return handleError(`${url}: Forbidden`) default: if (response.status >= 200 && response.status < 400) { diff --git a/packages/client/src/api/auth.js b/packages/client/src/api/auth.js index 426d4f08d0..6ea105d9f9 100644 --- a/packages/client/src/api/auth.js +++ b/packages/client/src/api/auth.js @@ -24,7 +24,12 @@ export const logIn = async ({ email, password }) => { export const fetchSelf = async () => { const user = await API.get({ url: "/api/self" }) if (user?._id) { - return (await enrichRows([user], TableNames.USERS))[0] + if (user.roleId === "PUBLIC") { + // Don't try to enrich a public user as it will 403 + return user + } else { + return (await enrichRows([user], TableNames.USERS))[0] + } } else { return null } diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 2675531a63..1076c0f568 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -19,6 +19,8 @@ import SettingsBar from "./preview/SettingsBar.svelte" import SelectionIndicator from "./preview/SelectionIndicator.svelte" import HoverIndicator from "./preview/HoverIndicator.svelte" + import { Layout, Heading, Body } from "@budibase/bbui" + import ErrorSVG from "../../../builder/assets/error.svg" // Provide contexts setContext("sdk", SDK) @@ -26,6 +28,7 @@ setContext("context", createContextStore()) let dataLoaded = false + let permissionError = false // Load app config onMount(async () => { @@ -47,12 +50,21 @@ }, ] - // Redirect to home layout if no matching route + // Handle no matching route - this is likely a permission error $: { if (dataLoaded && $routeStore.routerLoaded && !$routeStore.activeRoute) { if ($authStore) { - routeStore.actions.navigate("/") + // There is a logged in user, so handle them + if ($screenStore.screens.length) { + // Screens exist so navigate back to the home screen + const firstRoute = $screenStore.screens[0].routing?.route ?? "/" + routeStore.actions.navigate(firstRoute) + } else { + // No screens likely means the user has no permissions to view this app + permissionError = true + } } else { + // The user is not logged in, redirect them to login const returnUrl = `${window.location.pathname}${window.location.hash}` const encodedUrl = encodeURIComponent(returnUrl) window.location = `/builder/auth/login?returnUrl=${encodedUrl}` @@ -64,36 +76,46 @@ $builderStore.theme || $appStore.application?.theme || "spectrum--light" -{#if dataLoaded && $screenStore.activeLayout} +{#if dataLoaded}