Rafforza i requisiti password con regole di complessità di mercato.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,13 +9,18 @@ module Admin
|
||||
return render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if params[:password].blank? || params[:password].length < 8
|
||||
flash.now[:alert] = t("admin.flash.password_too_short")
|
||||
if params[:password] != params[:password_confirmation]
|
||||
flash.now[:alert] = t("admin.flash.password_mismatch")
|
||||
return render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if params[:password] != params[:password_confirmation]
|
||||
flash.now[:alert] = t("admin.flash.password_mismatch")
|
||||
if (code = PasswordComplexity.violation(params[:password]))
|
||||
key = case code
|
||||
when :blank, :too_short then :password_too_short
|
||||
when :too_long then :password_too_long
|
||||
else :password_too_weak
|
||||
end
|
||||
flash.now[:alert] = t("admin.flash.#{key}")
|
||||
return render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ module Api
|
||||
case code
|
||||
when :current_incorrect then "Current password is incorrect"
|
||||
when :too_short then "Password must be at least 8 characters"
|
||||
when :too_long then "Password cannot exceed 72 characters"
|
||||
when :too_weak
|
||||
"Password must include at least 3 of: lowercase, uppercase, number, symbol"
|
||||
when :mismatch then "Passwords do not match"
|
||||
else "Unable to update password"
|
||||
end
|
||||
|
||||
@@ -28,14 +28,14 @@ module Public
|
||||
return
|
||||
end
|
||||
|
||||
if params[:password].blank? || params[:password].length < 8
|
||||
flash.now[:alert] = t("flash.password_resets.password_min_length")
|
||||
if params[:password] != params[:password_confirmation]
|
||||
flash.now[:alert] = t("flash.password_resets.password_mismatch")
|
||||
@token = params[:token]
|
||||
return render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
if params[:password] != params[:password_confirmation]
|
||||
flash.now[:alert] = t("flash.password_resets.password_mismatch")
|
||||
if (code = PasswordComplexity.violation(params[:password]))
|
||||
flash.now[:alert] = t("flash.password_resets.password_#{code == :blank ? :too_short : code}")
|
||||
@token = params[:token]
|
||||
return render :edit, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class AdminAccount < ApplicationRecord
|
||||
include PasswordComplexity
|
||||
|
||||
has_secure_password
|
||||
|
||||
validates :username, presence: true, uniqueness: true
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
# Criteri "di mercato" (stile Cognito/Auth0 bilanciato):
|
||||
# - minimo 8 caratteri (max 72 per bcrypt)
|
||||
# - almeno 3 classi su 4: minuscole, maiuscole, numeri, simboli
|
||||
module PasswordComplexity
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
MIN_LENGTH = 8
|
||||
MAX_LENGTH = 72
|
||||
REQUIRED_CLASSES = 3
|
||||
|
||||
CLASS_CHECKS = {
|
||||
lowercase: /[a-z]/,
|
||||
uppercase: /[A-Z]/,
|
||||
digit: /\d/,
|
||||
symbol: /[^A-Za-z0-9]/
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
def violation(password)
|
||||
value = password.to_s
|
||||
return :blank if value.blank?
|
||||
return :too_short if value.length < MIN_LENGTH
|
||||
return :too_long if value.bytesize > MAX_LENGTH
|
||||
return :too_weak unless strong_enough?(value)
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def strong_enough?(password)
|
||||
matched = CLASS_CHECKS.count { |_, pattern| password.match?(pattern) }
|
||||
matched >= REQUIRED_CLASSES
|
||||
end
|
||||
|
||||
def requirement_summary
|
||||
I18n.t("password_policy.hint")
|
||||
end
|
||||
end
|
||||
|
||||
included do
|
||||
validate :password_meets_complexity_policy, if: -> { password.present? }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def password_meets_complexity_policy
|
||||
case PasswordComplexity.violation(password)
|
||||
when :too_short
|
||||
errors.add(:password, :too_short, count: MIN_LENGTH)
|
||||
when :too_long
|
||||
errors.add(:password, :too_long, count: MAX_LENGTH)
|
||||
when :too_weak
|
||||
errors.add(:password, :complexity)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,6 @@
|
||||
class User < ApplicationRecord
|
||||
include PasswordComplexity
|
||||
|
||||
ROLES = %w[admin coach parent volunteer].freeze
|
||||
|
||||
has_secure_password
|
||||
|
||||
@@ -23,15 +23,19 @@ module Users
|
||||
return Result.new(ok?: false, error: :current_incorrect)
|
||||
end
|
||||
|
||||
if @password.blank? || @password.length < 8
|
||||
return Result.new(ok?: false, error: :too_short)
|
||||
end
|
||||
|
||||
if @password != @password_confirmation
|
||||
return Result.new(ok?: false, error: :mismatch)
|
||||
end
|
||||
|
||||
@user.update!(password: @password)
|
||||
if (code = PasswordComplexity.violation(@password))
|
||||
return Result.new(ok?: false, error: code == :blank ? :too_short : code)
|
||||
end
|
||||
|
||||
unless @user.update(password: @password)
|
||||
complexity_error = @user.errors.details[:password]&.any? { |d| d[:error] == :complexity }
|
||||
return Result.new(ok?: false, error: complexity_error ? :too_weak : :too_short)
|
||||
end
|
||||
|
||||
@user.clear_password_reset!
|
||||
Result.new(ok?: true)
|
||||
end
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
<div class="card">
|
||||
<h2 style="font-size:1.1rem;margin-top:0"><%= t("auth.account.password_heading") %></h2>
|
||||
<p class="muted" style="font-size:0.9rem"><%= t("password_policy.hint") %></p>
|
||||
<%= form_with url: public_account_password_path, method: :patch, local: true do %>
|
||||
<%= render "shared/input_toggle",
|
||||
name: :current_password,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<section class="auth-page">
|
||||
<h1><%= t("auth.password_reset.title") %></h1>
|
||||
<div class="card">
|
||||
<p class="muted" style="font-size:0.9rem"><%= t("password_policy.hint") %></p>
|
||||
<%= form_with url: public_password_reset_path, method: :patch, local: true do %>
|
||||
<%= hidden_field_tag :token, @token %>
|
||||
<%= render "shared/input_toggle",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
required: true,
|
||||
minlength: 8,
|
||||
autocomplete: "new-password" %>
|
||||
<p class="muted" style="font-size:0.9rem;margin-top:-0.5rem"><%= t("password_policy.hint") %></p>
|
||||
<%= render "shared/input_toggle",
|
||||
name: "user[password_confirmation]",
|
||||
id: "user_password_confirmation",
|
||||
|
||||
@@ -18,6 +18,8 @@ de:
|
||||
logout_success: Abgemeldet
|
||||
password_current_incorrect: Das aktuelle Passwort ist falsch
|
||||
password_too_short: Das neue Passwort muss mindestens 8 Zeichen lang sein
|
||||
password_too_long: Das neue Passwort darf höchstens 72 Zeichen lang sein
|
||||
password_too_weak: Das neue Passwort muss mindestens 3 aus Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen enthalten
|
||||
password_mismatch: Die Passwörter stimmen nicht überein
|
||||
password_updated: Passwort aktualisiert
|
||||
ops_acknowledged: Vorfall übernommen
|
||||
@@ -53,7 +55,7 @@ de:
|
||||
edit:
|
||||
title: Passwort ändern
|
||||
current_password_label: Aktuelles Passwort
|
||||
new_password_label: "Neues Passwort (mind. 8 Zeichen)"
|
||||
new_password_label: "Neues Passwort (mind. 8, mindestens 3 Zeichenarten)"
|
||||
confirm_password_label: Neues Passwort bestätigen
|
||||
submit: Passwort speichern
|
||||
cancel: Abbrechen
|
||||
|
||||
@@ -18,6 +18,8 @@ en:
|
||||
logout_success: Signed out
|
||||
password_current_incorrect: Current password is incorrect
|
||||
password_too_short: The new password must be at least 8 characters long
|
||||
password_too_long: New password cannot exceed 72 characters
|
||||
password_too_weak: "New password must include at least 3 of: lowercase, uppercase, numbers and symbols"
|
||||
password_mismatch: Passwords do not match
|
||||
password_updated: Password updated
|
||||
ops_acknowledged: Incident acknowledged
|
||||
@@ -53,7 +55,7 @@ en:
|
||||
edit:
|
||||
title: Change password
|
||||
current_password_label: Current password
|
||||
new_password_label: "New password (min. 8 characters)"
|
||||
new_password_label: "New password (min. 8, at least 3 character types)"
|
||||
confirm_password_label: Confirm new password
|
||||
submit: Save password
|
||||
cancel: Cancel
|
||||
|
||||
@@ -18,6 +18,8 @@ es:
|
||||
logout_success: Sesión cerrada
|
||||
password_current_incorrect: La contraseña actual no es correcta
|
||||
password_too_short: La nueva contraseña debe tener al menos 8 caracteres
|
||||
password_too_long: La nueva contraseña no puede superar los 72 caracteres
|
||||
password_too_weak: "La nueva contraseña debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
|
||||
password_mismatch: Las contraseñas no coinciden
|
||||
password_updated: Contraseña actualizada
|
||||
ops_acknowledged: Incidencia asumida
|
||||
@@ -53,7 +55,7 @@ es:
|
||||
edit:
|
||||
title: Cambiar contraseña
|
||||
current_password_label: Contraseña actual
|
||||
new_password_label: "Nueva contraseña (mín. 8 caracteres)"
|
||||
new_password_label: "Nueva contraseña (mín. 8, al menos 3 tipos de caracteres)"
|
||||
confirm_password_label: Confirma la nueva contraseña
|
||||
submit: Guardar contraseña
|
||||
cancel: Cancelar
|
||||
|
||||
@@ -18,6 +18,8 @@ fr:
|
||||
logout_success: Déconnecté
|
||||
password_current_incorrect: Le mot de passe actuel est incorrect
|
||||
password_too_short: Le nouveau mot de passe doit contenir au moins 8 caractères
|
||||
password_too_long: Le nouveau mot de passe ne peut pas dépasser 72 caractères
|
||||
password_too_weak: "Le nouveau mot de passe doit inclure au moins 3 parmi : minuscules, majuscules, chiffres et symboles"
|
||||
password_mismatch: Les mots de passe ne correspondent pas
|
||||
password_updated: Mot de passe mis à jour
|
||||
ops_acknowledged: Incident pris en charge
|
||||
@@ -53,7 +55,7 @@ fr:
|
||||
edit:
|
||||
title: Changer le mot de passe
|
||||
current_password_label: Mot de passe actuel
|
||||
new_password_label: "Nouveau mot de passe (min. 8 caractères)"
|
||||
new_password_label: "Nouveau mot de passe (min. 8, au moins 3 types de caractères)"
|
||||
confirm_password_label: Confirmer le nouveau mot de passe
|
||||
submit: Enregistrer le mot de passe
|
||||
cancel: Annuler
|
||||
|
||||
@@ -18,6 +18,8 @@ it:
|
||||
logout_success: Disconnesso
|
||||
password_current_incorrect: Password attuale non corretta
|
||||
password_too_short: La nuova password deve avere almeno 8 caratteri
|
||||
password_too_long: La nuova password non può superare i 72 caratteri
|
||||
password_too_weak: "La nuova password deve includere almeno 3 tra: minuscole, maiuscole, numeri e simboli"
|
||||
password_mismatch: Le password non coincidono
|
||||
password_updated: Password aggiornata
|
||||
ops_acknowledged: Incidente preso in carico
|
||||
@@ -53,7 +55,7 @@ it:
|
||||
edit:
|
||||
title: Cambia password
|
||||
current_password_label: Password attuale
|
||||
new_password_label: "Nuova password (min. 8 caratteri)"
|
||||
new_password_label: "Nuova password (min. 8, almeno 3 tipi di caratteri)"
|
||||
confirm_password_label: Conferma nuova password
|
||||
submit: Salva password
|
||||
cancel: Annulla
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
de:
|
||||
password_policy:
|
||||
hint: "Mindestens 8 Zeichen, mit mindestens 3 aus: Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen."
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "muss mindestens 3 aus Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen enthalten"
|
||||
admin_account:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "muss mindestens 3 aus Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen enthalten"
|
||||
club:
|
||||
back_to_club: "← Verein"
|
||||
sport_label: Hauptsportart
|
||||
@@ -604,6 +621,9 @@ de:
|
||||
email_sent: Wenn die E-Mail registriert ist, erhältst du in Kürze einen Link zum Zurücksetzen des Passworts.
|
||||
invalid_or_expired_link: Link ungültig oder abgelaufen. Fordere ein neues Zurücksetzen des Passworts an.
|
||||
password_min_length: Das Passwort muss mindestens 8 Zeichen lang sein
|
||||
password_too_short: Das Passwort muss mindestens 8 Zeichen lang sein
|
||||
password_too_long: Das Passwort darf höchstens 72 Zeichen lang sein
|
||||
password_too_weak: Das Passwort muss mindestens 3 aus Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen enthalten
|
||||
password_mismatch: Die Passwörter stimmen nicht überein
|
||||
password_updated: Passwort aktualisiert. Du kannst dich jetzt anmelden.
|
||||
accounts:
|
||||
@@ -611,6 +631,8 @@ de:
|
||||
profile_updated: Profil aktualisiert.
|
||||
password_current_incorrect: Das aktuelle Passwort ist falsch
|
||||
password_too_short: Das Passwort muss mindestens 8 Zeichen haben
|
||||
password_too_long: Das Passwort darf höchstens 72 Zeichen lang sein
|
||||
password_too_weak: Das Passwort muss mindestens 3 aus Kleinbuchstaben, Großbuchstaben, Zahlen und Symbolen enthalten
|
||||
password_mismatch: Die Passwörter stimmen nicht überein
|
||||
password_updated: Passwort aktualisiert.
|
||||
replay:
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
en:
|
||||
password_policy:
|
||||
hint: "At least 8 characters, including 3 of: lowercase, uppercase, numbers and symbols."
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "must include at least 3 of: lowercase letters, uppercase letters, numbers and symbols"
|
||||
admin_account:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "must include at least 3 of: lowercase letters, uppercase letters, numbers and symbols"
|
||||
club:
|
||||
back_to_club: "← Club"
|
||||
sport_label: Main sport
|
||||
@@ -604,6 +621,9 @@ en:
|
||||
email_sent: If the email is registered, you'll receive a password reset link shortly.
|
||||
invalid_or_expired_link: Invalid or expired link. Request a new password reset.
|
||||
password_min_length: Password must be at least 8 characters
|
||||
password_too_short: Password must be at least 8 characters
|
||||
password_too_long: Password cannot exceed 72 characters
|
||||
password_too_weak: "Password must include at least 3 of: lowercase, uppercase, numbers and symbols"
|
||||
password_mismatch: Passwords don't match
|
||||
password_updated: Password updated. You can now log in.
|
||||
accounts:
|
||||
@@ -611,6 +631,8 @@ en:
|
||||
profile_updated: Profile updated.
|
||||
password_current_incorrect: Current password is incorrect
|
||||
password_too_short: Password must be at least 8 characters
|
||||
password_too_long: Password cannot exceed 72 characters
|
||||
password_too_weak: "Password must include at least 3 of: lowercase, uppercase, numbers and symbols"
|
||||
password_mismatch: Passwords do not match
|
||||
password_updated: Password updated.
|
||||
replay:
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
es:
|
||||
password_policy:
|
||||
hint: "Mínimo 8 caracteres, con al menos 3 entre: minúsculas, mayúsculas, números y símbolos."
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
|
||||
admin_account:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
|
||||
club:
|
||||
back_to_club: "← Club"
|
||||
sport_label: Deporte principal
|
||||
@@ -604,6 +621,9 @@ es:
|
||||
email_sent: Si el correo está registrado, recibirás en breve un enlace para restablecer la contraseña.
|
||||
invalid_or_expired_link: Enlace no válido o caducado. Solicita un nuevo restablecimiento de contraseña.
|
||||
password_min_length: La contraseña debe tener al menos 8 caracteres
|
||||
password_too_short: La contraseña debe tener al menos 8 caracteres
|
||||
password_too_long: La contraseña no puede superar los 72 caracteres
|
||||
password_too_weak: "La contraseña debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
|
||||
password_mismatch: Las contraseñas no coinciden
|
||||
password_updated: Contraseña actualizada. Ya puedes iniciar sesión.
|
||||
accounts:
|
||||
@@ -611,6 +631,8 @@ es:
|
||||
profile_updated: Perfil actualizado.
|
||||
password_current_incorrect: La contraseña actual no es correcta
|
||||
password_too_short: La contraseña debe tener al menos 8 caracteres
|
||||
password_too_long: La contraseña no puede superar los 72 caracteres
|
||||
password_too_weak: "La contraseña debe incluir al menos 3 entre: minúsculas, mayúsculas, números y símbolos"
|
||||
password_mismatch: Las contraseñas no coinciden
|
||||
password_updated: Contraseña actualizada.
|
||||
replay:
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
fr:
|
||||
password_policy:
|
||||
hint: "Au moins 8 caractères, avec au moins 3 parmi : minuscules, majuscules, chiffres et symboles."
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "doit inclure au moins 3 parmi : minuscules, majuscules, chiffres et symboles"
|
||||
admin_account:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "is too short (minimum is %{count} characters)"
|
||||
too_long: "is too long (maximum is %{count} characters)"
|
||||
complexity: "doit inclure au moins 3 parmi : minuscules, majuscules, chiffres et symboles"
|
||||
club:
|
||||
back_to_club: "← Club"
|
||||
sport_label: Sport principal
|
||||
@@ -604,6 +621,9 @@ fr:
|
||||
email_sent: Si l'e-mail est enregistré, tu recevras bientôt un lien pour réinitialiser le mot de passe.
|
||||
invalid_or_expired_link: Lien invalide ou expiré. Demande une nouvelle réinitialisation du mot de passe.
|
||||
password_min_length: Le mot de passe doit comporter au moins 8 caractères
|
||||
password_too_short: Le mot de passe doit comporter au moins 8 caractères
|
||||
password_too_long: Le mot de passe ne peut pas dépasser 72 caractères
|
||||
password_too_weak: "Le mot de passe doit inclure au moins 3 parmi : minuscules, majuscules, chiffres et symboles"
|
||||
password_mismatch: Les mots de passe ne correspondent pas
|
||||
password_updated: Mot de passe mis à jour. Tu peux maintenant te connecter.
|
||||
accounts:
|
||||
@@ -611,6 +631,8 @@ fr:
|
||||
profile_updated: Profil mis à jour.
|
||||
password_current_incorrect: Le mot de passe actuel est incorrect
|
||||
password_too_short: Le mot de passe doit contenir au moins 8 caractères
|
||||
password_too_long: Le mot de passe ne peut pas dépasser 72 caractères
|
||||
password_too_weak: "Le mot de passe doit inclure au moins 3 parmi : minuscules, majuscules, chiffres et symboles"
|
||||
password_mismatch: Les mots de passe ne correspondent pas
|
||||
password_updated: Mot de passe mis à jour.
|
||||
replay:
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
it:
|
||||
password_policy:
|
||||
hint: "Minimo 8 caratteri, con almeno 3 tra: minuscole, maiuscole, numeri e simboli."
|
||||
activerecord:
|
||||
errors:
|
||||
models:
|
||||
user:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "è troppo corta (minimo %{count} caratteri)"
|
||||
too_long: "è troppo lunga (massimo %{count} caratteri)"
|
||||
complexity: "deve includere almeno 3 tra: lettere minuscole, maiuscole, numeri e simboli"
|
||||
admin_account:
|
||||
attributes:
|
||||
password:
|
||||
too_short: "è troppo corta (minimo %{count} caratteri)"
|
||||
too_long: "è troppo lunga (massimo %{count} caratteri)"
|
||||
complexity: "deve includere almeno 3 tra: lettere minuscole, maiuscole, numeri e simboli"
|
||||
club:
|
||||
back_to_club: "← Società"
|
||||
sport_label: Sport principale
|
||||
@@ -604,6 +621,12 @@ it:
|
||||
email_sent: Se l'email è registrata, riceverai a breve un link per reimpostare la password.
|
||||
invalid_or_expired_link: Link non valido o scaduto. Richiedi un nuovo reset password.
|
||||
password_min_length: La password deve avere almeno 8 caratteri
|
||||
password_too_short: La password deve avere almeno 8 caratteri
|
||||
password_too_long: La password non può superare i 72 caratteri
|
||||
password_too_weak: "La password deve includere almeno 3 tra: minuscole, maiuscole, numeri e simboli"
|
||||
password_too_short: La password deve avere almeno 8 caratteri
|
||||
password_too_long: La password non può superare i 72 caratteri
|
||||
password_too_weak: "La password deve includere almeno 3 tra: minuscole, maiuscole, numeri e simboli"
|
||||
password_mismatch: Le password non coincidono
|
||||
password_updated: Password aggiornata. Ora puoi accedere.
|
||||
accounts:
|
||||
@@ -611,6 +634,10 @@ it:
|
||||
profile_updated: Profilo aggiornato.
|
||||
password_current_incorrect: La password attuale non è corretta
|
||||
password_too_short: La password deve avere almeno 8 caratteri
|
||||
password_too_long: La password non può superare i 72 caratteri
|
||||
password_too_weak: "La password deve includere almeno 3 tra: minuscole, maiuscole, numeri e simboli"
|
||||
password_too_long: La password non può superare i 72 caratteri
|
||||
password_too_weak: "La password deve includere almeno 3 tra: minuscole, maiuscole, numeri e simboli"
|
||||
password_mismatch: Le password non coincidono
|
||||
password_updated: Password aggiornata.
|
||||
replay:
|
||||
|
||||
@@ -103,7 +103,7 @@ de:
|
||||
password_reset:
|
||||
meta_title: "Neues Passwort — Match Live TV"
|
||||
title: Neues Passwort wählen
|
||||
new_password_label: "Neues Passwort (min. 8 Zeichen)"
|
||||
new_password_label: "Neues Passwort (mind. 8, mindestens 3 Zeichenarten)"
|
||||
submit: Passwort speichern
|
||||
back_to_login: Zurück zur Anmeldung
|
||||
invitation:
|
||||
@@ -128,7 +128,7 @@ de:
|
||||
name_label: Name
|
||||
role_label: "Rolle: %{role}"
|
||||
current_password_label: Aktuelles Passwort
|
||||
new_password_label: "Neues Passwort (mind. 8 Zeichen)"
|
||||
new_password_label: "Neues Passwort (mind. 8, mindestens 3 Zeichenarten)"
|
||||
save_profile: Profil speichern
|
||||
save_password: Passwort aktualisieren
|
||||
common:
|
||||
|
||||
@@ -103,7 +103,7 @@ en:
|
||||
password_reset:
|
||||
meta_title: "New password — Match Live TV"
|
||||
title: Choose a new password
|
||||
new_password_label: "New password (min. 8 characters)"
|
||||
new_password_label: "New password (min. 8, at least 3 character types)"
|
||||
submit: Save password
|
||||
back_to_login: Back to login
|
||||
invitation:
|
||||
@@ -128,7 +128,7 @@ en:
|
||||
name_label: Name
|
||||
role_label: "Role: %{role}"
|
||||
current_password_label: Current password
|
||||
new_password_label: "New password (min. 8 characters)"
|
||||
new_password_label: "New password (min. 8, at least 3 character types)"
|
||||
save_profile: Save profile
|
||||
save_password: Update password
|
||||
common:
|
||||
|
||||
@@ -103,7 +103,7 @@ es:
|
||||
password_reset:
|
||||
meta_title: "Nueva contraseña — Match Live TV"
|
||||
title: Elige una nueva contraseña
|
||||
new_password_label: "Nueva contraseña (mín. 8 caracteres)"
|
||||
new_password_label: "Nueva contraseña (mín. 8, al menos 3 tipos de caracteres)"
|
||||
submit: Guardar contraseña
|
||||
back_to_login: Volver al inicio de sesión
|
||||
invitation:
|
||||
@@ -128,7 +128,7 @@ es:
|
||||
name_label: Nombre
|
||||
role_label: "Rol: %{role}"
|
||||
current_password_label: Contraseña actual
|
||||
new_password_label: "Nueva contraseña (mín. 8 caracteres)"
|
||||
new_password_label: "Nueva contraseña (mín. 8, al menos 3 tipos de caracteres)"
|
||||
save_profile: Guardar perfil
|
||||
save_password: Actualizar contraseña
|
||||
common:
|
||||
|
||||
@@ -103,7 +103,7 @@ fr:
|
||||
password_reset:
|
||||
meta_title: "Nouveau mot de passe — Match Live TV"
|
||||
title: Choisissez un nouveau mot de passe
|
||||
new_password_label: "Nouveau mot de passe (8 caractères min.)"
|
||||
new_password_label: "Nouveau mot de passe (min. 8, au moins 3 types de caractères)"
|
||||
submit: Enregistrer le mot de passe
|
||||
back_to_login: Retour à la connexion
|
||||
invitation:
|
||||
@@ -128,7 +128,7 @@ fr:
|
||||
name_label: Nom
|
||||
role_label: "Rôle : %{role}"
|
||||
current_password_label: Mot de passe actuel
|
||||
new_password_label: "Nouveau mot de passe (min. 8 caractères)"
|
||||
new_password_label: "Nouveau mot de passe (min. 8, au moins 3 types de caractères)"
|
||||
save_profile: Enregistrer le profil
|
||||
save_password: Mettre à jour le mot de passe
|
||||
common:
|
||||
|
||||
@@ -103,7 +103,7 @@ it:
|
||||
password_reset:
|
||||
meta_title: "Nuova password — Match Live TV"
|
||||
title: Scegli una nuova password
|
||||
new_password_label: "Nuova password (min. 8 caratteri)"
|
||||
new_password_label: "Nuova password (min. 8, almeno 3 tipi di caratteri)"
|
||||
submit: Salva password
|
||||
back_to_login: Torna al login
|
||||
invitation:
|
||||
@@ -128,7 +128,7 @@ it:
|
||||
name_label: Nome
|
||||
role_label: "Ruolo: %{role}"
|
||||
current_password_label: Password attuale
|
||||
new_password_label: "Nuova password (min. 8 caratteri)"
|
||||
new_password_label: "Nuova password (min. 8, almeno 3 tipi di caratteri)"
|
||||
save_profile: Salva profilo
|
||||
save_password: Aggiorna password
|
||||
common:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe PasswordComplexity do
|
||||
describe ".violation" do
|
||||
it "accepts a password with 3 character classes" do
|
||||
expect(described_class.violation("NewPass123")).to be_nil
|
||||
end
|
||||
|
||||
it "accepts symbols instead of one letter class" do
|
||||
expect(described_class.violation("newpass1!")).to be_nil
|
||||
end
|
||||
|
||||
it "rejects passwords that are too short" do
|
||||
expect(described_class.violation("Ab1!")).to eq(:too_short)
|
||||
end
|
||||
|
||||
it "rejects passwords with fewer than 3 classes" do
|
||||
expect(described_class.violation("password123")).to eq(:too_weak)
|
||||
expect(described_class.violation("PASSWORD123")).to eq(:too_weak)
|
||||
expect(described_class.violation("Password")).to eq(:too_weak)
|
||||
end
|
||||
end
|
||||
|
||||
describe "User validation" do
|
||||
it "blocks weak passwords on create" do
|
||||
user = User.new(email: "weak@example.com", name: "Weak", password: "password123", role: "coach")
|
||||
expect(user).not_to be_valid
|
||||
expect(user.errors[:password]).to be_present
|
||||
end
|
||||
|
||||
it "allows strong passwords on create" do
|
||||
user = User.new(email: "strong@example.com", name: "Strong", password: "NewPass123", role: "coach")
|
||||
expect(user).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,9 +1,9 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Account API", type: :request do
|
||||
let!(:user) { User.create!(email: "account@example.com", name: "Account User", password: "password123", role: "coach") }
|
||||
let!(:user) { User.create!(email: "account@example.com", name: "Account User", password: "Password123", role: "coach") }
|
||||
let(:auth_headers) do
|
||||
post "/api/v1/auth/login", params: { email: user.email, password: "password123" }
|
||||
post "/api/v1/auth/login", params: { email: user.email, password: "Password123" }
|
||||
token = JSON.parse(response.body).fetch("access_token")
|
||||
{ "Authorization" => "Bearer #{token}" }
|
||||
end
|
||||
@@ -40,25 +40,38 @@ RSpec.describe "Account API", type: :request do
|
||||
it "changes the password with the current password" do
|
||||
patch "/api/v1/account/password",
|
||||
params: {
|
||||
current_password: "password123",
|
||||
password: "newpass123",
|
||||
password_confirmation: "newpass123"
|
||||
current_password: "Password123",
|
||||
password: "NewPass123",
|
||||
password_confirmation: "NewPass123"
|
||||
},
|
||||
headers: auth_headers
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(user.reload.authenticate("newpass123")).to be_truthy
|
||||
expect(user.reload.authenticate("NewPass123")).to be_truthy
|
||||
end
|
||||
|
||||
it "rejects an incorrect current password" do
|
||||
patch "/api/v1/account/password",
|
||||
params: {
|
||||
current_password: "wrong",
|
||||
password: "NewPass123",
|
||||
password_confirmation: "NewPass123"
|
||||
},
|
||||
headers: auth_headers
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(user.reload.authenticate("Password123")).to be_truthy
|
||||
end
|
||||
|
||||
it "rejects a password that fails complexity rules" do
|
||||
patch "/api/v1/account/password",
|
||||
params: {
|
||||
current_password: "Password123",
|
||||
password: "newpass123",
|
||||
password_confirmation: "newpass123"
|
||||
},
|
||||
headers: auth_headers
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(user.reload.authenticate("password123")).to be_truthy
|
||||
expect(JSON.parse(response.body)["error"]).to match(/3 of/i)
|
||||
expect(user.reload.authenticate("Password123")).to be_truthy
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe "Auth API", type: :request do
|
||||
let!(:user) { User.create!(email: "test@example.com", name: "Test", password: "password123", role: "coach") }
|
||||
let!(:user) { User.create!(email: "test@example.com", name: "Test", password: "Password123", role: "coach") }
|
||||
|
||||
it "logs in with valid credentials" do
|
||||
post "/api/v1/auth/login", params: { email: user.email, password: "password123" }
|
||||
post "/api/v1/auth/login", params: { email: user.email, password: "Password123" }
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(JSON.parse(response.body)).to have_key("access_token")
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user