Module: EncryptedUserPassword
- Extended by:
- ActiveSupport::Concern
- Included in:
- User
- Defined in:
- app/models/concerns/encrypted_user_password.rb
Overview
Support for both BCrypt and PBKDF2+SHA512 user passwords Meant to be used exclusively with User model but extracted to a concern for isolation and clarity.
Constant Summary collapse
- BCRYPT_PREFIX =
'$2a$'- PBKDF2_SHA512_PREFIX =
'$pbkdf2-sha512$'- PBKDF2_SALT_LENGTH =
64- BCRYPT_STRATEGY =
:bcrypt- PBKDF2_SHA512_STRATEGY =
:pbkdf2_sha512
Instance Method Summary collapse
-
#authenticatable_salt ⇒ Object
Use Devise DatabaseAuthenticatable#authenticatable_salt unless encrypted password is PBKDF2+SHA512.
- #migrated_password? ⇒ Boolean
- #password=(new_password) ⇒ Object
-
#valid_password?(password) ⇒ Boolean
Called by Devise during database authentication.
Instance Method Details
#authenticatable_salt ⇒ Object
Use Devise DatabaseAuthenticatable#authenticatable_salt unless encrypted password is PBKDF2+SHA512.
18 19 20 21 22 |
# File 'app/models/concerns/encrypted_user_password.rb', line 18 def authenticatable_salt return super unless pbkdf2_password? Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.split_digest(encrypted_password)[:salt] end |
#migrated_password? ⇒ Boolean
54 55 56 57 58 59 60 61 62 63 64 |
# File 'app/models/concerns/encrypted_user_password.rb', line 54 def migrated_password? return false if password_strategy != encryptor if BCRYPT_STRATEGY == password_strategy return true if bcrypt_password_matches_current_stretches? elsif PBKDF2_SHA512_STRATEGY == password_strategy return true if pbkdf2_password_matches_salt_length? end false end |
#password=(new_password) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'app/models/concerns/encrypted_user_password.rb', line 35 def password=(new_password) @password = new_password # rubocop:disable Gitlab/ModuleWithInstanceVariables return unless new_password.present? return unless new_password.to_s.length >= ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH # Use SafeRequestStore to cache the password hash during registration # This prevents redundant bcrypt operations when the same password is set on multiple # User objects during registration. Our analysis showed that two separate User objects # are created during registration (one by BuildService and another by Devise), and # both trigger expensive password hashing operations. By caching within the request, # we reduce registration time by ~31% while maintaining security. hash_key = "password_hash:#{Digest::SHA256.hexdigest(new_password.to_s)}" self.encrypted_password = Gitlab::SafeRequestStore.fetch(hash_key) do hash_this_password(new_password) end end |
#valid_password?(password) ⇒ Boolean
Called by Devise during database authentication. Also migrates the user password to the configured encryption type (BCrypt or PBKDF2+SHA512), if needed.
27 28 29 30 31 32 33 |
# File 'app/models/concerns/encrypted_user_password.rb', line 27 def valid_password?(password) # On Ubuntu 22.04 FIPS, attempting to hash a password < 8 bytes results in PKCS5_PBKDF2_HMAC: invalid key length return false unless password.length >= ApplicationSetting::DEFAULT_MINIMUM_PASSWORD_LENGTH return false unless password_matches?(password) migrate_password!(password) end |