diff --git a/lerna.json b/lerna.json index fa3124e9a1..73ae4e4f6e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.4.44-alpha.26", + "version": "2.5.4", "npmClient": "yarn", "useWorkspaces": true, "packages": ["packages/*"], diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index c32983d418..9ca36c5d83 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/backend-core", - "version": "2.4.44-alpha.26", + "version": "2.5.4", "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.44-alpha.26", + "@budibase/types": "^2.5.4", "@shopify/jest-koa-mocks": "5.0.1", "@techpass/passport-openidconnect": "0.3.2", "aws-cloudfront-sign": "2.2.0", diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 720046520b..e8db77cd38 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.44-alpha.26", + "version": "2.5.4", "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.44-alpha.26", - "@budibase/string-templates": "2.4.44-alpha.26", + "@budibase/shared-core": "^2.5.4", + "@budibase/string-templates": "^2.5.4", "@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 12a6cbcea7..622f38cd4d 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/builder", - "version": "2.4.44-alpha.26", + "version": "2.5.4", "license": "GPL-3.0", "private": true, "scripts": { @@ -58,11 +58,11 @@ } }, "dependencies": { - "@budibase/bbui": "2.4.44-alpha.26", - "@budibase/client": "2.4.44-alpha.26", - "@budibase/frontend-core": "2.4.44-alpha.26", - "@budibase/shared-core": "2.4.44-alpha.26", - "@budibase/string-templates": "2.4.44-alpha.26", + "@budibase/bbui": "^2.5.4", + "@budibase/client": "^2.5.4", + "@budibase/frontend-core": "^2.5.4", + "@budibase/shared-core": "^2.5.4", + "@budibase/string-templates": "^2.5.4", "@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/builderStore/dataBinding.js b/packages/builder/src/builderStore/dataBinding.js index fca106edbd..0d41931a55 100644 --- a/packages/builder/src/builderStore/dataBinding.js +++ b/packages/builder/src/builderStore/dataBinding.js @@ -120,7 +120,7 @@ export const toBindingsArray = (valueMap, prefix, category) => { return [] } return Object.keys(valueMap).reduce((acc, binding) => { - if (!binding || !valueMap[binding]) { + if (!binding) { return acc } diff --git a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte index 1c93880ec1..1a4ced9f3a 100644 --- a/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte +++ b/packages/builder/src/components/backend/DataTable/RowFieldControl.svelte @@ -42,7 +42,13 @@ {#if type === "options" && meta.constraints.inclusion.length !== 0} - {:else if type === "datetime"} >>>>>> f45da9ccfde1fedb83ec757e64fb972b510c67d6 "worker-farm": "1.7.0", "xml2js": "0.5.0", "yargs": "13.2.4", diff --git a/packages/shared-core/package.json b/packages/shared-core/package.json index 66471474a6..dba357b7b0 100644 --- a/packages/shared-core/package.json +++ b/packages/shared-core/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/shared-core", - "version": "2.4.44-alpha.26", + "version": "2.5.4", "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.44-alpha.26" + "@budibase/types": "^2.5.4" }, "devDependencies": { "concurrently": "^7.6.0", diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 22810f31f0..58d6819113 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "2.4.44-alpha.26", + "version": "2.5.4", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", @@ -30,7 +30,7 @@ "handlebars": "^4.7.6", "handlebars-utils": "^1.0.6", "lodash": "^4.17.20", - "vm2": "^3.9.4" + "vm2": "^3.9.15" }, "devDependencies": { "@rollup/plugin-commonjs": "^17.1.0", diff --git a/packages/types/package.json b/packages/types/package.json index 1c65c6212f..e4c62c4b3d 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/types", - "version": "2.4.44-alpha.26", + "version": "2.5.4", "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 83d4d4e44c..e56bd52848 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.44-alpha.26", + "version": "2.5.4", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -37,10 +37,10 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "2.4.44-alpha.26", - "@budibase/pro": "2.4.44-alpha.26", - "@budibase/string-templates": "2.4.44-alpha.26", - "@budibase/types": "2.4.44-alpha.26", + "@budibase/backend-core": "^2.5.4", + "@budibase/pro": "2.5.4", + "@budibase/string-templates": "^2.5.4", + "@budibase/types": "^2.5.4", "@koa/router": "8.0.8", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "0.3.2", diff --git a/packages/worker/src/api/routes/global/tests/auth.spec.ts b/packages/worker/src/api/routes/global/tests/auth.spec.ts index 6c133df652..5e62b2123f 100644 --- a/packages/worker/src/api/routes/global/tests/auth.spec.ts +++ b/packages/worker/src/api/routes/global/tests/auth.spec.ts @@ -126,9 +126,8 @@ describe("/api/global/auth", () => { it("should prevent user from logging in", async () => { user = await config.createUser() const account = structures.accounts.ssoAccount() as CloudAccount - mocks.accounts.getAccount.mockReturnValueOnce( - Promise.resolve(account) - ) + account.email = user.email + mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) await testSSOUser() }) @@ -186,9 +185,8 @@ describe("/api/global/auth", () => { it("should prevent user from generating password reset email", async () => { user = await config.createUser(structures.users.user()) const account = structures.accounts.ssoAccount() as CloudAccount - mocks.accounts.getAccount.mockReturnValueOnce( - Promise.resolve(account) - ) + account.email = user.email + mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) await testSSOUser() }) diff --git a/packages/worker/src/sdk/users/tests/users.spec.ts b/packages/worker/src/sdk/users/tests/users.spec.ts index 77f02eec7a..a24f074512 100644 --- a/packages/worker/src/sdk/users/tests/users.spec.ts +++ b/packages/worker/src/sdk/users/tests/users.spec.ts @@ -1,6 +1,6 @@ import { structures } from "../../../tests" import { mocks } from "@budibase/backend-core/tests" -import { env } from "@budibase/backend-core" +import { env, context } from "@budibase/backend-core" import * as users from "../users" import { CloudAccount } from "@budibase/types" import { isPreventPasswordActions } from "../users" @@ -16,32 +16,50 @@ describe("users", () => { describe("isPreventPasswordActions", () => { it("returns false for non sso user", async () => { - const user = structures.users.user() - const result = await users.isPreventPasswordActions(user) - expect(result).toBe(false) + await context.doInTenant(structures.tenant.id(), async () => { + const user = structures.users.user() + const result = await users.isPreventPasswordActions(user) + expect(result).toBe(false) + }) }) it("returns true for sso account user", async () => { - const user = structures.users.user() - mocks.accounts.getAccount.mockReturnValue( - Promise.resolve(structures.accounts.ssoAccount() as CloudAccount) - ) - const result = await users.isPreventPasswordActions(user) - expect(result).toBe(true) + await context.doInTenant(structures.tenant.id(), async () => { + const user = structures.users.user() + const account = structures.accounts.ssoAccount() as CloudAccount + account.email = user.email + mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) + const result = await users.isPreventPasswordActions(user) + expect(result).toBe(true) + }) + }) + + it("returns false when account doesn't match user email", async () => { + await context.doInTenant(structures.tenant.id(), async () => { + const user = structures.users.user() + const account = structures.accounts.ssoAccount() as CloudAccount + mocks.accounts.getAccountByTenantId.mockResolvedValueOnce(account) + const result = await users.isPreventPasswordActions(user) + expect(result).toBe(false) + }) }) it("returns true for sso user", async () => { - const user = structures.users.ssoUser() - const result = await users.isPreventPasswordActions(user) - expect(result).toBe(true) + await context.doInTenant(structures.tenant.id(), async () => { + const user = structures.users.ssoUser() + const result = await users.isPreventPasswordActions(user) + expect(result).toBe(true) + }) }) describe("enforced sso", () => { it("returns true for all users when sso is enforced", async () => { - const user = structures.users.user() - pro.features.isSSOEnforced.mockReturnValue(Promise.resolve(true)) - const result = await users.isPreventPasswordActions(user) - expect(result).toBe(true) + await context.doInTenant(structures.tenant.id(), async () => { + const user = structures.users.user() + pro.features.isSSOEnforced.mockResolvedValueOnce(true) + const result = await users.isPreventPasswordActions(user) + expect(result).toBe(true) + }) }) }) diff --git a/packages/worker/src/sdk/users/users.ts b/packages/worker/src/sdk/users/users.ts index 2150654ae9..9b53866f37 100644 --- a/packages/worker/src/sdk/users/users.ts +++ b/packages/worker/src/sdk/users/users.ts @@ -33,6 +33,7 @@ import { SearchUsersRequest, User, SaveUserOpts, + Account, } from "@budibase/types" import { sendEmail } from "../../utilities/email" import { EmailTemplatePurpose } from "../../constants" @@ -90,7 +91,8 @@ const buildUser = async ( requirePassword: true, }, tenantId: string, - dbUser?: any + dbUser?: any, + account?: Account ): Promise => { let { password, _id } = user @@ -101,7 +103,7 @@ const buildUser = async ( let hashedPassword if (password) { - if (await isPreventPasswordActions(user)) { + if (await isPreventPasswordActions(user, account)) { throw new HTTPError("Password change is disabled for this user", 400) } hashedPassword = opts.hashPassword ? await utils.hash(password) : password @@ -172,7 +174,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => { } } -export async function isPreventPasswordActions(user: User) { +export async function isPreventPasswordActions(user: User, account?: Account) { // when in maintenance mode we allow sso users with the admin role // to perform any password action - this prevents lockout if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE && user.admin?.global) { @@ -190,8 +192,10 @@ export async function isPreventPasswordActions(user: User) { } // Check account sso - const account = await accountSdk.api.getAccount(user.email) - return !!(account && isSSOAccount(account)) + if (!account) { + account = await accountSdk.api.getAccountByTenantId(tenancy.getTenantId()) + } + return !!(account && account.email === user.email && isSSOAccount(account)) } export const save = async ( @@ -402,6 +406,7 @@ export const bulkCreate = async ( newUsers.push(newUser) } + const account = await accountSdk.api.getAccountByTenantId(tenantId) // create the promises array that will be called by bulkDocs newUsers.forEach((user: any) => { usersToSave.push( @@ -411,7 +416,9 @@ export const bulkCreate = async ( hashPassword: true, requirePassword: user.requirePassword, }, - tenantId + tenantId, + undefined, // no dbUser + account ) ) })