mirror of
https://github.com/status-im/status-react.git
synced 2025-01-10 19:16:59 +00:00
feature!: add maximum character limit to password length (#21593)
This change now limits the password for a profile to be a maximum of a 100 characters.
This commit is contained in:
parent
9f0fb41713
commit
cf0fba7891
4
src/status_im/common/password_with_hint/style.cljs
Normal file
4
src/status_im/common/password_with_hint/style.cljs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
(ns status-im.common.password-with-hint.style)
|
||||||
|
|
||||||
|
(def info-message
|
||||||
|
{:margin-top 8})
|
24
src/status_im/common/password_with_hint/view.cljs
Normal file
24
src/status_im/common/password_with_hint/view.cljs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
(ns status-im.common.password-with-hint.view
|
||||||
|
(:require
|
||||||
|
[quo.core :as quo]
|
||||||
|
[quo.foundations.colors :as colors]
|
||||||
|
[react-native.core :as rn]
|
||||||
|
[status-im.common.password-with-hint.style :as style]))
|
||||||
|
|
||||||
|
(defn view
|
||||||
|
[{{:keys [text status shown?]} :hint :as input-props}]
|
||||||
|
[:<>
|
||||||
|
[quo/input
|
||||||
|
(-> input-props
|
||||||
|
(dissoc :hint)
|
||||||
|
(assoc :type :password
|
||||||
|
:blur? true))]
|
||||||
|
[rn/view {:style style/info-message}
|
||||||
|
(when shown?
|
||||||
|
[quo/info-message
|
||||||
|
(cond-> {:status status
|
||||||
|
:size :default}
|
||||||
|
(not= :success status) (assoc :icon :i/info)
|
||||||
|
(= :success status) (assoc :icon :i/check-circle)
|
||||||
|
(= :default status) (assoc :color colors/white-70-blur))
|
||||||
|
text])]])
|
36
src/status_im/common/validation/password.cljs
Normal file
36
src/status_im/common/validation/password.cljs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
(ns status-im.common.validation.password
|
||||||
|
(:require
|
||||||
|
[status-im.constants :as constants]
|
||||||
|
[utils.string :as utils.string]))
|
||||||
|
|
||||||
|
(defn validate-short-enough?
|
||||||
|
[password]
|
||||||
|
(utils.string/at-most-n-chars? password
|
||||||
|
constants/new-password-max-length))
|
||||||
|
|
||||||
|
(defn validate-long-enough?
|
||||||
|
[password]
|
||||||
|
(utils.string/at-least-n-chars? password
|
||||||
|
constants/new-password-min-length))
|
||||||
|
|
||||||
|
(defn validate
|
||||||
|
[password]
|
||||||
|
(let [validations (juxt
|
||||||
|
utils.string/has-lower-case?
|
||||||
|
utils.string/has-upper-case?
|
||||||
|
utils.string/has-numbers?
|
||||||
|
utils.string/has-symbols?
|
||||||
|
validate-long-enough?
|
||||||
|
validate-short-enough?)]
|
||||||
|
(->> password
|
||||||
|
validations
|
||||||
|
(zipmap (conj constants/password-tips
|
||||||
|
:long-enough?
|
||||||
|
:short-enough?)))))
|
||||||
|
|
||||||
|
(defn strength
|
||||||
|
[validations]
|
||||||
|
(->> (select-keys validations constants/password-tips)
|
||||||
|
(vals)
|
||||||
|
(filter true?)
|
||||||
|
count))
|
@ -133,12 +133,18 @@
|
|||||||
(def ^:const min-password-length 6)
|
(def ^:const min-password-length 6)
|
||||||
(def ^:const pincode-length 6)
|
(def ^:const pincode-length 6)
|
||||||
(def ^:const new-password-min-length 10)
|
(def ^:const new-password-min-length 10)
|
||||||
|
(def ^:const new-password-max-length 100)
|
||||||
(def ^:const max-group-chat-participants 20)
|
(def ^:const max-group-chat-participants 20)
|
||||||
(def ^:const max-group-chat-name-length 24)
|
(def ^:const max-group-chat-name-length 24)
|
||||||
(def ^:const default-number-of-messages 20)
|
(def ^:const default-number-of-messages 20)
|
||||||
(def ^:const default-number-of-pin-messages 3)
|
(def ^:const default-number-of-pin-messages 3)
|
||||||
|
|
||||||
(def ^:const password-tips [:lower-case? :upper-case? :numbers? :symbols?])
|
(def ^:const password-tips
|
||||||
|
[:lower-case?
|
||||||
|
:upper-case?
|
||||||
|
:numbers?
|
||||||
|
:symbols?])
|
||||||
|
|
||||||
(def ^:const strength-status
|
(def ^:const strength-status
|
||||||
{1 :very-weak
|
{1 :very-weak
|
||||||
2 :weak
|
2 :weak
|
||||||
|
@ -6,12 +6,13 @@
|
|||||||
[react-native.platform :as platform]
|
[react-native.platform :as platform]
|
||||||
[react-native.safe-area :as safe-area]
|
[react-native.safe-area :as safe-area]
|
||||||
[status-im.common.floating-button-page.view :as floating-button]
|
[status-im.common.floating-button-page.view :as floating-button]
|
||||||
|
[status-im.common.password-with-hint.view :as password-with-hint]
|
||||||
|
[status-im.common.validation.password :as password]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.contexts.onboarding.create-password.style :as style]
|
[status-im.contexts.onboarding.create-password.style :as style]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.security.core :as security]
|
[utils.security.core :as security]))
|
||||||
[utils.string :as utils.string]))
|
|
||||||
|
|
||||||
(defn header
|
(defn header
|
||||||
[]
|
[]
|
||||||
@ -27,27 +28,9 @@
|
|||||||
:size :paragraph-1}
|
:size :paragraph-1}
|
||||||
(i18n/label :t/password-creation-subtitle)]])
|
(i18n/label :t/password-creation-subtitle)]])
|
||||||
|
|
||||||
(defn password-with-hint
|
|
||||||
[{{:keys [text status shown]} :hint :as input-props}]
|
|
||||||
[rn/view
|
|
||||||
[quo/input
|
|
||||||
(-> input-props
|
|
||||||
(dissoc :hint)
|
|
||||||
(assoc :type :password
|
|
||||||
:blur? true))]
|
|
||||||
[rn/view {:style style/info-message}
|
|
||||||
(when shown
|
|
||||||
[quo/info-message
|
|
||||||
{:status status
|
|
||||||
:size :default
|
|
||||||
:icon (if (= status :success) :i/check-circle :i/info)
|
|
||||||
:color (when (= status :default)
|
|
||||||
colors/white-70-blur)}
|
|
||||||
text])]])
|
|
||||||
|
|
||||||
(defn password-inputs
|
(defn password-inputs
|
||||||
[{:keys [passwords-match? on-change-password on-change-repeat-password on-input-focus
|
[{:keys [passwords-match? on-change-password on-change-repeat-password on-input-focus
|
||||||
password-long-enough? empty-password? show-password-validation?
|
password-long-enough? password-short-enough? empty-password? show-password-validation?
|
||||||
on-blur-repeat-password]}]
|
on-blur-repeat-password]}]
|
||||||
(let [hint-1-status (if password-long-enough? :success :default)
|
(let [hint-1-status (if password-long-enough? :success :default)
|
||||||
hint-2-status (if passwords-match? :success :error)
|
hint-2-status (if passwords-match? :success :error)
|
||||||
@ -58,19 +41,24 @@
|
|||||||
(not passwords-match?)
|
(not passwords-match?)
|
||||||
(not empty-password?))]
|
(not empty-password?))]
|
||||||
[:<>
|
[:<>
|
||||||
[password-with-hint
|
[password-with-hint/view
|
||||||
{:hint {:text (i18n/label :t/password-creation-hint)
|
{:hint (if (not password-short-enough?)
|
||||||
:status hint-1-status
|
{:text (i18n/label
|
||||||
:shown true}
|
:t/password-creation-max-length-hint)
|
||||||
|
:status :error
|
||||||
|
:shown? true}
|
||||||
|
{:text (i18n/label :t/password-creation-hint)
|
||||||
|
:status hint-1-status
|
||||||
|
:shown? true})
|
||||||
:placeholder (i18n/label :t/password-creation-placeholder-1)
|
:placeholder (i18n/label :t/password-creation-placeholder-1)
|
||||||
:on-change-text on-change-password
|
:on-change-text on-change-password
|
||||||
:on-focus on-input-focus
|
:on-focus on-input-focus
|
||||||
:auto-focus true}]
|
:auto-focus true}]
|
||||||
[rn/view {:style style/space-between-inputs}]
|
[rn/view {:style style/space-between-inputs}]
|
||||||
[password-with-hint
|
[password-with-hint/view
|
||||||
{:hint {:text hint-2-text
|
{:hint {:text hint-2-text
|
||||||
:status hint-2-status
|
:status hint-2-status
|
||||||
:shown (and (not empty-password?)
|
:shown? (and (not empty-password?)
|
||||||
show-password-validation?)}
|
show-password-validation?)}
|
||||||
:error? error?
|
:error? error?
|
||||||
:placeholder (i18n/label :t/password-creation-placeholder-2)
|
:placeholder (i18n/label :t/password-creation-placeholder-2)
|
||||||
@ -94,33 +82,17 @@
|
|||||||
[quo/tips {:completed? symbols?}
|
[quo/tips {:completed? symbols?}
|
||||||
(i18n/label :t/password-creation-tips-4)]]])
|
(i18n/label :t/password-creation-tips-4)]]])
|
||||||
|
|
||||||
(defn validate-password
|
|
||||||
[password]
|
|
||||||
(let [validations (juxt utils.string/has-lower-case?
|
|
||||||
utils.string/has-upper-case?
|
|
||||||
utils.string/has-numbers?
|
|
||||||
utils.string/has-symbols?
|
|
||||||
#(utils.string/at-least-n-chars? % constants/new-password-min-length))]
|
|
||||||
(->> password
|
|
||||||
validations
|
|
||||||
(zipmap (conj constants/password-tips :long-enough?)))))
|
|
||||||
|
|
||||||
(defn calc-password-strength
|
|
||||||
[validations]
|
|
||||||
(->> (vals validations)
|
|
||||||
(filter true?)
|
|
||||||
count))
|
|
||||||
|
|
||||||
(defn- use-password-checks
|
(defn- use-password-checks
|
||||||
[password]
|
[password]
|
||||||
(rn/use-memo
|
(rn/use-memo
|
||||||
(fn []
|
(fn []
|
||||||
(let [{:keys [long-enough?]
|
(let [{:keys [long-enough? short-enough?]
|
||||||
:as validations} (validate-password password)]
|
:as validations} (password/validate password)]
|
||||||
{:password-long-enough? long-enough?
|
{:password-long-enough? long-enough?
|
||||||
:password-validations validations
|
:password-short-enough? short-enough?
|
||||||
:password-strength (calc-password-strength validations)
|
:password-validations validations
|
||||||
:empty-password? (empty? password)}))
|
:password-strength (password/strength validations)
|
||||||
|
:empty-password? (empty? password)}))
|
||||||
[password]))
|
[password]))
|
||||||
|
|
||||||
(defn- use-repeat-password-checks
|
(defn- use-repeat-password-checks
|
||||||
@ -174,20 +146,18 @@
|
|||||||
|
|
||||||
|
|
||||||
{:keys [password-long-enough?
|
{:keys [password-long-enough?
|
||||||
|
password-short-enough?
|
||||||
password-validations password-strength
|
password-validations password-strength
|
||||||
empty-password?]} (use-password-checks password)
|
empty-password?]} (use-password-checks password)
|
||||||
|
|
||||||
{:keys [same-password-length? same-passwords?]} (use-repeat-password-checks password
|
{:keys [same-password-length? same-passwords?]} (use-repeat-password-checks password
|
||||||
repeat-password)
|
repeat-password)
|
||||||
|
|
||||||
meet-requirements? (rn/use-memo
|
meet-requirements? (and (not empty-password?)
|
||||||
#(and (not empty-password?)
|
password-long-enough?
|
||||||
(utils.string/at-least-n-chars? password
|
password-short-enough?
|
||||||
10)
|
same-passwords?
|
||||||
same-passwords?
|
accepts-disclaimer?)]
|
||||||
accepts-disclaimer?)
|
|
||||||
[password repeat-password
|
|
||||||
accepts-disclaimer?])]
|
|
||||||
|
|
||||||
[floating-button/view
|
[floating-button/view
|
||||||
{:header [page-nav]
|
{:header [page-nav]
|
||||||
@ -231,6 +201,7 @@
|
|||||||
[header]
|
[header]
|
||||||
[password-inputs
|
[password-inputs
|
||||||
{:password-long-enough? password-long-enough?
|
{:password-long-enough? password-long-enough?
|
||||||
|
:password-short-enough? password-short-enough?
|
||||||
:passwords-match? same-passwords?
|
:passwords-match? same-passwords?
|
||||||
:empty-password? empty-password?
|
:empty-password? empty-password?
|
||||||
:show-password-validation? show-password-validation?
|
:show-password-validation? show-password-validation?
|
||||||
|
@ -2,45 +2,21 @@
|
|||||||
(:require
|
(:require
|
||||||
[clojure.string :as string]
|
[clojure.string :as string]
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[quo.foundations.colors :as colors]
|
|
||||||
[react-native.core :as rn]
|
[react-native.core :as rn]
|
||||||
|
[status-im.common.password-with-hint.view :as password-with-hint]
|
||||||
|
[status-im.common.validation.password :as password]
|
||||||
[status-im.constants :as constant]
|
[status-im.constants :as constant]
|
||||||
[status-im.contexts.profile.settings.screens.password.change-password.events]
|
[status-im.contexts.profile.settings.screens.password.change-password.events]
|
||||||
[status-im.contexts.profile.settings.screens.password.change-password.header :as header]
|
[status-im.contexts.profile.settings.screens.password.change-password.header :as header]
|
||||||
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
|
[status-im.contexts.profile.settings.screens.password.change-password.style :as style]
|
||||||
[utils.i18n :as i18n]
|
[utils.i18n :as i18n]
|
||||||
[utils.re-frame :as rf]
|
[utils.re-frame :as rf]
|
||||||
[utils.security.core :as security]
|
[utils.security.core :as security]))
|
||||||
[utils.string :as utils.string]))
|
|
||||||
|
|
||||||
(defn- password-with-hint
|
|
||||||
[{{:keys [text status shown?]} :hint :as input-props}]
|
|
||||||
[:<>
|
|
||||||
[quo/input
|
|
||||||
(-> input-props
|
|
||||||
(dissoc :hint)
|
|
||||||
(assoc :type :password
|
|
||||||
:blur? true))]
|
|
||||||
[rn/view {:style style/info-message}
|
|
||||||
(when shown?
|
|
||||||
[quo/info-message
|
|
||||||
(cond-> {:status status
|
|
||||||
:size :default}
|
|
||||||
(not= :success status) (assoc :icon :i/info)
|
|
||||||
(= :success status) (assoc :icon :i/check-circle)
|
|
||||||
(= :default status) (assoc :color colors/white-70-blur))
|
|
||||||
text])]])
|
|
||||||
|
|
||||||
(defn- calc-password-strength
|
|
||||||
[validations]
|
|
||||||
(->> (vals validations)
|
|
||||||
(filter true?)
|
|
||||||
count))
|
|
||||||
|
|
||||||
(defn- help
|
(defn- help
|
||||||
[{:keys [validations]}]
|
[{:keys [validations]}]
|
||||||
(let [{:keys [lower-case? upper-case? numbers? symbols?]} validations
|
(let [{:keys [lower-case? upper-case? numbers? symbols?]} validations
|
||||||
password-strength (calc-password-strength validations)]
|
password-strength (password/strength validations)]
|
||||||
[rn/view
|
[rn/view
|
||||||
[quo/strength-divider {:type (constant/strength-status password-strength :info)}
|
[quo/strength-divider {:type (constant/strength-status password-strength :info)}
|
||||||
(i18n/label :t/password-creation-tips-title)]
|
(i18n/label :t/password-creation-tips-title)]
|
||||||
@ -54,12 +30,7 @@
|
|||||||
[quo/tips {:completed? symbols?}
|
[quo/tips {:completed? symbols?}
|
||||||
(i18n/label :t/password-creation-tips-4)]]]))
|
(i18n/label :t/password-creation-tips-4)]]]))
|
||||||
|
|
||||||
(defn- password-validations
|
(def not-blank? (complement string/blank?))
|
||||||
[password]
|
|
||||||
{:lower-case? (utils.string/has-lower-case? password)
|
|
||||||
:upper-case? (utils.string/has-upper-case? password)
|
|
||||||
:numbers? (utils.string/has-numbers? password)
|
|
||||||
:symbols? (utils.string/has-symbols? password)})
|
|
||||||
|
|
||||||
(defn view
|
(defn view
|
||||||
[]
|
[]
|
||||||
@ -70,17 +41,14 @@
|
|||||||
[focused? set-focused] (rn/use-state false)
|
[focused? set-focused] (rn/use-state false)
|
||||||
[show-validation? set-show-validation] (rn/use-state false)
|
[show-validation? set-show-validation] (rn/use-state false)
|
||||||
|
|
||||||
;; validations
|
{:keys [long-enough? short-enough?]
|
||||||
not-blank? (complement string/blank?)
|
:as validations} (password/validate password)
|
||||||
validations (password-validations password)
|
|
||||||
long-enough? (utils.string/at-least-n-chars?
|
|
||||||
password
|
|
||||||
constant/new-password-min-length)
|
|
||||||
empty-password? (string/blank? password)
|
empty-password? (string/blank? password)
|
||||||
same-passwords? (and (not empty-password?)
|
same-passwords? (and (not empty-password?)
|
||||||
(= password repeat-password))
|
(= password repeat-password))
|
||||||
meet-requirements? (and (not empty-password?)
|
meet-requirements? (and (not empty-password?)
|
||||||
long-enough?
|
long-enough?
|
||||||
|
short-enough?
|
||||||
same-passwords?
|
same-passwords?
|
||||||
disclaimer-accepted?)
|
disclaimer-accepted?)
|
||||||
error? (and show-validation?
|
error? (and show-validation?
|
||||||
@ -115,17 +83,21 @@
|
|||||||
[:<>
|
[:<>
|
||||||
[rn/scroll-view {:style style/form-container}
|
[rn/scroll-view {:style style/form-container}
|
||||||
[header/view]
|
[header/view]
|
||||||
[password-with-hint
|
[password-with-hint/view
|
||||||
{:hint {:text (i18n/label :t/password-creation-hint)
|
{:hint (if (not short-enough?)
|
||||||
:status (if long-enough? :success :default)
|
{:text (i18n/label :t/password-creation-max-length-hint)
|
||||||
:shown? true}
|
:status :error
|
||||||
|
:shown? true}
|
||||||
|
{:text (i18n/label :t/password-creation-hint)
|
||||||
|
:status (if long-enough? :success :default)
|
||||||
|
:shown? true})
|
||||||
:placeholder (i18n/label :t/change-password-new-password-placeholder)
|
:placeholder (i18n/label :t/change-password-new-password-placeholder)
|
||||||
:label (i18n/label :t/change-password-new-password-label)
|
:label (i18n/label :t/change-password-new-password-label)
|
||||||
:on-change-text on-change-password
|
:on-change-text on-change-password
|
||||||
:on-focus on-input-focus
|
:on-focus on-input-focus
|
||||||
:auto-focus true}]
|
:auto-focus true}]
|
||||||
[rn/view {:style style/space-between-inputs}]
|
[rn/view {:style style/space-between-inputs}]
|
||||||
[password-with-hint
|
[password-with-hint/view
|
||||||
{:hint {:text (if same-passwords?
|
{:hint {:text (if same-passwords?
|
||||||
(i18n/label :t/password-creation-match)
|
(i18n/label :t/password-creation-match)
|
||||||
(i18n/label :t/password-creation-dont-match))
|
(i18n/label :t/password-creation-dont-match))
|
||||||
|
@ -42,6 +42,10 @@
|
|||||||
[s n]
|
[s n]
|
||||||
(>= (count s) n))
|
(>= (count s) n))
|
||||||
|
|
||||||
|
(defn at-most-n-chars?
|
||||||
|
[s n]
|
||||||
|
(<= (count s) n))
|
||||||
|
|
||||||
(defn safe-trim
|
(defn safe-trim
|
||||||
[s]
|
[s]
|
||||||
(when (string? s)
|
(when (string? s)
|
||||||
|
@ -1900,6 +1900,7 @@
|
|||||||
"password-creation-dont-match": "Passwords do not match",
|
"password-creation-dont-match": "Passwords do not match",
|
||||||
"password-creation-hint": "Minimum 10 characters",
|
"password-creation-hint": "Minimum 10 characters",
|
||||||
"password-creation-match": "Passwords match",
|
"password-creation-match": "Passwords match",
|
||||||
|
"password-creation-max-length-hint": "Maximum 100 characters",
|
||||||
"password-creation-placeholder-1": "Type password",
|
"password-creation-placeholder-1": "Type password",
|
||||||
"password-creation-placeholder-2": "Repeat password",
|
"password-creation-placeholder-2": "Repeat password",
|
||||||
"password-creation-subtitle": "To log in to Status and sign transactions",
|
"password-creation-subtitle": "To log in to Status and sign transactions",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user