Welcome to mirror list, hosted at ThFree Co, Russian Federation.

encrypted_user_password.rb « concerns « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 97e6592f44264197f5b9af7d5940e5edffbd5553 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# frozen_string_literal: true

# 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.
module EncryptedUserPassword
  extend ActiveSupport::Concern

  BCRYPT_PREFIX = '$2a$'
  PBKDF2_SHA512_PREFIX = '$pbkdf2-sha512$'

  BCRYPT_STRATEGY = :bcrypt
  PBKDF2_SHA512_STRATEGY = :pbkdf2_sha512

  # Use Devise DatabaseAuthenticatable#authenticatable_salt
  # unless encrypted password is PBKDF2+SHA512.
  def authenticatable_salt
    return super unless pbkdf2_password?

    Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.split_digest(encrypted_password)[:salt]
  end

  # Called by Devise during database authentication.
  # Also migrates the user password to the configured
  # encryption type (BCrypt or PBKDF2+SHA512), if needed.
  def valid_password?(password)
    return false unless password_matches?(password)

    migrate_password!(password)
  end

  def password=(new_password)
    @password = new_password # rubocop:disable Gitlab/ModuleWithInstanceVariables
    return unless new_password.present?

    self.encrypted_password = if Gitlab::FIPS.enabled?
                                Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.digest(
                                  new_password,
                                  Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512::STRETCHES,
                                  Devise.friendly_token[0, 16])
                              else
                                Devise::Encryptor.digest(self.class, new_password)
                              end
  end

  private

  def password_strategy
    return BCRYPT_STRATEGY if encrypted_password.starts_with?(BCRYPT_PREFIX)
    return PBKDF2_SHA512_STRATEGY if encrypted_password.starts_with?(PBKDF2_SHA512_PREFIX)

    :unknown
  end

  def pbkdf2_password?
    password_strategy == PBKDF2_SHA512_STRATEGY
  end

  def bcrypt_password?
    password_strategy == BCRYPT_STRATEGY
  end

  def password_matches?(password)
    if bcrypt_password?
      Devise::Encryptor.compare(self.class, encrypted_password, password)
    elsif pbkdf2_password?
      Devise::Pbkdf2Encryptable::Encryptors::Pbkdf2Sha512.compare(encrypted_password, password)
    end
  end

  def migrate_password!(password)
    return true if password_strategy == encryptor

    update_attribute(:password, password)
  end

  def encryptor
    return BCRYPT_STRATEGY unless Gitlab::FIPS.enabled?

    PBKDF2_SHA512_STRATEGY
  end
end