diff --git a/.github/workflows/deploy-preprod.yml b/.github/workflows/deploy-preprod.yml index 803dd6af52..a1eee2c465 100644 --- a/.github/workflows/deploy-preprod.yml +++ b/.github/workflows/deploy-preprod.yml @@ -1,6 +1,10 @@ name: "deploy-preprod" on: workflow_dispatch: + inputs: + version: + description: Budibase release version. For example - 1.0.0 + required: false workflow_call: jobs: @@ -8,10 +12,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: 'Get Previous tag' - id: previoustag - uses: "WyriHaximus/github-action-get-previous-tag@v1" + - name: Get the latest budibase release version + id: version + run: | + if [ -z "${{ github.event.inputs.version }}" ]; then + release_version=$(cat lerna.json | jq -r '.version') + else + release_version=${{ github.event.inputs.version }} + fi + echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: @@ -26,7 +36,6 @@ jobs: -o values.preprod.yaml \ -L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml wc -l values.preprod.yaml - - name: Deploy to Preprod Environment uses: budibase/helm@v1.8.0 with: @@ -37,7 +46,7 @@ jobs: helm: helm3 values: | globals: - appVersion: ${{ steps.previoustag.outputs.tag }} + appVersion: v${{ env.RELEASE_VERSION }} ingress: enabled: true nginx: true @@ -52,5 +61,5 @@ jobs: uses: tsickert/discord-webhook@v4.0.0 with: webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }} - content: "Preprod Deployment Complete: ${{ steps.previoustag.outputs.tag }} deployed to Budibase Pre-prod." - embed-title: ${{ steps.previoustag.outputs.tag }} \ No newline at end of file + content: "Preprod Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Pre-prod." + embed-title: ${{ env.RELEASE_VERSION }} diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 3ae265fa21..20a48f5802 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -91,9 +91,11 @@ jobs: uses: azure/setup-helm@v1 id: helm-install - - name: 'Get Previous tag' - id: previoustag - uses: "WyriHaximus/github-action-get-previous-tag@v1" + - name: Get the latest budibase release version + id: version + run: | + release_version=$(cat lerna.json | jq -r '.version') + echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV # due to helm repo index issue: https://github.com/helm/helm/issues/7363 # we need to create new package in a different dir, merge the index and move the package back @@ -116,8 +118,6 @@ jobs: git add -A git commit -m "Helm Release: ${{ env.RELEASE_VERSION }}" git push - env: - RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }} deploy-to-legacy-preprod-env: needs: [release-images] @@ -130,13 +130,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: 'Get Previous tag' - id: previoustag - uses: "WyriHaximus/github-action-get-previous-tag@v1" + + - name: Get the latest budibase release version + id: version + run: | + release_version=$(cat lerna.json | jq -r '.version') + echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - uses: passeidireto/trigger-external-workflow-action@main env: - PAYLOAD_VERSION: ${{ steps.previoustag.outputs.tag }} + PAYLOAD_VERSION: ${{ env.RELEASE_VERSION }} with: repository: budibase/budibase-deploys event: budicloud-preprod-deploy diff --git a/charts/budibase/templates/app-service-deployment.yaml b/charts/budibase/templates/app-service-deployment.yaml index 6b0a0338d6..41b0dc48c9 100644 --- a/charts/budibase/templates/app-service-deployment.yaml +++ b/charts/budibase/templates/app-service-deployment.yaml @@ -62,16 +62,22 @@ spec: {{ end }} - name: ENABLE_ANALYTICS value: {{ .Values.globals.enableAnalytics | quote }} + - name: API_ENCRYPTION_KEY + value: {{ .Values.globals.apiEncryptionKey | quote }} - name: INTERNAL_API_KEY valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: internalApiKey + - name: INTERNAL_API_KEY_FALLBACK + value: {{ .Values.globals.internalApiKeyFallback | quote }} - name: JWT_SECRET valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: jwtSecret + - name: JWT_SECRET_FALLBACK + value: {{ .Values.globals.jwtSecretFallback | quote }} {{ if .Values.services.objectStore.region }} - name: AWS_REGION value: {{ .Values.services.objectStore.region }} @@ -125,9 +131,9 @@ spec: - name: SELF_HOSTED value: {{ .Values.globals.selfHosted | quote }} - name: SENTRY_DSN - value: {{ .Values.globals.sentryDSN }} + value: {{ .Values.globals.sentryDSN | quote }} - name: POSTHOG_TOKEN - value: {{ .Values.globals.posthogToken }} + value: {{ .Values.globals.posthogToken | quote }} - name: WORKER_URL value: http://worker-service:{{ .Values.services.worker.port }} - name: PLATFORM_URL @@ -198,8 +204,6 @@ spec: - name: GLOBAL_AGENT_NO_PROXY value: {{ .Values.globals.globalAgentNoProxy | quote }} {{ end }} - - name: CDN_URL - value: {{ .Values.globals.cdnUrl }} {{ if .Values.services.tlsRejectUnauthorized }} - name: NODE_TLS_REJECT_UNAUTHORIZED value: {{ .Values.services.tlsRejectUnauthorized }} diff --git a/charts/budibase/templates/worker-service-deployment.yaml b/charts/budibase/templates/worker-service-deployment.yaml index f4305fbb00..7886d55b28 100644 --- a/charts/budibase/templates/worker-service-deployment.yaml +++ b/charts/budibase/templates/worker-service-deployment.yaml @@ -62,16 +62,22 @@ spec: {{ else }} value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }} {{ end }} + - name: API_ENCRYPTION_KEY + value: {{ .Values.globals.apiEncryptionKey | quote }} - name: INTERNAL_API_KEY valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: internalApiKey + - name: INTERNAL_API_KEY_FALLBACK + value: {{ .Values.globals.internalApiKeyFallback | quote }} - name: JWT_SECRET valueFrom: secretKeyRef: name: {{ template "budibase.fullname" . }} key: jwtSecret + - name: JWT_SECRET_FALLBACK + value: {{ .Values.globals.jwtSecretFallback | quote }} {{ if .Values.services.objectStore.region }} - name: AWS_REGION value: {{ .Values.services.objectStore.region }} @@ -188,8 +194,6 @@ spec: - name: GLOBAL_AGENT_NO_PROXY value: {{ .Values.globals.globalAgentNoProxy | quote }} {{ end }} - - name: CDN_URL - value: {{ .Values.globals.cdnUrl }} {{ if .Values.services.tlsRejectUnauthorized }} - name: NODE_TLS_REJECT_UNAUTHORIZED value: {{ .Values.services.tlsRejectUnauthorized }} diff --git a/charts/budibase/values.yaml b/charts/budibase/values.yaml index 536af8560f..ed4ff014a9 100644 --- a/charts/budibase/values.yaml +++ b/charts/budibase/values.yaml @@ -96,9 +96,13 @@ globals: createSecrets: true # creates an internal API key, JWT secrets and redis password for you # if createSecrets is set to false, you can hard-code your secrets here + apiEncryptionKey: "" internalApiKey: "" jwtSecret: "" cdnUrl: "" + # fallback values used during live rotation + internalApiKeyFallback: "" + jwtSecretFallback: "" smtp: enabled: false diff --git a/hosting/.env b/hosting/.env index 07b506a6b2..c2b6d55eef 100644 --- a/hosting/.env +++ b/hosting/.env @@ -3,6 +3,7 @@ MAIN_PORT=10000 # This section contains all secrets pertaining to the system # These should be updated +API_ENCRYPTION_KEY=testsecret JWT_SECRET=testsecret MINIO_ACCESS_KEY=budibase MINIO_SECRET_KEY=budibase diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml index d36937910f..bad34a20ea 100644 --- a/hosting/docker-compose.yaml +++ b/hosting/docker-compose.yaml @@ -17,6 +17,7 @@ services: INTERNAL_API_KEY: ${INTERNAL_API_KEY} BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT} PORT: 4002 + API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY} JWT_SECRET: ${JWT_SECRET} LOG_LEVEL: info SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131 @@ -40,6 +41,7 @@ services: SELF_HOSTED: 1 PORT: 4003 CLUSTER_PORT: ${MAIN_PORT} + API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY} JWT_SECRET: ${JWT_SECRET} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} diff --git a/hosting/hosting.properties b/hosting/hosting.properties index c5638a266f..6c1d9e5dbd 100644 --- a/hosting/hosting.properties +++ b/hosting/hosting.properties @@ -3,6 +3,7 @@ MAIN_PORT=10000 # This section contains all secrets pertaining to the system # These should be updated +API_ENCRYPTION_KEY=testsecret JWT_SECRET=testsecret MINIO_ACCESS_KEY=budibase MINIO_SECRET_KEY=budibase diff --git a/lerna.json b/lerna.json index a6d700e2c3..b47cca4c23 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.4.12-alpha.6", + "version": "2.4.26", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 3576c49811..c27893051d 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Budibase backend core libraries used in server and worker", "main": "dist/src/index.js", "types": "dist/src/index.d.ts", @@ -24,7 +24,7 @@ "dependencies": { "@budibase/nano": "10.1.2", "@budibase/pouchdb-replication-stream": "1.2.10", - "@budibase/types": "2.4.12-alpha.6", + "@budibase/types": "^2.4.26", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-cloudfront-sign": "2.2.0", diff --git a/packages/backend-core/src/auth/auth.ts b/packages/backend-core/src/auth/auth.ts index 7e6fe4bcee..26c7cd4e26 100644 --- a/packages/backend-core/src/auth/auth.ts +++ b/packages/backend-core/src/auth/auth.ts @@ -1,6 +1,5 @@ const _passport = require("koa-passport") const LocalStrategy = require("passport-local").Strategy -const JwtStrategy = require("passport-jwt").Strategy import { getGlobalDB } from "../context" import { Cookie } from "../constants" import { getSessionsForUser, invalidateSessions } from "../security/sessions" @@ -8,7 +7,6 @@ import { authenticated, csrf, google, - jwt as jwtPassport, local, oidc, tenancy, @@ -21,14 +19,11 @@ import { OIDCInnerConfig, PlatformLogoutOpts, SSOProviderType, - User, } from "@budibase/types" -import { logAlert } from "../logging" import * as events from "../events" import * as configs from "../configs" import { clearCookie, getCookie } from "../utils" import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso" -import env from "../environment" const refresh = require("passport-oauth2-refresh") export { @@ -51,25 +46,6 @@ export const jwt = require("jsonwebtoken") // Strategies _passport.use(new LocalStrategy(local.options, local.authenticate)) -if (jwtPassport.options.secretOrKey) { - _passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate)) -} else if (!env.DISABLE_JWT_WARNING) { - logAlert("No JWT Secret supplied, cannot configure JWT strategy") -} - -_passport.serializeUser((user: User, done: any) => done(null, user)) - -_passport.deserializeUser(async (user: User, done: any) => { - const db = getGlobalDB() - - try { - const dbUser = await db.get(user._id) - return done(null, dbUser) - } catch (err) { - console.error(`User not found`, err) - return done(null, false, { message: "User not found" }) - } -}) async function refreshOIDCAccessToken( chosenConfig: OIDCInnerConfig, diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts index 8dc2cce487..f1c96c7fec 100644 --- a/packages/backend-core/src/environment.ts +++ b/packages/backend-core/src/environment.ts @@ -30,6 +30,12 @@ const DefaultBucketName = { const selfHosted = !!parseInt(process.env.SELF_HOSTED || "") +function getAPIEncryptionKey() { + return process.env.API_ENCRYPTION_KEY + ? process.env.API_ENCRYPTION_KEY + : process.env.JWT_SECRET // fallback to the JWT_SECRET used historically +} + const environment = { isTest, isJest, @@ -39,7 +45,9 @@ const environment = { }, JS_BCRYPT: process.env.JS_BCRYPT, JWT_SECRET: process.env.JWT_SECRET, + JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK, ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, + API_ENCRYPTION_KEY: getAPIEncryptionKey(), COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", COUCH_DB_USERNAME: process.env.COUCH_DB_USER, COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD, @@ -55,6 +63,7 @@ const environment = { MINIO_URL: process.env.MINIO_URL, MINIO_ENABLED: process.env.MINIO_ENABLED || 1, INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, + INTERNAL_API_KEY_FALLBACK: process.env.INTERNAL_API_KEY_FALLBACK, MULTI_TENANCY: process.env.MULTI_TENANCY, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app", diff --git a/packages/backend-core/src/middleware/authenticated.ts b/packages/backend-core/src/middleware/authenticated.ts index 0708581570..5e546b4c1c 100644 --- a/packages/backend-core/src/middleware/authenticated.ts +++ b/packages/backend-core/src/middleware/authenticated.ts @@ -1,5 +1,10 @@ import { Cookie, Header } from "../constants" -import { getCookie, clearCookie, openJwt } from "../utils" +import { + getCookie, + clearCookie, + openJwt, + isValidInternalAPIKey, +} from "../utils" import { getUser } from "../cache/user" import { getSession, updateSessionTTL } from "../security/sessions" import { buildMatcherRegex, matches } from "./matchers" @@ -35,7 +40,9 @@ function finalise(ctx: any, opts: FinaliseOpts = {}) { } async function checkApiKey(apiKey: string, populateUser?: Function) { - if (apiKey === env.INTERNAL_API_KEY) { + // check both the primary and the fallback internal api keys + // this allows for rotation + if (isValidInternalAPIKey(apiKey)) { return { valid: true } } const decrypted = decrypt(apiKey) diff --git a/packages/backend-core/src/middleware/index.ts b/packages/backend-core/src/middleware/index.ts index addeac6a1a..dce07168d4 100644 --- a/packages/backend-core/src/middleware/index.ts +++ b/packages/backend-core/src/middleware/index.ts @@ -1,4 +1,3 @@ -export * as jwt from "./passport/jwt" export * as local from "./passport/local" export * as google from "./passport/sso/google" export * as oidc from "./passport/sso/oidc" diff --git a/packages/backend-core/src/middleware/internalApi.ts b/packages/backend-core/src/middleware/internalApi.ts index fff761928b..dc73cd6b66 100644 --- a/packages/backend-core/src/middleware/internalApi.ts +++ b/packages/backend-core/src/middleware/internalApi.ts @@ -1,13 +1,21 @@ -import env from "../environment" import { Header } from "../constants" import { BBContext } from "@budibase/types" +import { isValidInternalAPIKey } from "../utils" /** * API Key only endpoint. */ export default async (ctx: BBContext, next: any) => { const apiKey = ctx.request.headers[Header.API_KEY] - if (apiKey !== env.INTERNAL_API_KEY) { + if (!apiKey) { + ctx.throw(403, "Unauthorized") + } + + if (Array.isArray(apiKey)) { + ctx.throw(403, "Unauthorized") + } + + if (!isValidInternalAPIKey(apiKey)) { ctx.throw(403, "Unauthorized") } diff --git a/packages/backend-core/src/middleware/passport/jwt.ts b/packages/backend-core/src/middleware/passport/jwt.ts deleted file mode 100644 index 95dc8f2656..0000000000 --- a/packages/backend-core/src/middleware/passport/jwt.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Cookie } from "../../constants" -import env from "../../environment" -import { authError } from "./utils" -import { BBContext } from "@budibase/types" - -export const options = { - secretOrKey: env.JWT_SECRET, - jwtFromRequest: function (ctx: BBContext) { - return ctx.cookies.get(Cookie.Auth) - }, -} - -export async function authenticate(jwt: Function, done: Function) { - try { - return done(null, jwt) - } catch (err) { - return authError(done, "JWT invalid", err) - } -} diff --git a/packages/backend-core/src/security/encryption.ts b/packages/backend-core/src/security/encryption.ts index d0707cb850..d2c8f18f73 100644 --- a/packages/backend-core/src/security/encryption.ts +++ b/packages/backend-core/src/security/encryption.ts @@ -8,7 +8,7 @@ const RANDOM_BYTES = 16 const STRETCH_LENGTH = 32 export enum SecretOption { - JWT = "jwt", + API = "api", ENCRYPTION = "encryption", } @@ -19,10 +19,10 @@ function getSecret(secretOption: SecretOption): string { secret = env.ENCRYPTION_KEY secretName = "ENCRYPTION_KEY" break - case SecretOption.JWT: + case SecretOption.API: default: - secret = env.JWT_SECRET - secretName = "JWT_SECRET" + secret = env.API_ENCRYPTION_KEY + secretName = "API_ENCRYPTION_KEY" break } if (!secret) { @@ -37,7 +37,7 @@ function stretchString(string: string, salt: Buffer) { export function encrypt( input: string, - secretOption: SecretOption = SecretOption.JWT + secretOption: SecretOption = SecretOption.API ) { const salt = crypto.randomBytes(RANDOM_BYTES) const stretched = stretchString(getSecret(secretOption), salt) @@ -50,7 +50,7 @@ export function encrypt( export function decrypt( input: string, - secretOption: SecretOption = SecretOption.JWT + secretOption: SecretOption = SecretOption.API ) { const [salt, encrypted] = input.split(SEPARATOR) const saltBuffer = Buffer.from(salt, "hex") diff --git a/packages/backend-core/src/utils/utils.ts b/packages/backend-core/src/utils/utils.ts index 3efd40ca80..7c222a9831 100644 --- a/packages/backend-core/src/utils/utils.ts +++ b/packages/backend-core/src/utils/utils.ts @@ -1,5 +1,4 @@ import { getAllApps, queryGlobalView } from "../db" -import { options } from "../middleware/passport/jwt" import { Header, MAX_VALID_DATE, @@ -133,7 +132,30 @@ export function openJwt(token: string) { if (!token) { return token } - return jwt.verify(token, options.secretOrKey) + try { + return jwt.verify(token, env.JWT_SECRET) + } catch (e) { + if (env.JWT_SECRET_FALLBACK) { + // fallback to enable rotation + return jwt.verify(token, env.JWT_SECRET_FALLBACK) + } else { + throw e + } + } +} + +export function isValidInternalAPIKey(apiKey: string) { + if (env.INTERNAL_API_KEY && env.INTERNAL_API_KEY === apiKey) { + return true + } + // fallback to enable rotation + if ( + env.INTERNAL_API_KEY_FALLBACK && + env.INTERNAL_API_KEY_FALLBACK === apiKey + ) { + return true + } + return false } /** @@ -165,7 +187,7 @@ export function setCookie( opts = { sign: true } ) { if (value && opts && opts.sign) { - value = jwt.sign(value, options.secretOrKey) + value = jwt.sign(value, env.JWT_SECRET) } const config: SetOption = { diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 109bb42845..6f37501ca6 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/bbui", "description": "A UI solution used in the different Budibase projects.", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "license": "MPL-2.0", "svelte": "src/index.js", "module": "dist/bbui.es.js", @@ -38,8 +38,8 @@ ], "dependencies": { "@adobe/spectrum-css-workflow-icons": "1.2.1", - "@budibase/shared-core": "2.4.12-alpha.6", - "@budibase/string-templates": "2.4.12-alpha.6", + "@budibase/shared-core": "^2.4.26", + "@budibase/string-templates": "^2.4.26", "@spectrum-css/accordion": "3.0.24", "@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actiongroup": "1.0.1", diff --git a/packages/builder/package.json b/packages/builder/package.json index 7cdd7dc6e8..1b910f2def 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "license": "GPL-3.0", "private": true, "scripts": { @@ -58,11 +58,11 @@ } }, "dependencies": { - "@budibase/bbui": "2.4.12-alpha.6", - "@budibase/client": "2.4.12-alpha.6", - "@budibase/frontend-core": "2.4.12-alpha.6", - "@budibase/shared-core": "2.4.12-alpha.6", - "@budibase/string-templates": "2.4.12-alpha.6", + "@budibase/bbui": "^2.4.26", + "@budibase/client": "^2.4.26", + "@budibase/frontend-core": "^2.4.26", + "@budibase/shared-core": "^2.4.26", + "@budibase/string-templates": "^2.4.26", "@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1", diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte index 53d50d57a3..3bc1a1cdd9 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte @@ -74,6 +74,14 @@ } return capitalise(name) } + + function getDisplayError(error, configKey) { + return error?.replace( + new RegExp(`${configKey}`, "i"), + getDisplayName(configKey) + ) + } + function getFieldGroupKeys(fieldGroup) { return Object.entries(schema[fieldGroup].fields || {}) .filter(el => filter(el)) @@ -147,7 +155,7 @@ type={schema[configKey].type} on:change bind:value={config[configKey]} - error={$validation.errors[configKey]} + error={getDisplayError($validation.errors[configKey], configKey)} /> {:else if schema[configKey].type === "fieldGroup"} @@ -180,7 +188,7 @@ type={configKey === "port" ? "string" : schema[configKey].type} on:change bind:value={config[configKey]} - error={$validation.errors[configKey]} + error={getDisplayError($validation.errors[configKey], configKey)} environmentVariablesEnabled={$licensing.environmentVariablesEnabled} {handleUpgradePanel} /> diff --git a/packages/cli/package.json b/packages/cli/package.json index c63d01a118..146e76f19e 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "dist/index.js", "bin": { @@ -29,9 +29,9 @@ "outputPath": "build" }, "dependencies": { - "@budibase/backend-core": "2.4.12-alpha.6", - "@budibase/string-templates": "2.4.12-alpha.6", - "@budibase/types": "2.4.12-alpha.6", + "@budibase/backend-core": "^2.4.26", + "@budibase/string-templates": "^2.4.26", + "@budibase/types": "^2.4.26", "axios": "0.21.2", "chalk": "4.1.0", "cli-progress": "3.11.2", diff --git a/packages/cli/src/hosting/makeFiles.ts b/packages/cli/src/hosting/makeFiles.ts index 9574e107a6..b11a07c107 100644 --- a/packages/cli/src/hosting/makeFiles.ts +++ b/packages/cli/src/hosting/makeFiles.ts @@ -13,6 +13,7 @@ export const ENV_PATH = path.resolve("./.env") function getSecrets(opts = { single: false }) { const secrets = [ + "API_ENCRYPTION_KEY", "JWT_SECRET", "MINIO_ACCESS_KEY", "MINIO_SECRET_KEY", diff --git a/packages/cli/src/hosting/update.ts b/packages/cli/src/hosting/update.ts index 161cc04ae1..ca0ecce615 100644 --- a/packages/cli/src/hosting/update.ts +++ b/packages/cli/src/hosting/update.ts @@ -4,6 +4,8 @@ import { downloadDockerCompose, handleError, getServices, + getServiceImage, + setServiceImage, } from "./utils" import { confirmation } from "../questions" import compose from "docker-compose" @@ -23,7 +25,11 @@ export async function update() { !isSingle && (await confirmation("Do you wish to update you docker-compose.yaml?")) ) { + // get current MinIO image + const image = await getServiceImage("minio") await downloadDockerCompose() + // replace MinIO image + setServiceImage("minio", image) } await handleError(async () => { const status = await compose.ps() diff --git a/packages/cli/src/hosting/utils.ts b/packages/cli/src/hosting/utils.ts index 9e5bd367ed..93e31b8aea 100644 --- a/packages/cli/src/hosting/utils.ts +++ b/packages/cli/src/hosting/utils.ts @@ -9,10 +9,44 @@ const ERROR_FILE = "docker-error.log" const COMPOSE_URL = "https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml" -export async function downloadDockerCompose() { - const fileName = COMPOSE_URL.split("/").slice(-1)[0] +function composeFilename() { + return COMPOSE_URL.split("/").slice(-1)[0] +} + +export function getServiceImage(service: string) { + const filename = composeFilename() try { - await downloadFile(COMPOSE_URL, `./${fileName}`) + const { services } = getServices(filename) + const serviceKey = Object.keys(services).find(name => + name.includes(service) + ) + if (serviceKey) { + return services[serviceKey].image + } else { + return null + } + } catch (err) { + return null + } +} + +export function setServiceImage(service: string, image: string) { + const filename = composeFilename() + if (!fs.existsSync(filename)) { + throw new Error( + `File ${filename} not found, cannot update ${service} image.` + ) + } + const current = getServiceImage(service)! + let contents = fs.readFileSync(filename, "utf8") + contents = contents.replace(`image: ${current}`, `image: ${image}`) + fs.writeFileSync(filename, contents) +} + +export async function downloadDockerCompose() { + const filename = composeFilename() + try { + await downloadFile(COMPOSE_URL, `./${filename}`) } catch (err) { console.error(error(`Failed to retrieve compose file - ${err}`)) } @@ -49,6 +83,9 @@ export async function handleError(func: Function) { } export function getServices(path: string) { + if (!fs.existsSync(path)) { + throw new Error(`No yaml found at path: ${path}`) + } const dockerYaml = fs.readFileSync(path, "utf8") const parsedYaml = yaml.parse(dockerYaml) return { yaml: parsedYaml, services: parsedYaml.services } diff --git a/packages/client/manifest.json b/packages/client/manifest.json index e24fa3a68a..2579cdedaa 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -17,10 +17,7 @@ "description": "This component is specific only to layouts", "icon": "Sandbox", "hasChildren": true, - "styles": [ - "padding", - "background" - ], + "styles": ["padding", "background"], "settings": [ { "type": "text", @@ -36,23 +33,14 @@ "type": "select", "label": "Navigation", "key": "navigation", - "options": [ - "Top", - "Left", - "None" - ], + "options": ["Top", "Left", "None"], "defaultValue": "Top" }, { "type": "select", "label": "Width", "key": "width", - "options": [ - "Small", - "Medium", - "Large", - "Max" - ], + "options": ["Small", "Medium", "Large", "Max"], "defaultValue": "Large" }, { @@ -89,13 +77,7 @@ "width": 400, "height": 200 }, - "styles": [ - "padding", - "size", - "background", - "border", - "shadow" - ], + "styles": ["padding", "size", "background", "border", "shadow"], "settings": [ { "type": "select", @@ -255,9 +237,7 @@ "description": "Add a section to your application", "icon": "ColumnTwoB", "hasChildren": true, - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "showEmptyState": false, "size": { "width": 400, @@ -376,9 +356,7 @@ "name": "Divider", "description": "A basic divider", "icon": "Separator", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "size": { "width": 400, "height": 10 @@ -415,9 +393,7 @@ "name": "Repeater", "description": "A configurable data list that attaches to your backend tables.", "icon": "JourneyData", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "hasChildren": true, "size": { "width": 400, @@ -574,9 +550,7 @@ "name": "Stacked List", "icon": "TaskList", "description": "A basic card component that can contain content and actions.", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "settings": [ { "type": "text", @@ -606,9 +580,7 @@ "name": "Vertical Card", "description": "A basic card component that can contain content and actions.", "icon": "ViewColumn", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "settings": [ { "type": "text", @@ -652,24 +624,14 @@ "type": "select", "label": "Image Height", "key": "imageHeight", - "options": [ - "auto", - "12rem", - "16rem", - "20rem", - "24rem" - ], + "options": ["auto", "12rem", "16rem", "20rem", "24rem"], "defaultValue": "auto" }, { "type": "select", "label": "Card Width", "key": "cardWidth", - "options": [ - "16rem", - "20rem", - "24rem" - ], + "options": ["16rem", "20rem", "24rem"], "defaultValue": "20rem" } ] @@ -678,9 +640,7 @@ "name": "Paragraph", "description": "A component for displaying paragraph text.", "icon": "TextParagraph", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "editable": true, "size": { "width": 400, @@ -803,9 +763,7 @@ "name": "Headline", "icon": "TextBold", "description": "A component for displaying heading text", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "editable": true, "size": { "width": 400, @@ -982,9 +940,7 @@ "name": "Image", "description": "A basic component for displaying images", "icon": "Image", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 400, "height": 300 @@ -1002,9 +958,8 @@ "name": "Background Image", "description": "A background image", "icon": "Images", - "styles": [ - "size" - ], + "hasChildren": true, + "styles": ["size"], "size": { "width": 400, "height": 300 @@ -1162,9 +1117,7 @@ "name": "Nav Bar", "description": "A component for handling the navigation within your app.", "icon": "BreadcrumbNavigation", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "hasChildren": true, "settings": [ { @@ -1365,25 +1318,14 @@ "type": "select", "label": "Image Width", "key": "imageWidth", - "options": [ - "auto", - "8rem", - "12rem", - "16rem" - ], + "options": ["auto", "8rem", "12rem", "16rem"], "defaultValue": "8rem" }, { "type": "select", "label": "Image Height", "key": "imageHeight", - "options": [ - "auto", - "8rem", - "12rem", - "16rem", - "auto" - ], + "options": ["auto", "8rem", "12rem", "16rem", "auto"], "defaultValue": "auto" } ] @@ -1424,9 +1366,7 @@ "name": "Embed", "icon": "Code", "description": "Embed content from 3rd party sources", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 400, "height": 100 @@ -1478,11 +1418,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -1640,11 +1576,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -1736,11 +1668,7 @@ "type": "select", "label": "Curve", "key": "curve", - "options": [ - "Smooth", - "Straight", - "Stepline" - ], + "options": ["Smooth", "Straight", "Stepline"], "defaultValue": "Smooth" }, { @@ -1801,11 +1729,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -1897,11 +1821,7 @@ "type": "select", "label": "Curve", "key": "curve", - "options": [ - "Smooth", - "Straight", - "Stepline" - ], + "options": ["Smooth", "Straight", "Stepline"], "defaultValue": "Smooth" }, { @@ -2253,11 +2173,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -2293,19 +2209,14 @@ "name": "Form", "icon": "Form", "hasChildren": true, - "illegalChildren": [ - "section", - "form" - ], + "illegalChildren": ["section", "form"], "actions": [ "ValidateForm", "ClearForm", "ChangeFormStep", "UpdateFieldValue" ], - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 400, "height": 400 @@ -2315,10 +2226,7 @@ "type": "select", "label": "Type", "key": "actionType", - "options": [ - "Create", - "Update" - ], + "options": ["Create", "Update"], "defaultValue": "Create" }, { @@ -2388,14 +2296,8 @@ "name": "Form Step", "icon": "AssetsAdded", "hasChildren": true, - "illegalChildren": [ - "section", - "form", - "form step" - ], - "styles": [ - "size" - ], + "illegalChildren": ["section", "form", "form step"], + "styles": ["size"], "size": { "width": 400, "height": 400 @@ -2413,12 +2315,8 @@ "fieldgroup": { "name": "Field Group", "icon": "Group", - "illegalChildren": [ - "section" - ], - "styles": [ - "size" - ], + "illegalChildren": ["section"], + "styles": ["size"], "hasChildren": true, "size": { "width": 400, @@ -2451,9 +2349,7 @@ "skeleton": false, "name": "Text Field", "icon": "Text", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -2543,9 +2439,7 @@ "skeleton": false, "name": "Number Field", "icon": "123", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -2601,9 +2495,7 @@ "skeleton": false, "name": "Password Field", "icon": "LockClosed", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -2659,9 +2551,7 @@ "skeleton": false, "name": "Options Picker", "icon": "Menu", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -2828,9 +2718,7 @@ "skeleton": false, "name": "Multi-select Picker", "icon": "ViewList", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3070,9 +2958,7 @@ "skeleton": false, "name": "Long Form Field", "icon": "TextAlignLeft", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3150,9 +3036,7 @@ "skeleton": false, "name": "Date Picker", "icon": "Date", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3232,9 +3116,7 @@ "skeleton": false, "name": "Barcode/QR Scanner", "icon": "Camera", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 400, "height": 50 @@ -3283,9 +3165,7 @@ "embeddedmap": { "name": "Embedded Map", "icon": "Location", - "styles": [ - "size" - ], + "styles": ["size"], "draggable": false, "size": { "width": 400, @@ -3398,9 +3278,7 @@ "skeleton": false, "name": "Attachment", "icon": "Attach", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3463,9 +3341,7 @@ "skeleton": false, "name": "Relationship Picker", "icon": "TaskList", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3527,9 +3403,7 @@ "skeleton": false, "name": "JSON Field", "icon": "Brackets", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3579,9 +3453,7 @@ "s3upload": { "name": "S3 File Upload", "icon": "UploadToCloud", - "styles": [ - "size" - ], + "styles": ["size"], "editable": true, "size": { "width": 400, @@ -3642,13 +3514,9 @@ "dataprovider": { "name": "Data Provider", "icon": "Data", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "hasChildren": true, - "actions": [ - "RefreshDatasource" - ], + "actions": ["RefreshDatasource"], "size": { "width": 400, "height": 100 @@ -3674,10 +3542,7 @@ "type": "select", "label": "Sort Order", "key": "sortOrder", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "defaultValue": "Ascending" }, { @@ -3729,9 +3594,7 @@ "skeleton": false, "name": "Table", "icon": "Table", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "hasChildren": true, "showEmptyState": false, "size": { @@ -3815,9 +3678,7 @@ "daterangepicker": { "name": "Date Range", "icon": "Calendar", - "styles": [ - "size" - ], + "styles": ["size"], "hasChildren": false, "size": { "width": 200, @@ -3856,9 +3717,7 @@ "spectrumcard": { "name": "Card", "icon": "PersonalizationField", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 300, "height": 120 @@ -4031,10 +3890,7 @@ "type": "select", "label": "Sort Order", "key": "sortOrder", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "defaultValue": "Ascending" }, { @@ -4213,11 +4069,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -4271,11 +4123,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -4292,11 +4140,7 @@ "type": "select", "label": "Curve", "key": "curve", - "options": [ - "Smooth", - "Straight", - "Stepline" - ], + "options": ["Smooth", "Straight", "Stepline"], "defaultValue": "Smooth" } ] @@ -4328,11 +4172,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -4349,11 +4189,7 @@ "type": "select", "label": "Curve", "key": "curve", - "options": [ - "Smooth", - "Straight", - "Stepline" - ], + "options": ["Smooth", "Straight", "Stepline"], "defaultValue": "Smooth" }, { @@ -4418,11 +4254,7 @@ "type": "select", "label": "Format", "key": "yAxisUnits", - "options": [ - "Default", - "Thousands", - "Millions" - ], + "options": ["Default", "Thousands", "Millions"], "defaultValue": "Default" }, { @@ -4443,9 +4275,7 @@ "block": true, "name": "Table block", "icon": "Table", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 600, "height": 400 @@ -4483,10 +4313,7 @@ "type": "select", "label": "Sort Order", "key": "sortOrder", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "defaultValue": "Ascending" }, { @@ -4638,9 +4465,7 @@ "block": true, "name": "Cards block", "icon": "PersonalizationField", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 600, "height": 400 @@ -4679,10 +4504,7 @@ "type": "select", "label": "Sort Order", "key": "sortOrder", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "defaultValue": "Descending" }, { @@ -4816,9 +4638,7 @@ "block": true, "name": "Repeater block", "icon": "ViewList", - "illegalChildren": [ - "section" - ], + "illegalChildren": ["section"], "hasChildren": true, "size": { "width": 400, @@ -4846,10 +4666,7 @@ "type": "select", "label": "Sort Order", "key": "sortOrder", - "options": [ - "Ascending", - "Descending" - ], + "options": ["Ascending", "Descending"], "defaultValue": "Descending" }, { @@ -5044,9 +4861,7 @@ "markdownviewer": { "name": "Markdown Viewer", "icon": "Preview", - "styles": [ - "size" - ], + "styles": ["size"], "size": { "width": 400, "height": 100 @@ -5063,9 +4878,7 @@ "formblock": { "name": "Form Block", "icon": "Form", - "styles": [ - "size" - ], + "styles": ["size"], "block": true, "info": "Form blocks are only compatible with internal or SQL tables", "size": { @@ -5077,11 +4890,7 @@ "type": "select", "label": "Type", "key": "actionType", - "options": [ - "Create", - "Update", - "View" - ], + "options": ["Create", "Update", "View"], "defaultValue": "Create" }, { @@ -5215,10 +5024,7 @@ "name": "Side Panel", "icon": "RailRight", "hasChildren": true, - "illegalChildren": [ - "section", - "sidepanel" - ], + "illegalChildren": ["section", "sidepanel"], "showEmptyState": false, "draggable": false, "info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action." @@ -5307,4 +5113,4 @@ "suffix": "repeater" } } -} \ No newline at end of file +} diff --git a/packages/client/package.json b/packages/client/package.json index f83cb6dea9..ea62d102fc 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,11 +19,11 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "2.4.12-alpha.6", - "@budibase/frontend-core": "2.4.12-alpha.6", - "@budibase/shared-core": "2.4.12-alpha.6", - "@budibase/string-templates": "2.4.12-alpha.6", - "@budibase/types": "2.4.12-alpha.6", + "@budibase/bbui": "^2.4.26", + "@budibase/frontend-core": "^2.4.26", + "@budibase/shared-core": "^2.4.26", + "@budibase/string-templates": "^2.4.26", + "@budibase/types": "^2.4.26", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/client/src/components/app/BackgroundImage.svelte b/packages/client/src/components/app/BackgroundImage.svelte index 909e0fd3fd..df6459c417 100644 --- a/packages/client/src/components/app/BackgroundImage.svelte +++ b/packages/client/src/components/app/BackgroundImage.svelte @@ -21,7 +21,9 @@ {#if url}
-
+
+ +
{:else if $builderStore.inBuilder}
{ - ctx.config = { - jwtSecret: env.JWT_SECRET, - useAppRootPath: true, - } - await next() - }) // re-direct before any middlewares occur .redirect("/", "/builder") .use( diff --git a/packages/server/src/environment.ts b/packages/server/src/environment.ts index 256d8d10c3..058e8bdff8 100644 --- a/packages/server/src/environment.ts +++ b/packages/server/src/environment.ts @@ -39,17 +39,14 @@ let inThread = false const environment = { // important - prefer app port to generic port PORT: process.env.APP_PORT || process.env.PORT, - JWT_SECRET: process.env.JWT_SECRET, COUCH_DB_URL: process.env.COUCH_DB_URL, MINIO_URL: process.env.MINIO_URL, WORKER_URL: process.env.WORKER_URL, AWS_REGION: process.env.AWS_REGION, MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, - CDN_URL: process.env.CDN_URL || "https://cdn.budi.live", REDIS_URL: process.env.REDIS_URL, REDIS_PASSWORD: process.env.REDIS_PASSWORD, - INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS, API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, diff --git a/packages/server/src/integrations/oracle.ts b/packages/server/src/integrations/oracle.ts index abf9bcbbe3..65e0829905 100644 --- a/packages/server/src/integrations/oracle.ts +++ b/packages/server/src/integrations/oracle.ts @@ -67,6 +67,7 @@ const SCHEMA: Integration = { database: { type: DatasourceFieldType.STRING, required: true, + display: "Service Name", }, user: { type: DatasourceFieldType.STRING, diff --git a/packages/server/src/integrations/s3.ts b/packages/server/src/integrations/s3.ts index b965b177d0..ad3bb09109 100644 --- a/packages/server/src/integrations/s3.ts +++ b/packages/server/src/integrations/s3.ts @@ -107,6 +107,7 @@ const SCHEMA: Integration = { readCsv: { displayName: "Read CSV", type: QueryType.FIELDS, + readable: true, fields: { bucket: { type: DatasourceFieldType.STRING, diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 9aab76ba4d..cf0585efd1 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -205,7 +205,6 @@ class TestConfiguration { request.appId = appId // fake cookies, we don't need them request.cookies = { set: () => {}, get: () => {} } - request.config = { jwtSecret: env.JWT_SECRET } request.user = { appId, tenantId: this.getTenantId() } request.query = {} request.request = { @@ -332,8 +331,8 @@ class TestConfiguration { roleId: roleId, appId, } - const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) - const appToken = auth.jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET) + const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET) // returning necessary request headers await cache.user.invalidateUser(userId) @@ -361,8 +360,8 @@ class TestConfiguration { roleId: roles.BUILTIN_ROLE_IDS.ADMIN, appId: this.appId, } - const authToken = auth.jwt.sign(authObj, env.JWT_SECRET) - const appToken = auth.jwt.sign(app, env.JWT_SECRET) + const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET) + const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET) const headers: any = { Accept: "application/json", Cookie: [ diff --git a/packages/server/src/utilities/workerRequests.ts b/packages/server/src/utilities/workerRequests.ts index e318b12f82..82e1aac428 100644 --- a/packages/server/src/utilities/workerRequests.ts +++ b/packages/server/src/utilities/workerRequests.ts @@ -6,6 +6,7 @@ import { constants, tenancy, logging, + env as coreEnv, } from "@budibase/backend-core" import { updateAppRole } from "./global" import { BBContext, User } from "@budibase/types" @@ -15,7 +16,7 @@ export function request(ctx?: BBContext, request?: any) { request.headers = {} } if (!ctx) { - request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY + request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY if (tenancy.isTenantIdSet()) { request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() } diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 7b405b9274..d09ac3c7c9 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1278,14 +1278,14 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.6.tgz#612a8a71482ebc0894c614ef4417516647908db0" - integrity sha512-skA88LrPlvu2lFOjmtatB2YBuZT8V/+ALT02QM5fq+2NhmF8AS7bQacuibA8Vh2rFKDs4eJuSIhi1QLr3wr7Sw== +"@budibase/backend-core@2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4" + integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow== dependencies: "@budibase/nano" "10.1.2" "@budibase/pouchdb-replication-stream" "1.2.10" - "@budibase/types" "2.4.12-alpha.6" + "@budibase/types" "^2.4.26" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-cloudfront-sign "2.2.0" @@ -1417,14 +1417,14 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.6.tgz#5255a5a64bb83e7b44d2258ae7fba5058d040414" - integrity sha512-kd/2NJE8GMGVsTUsp59exQJgnSgNCCwrb9jnDNJJg7z2HFr72k4zuzuxuhvcgMrq550j9YaKOuVIkB6aKwm22w== +"@budibase/pro@2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f" + integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w== dependencies: - "@budibase/backend-core" "2.4.12-alpha.6" + "@budibase/backend-core" "2.4.26" "@budibase/string-templates" "2.3.20" - "@budibase/types" "2.4.12-alpha.6" + "@budibase/types" "2.4.26" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -1463,10 +1463,10 @@ lodash "^4.17.20" vm2 "^3.9.4" -"@budibase/types@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.6.tgz#8a17dc073f4fd4232162ba114167902723fcc2a1" - integrity sha512-TFx2F4SkG6EQmI7RX7GhtfIUOVheqMfAQDfFifDDfUA/XcA3SZOS3Eat2AYIU5TofhcbfBXRgQmQJCiMqllVRQ== +"@budibase/types@2.4.26", "@budibase/types@^2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94" + integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index 404265f59e..1ed7d4243e 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/shared-core", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Shared data utils", "main": "dist/cjs/src/index.js", "types": "dist/mjs/src/index.d.ts", @@ -20,7 +20,7 @@ "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" }, "dependencies": { - "@budibase/types": "2.4.12-alpha.6" + "@budibase/types": "^2.4.26" }, "devDependencies": { "concurrently": "^7.6.0", diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 2486ca1050..34186cf554 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -62,7 +62,7 @@ export const getValidOperatorsForType = ( // Only allow equal/not equal for _id in SQL tables if (field === "_id" && externalTable) { - ops = [Op.Equals, Op.NotEquals] + ops = [Op.Equals, Op.NotEquals, Op.In] } return ops diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 4221cd1e75..998715179f 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/types/package.json b/packages/types/package.json index 3efeb255ac..56f4e56dd0 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Budibase types", "main": "dist/cjs/index.js", "types": "dist/mjs/index.d.ts", diff --git a/packages/worker/package.json b/packages/worker/package.json index 42843d33fb..7f4797bd1b 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "2.4.12-alpha.6", + "version": "2.4.26", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -36,10 +36,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.4.12-alpha.6", - "@budibase/pro": "2.4.12-alpha.6", - "@budibase/string-templates": "2.4.12-alpha.6", - "@budibase/types": "2.4.12-alpha.6", + "@budibase/backend-core": "^2.4.26", + "@budibase/pro": "2.4.26", + "@budibase/string-templates": "^2.4.26", + "@budibase/types": "^2.4.26", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/worker/src/api/controllers/global/configs.ts b/packages/worker/src/api/controllers/global/configs.ts index 02459855c2..4f4575074b 100644 --- a/packages/worker/src/api/controllers/global/configs.ts +++ b/packages/worker/src/api/controllers/global/configs.ts @@ -287,7 +287,7 @@ export async function publicSettings( // google const googleConfig = await configs.getGoogleConfig() - const preActivated = googleConfig?.activated == null + const preActivated = googleConfig && googleConfig.activated == null const google = preActivated || !!googleConfig?.activated const _googleCallbackUrl = await googleCallbackUrl(googleConfig) diff --git a/packages/worker/src/api/routes/global/tests/configs.spec.ts b/packages/worker/src/api/routes/global/tests/configs.spec.ts index 892fe8a67b..1abe575b02 100644 --- a/packages/worker/src/api/routes/global/tests/configs.spec.ts +++ b/packages/worker/src/api/routes/global/tests/configs.spec.ts @@ -288,7 +288,7 @@ describe("configs", () => { company: "Budibase", logoUrl: "", analyticsEnabled: false, - google: true, + google: false, googleCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/google/callback`, isSSOEnforced: false, oidc: false, diff --git a/packages/worker/src/environment.ts b/packages/worker/src/environment.ts index 3b5960f6f5..812af6aacd 100644 --- a/packages/worker/src/environment.ts +++ b/packages/worker/src/environment.ts @@ -30,10 +30,8 @@ const environment = { // auth MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY, MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY, - JWT_SECRET: process.env.JWT_SECRET, SALT_ROUNDS: process.env.SALT_ROUNDS, REDIS_PASSWORD: process.env.REDIS_PASSWORD, - INTERNAL_API_KEY: process.env.INTERNAL_API_KEY, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, // urls MINIO_URL: process.env.MINIO_URL, @@ -42,7 +40,6 @@ const environment = { ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, PLATFORM_URL: process.env.PLATFORM_URL, APPS_URL: process.env.APPS_URL, - CDN_URL: process.env.CDN_URL || "https://tenants.cdn.budi.live", // ports // prefer worker port to generic port PORT: process.env.WORKER_PORT || process.env.PORT, diff --git a/packages/worker/src/middleware/cloudRestricted.ts b/packages/worker/src/middleware/cloudRestricted.ts index 5440629de3..f9ab86e2e9 100644 --- a/packages/worker/src/middleware/cloudRestricted.ts +++ b/packages/worker/src/middleware/cloudRestricted.ts @@ -1,5 +1,5 @@ import env from "../environment" -import { constants } from "@budibase/backend-core" +import { constants, utils } from "@budibase/backend-core" import { BBContext } from "@budibase/types" /** @@ -9,7 +9,15 @@ import { BBContext } from "@budibase/types" export default async (ctx: BBContext, next: any) => { if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) { const apiKey = ctx.request.headers[constants.Header.API_KEY] - if (apiKey !== env.INTERNAL_API_KEY) { + if (!apiKey) { + ctx.throw(403, "Unauthorized") + } + + if (Array.isArray(apiKey)) { + ctx.throw(403, "Unauthorized") + } + + if (!utils.isValidInternalAPIKey(apiKey)) { ctx.throw(403, "Unauthorized") } } diff --git a/packages/worker/src/sdk/auth/auth.ts b/packages/worker/src/sdk/auth/auth.ts index 98830c576d..2e716426d5 100644 --- a/packages/worker/src/sdk/auth/auth.ts +++ b/packages/worker/src/sdk/auth/auth.ts @@ -5,10 +5,10 @@ import { sessions, events, HTTPError, + env as coreEnv, } from "@budibase/backend-core" import { PlatformLogoutOpts, User } from "@budibase/types" import jwt from "jsonwebtoken" -import env from "../../environment" import * as userSdk from "../users" import * as emails from "../../utilities/email" import * as redis from "../../utilities/redis" @@ -26,7 +26,7 @@ export async function loginUser(user: User) { sessionId, tenantId, }, - env.JWT_SECRET! + coreEnv.JWT_SECRET! ) return token } diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index 3004d0aed4..e5ed9e8141 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -74,7 +74,6 @@ class TestConfiguration { const request: any = {} // fake cookies, we don't need them request.cookies = { set: () => {}, get: () => {} } - request.config = { jwtSecret: env.JWT_SECRET } request.user = { tenantId: this.getTenantId() } request.query = {} request.request = { @@ -180,7 +179,7 @@ class TestConfiguration { sessionId: "sessionid", tenantId: user.tenantId, } - const authCookie = auth.jwt.sign(authToken, env.JWT_SECRET) + const authCookie = auth.jwt.sign(authToken, coreEnv.JWT_SECRET) return { Accept: "application/json", ...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]), @@ -197,7 +196,7 @@ class TestConfiguration { } internalAPIHeaders() { - return { [constants.Header.API_KEY]: env.INTERNAL_API_KEY } + return { [constants.Header.API_KEY]: coreEnv.INTERNAL_API_KEY } } adminOnlyResponse = () => { @@ -277,7 +276,7 @@ class TestConfiguration { // CONFIGS - OIDC getOIDConfigCookie(configId: string) { - const token = auth.jwt.sign(configId, env.JWT_SECRET) + const token = auth.jwt.sign(configId, coreEnv.JWT_SECRET) return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]]) } diff --git a/packages/worker/src/utilities/appService.ts b/packages/worker/src/utilities/appService.ts index 478e986fe8..8f411d58fa 100644 --- a/packages/worker/src/utilities/appService.ts +++ b/packages/worker/src/utilities/appService.ts @@ -1,5 +1,10 @@ import fetch from "node-fetch" -import { constants, tenancy, logging } from "@budibase/backend-core" +import { + constants, + tenancy, + logging, + env as coreEnv, +} from "@budibase/backend-core" import { checkSlashesInUrl } from "../utilities" import env from "../environment" import { SyncUserRequest, User } from "@budibase/types" @@ -9,7 +14,7 @@ async function makeAppRequest(url: string, method: string, body: any) { return } const request: any = { headers: {} } - request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY + request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY if (tenancy.isTenantIdSet()) { request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId() } diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 3b33c55abf..7855e71965 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -475,14 +475,14 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.6.tgz#612a8a71482ebc0894c614ef4417516647908db0" - integrity sha512-skA88LrPlvu2lFOjmtatB2YBuZT8V/+ALT02QM5fq+2NhmF8AS7bQacuibA8Vh2rFKDs4eJuSIhi1QLr3wr7Sw== +"@budibase/backend-core@2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4" + integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow== dependencies: "@budibase/nano" "10.1.2" "@budibase/pouchdb-replication-stream" "1.2.10" - "@budibase/types" "2.4.12-alpha.6" + "@budibase/types" "^2.4.26" "@shopify/jest-koa-mocks" "5.0.1" "@techpass/passport-openidconnect" "0.3.2" aws-cloudfront-sign "2.2.0" @@ -564,14 +564,14 @@ pouchdb-promise "^6.0.4" through2 "^2.0.0" -"@budibase/pro@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.6.tgz#5255a5a64bb83e7b44d2258ae7fba5058d040414" - integrity sha512-kd/2NJE8GMGVsTUsp59exQJgnSgNCCwrb9jnDNJJg7z2HFr72k4zuzuxuhvcgMrq550j9YaKOuVIkB6aKwm22w== +"@budibase/pro@2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f" + integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w== dependencies: - "@budibase/backend-core" "2.4.12-alpha.6" + "@budibase/backend-core" "2.4.26" "@budibase/string-templates" "2.3.20" - "@budibase/types" "2.4.12-alpha.6" + "@budibase/types" "2.4.26" "@koa/router" "8.0.8" bull "4.10.1" joi "17.6.0" @@ -592,10 +592,10 @@ lodash "^4.17.20" vm2 "^3.9.4" -"@budibase/types@2.4.12-alpha.6": - version "2.4.12-alpha.6" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.6.tgz#8a17dc073f4fd4232162ba114167902723fcc2a1" - integrity sha512-TFx2F4SkG6EQmI7RX7GhtfIUOVheqMfAQDfFifDDfUA/XcA3SZOS3Eat2AYIU5TofhcbfBXRgQmQJCiMqllVRQ== +"@budibase/types@2.4.26", "@budibase/types@^2.4.26": + version "2.4.26" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94" + integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag== "@cspotcode/source-map-support@^0.8.0": version "0.8.1"