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
|
@ -0,0 +1,4 @@
|
|||
(ns status-im.common.password-with-hint.style)
|
||||
|
||||
(def info-message
|
||||
{:margin-top 8})
|
|
@ -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])]])
|
|
@ -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 pincode-length 6)
|
||||
(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-name-length 24)
|
||||
(def ^:const default-number-of-messages 20)
|
||||
(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
|
||||
{1 :very-weak
|
||||
2 :weak
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[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.contexts.onboarding.create-password.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.security.core :as security]
|
||||
[utils.string :as utils.string]))
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn header
|
||||
[]
|
||||
|
@ -27,27 +28,9 @@
|
|||
:size :paragraph-1}
|
||||
(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
|
||||
[{: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]}]
|
||||
(let [hint-1-status (if password-long-enough? :success :default)
|
||||
hint-2-status (if passwords-match? :success :error)
|
||||
|
@ -58,19 +41,24 @@
|
|||
(not passwords-match?)
|
||||
(not empty-password?))]
|
||||
[:<>
|
||||
[password-with-hint
|
||||
{:hint {:text (i18n/label :t/password-creation-hint)
|
||||
:status hint-1-status
|
||||
:shown true}
|
||||
[password-with-hint/view
|
||||
{:hint (if (not password-short-enough?)
|
||||
{:text (i18n/label
|
||||
: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)
|
||||
:on-change-text on-change-password
|
||||
:on-focus on-input-focus
|
||||
:auto-focus true}]
|
||||
[rn/view {:style style/space-between-inputs}]
|
||||
[password-with-hint
|
||||
[password-with-hint/view
|
||||
{:hint {:text hint-2-text
|
||||
:status hint-2-status
|
||||
:shown (and (not empty-password?)
|
||||
:shown? (and (not empty-password?)
|
||||
show-password-validation?)}
|
||||
:error? error?
|
||||
:placeholder (i18n/label :t/password-creation-placeholder-2)
|
||||
|
@ -94,33 +82,17 @@
|
|||
[quo/tips {:completed? symbols?}
|
||||
(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
|
||||
[password]
|
||||
(rn/use-memo
|
||||
(fn []
|
||||
(let [{:keys [long-enough?]
|
||||
:as validations} (validate-password password)]
|
||||
{:password-long-enough? long-enough?
|
||||
:password-validations validations
|
||||
:password-strength (calc-password-strength validations)
|
||||
:empty-password? (empty? password)}))
|
||||
(let [{:keys [long-enough? short-enough?]
|
||||
:as validations} (password/validate password)]
|
||||
{:password-long-enough? long-enough?
|
||||
:password-short-enough? short-enough?
|
||||
:password-validations validations
|
||||
:password-strength (password/strength validations)
|
||||
:empty-password? (empty? password)}))
|
||||
[password]))
|
||||
|
||||
(defn- use-repeat-password-checks
|
||||
|
@ -174,20 +146,18 @@
|
|||
|
||||
|
||||
{:keys [password-long-enough?
|
||||
password-short-enough?
|
||||
password-validations password-strength
|
||||
empty-password?]} (use-password-checks password)
|
||||
|
||||
{:keys [same-password-length? same-passwords?]} (use-repeat-password-checks password
|
||||
repeat-password)
|
||||
|
||||
meet-requirements? (rn/use-memo
|
||||
#(and (not empty-password?)
|
||||
(utils.string/at-least-n-chars? password
|
||||
10)
|
||||
same-passwords?
|
||||
accepts-disclaimer?)
|
||||
[password repeat-password
|
||||
accepts-disclaimer?])]
|
||||
meet-requirements? (and (not empty-password?)
|
||||
password-long-enough?
|
||||
password-short-enough?
|
||||
same-passwords?
|
||||
accepts-disclaimer?)]
|
||||
|
||||
[floating-button/view
|
||||
{:header [page-nav]
|
||||
|
@ -231,6 +201,7 @@
|
|||
[header]
|
||||
[password-inputs
|
||||
{:password-long-enough? password-long-enough?
|
||||
:password-short-enough? password-short-enough?
|
||||
:passwords-match? same-passwords?
|
||||
:empty-password? empty-password?
|
||||
:show-password-validation? show-password-validation?
|
||||
|
|
|
@ -2,45 +2,21 @@
|
|||
(:require
|
||||
[clojure.string :as string]
|
||||
[quo.core :as quo]
|
||||
[quo.foundations.colors :as colors]
|
||||
[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.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.style :as style]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[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))
|
||||
[utils.security.core :as security]))
|
||||
|
||||
(defn- help
|
||||
[{:keys [validations]}]
|
||||
(let [{:keys [lower-case? upper-case? numbers? symbols?]} validations
|
||||
password-strength (calc-password-strength validations)]
|
||||
password-strength (password/strength validations)]
|
||||
[rn/view
|
||||
[quo/strength-divider {:type (constant/strength-status password-strength :info)}
|
||||
(i18n/label :t/password-creation-tips-title)]
|
||||
|
@ -54,12 +30,7 @@
|
|||
[quo/tips {:completed? symbols?}
|
||||
(i18n/label :t/password-creation-tips-4)]]]))
|
||||
|
||||
(defn- password-validations
|
||||
[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)})
|
||||
(def not-blank? (complement string/blank?))
|
||||
|
||||
(defn view
|
||||
[]
|
||||
|
@ -70,17 +41,14 @@
|
|||
[focused? set-focused] (rn/use-state false)
|
||||
[show-validation? set-show-validation] (rn/use-state false)
|
||||
|
||||
;; validations
|
||||
not-blank? (complement string/blank?)
|
||||
validations (password-validations password)
|
||||
long-enough? (utils.string/at-least-n-chars?
|
||||
password
|
||||
constant/new-password-min-length)
|
||||
{:keys [long-enough? short-enough?]
|
||||
:as validations} (password/validate password)
|
||||
empty-password? (string/blank? password)
|
||||
same-passwords? (and (not empty-password?)
|
||||
(= password repeat-password))
|
||||
meet-requirements? (and (not empty-password?)
|
||||
long-enough?
|
||||
short-enough?
|
||||
same-passwords?
|
||||
disclaimer-accepted?)
|
||||
error? (and show-validation?
|
||||
|
@ -115,17 +83,21 @@
|
|||
[:<>
|
||||
[rn/scroll-view {:style style/form-container}
|
||||
[header/view]
|
||||
[password-with-hint
|
||||
{:hint {:text (i18n/label :t/password-creation-hint)
|
||||
:status (if long-enough? :success :default)
|
||||
:shown? true}
|
||||
[password-with-hint/view
|
||||
{:hint (if (not short-enough?)
|
||||
{:text (i18n/label :t/password-creation-max-length-hint)
|
||||
: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)
|
||||
:label (i18n/label :t/change-password-new-password-label)
|
||||
:on-change-text on-change-password
|
||||
:on-focus on-input-focus
|
||||
:auto-focus true}]
|
||||
[rn/view {:style style/space-between-inputs}]
|
||||
[password-with-hint
|
||||
[password-with-hint/view
|
||||
{:hint {:text (if same-passwords?
|
||||
(i18n/label :t/password-creation-match)
|
||||
(i18n/label :t/password-creation-dont-match))
|
||||
|
|
|
@ -42,6 +42,10 @@
|
|||
[s n]
|
||||
(>= (count s) n))
|
||||
|
||||
(defn at-most-n-chars?
|
||||
[s n]
|
||||
(<= (count s) n))
|
||||
|
||||
(defn safe-trim
|
||||
[s]
|
||||
(when (string? s)
|
||||
|
|
|
@ -1900,6 +1900,7 @@
|
|||
"password-creation-dont-match": "Passwords do not match",
|
||||
"password-creation-hint": "Minimum 10 characters",
|
||||
"password-creation-match": "Passwords match",
|
||||
"password-creation-max-length-hint": "Maximum 100 characters",
|
||||
"password-creation-placeholder-1": "Type password",
|
||||
"password-creation-placeholder-2": "Repeat password",
|
||||
"password-creation-subtitle": "To log in to Status and sign transactions",
|
||||
|
|
Loading…
Reference in New Issue