diff --git a/frontend/src/app/main/ui/settings/password.cljs b/frontend/src/app/main/ui/settings/password.cljs index 301bff0d00..651b97c1a7 100644 --- a/frontend/src/app/main/ui/settings/password.cljs +++ b/frontend/src/app/main/ui/settings/password.cljs @@ -48,12 +48,14 @@ :on-error (partial on-error form)})] (st/emit! (udu/update-password params)))) -(def ^:private schema:password-form +(def schema:password-form [:and [:map {:title "PasswordForm"} [:password-1 ::sm/password] [:password-2 ::sm/password] - [:password-old ::sm/password]] + ;; The old password is validated by the backend, so it only needs to be + ;; present here; it may predate the current minimum length policy. + [:password-old [::sm/text {:max 500}]]] [:fn {:error/code "errors.password-invalid-confirmation" :error/field :password-2} (fn [{:keys [password-1 password-2]}] diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index 3c03ee7e18..86ff0793b8 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -60,6 +60,7 @@ [frontend-tests.ui.ds-controls-numeric-input-test] [frontend-tests.ui.layout-container-multiple-test] [frontend-tests.ui.measures-menu-props-test] + [frontend-tests.ui.settings-password-schema-test] [frontend-tests.util-clipboard-test] [frontend-tests.util-object-test] [frontend-tests.util-range-tree-test] @@ -136,6 +137,7 @@ 'frontend-tests.ui.ds-controls-numeric-input-test 'frontend-tests.ui.layout-container-multiple-test 'frontend-tests.ui.measures-menu-props-test + 'frontend-tests.ui.settings-password-schema-test 'frontend-tests.util-clipboard-test 'frontend-tests.util-object-test 'frontend-tests.util-range-tree-test diff --git a/frontend/test/frontend_tests/ui/settings_password_schema_test.cljs b/frontend/test/frontend_tests/ui/settings_password_schema_test.cljs new file mode 100644 index 0000000000..ac305b81fb --- /dev/null +++ b/frontend/test/frontend_tests/ui/settings_password_schema_test.cljs @@ -0,0 +1,77 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC Sucursal en España SL + +(ns frontend-tests.ui.settings-password-schema-test + (:require + [app.common.schema :as sm] + [app.main.ui.settings.password :as passwd] + [cljs.test :as t :include-macros true] + [malli.core :as m])) + +(def ^:private new-password "a-long-enough-password") + +(defn- params + [password-old] + {:password-old password-old + :password-1 new-password + :password-2 new-password}) + +(defn- valid? + [data] + (sm/validate passwd/schema:password-form data)) + +(defn- error-codes + "Maps every schema problem to [field error-code], mirroring how + app.common.schema.messages resolves the message shown next to an input." + [data] + (->> (:errors (sm/explain passwd/schema:password-form data)) + (map (fn [{:keys [in schema]}] + (let [props (m/properties schema) + tprops (m/type-properties schema)] + [(or (:error/field props) (first in)) + (or (:error/code props) (:error/code tprops))]))) + (into {}))) + +(t/deftest short-old-password-is-accepted + (t/testing "an existing password shorter than the 8 char policy can still be typed in" + (t/is (true? (valid? (params "short")))) + (t/is (empty? (error-codes (params "short")))))) + +(t/deftest single-char-old-password-is-accepted + (t/is (true? (valid? (params "x"))))) + +(t/deftest unicode-old-password-is-accepted + (t/is (true? (valid? (params "🔑é")))) + (t/is (true? (valid? (params " hunter2 "))))) + +(t/deftest empty-old-password-is-rejected + (t/is (false? (valid? (params "")))) + (t/is (contains? (error-codes (params "")) :password-old))) + +(t/deftest blank-old-password-is-rejected + (t/is (false? (valid? (params " ")))) + (t/is (contains? (error-codes (params " ")) :password-old))) + +(t/deftest missing-old-password-is-rejected + (t/is (false? (valid? (dissoc (params "short") :password-old))))) + +(t/deftest overlong-old-password-is-rejected + (t/is (false? (valid? (params (apply str (repeat 501 "a"))))))) + +(t/deftest short-new-password-is-rejected + (t/testing "the 8 char policy still applies to the new password" + (let [data {:password-old "short" :password-1 "abc" :password-2 "abc"}] + (t/is (false? (valid? data))) + (t/is (= "errors.password-too-short" + (get (error-codes data) :password-1)))))) + +(t/deftest confirmation-mismatch-is-rejected + (let [data {:password-old "short" + :password-1 new-password + :password-2 "another-long-password"}] + (t/is (false? (valid? data))) + (t/is (= "errors.password-invalid-confirmation" + (get (error-codes data) :password-2)))))