diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml index c6ded3cee2..c7c4481122 100644 --- a/charts/budibase/templates/app-service-deployment.yaml +++ b/charts/budibase/templates/app-service-deployment.yaml @@ -252,4 +252,10 @@ spec: {{ end }} restartPolicy: Always serviceAccountName: "" + {{ if .Values.services.apps.ndots }} + dnsConfig: + options: + - name: ndots + value: {{ .Values.services.apps.ndots | quote }} + {{ end }} status: {} diff --git a/charts/budibase/templates/automation-worker-service-deployment.yaml b/charts/budibase/templates/automation-worker-service-deployment.yaml index b7eece6b85..36c3a8ffbf 100644 --- a/charts/budibase/templates/automation-worker-service-deployment.yaml +++ b/charts/budibase/templates/automation-worker-service-deployment.yaml @@ -227,6 +227,7 @@ spec: resources: {{- toYaml . | nindent 10 }} {{ end }} + {{ if .Values.services.automationWorkers.command }} command: {{- toYaml .Values.services.automationWorkers.command | nindent 10 }} {{ end }} @@ -251,6 +252,11 @@ spec: {{ end }} restartPolicy: Always serviceAccountName: "" - {{ if .Values.services.automationWorkers.command }}} + {{ if .Values.services.automationWorkers.ndots }} + dnsConfig: + options: + - name: ndots + value: {{ .Values.services.automationWorkers.ndots | quote }} + {{ end }} status: {} {{- end }} \ No newline at end of file diff --git a/charts/budibase/templates/proxy-service-deployment.yaml b/charts/budibase/templates/proxy-service-deployment.yaml index 2e6217008b..233028cafe 100644 --- a/charts/budibase/templates/proxy-service-deployment.yaml +++ b/charts/budibase/templates/proxy-service-deployment.yaml @@ -109,4 +109,10 @@ spec: {{- toYaml .Values.services.proxy.args | nindent 8 }} {{ end }} volumes: + {{ if .Values.services.proxy.ndots }} + dnsConfig: + options: + - name: ndots + value: {{ .Values.services.proxy.ndots | quote }} + {{ end }} status: {} diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml index 04791df869..2f97508ae3 100644 --- a/charts/budibase/templates/worker-service-deployment.yaml +++ b/charts/budibase/templates/worker-service-deployment.yaml @@ -238,4 +238,10 @@ spec: {{ end }} restartPolicy: Always serviceAccountName: "" + {{ if .Values.services.worker.ndots }} + dnsConfig: + options: + - name: ndots + value: {{ .Values.services.worker.ndots | quote }} + {{ end }} status: {} diff --git a/hosting/couchdb/runner.sh b/hosting/couchdb/runner.sh index e56b8e0e7f..9f6a853ca7 100644 --- a/hosting/couchdb/runner.sh +++ b/hosting/couchdb/runner.sh @@ -76,6 +76,6 @@ done # CouchDB needs the `_users` and `_replicator` databases to exist before it will # function correctly, so we create them here. -curl -X PUT http://${COUCHDB_USER}:${COUCHDB_PASSWORD}@localhost:5984/_users -curl -X PUT http://${COUCHDB_USER}:${COUCHDB_PASSWORD}@localhost:5984/_replicator +curl -X PUT -u "${COUCHDB_USER}:${COUCHDB_PASSWORD}" http://localhost:5984/_users +curl -X PUT -u "${COUCHDB_USER}:${COUCHDB_PASSWORD}" http://localhost:5984/_replicator sleep infinity \ No newline at end of file diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index 7803916069..36b88466fe 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -26,7 +26,7 @@ services: BB_ADMIN_USER_EMAIL: ${BB_ADMIN_USER_EMAIL} BB_ADMIN_USER_PASSWORD: ${BB_ADMIN_USER_PASSWORD} PLUGINS_DIR: ${PLUGINS_DIR} - OFFLINE_MODE: ${OFFLINE_MODE} + OFFLINE_MODE: ${OFFLINE_MODE:-} depends_on: - worker-service - redis-service @@ -53,7 +53,7 @@ services: INTERNAL_API_KEY: ${INTERNAL_API_KEY} REDIS_URL: redis-service:6379 REDIS_PASSWORD: ${REDIS_PASSWORD} - OFFLINE_MODE: ${OFFLINE_MODE} + OFFLINE_MODE: ${OFFLINE_MODE:-} depends_on: - redis-service - minio-service @@ -109,7 +109,7 @@ services: redis-service: restart: unless-stopped image: redis - command: redis-server --requirepass ${REDIS_PASSWORD} + command: redis-server --requirepass "${REDIS_PASSWORD}" volumes: - redis_data:/data diff --git a/lerna.json b/lerna.json index a63b824b87..d6fa262685 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.14.0", + "version": "2.14.3", "npmClient": "yarn", "packages": [ "packages/*", diff --git a/packages/account-portal b/packages/account-portal index b11e6b4737..8ee2734e77 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit b11e6b47370d9b77c63648b45929c86bfed6360c +Subproject commit 8ee2734e77709438cbcaaabc024f677c7b24c883 diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 138dbbd9e0..0fec786c31 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -166,6 +166,8 @@ const environment = { DISABLE_JWT_WARNING: process.env.DISABLE_JWT_WARNING, BLACKLIST_IPS: process.env.BLACKLIST_IPS, SERVICE_TYPE: "unknown", + PASSWORD_MIN_LENGTH: process.env.PASSWORD_MIN_LENGTH, + PASSWORD_MAX_LENGTH: process.env.PASSWORD_MAX_LENGTH, /** * Enable to allow an admin user to login using a password. * This can be useful to prevent lockout when configuring SSO. diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index 16f658b90a..d357dbdbdc 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -15,6 +15,7 @@ import * as identity from "../context/identity" import env from "../environment" import { Ctx, EndpointMatcher, SessionCookie } from "@budibase/types" import { InvalidAPIKeyError, ErrorCode } from "../errors" +import tracer from "dd-trace" const ONE_MINUTE = env.SESSION_UPDATE_PERIOD ? parseInt(env.SESSION_UPDATE_PERIOD) @@ -166,6 +167,16 @@ export default function ( if (!authenticated) { authenticated = false } + + if (user) { + tracer.setUser({ + id: user?._id, + tenantId: user?.tenantId, + budibaseAccess: user?.budibaseAccess, + status: user?.status, + }) + } + // isAuthenticated is a function, so use a variable to be able to check authed state finalise(ctx, { authenticated, user, internal, version, publicEndpoint }) diff --git a/packages/backend-core/src/redis/redlockImpl.ts b/packages/backend-core/src/redis/redlockImpl.ts index e57a3721b5..7009dc6f55 100644 --- a/packages/backend-core/src/redis/redlockImpl.ts +++ b/packages/backend-core/src/redis/redlockImpl.ts @@ -2,7 +2,6 @@ import Redlock from "redlock" import { getLockClient } from "./init" import { LockOptions, LockType } from "@budibase/types" import * as context from "../context" -import { logWarn } from "../logging" import { utils } from "@budibase/shared-core" import { Duration } from "../utils" diff --git a/packages/backend-core/src/security/auth.ts b/packages/backend-core/src/security/auth.ts index c90d9df09b..1cce35a0af 100644 --- a/packages/backend-core/src/security/auth.ts +++ b/packages/backend-core/src/security/auth.ts @@ -1,7 +1,7 @@ -import { env } from ".." +import env from "../environment" -export const PASSWORD_MIN_LENGTH = +(process.env.PASSWORD_MIN_LENGTH || 8) -export const PASSWORD_MAX_LENGTH = +(process.env.PASSWORD_MAX_LENGTH || 512) +export const PASSWORD_MIN_LENGTH = +(env.PASSWORD_MIN_LENGTH || 8) +export const PASSWORD_MAX_LENGTH = +(env.PASSWORD_MAX_LENGTH || 512) export function validatePassword( password: string diff --git a/packages/backend-core/src/timers/timers.ts b/packages/backend-core/src/timers/timers.ts index 9de57af7f1..9121c576cd 100644 --- a/packages/backend-core/src/timers/timers.ts +++ b/packages/backend-core/src/timers/timers.ts @@ -50,7 +50,7 @@ export class ExecutionTimeTracker { return this.totalTimeMs } - private checkLimit() { + checkLimit() { if (this.totalTimeMs > this.limitMs) { throw new ExecutionTimeoutError( `Execution time limit of ${this.limitMs}ms exceeded: ${this.totalTimeMs}ms` diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 3214b3ab63..4d0d216603 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -44,6 +44,12 @@ type GroupFns = { getBulk: GroupGetFn getGroupBuilderAppIds: GroupBuildersFn } +type CreateAdminUserOpts = { + ssoId?: string + hashPassword?: boolean + requirePassword?: boolean + skipPasswordValidation?: boolean +} type FeatureFns = { isSSOEnforced: FeatureFn; isAppBuildersEnabled: FeatureFn } const bulkDeleteProcessing = async (dbUser: User) => { @@ -112,9 +118,11 @@ export class UserDB { throw new HTTPError("Password change is disabled for this user", 400) } - const passwordValidation = validatePassword(password) - if (!passwordValidation.valid) { - throw new HTTPError(passwordValidation.error, 400) + if (!opts.skipPasswordValidation) { + const passwordValidation = validatePassword(password) + if (!passwordValidation.valid) { + throw new HTTPError(passwordValidation.error, 400) + } } hashedPassword = opts.hashPassword ? await hash(password) : password @@ -489,7 +497,7 @@ export class UserDB { email: string, password: string, tenantId: string, - opts?: { ssoId?: string; hashPassword?: boolean; requirePassword?: boolean } + opts?: CreateAdminUserOpts ) { const user: User = { email: email, @@ -513,6 +521,7 @@ export class UserDB { return await UserDB.save(user, { hashPassword: opts?.hashPassword, requirePassword: opts?.requirePassword, + skipPasswordValidation: opts?.skipPasswordValidation, }) } diff --git a/packages/bbui/src/ActionButton/ActionButton.svelte b/packages/bbui/src/ActionButton/ActionButton.svelte index 427a98f888..0e6ec3d155 100644 --- a/packages/bbui/src/ActionButton/ActionButton.svelte +++ b/packages/bbui/src/ActionButton/ActionButton.svelte @@ -130,5 +130,6 @@ max-width: 150px; transform: translateX(-50%); text-align: center; + z-index: 1; } diff --git a/packages/bbui/src/DetailSummary/DetailSummary.svelte b/packages/bbui/src/DetailSummary/DetailSummary.svelte index e5d6fda86b..2cbb6796f3 100644 --- a/packages/bbui/src/DetailSummary/DetailSummary.svelte +++ b/packages/bbui/src/DetailSummary/DetailSummary.svelte @@ -78,7 +78,7 @@ var(--spacing-xl); } .property-panel.no-title { - padding: var(--spacing-xl); + padding-top: var(--spacing-xl); } .show { diff --git a/packages/bbui/src/Form/Field.svelte b/packages/bbui/src/Form/Field.svelte index 0c031b0235..1770438c3c 100644 --- a/packages/bbui/src/Form/Field.svelte +++ b/packages/bbui/src/Form/Field.svelte @@ -51,15 +51,13 @@ margin-top: var(--spectrum-global-dimension-size-75); align-items: center; } - .helpText :global(svg) { - width: 14px; - color: var(--grey-5); + width: 13px; + color: var(--spectrum-global-color-gray-600); margin-right: 6px; } - .helpText span { - color: var(--grey-7); + color: var(--spectrum-global-color-gray-800); font-size: var(--spectrum-global-dimension-font-size-75); } diff --git a/packages/builder/src/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index d86e94aba2..52368a0723 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -465,8 +465,8 @@ const filterCategoryByContext = (component, context) => { const { _component } = component if (_component.endsWith("formblock")) { if ( - (component.actionType == "Create" && context.type === "schema") || - (component.actionType == "View" && context.type === "form") + (component.actionType === "Create" && context.type === "schema") || + (component.actionType === "View" && context.type === "form") ) { return false } @@ -474,20 +474,21 @@ const filterCategoryByContext = (component, context) => { return true } +// Enrich binding category information for certain components const getComponentBindingCategory = (component, context, def) => { let icon = def.icon let category = component._instanceName if (component._component.endsWith("formblock")) { - let contextCategorySuffix = { - form: "Fields", - schema: "Row", + if (context.type === "form") { + category = `${component._instanceName} - Fields` + icon = "Form" + } else if (context.type === "schema") { + category = `${component._instanceName} - Row` + icon = "Data" } - category = `${component._instanceName} - ${ - contextCategorySuffix[context.type] - }` - icon = context.type === "form" ? "Form" : "Data" } + return { icon, category, diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index aaa0eb0184..2d62a0667e 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -610,12 +610,12 @@ export const getFrontendStore = () => { // Use default config if the 'buttons' prop has never been initialised if (!("buttons" in enrichedComponent)) { enrichedComponent["buttons"] = - Utils.buildDynamicButtonConfig(enrichedComponent) + Utils.buildFormBlockButtonConfig(enrichedComponent) migrated = true } else if (enrichedComponent["buttons"] == null) { // Ignore legacy config if 'buttons' has been reset by 'resetOn' const { _id, actionType, dataSource } = enrichedComponent - enrichedComponent["buttons"] = Utils.buildDynamicButtonConfig({ + enrichedComponent["buttons"] = Utils.buildFormBlockButtonConfig({ _id, actionType, dataSource, @@ -1289,15 +1289,14 @@ export const getFrontendStore = () => { const settings = getComponentSettings(component._component) const updatedSetting = settings.find(setting => setting.key === name) - // Can be a single string or array of strings - const resetFields = settings.filter(setting => { - return ( + // Reset dependent fields + settings.forEach(setting => { + const needsReset = name === setting.resetOn || (Array.isArray(setting.resetOn) && setting.resetOn.includes(name)) - ) - }) - resetFields?.forEach(setting => { - component[setting.key] = null + if (needsReset) { + component[setting.key] = setting.defaultValue || null + } }) if ( diff --git a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte index 76d7a58ef1..a39634f9a3 100644 --- a/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte +++ b/packages/builder/src/components/common/CodeEditor/CodeEditor.svelte @@ -53,6 +53,7 @@ export let value = "" export let placeholder = null export let autocompleteEnabled = true + export let autofocus = false // Export a function to expose caret position export const getCaretPosition = () => { @@ -241,6 +242,12 @@ }) } + $: { + if (autofocus && isEditorInitialised) { + editor.focus() + } + } + $: editorHeight = typeof height === "number" ? `${height}px` : height // Init when all elements are ready diff --git a/packages/builder/src/components/common/bindings/BindingPanel.svelte b/packages/builder/src/components/common/bindings/BindingPanel.svelte index 4df26c5d03..548a71b483 100644 --- a/packages/builder/src/components/common/bindings/BindingPanel.svelte +++ b/packages/builder/src/components/common/bindings/BindingPanel.svelte @@ -45,6 +45,7 @@ export let valid export let allowJS = false export let allowHelpers = true + export let autofocusEditor = false const drawerActions = getContext("drawer-actions") const bindingDrawerActions = getContext("binding-drawer-actions") @@ -199,6 +200,7 @@ ]} placeholder="" height="100%" + autofocus={autofocusEditor} />