From 3cfe641486af117b1db5468e7054909c720b3427 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 22 Dec 2023 18:28:07 +0000 Subject: [PATCH 01/11] Fixing issue with Redis disconnection - this should correctly reconnect the service when Redis service becomes available again. --- packages/backend-core/src/queue/queue.ts | 2 +- packages/backend-core/src/redis/redis.ts | 21 +++++++-------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index 0657437a3b..b95dace5b2 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -47,7 +47,7 @@ export function createQueue( cleanupInterval = timers.set(cleanup, CLEANUP_PERIOD_MS) // fire off an initial cleanup cleanup().catch(err => { - console.error(`Unable to cleanup automation queue initially - ${err}`) + console.error(`Unable to cleanup ${jobQueue} initially - ${err}`) }) } return queue diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index 701e262091..d15453ba62 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -18,6 +18,7 @@ import { SelectableDatabase, getRedisConnectionDetails, } from "./utils" +import { logAlert } from "../logging" import * as timers from "../timers" const RETRY_PERIOD_MS = 2000 @@ -39,21 +40,16 @@ function pickClient(selectDb: number): any { return CLIENTS[selectDb] } -function connectionError( - selectDb: number, - timeout: NodeJS.Timeout, - err: Error | string -) { +function connectionError(timeout: NodeJS.Timeout, err: Error | string) { // manually shut down, ignore errors if (CLOSED) { return } - pickClient(selectDb).disconnect() CLOSED = true // always clear this on error clearTimeout(timeout) CONNECTED = false - console.error("Redis connection failed - " + err) + logAlert("Redis connection failed", err) setTimeout(() => { init() }, RETRY_PERIOD_MS) @@ -79,11 +75,7 @@ function init(selectDb = DEFAULT_SELECT_DB) { // start the timer - only allowed 5 seconds to connect timeout = setTimeout(() => { if (!CONNECTED) { - connectionError( - selectDb, - timeout, - "Did not successfully connect in timeout" - ) + connectionError(timeout, "Did not successfully connect in timeout") } }, STARTUP_TIMEOUT_MS) @@ -106,12 +98,13 @@ function init(selectDb = DEFAULT_SELECT_DB) { // allow the process to exit return } - connectionError(selectDb, timeout, err) + connectionError(timeout, err) }) client.on("error", (err: Error) => { - connectionError(selectDb, timeout, err) + connectionError(timeout, err) }) client.on("connect", () => { + console.log(`Connected to Redis DB: ${selectDb}`) clearTimeout(timeout) CONNECTED = true }) From a6537e66c3085e70e89ac76aeaea4ee33b22e9d6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 10:26:03 +0100 Subject: [PATCH 02/11] Set scim config on beforeall --- packages/worker/src/api/routes/global/tests/scim.spec.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/worker/src/api/routes/global/tests/scim.spec.ts b/packages/worker/src/api/routes/global/tests/scim.spec.ts index 884625805c..3614328d91 100644 --- a/packages/worker/src/api/routes/global/tests/scim.spec.ts +++ b/packages/worker/src/api/routes/global/tests/scim.spec.ts @@ -14,9 +14,14 @@ import { events } from "@budibase/backend-core" jest.retryTimes(2, { logErrorsBeforeRetry: true }) jest.setTimeout(30000) -mocks.licenses.useScimIntegration() - describe("scim", () => { + beforeAll(async () => { + tk.freeze(mocks.date.MOCK_DATE) + mocks.licenses.useScimIntegration() + + await config.setSCIMConfig(true) + }) + beforeEach(async () => { jest.resetAllMocks() tk.freeze(mocks.date.MOCK_DATE) From fede6dc3e4136925946d97c4a45eceef7a988d5f Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 11:12:31 +0100 Subject: [PATCH 03/11] Fix flaky scim test --- .../worker/src/api/routes/global/tests/scim.spec.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/worker/src/api/routes/global/tests/scim.spec.ts b/packages/worker/src/api/routes/global/tests/scim.spec.ts index 3614328d91..56b7ca9f40 100644 --- a/packages/worker/src/api/routes/global/tests/scim.spec.ts +++ b/packages/worker/src/api/routes/global/tests/scim.spec.ts @@ -1,6 +1,6 @@ import tk from "timekeeper" import _ from "lodash" -import { mocks, structures } from "@budibase/backend-core/tests" +import { generator, mocks, structures } from "@budibase/backend-core/tests" import { ScimCreateUserRequest, ScimGroupResponse, @@ -575,8 +575,15 @@ describe("scim", () => { beforeAll(async () => { groups = [] - for (let i = 0; i < groupCount; i++) { - const body = structures.scim.createGroupRequest() + const groupNames = generator.unique( + () => generator.word(), + groupCount + ) + + for (const groupName of groupNames) { + const body = structures.scim.createGroupRequest({ + displayName: groupName, + }) groups.push(await config.api.scimGroupsAPI.post({ body })) } From f722f9e2d63997f1b2da6a89b23614d8bc19b7c2 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 15:06:04 +0100 Subject: [PATCH 04/11] Invalidate reset code once used --- packages/backend-core/src/cache/passwordReset.ts | 11 ++++++++++- packages/worker/src/sdk/auth/auth.ts | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/src/cache/passwordReset.ts b/packages/backend-core/src/cache/passwordReset.ts index 7f5a93f149..a19e99745a 100644 --- a/packages/backend-core/src/cache/passwordReset.ts +++ b/packages/backend-core/src/cache/passwordReset.ts @@ -1,6 +1,6 @@ import * as redis from "../redis/init" import * as utils from "../utils" -import { Duration, DurationType } from "../utils" +import { Duration } from "../utils" const TTL_SECONDS = Duration.fromHours(1).toSeconds() @@ -36,3 +36,12 @@ export async function getCode(code: string): Promise { } return value } + +/** + * Given a reset code this will invalidate it. + * @param code The code provided via the email link. + */ +export async function invalidateCode(code: string): Promise { + const client = await redis.getPasswordResetClient() + await client.delete(code) +} diff --git a/packages/worker/src/sdk/auth/auth.ts b/packages/worker/src/sdk/auth/auth.ts index 1f9da8a260..bdc5fc2366 100644 --- a/packages/worker/src/sdk/auth/auth.ts +++ b/packages/worker/src/sdk/auth/auth.ts @@ -79,6 +79,8 @@ export const resetUpdate = async (resetCode: string, password: string) => { user.password = password user = await userSdk.db.save(user) + await cache.passwordReset.invalidateCode(resetCode) + // remove password from the user before sending events delete user.password await events.user.passwordReset(user) From dcacd6bf17a2491f64708b6c4b88f428c5fdc6d3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 16:07:26 +0100 Subject: [PATCH 05/11] Add basic test --- packages/backend-core/src/users/db.ts | 2 +- .../worker/src/sdk/auth/tests/auth.spec.ts | 28 +++++++++++++++++++ .../worker/src/sdk/users/tests/users.spec.ts | 1 - 3 files changed, 29 insertions(+), 2 deletions(-) create mode 100644 packages/worker/src/sdk/auth/tests/auth.spec.ts diff --git a/packages/backend-core/src/users/db.ts b/packages/backend-core/src/users/db.ts index 326bed3cc5..01fa4899d1 100644 --- a/packages/backend-core/src/users/db.ts +++ b/packages/backend-core/src/users/db.ts @@ -2,7 +2,7 @@ import env from "../environment" import * as eventHelpers from "./events" import * as accountSdk from "../accounts" import * as cache from "../cache" -import { doInTenant, getGlobalDB, getIdentity, getTenantId } from "../context" +import { getGlobalDB, getIdentity, getTenantId } from "../context" import * as dbUtils from "../db" import { EmailUnavailableError, HTTPError } from "../errors" import * as platform from "../platform" diff --git a/packages/worker/src/sdk/auth/tests/auth.spec.ts b/packages/worker/src/sdk/auth/tests/auth.spec.ts new file mode 100644 index 0000000000..b1758e79c6 --- /dev/null +++ b/packages/worker/src/sdk/auth/tests/auth.spec.ts @@ -0,0 +1,28 @@ +import { cache, context, utils } from "@budibase/backend-core" +import { resetUpdate } from "../auth" +import { generator, structures } from "@budibase/backend-core/tests" +import { TestConfiguration } from "../../../tests" + +describe("auth", () => { + const config = new TestConfiguration() + + describe("resetUpdate", () => { + it("providing a valid code will update the password", async () => { + await context.doInTenant(structures.tenant.id(), async () => { + const user = await config.createUser() + const previousPassword = user.password + + const code = await cache.passwordReset.createCode(user._id!, {}) + const newPassword = generator.hash() + + await resetUpdate(code, newPassword) + + const persistedUser = await config.getUser(user.email) + expect(persistedUser.password).not.toBe(previousPassword) + expect( + await utils.compare(newPassword, persistedUser.password!) + ).toBeTruthy() + }) + }) + }) +}) diff --git a/packages/worker/src/sdk/users/tests/users.spec.ts b/packages/worker/src/sdk/users/tests/users.spec.ts index df1aa74200..a02ca42c2f 100644 --- a/packages/worker/src/sdk/users/tests/users.spec.ts +++ b/packages/worker/src/sdk/users/tests/users.spec.ts @@ -1,6 +1,5 @@ import { structures, mocks } from "../../../tests" import { env, context } from "@budibase/backend-core" -import * as users from "../users" import { db as userDb } from "../" import { CloudAccount } from "@budibase/types" From f74264c1c82dce62fdbbe45c3e94369d9e857a74 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 16:37:34 +0100 Subject: [PATCH 06/11] Add tests --- .../backend-core/src/cache/passwordReset.ts | 4 ++- .../worker/src/sdk/auth/tests/auth.spec.ts | 25 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/src/cache/passwordReset.ts b/packages/backend-core/src/cache/passwordReset.ts index a19e99745a..db32b520f7 100644 --- a/packages/backend-core/src/cache/passwordReset.ts +++ b/packages/backend-core/src/cache/passwordReset.ts @@ -32,7 +32,9 @@ export async function getCode(code: string): Promise { const client = await redis.getPasswordResetClient() const value = (await client.get(code)) as PasswordReset | undefined if (!value) { - throw "Provided information is not valid, cannot reset password - please try again." + throw new Error( + "Provided information is not valid, cannot reset password - please try again." + ) } return value } diff --git a/packages/worker/src/sdk/auth/tests/auth.spec.ts b/packages/worker/src/sdk/auth/tests/auth.spec.ts index b1758e79c6..0d05a3fbb3 100644 --- a/packages/worker/src/sdk/auth/tests/auth.spec.ts +++ b/packages/worker/src/sdk/auth/tests/auth.spec.ts @@ -24,5 +24,30 @@ describe("auth", () => { ).toBeTruthy() }) }) + + it("wrong code will not allow to reset the password", async () => { + await context.doInTenant(structures.tenant.id(), async () => { + const code = generator.hash() + const newPassword = generator.hash() + + await expect(resetUpdate(code, newPassword)).rejects.toThrow( + "Provided information is not valid, cannot reset password - please try again." + ) + }) + }) + + it("the same code cannot be used twice", async () => { + await context.doInTenant(structures.tenant.id(), async () => { + const user = await config.createUser() + + const code = await cache.passwordReset.createCode(user._id!, {}) + const newPassword = generator.hash() + + await resetUpdate(code, newPassword) + await expect(resetUpdate(code, newPassword)).rejects.toThrow( + "Provided information is not valid, cannot reset password - please try again." + ) + }) + }) }) }) From d1ffe242692570e4f04d147178710c319a2bfd8c Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 29 Dec 2023 16:54:47 +0100 Subject: [PATCH 07/11] Invalidate session on password update --- packages/worker/src/sdk/auth/auth.ts | 1 + .../worker/src/sdk/auth/tests/auth.spec.ts | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/worker/src/sdk/auth/auth.ts b/packages/worker/src/sdk/auth/auth.ts index bdc5fc2366..3f24de440a 100644 --- a/packages/worker/src/sdk/auth/auth.ts +++ b/packages/worker/src/sdk/auth/auth.ts @@ -80,6 +80,7 @@ export const resetUpdate = async (resetCode: string, password: string) => { user = await userSdk.db.save(user) await cache.passwordReset.invalidateCode(resetCode) + await sessions.invalidateSessions(userId) // remove password from the user before sending events delete user.password diff --git a/packages/worker/src/sdk/auth/tests/auth.spec.ts b/packages/worker/src/sdk/auth/tests/auth.spec.ts index 0d05a3fbb3..e9f348f7c7 100644 --- a/packages/worker/src/sdk/auth/tests/auth.spec.ts +++ b/packages/worker/src/sdk/auth/tests/auth.spec.ts @@ -1,5 +1,5 @@ -import { cache, context, utils } from "@budibase/backend-core" -import { resetUpdate } from "../auth" +import { cache, context, sessions, utils } from "@budibase/backend-core" +import { loginUser, resetUpdate } from "../auth" import { generator, structures } from "@budibase/backend-core/tests" import { TestConfiguration } from "../../../tests" @@ -49,5 +49,22 @@ describe("auth", () => { ) }) }) + + it("updating the password will invalidate all the sessions", async () => { + await context.doInTenant(structures.tenant.id(), async () => { + const user = await config.createUser() + + await loginUser(user) + + expect(await sessions.getSessionsForUser(user._id!)).toHaveLength(1) + + const code = await cache.passwordReset.createCode(user._id!, {}) + const newPassword = generator.hash() + + await resetUpdate(code, newPassword) + + expect(await sessions.getSessionsForUser(user._id!)).toHaveLength(0) + }) + }) }) }) From 865038d02f42bc6b23567359fdb3dc152a28a213 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 29 Dec 2023 17:24:51 +0000 Subject: [PATCH 08/11] Bump version to 2.13.51 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 671935c34b..bbe4da4264 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.13.50", + "version": "2.13.51", "npmClient": "yarn", "packages": [ "packages/*", From d277832f47b9c4bcb769e6f5c90dfbc7d6592256 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Tue, 2 Jan 2024 12:01:17 +0100 Subject: [PATCH 09/11] Fix enterprise license color --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index e46a352a63..ebdd4aa056 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit e46a352a6326a838faa00f912de069aee95d7300 +Subproject commit ebdd4aa0561dfcc3de3a906eead3979b680d039a From e38624161ecc486fb49c862a8808c6c4701a842e Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Tue, 2 Jan 2024 11:02:25 +0000 Subject: [PATCH 10/11] Update account-portal submodule ref --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index e46a352a63..af1ae16221 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit e46a352a6326a838faa00f912de069aee95d7300 +Subproject commit af1ae16221a4e6ababe8cc1f14803cbc849b2e85 From debf2b3d1e127f236bb09be0e3e910f9a7668263 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Tue, 2 Jan 2024 12:06:18 +0100 Subject: [PATCH 11/11] Update account portal submodule --- packages/account-portal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/account-portal b/packages/account-portal index ebdd4aa056..d6a1f89aa5 160000 --- a/packages/account-portal +++ b/packages/account-portal @@ -1 +1 @@ -Subproject commit ebdd4aa0561dfcc3de3a906eead3979b680d039a +Subproject commit d6a1f89aa543bdce7acde5fbe4ce650a1344e2fe