diff --git a/packages/backend-core/src/security/auth.ts b/packages/backend-core/src/security/auth.ts index 1cce35a0af..7dacecdb3b 100644 --- a/packages/backend-core/src/security/auth.ts +++ b/packages/backend-core/src/security/auth.ts @@ -1,6 +1,6 @@ import env from "../environment" -export const PASSWORD_MIN_LENGTH = +(env.PASSWORD_MIN_LENGTH || 8) +export const PASSWORD_MIN_LENGTH = +(env.PASSWORD_MIN_LENGTH || 12) export const PASSWORD_MAX_LENGTH = +(env.PASSWORD_MAX_LENGTH || 512) export function validatePassword( diff --git a/packages/backend-core/src/security/tests/auth.spec.ts b/packages/backend-core/src/security/tests/auth.spec.ts index b1835fdfb3..78e25b0a81 100644 --- a/packages/backend-core/src/security/tests/auth.spec.ts +++ b/packages/backend-core/src/security/tests/auth.spec.ts @@ -4,7 +4,7 @@ import { PASSWORD_MAX_LENGTH, validatePassword } from "../auth" describe("auth", () => { describe("validatePassword", () => { it("a valid password returns successful", () => { - expect(validatePassword("password")).toEqual({ valid: true }) + expect(validatePassword("password123!")).toEqual({ valid: true }) }) it.each([ @@ -14,7 +14,7 @@ describe("auth", () => { ])("%s returns unsuccessful", (_, password) => { expect(validatePassword(password as string)).toEqual({ valid: false, - error: "Password invalid. Minimum 8 characters.", + error: "Password invalid. Minimum 12 characters.", }) }) diff --git a/packages/backend-core/tests/core/utilities/structures/users.ts b/packages/backend-core/tests/core/utilities/structures/users.ts index db90887af2..0171353e23 100644 --- a/packages/backend-core/tests/core/utilities/structures/users.ts +++ b/packages/backend-core/tests/core/utilities/structures/users.ts @@ -21,7 +21,7 @@ export const user = (userProps?: Partial>): User => { _id: userId, userId, email: newEmail(), - password: "password", + password: "password123!", roles: { app_test: "admin" }, firstName: generator.first(), lastName: generator.last(), diff --git a/packages/builder/src/components/common/users/PasswordRepeatInput.svelte b/packages/builder/src/components/common/users/PasswordRepeatInput.svelte index 9aa9000720..bd7f5b50c7 100644 --- a/packages/builder/src/components/common/users/PasswordRepeatInput.svelte +++ b/packages/builder/src/components/common/users/PasswordRepeatInput.svelte @@ -1,14 +1,17 @@ diff --git a/packages/builder/src/components/settings/ChangePasswordModal.svelte b/packages/builder/src/components/settings/ChangePasswordModal.svelte index 358e238092..32b8b53fa9 100644 --- a/packages/builder/src/components/settings/ChangePasswordModal.svelte +++ b/packages/builder/src/components/settings/ChangePasswordModal.svelte @@ -14,8 +14,15 @@ notifications.error("Failed to update password") } } + + const handleKeydown = evt => { + if (evt.key === "Enter" && !error && password) { + updatePassword() + } + } + { + if (evt.key === "Enter") { + save() + } + } + @@ -83,9 +95,15 @@ validate={() => { let fieldError = {} - fieldError["password"] = !formData.password - ? "Please enter a password" - : undefined + if (!formData.password) { + fieldError["password"] = "Please enter a password" + } else if (formData.password.length < passwordMinLength) { + fieldError[ + "password" + ] = `Password must be at least ${passwordMinLength} characters` + } else { + fieldError["password"] = undefined + } fieldError["confirmationPassword"] = !passwordsMatch( diff --git a/packages/builder/src/pages/builder/invite/index.svelte b/packages/builder/src/pages/builder/invite/index.svelte index 245309dc32..597deed7c9 100644 --- a/packages/builder/src/pages/builder/invite/index.svelte +++ b/packages/builder/src/pages/builder/invite/index.svelte @@ -9,7 +9,7 @@ FancyInput, } from "@budibase/bbui" import { goto, params } from "@roxi/routify" - import { users, organisation, auth } from "stores/portal" + import { users, organisation, auth, admin } from "stores/portal" import Logo from "assets/bb-emblem.svg" import { TestimonialPage } from "@budibase/frontend-core/src/components" import { onMount } from "svelte" @@ -23,6 +23,7 @@ let loaded = false $: company = $organisation.company || "Budibase" + $: passwordMinLength = $admin.passwordMinLength ?? 12 async function acceptInvite() { form.validate() @@ -85,8 +86,15 @@ notifications.error("Error getting invite config") } }) + + const handleKeydown = evt => { + if (evt.key === "Enter") { + acceptInvite() + } + } + {#if loaded} @@ -154,8 +162,8 @@ function validatePassword() { if (!formData.password) { return "Please enter a password" - } else if (formData.password.length < 8) { - return "Please enter at least 8 characters" + } else if (formData.password.length < passwordMinLength) { + return `Please enter at least ${passwordMinLength} characters` } return undefined } diff --git a/packages/builder/src/stores/portal/admin.js b/packages/builder/src/stores/portal/admin.js index 29d4585c06..54757fb314 100644 --- a/packages/builder/src/stores/portal/admin.js +++ b/packages/builder/src/stores/portal/admin.js @@ -50,6 +50,7 @@ export function createAdminStore() { store.baseUrl = environment.baseUrl store.offlineMode = environment.offlineMode store.maintenance = environment.maintenance + store.passwordMinLength = environment.passwordMinLength return store }) } diff --git a/packages/worker/src/api/controllers/system/environment.ts b/packages/worker/src/api/controllers/system/environment.ts index 59a4e095dc..ae426ad8b1 100644 --- a/packages/worker/src/api/controllers/system/environment.ts +++ b/packages/worker/src/api/controllers/system/environment.ts @@ -42,6 +42,7 @@ export const fetch = async (ctx: Ctx) => { baseUrl: env.PLATFORM_URL, isDev: env.isDev() && !env.isTest(), maintenance: [], + passwordMinLength: env.PASSWORD_MIN_LENGTH, } if (env.SELF_HOSTED) { 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 9a004497c7..f6bdd7f663 100644 --- a/packages/worker/src/api/routes/global/tests/auth.spec.ts +++ b/packages/worker/src/api/routes/global/tests/auth.spec.ts @@ -66,7 +66,7 @@ describe("/api/global/auth", () => { it("should return 403 with incorrect credentials", async () => { const tenantId = config.tenantId! const email = config.user?.email! - const password = "incorrect" + const password = "incorrect123" const response = await config.api.auth.login( tenantId, @@ -83,7 +83,7 @@ describe("/api/global/auth", () => { it("should return 403 when user doesn't exist", async () => { const tenantId = config.tenantId! const email = "invaliduser@example.com" - const password = "password" + const password = "password123!" const response = await config.api.auth.login( tenantId, @@ -203,7 +203,7 @@ describe("/api/global/auth", () => { ) delete user.password - const newPassword = "newpassword" + const newPassword = "newpassword1" const res = await config.api.auth.updatePassword(code!, newPassword) user = (await config.getUser(user.email))! diff --git a/packages/worker/src/api/routes/global/tests/self.spec.ts b/packages/worker/src/api/routes/global/tests/self.spec.ts index 1c0542e885..277c11b081 100644 --- a/packages/worker/src/api/routes/global/tests/self.spec.ts +++ b/packages/worker/src/api/routes/global/tests/self.spec.ts @@ -32,7 +32,7 @@ describe("/api/global/self", () => { const res = await config.api.self .updateSelf(user, { - password: "newPassword", + password: "newPassword1", }) .expect(200) diff --git a/packages/worker/src/api/routes/global/tests/tenant.spec.ts b/packages/worker/src/api/routes/global/tests/tenant.spec.ts index e039b4139b..390cfa2af5 100644 --- a/packages/worker/src/api/routes/global/tests/tenant.spec.ts +++ b/packages/worker/src/api/routes/global/tests/tenant.spec.ts @@ -29,7 +29,7 @@ describe("/api/global/tenant", () => { const tenantInfo: TenantInfo = { owner: { email: "test@example.com", - password: "PASSWORD", + password: "PASSWORD123!", ssoId: "SSO_ID", givenName: "Jane", familyName: "Doe", diff --git a/packages/worker/src/environment.ts b/packages/worker/src/environment.ts index 6e36b45a3b..ed618f7c71 100644 --- a/packages/worker/src/environment.ts +++ b/packages/worker/src/environment.ts @@ -26,6 +26,7 @@ const environment = { SALT_ROUNDS: process.env.SALT_ROUNDS, REDIS_PASSWORD: process.env.REDIS_PASSWORD, COOKIE_DOMAIN: process.env.COOKIE_DOMAIN, + PASSWORD_MIN_LENGTH: process.env.PASSWORD_MIN_LENGTH, // urls MINIO_URL: process.env.MINIO_URL, COUCH_DB_URL: process.env.COUCH_DB_URL, diff --git a/packages/worker/src/tests/TestConfiguration.ts b/packages/worker/src/tests/TestConfiguration.ts index e44e9e2923..c1c8aa8b19 100644 --- a/packages/worker/src/tests/TestConfiguration.ts +++ b/packages/worker/src/tests/TestConfiguration.ts @@ -44,7 +44,7 @@ class TestConfiguration { tenantId: string user?: User apiKey?: string - userPassword = "password" + userPassword = "password123!" constructor(opts: { openServer: boolean } = { openServer: true }) { // default to cloud hosting diff --git a/packages/worker/src/tests/api/users.ts b/packages/worker/src/tests/api/users.ts index 541004391d..8ff31f04fc 100644 --- a/packages/worker/src/tests/api/users.ts +++ b/packages/worker/src/tests/api/users.ts @@ -48,7 +48,7 @@ export class UserAPI extends TestAPI { return this.request .post(`/api/global/users/invite/accept`) .send({ - password: "newpassword", + password: "newpassword1", inviteCode: code, firstName: "Ted", }) @@ -101,7 +101,7 @@ export class UserAPI extends TestAPI { if (!request) { request = { email: structures.email(), - password: generator.string({ length: 8 }), + password: generator.string({ length: 12 }), tenantId: structures.tenant.id(), } }