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:
Sean Hagstrom 2024-11-13 09:30:47 -08:00 committed by GitHub
parent 9f0fb41713
commit cf0fba7891
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 122 additions and 104 deletions

View File

@ -0,0 +1,4 @@
(ns status-im.common.password-with-hint.style)
(def info-message
{:margin-top 8})

View 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])]])

View 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))

View File

@ -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

View File

@ -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?

View File

@ -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))

View File

@ -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)

View File

@ -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",