Class: Passlib::Password Abstract

Inherits:
Object
  • Object
show all
Extended by:
Internal::DSL
Defined in:
lib/passlib/password.rb

Overview

This class is abstract.

Subclass and implement the private #create method (and optionally #load and/or #verify) to add a new algorithm.

Abstract base class for all password hash implementations.

Concrete subclasses (e.g. BCrypt, PBKDF2, SHA2Crypt) each implement a specific algorithm. The public interface is identical across all of them: use Password.load to parse a stored hash, Password.create to generate a new one, and #verify to verify a plaintext secret.

Instances are immutable value objects. The underlying hash string is accessible via #to_s/#to_str and is always frozen.

Examples:

Parsing an existing hash

hash = Passlib::BCrypt.load("$2a$12$...")
hash.verify("hunter2")  # => true

Creating a new hash

hash = Passlib::BCrypt.create("hunter2", cost: 10)
hash.to_s  # => "$2a$10$..."

Constant Summary

Constants included from Internal::DSL

Internal::DSL::Config

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Internal::DSL

identifier

Constructor Details

#initialize(config, method) ⇒ Password

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new instance of Password.



99
100
101
102
103
104
105
# File 'lib/passlib/password.rb', line 99

def initialize(config, method, ...)
  super()
  @config   = config
  result    = send(method, ...)
  @string ||= result.to_str
  @string   = @string.frozen? ? @string : @string.dup.freeze
end

Instance Attribute Details

#configConfiguration (readonly) Also known as: configuration

The configuration used when this instance was created or loaded.

Returns:



89
90
91
# File 'lib/passlib/password.rb', line 89

def config
  @config
end

#stringString (readonly) Also known as: to_str, to_s

The frozen hash string, e.g. “$2a$12$…” or “{SSHA}…” .

Returns:

  • (String)


94
95
96
# File 'lib/passlib/password.rb', line 94

def string
  @string
end

Class Method Details

.available?Boolean

Returns whether this algorithm’s dependency gem is installed and available.

Returns:

  • (Boolean)


83
# File 'lib/passlib/password.rb', line 83

def self.available? = true

.create(secret, config = nil) ⇒ Password

Creates a new password hash for the given secret.

When called on a concrete subclass the algorithm is fixed. When called on Passlib::Password directly, the algorithm is selected from the configuration: set preferred_scheme (a single identifier) or preferred_schemes (an ordered list) in the supplied Configuration::Passlib and the first available algorithm in the list is used. Raises UnsupportedAlgorithm when no usable scheme can be found.

Parameters:

  • secret (String)

    the plaintext password to hash

  • config (Configuration, Hash, nil) (defaults to: nil)

    optional configuration overrides

  • options (Hash)

    algorithm-specific keyword options (merged into config)

Returns:

Raises:

  • (UnsupportedAlgorithm)

    if called on Passlib::Password directly and no preferred scheme is configured or available



72
73
74
75
76
77
78
79
# File 'lib/passlib/password.rb', line 72

def self.create(secret, config = nil, **)
  config = config(config, **)
  return new(config, :create, secret) if self != Password
  scheme = config.preferred_scheme
  return ::Passlib[scheme].create(secret, config) if scheme
  raise UnsupportedAlgorithm, "no preferred schemes configured" if config.preferred_schemes.empty?
  raise UnsupportedAlgorithm, "no available preferred schemes: #{config.preferred_schemes.join(", ")}"
end

.load(string, config = nil) ⇒ Password

Parses a stored hash string and returns a Passlib::Password instance.

When called on Passlib::Password directly (rather than a concrete subclass), the algorithm is auto-detected from the hash format. When called on a subclass, the string must be a valid hash for that algorithm.

Parameters:

  • string (String)

    the stored password hash string

  • config (Configuration, Hash, nil) (defaults to: nil)

    optional configuration overrides

Returns:

Raises:

  • (ArgumentError)

    if string does not match any known format (only when called on Passlib::Password directly)

  • (Passlib::UnknownHashFormat)

    if string is not valid for the target algorithm (when called on a concrete subclass)



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/passlib/password.rb', line 41

def self.load(string, config = nil, **)
  return new(config(config, **), :load, string) if self != Password
  case string
  when /\A\$([\w-]+)/            then klass = Internal::Register::MFC_IDS[$1] || Internal::Register::MFC_IDS[$1[/\A\w+/]]
  when /\A{([\w-]+)(?:,[^}]*)?}/ then klass = Internal::Register::LDAP_IDS[$1.upcase]
  else
    classes = Set.new
    Internal::Register::PATTERNS.each do |pattern, candidate|
      classes << candidate if pattern === string
    end
    klass = classes.first if classes.size == 1
  end
  return klass.load(string, config, **) if klass
  raise ArgumentError, "unknown password hash format: #{string.inspect}"
end

Instance Method Details

#create_comparable(secret) ⇒ Password

This method is abstract.

Re-hashes secret using the same parameters (salt, rounds, etc.).

Not all subclasses define it, use respond_to?(:create_comparable) to check.

Parameters:

  • secret (String)

    the plaintext password to re-hash

Returns:

  • (Password)

    a new instance hashed with the same parameters



# File 'lib/passlib/password.rb', line 107


#inspectString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a human-readable representation including the class name and hash string.

Returns:

  • (String)


140
# File 'lib/passlib/password.rb', line 140

def inspect = "#<#{self.class.name} #{string.inspect}>"

#pretty_print(pp) ⇒ void

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This method returns an undefined value.

Pretty-print support for pp.

Parameters:

  • pp (PP)

    the pretty-printer instance



146
147
148
149
150
151
# File 'lib/passlib/password.rb', line 146

def pretty_print(pp)
  pp.object_group(self) do
    pp.breakable
    pp.pp string
  end
end

#upgrade?Boolean

Returns whether this hash should be re-hashed with the current configuration.

Called by Configuration::Context#upgrade? after confirming the hash already uses the preferred algorithm. Returns true if any parameter (cost, rounds, etc.) does not exactly match the active configuration, including when stored costs are higher than configured (allowing a downgrade when parameters were set too high).

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


162
# File 'lib/passlib/password.rb', line 162

def upgrade? = raise NotImplementedError, "subclass must implement #upgrade?"

#verify(secret) ⇒ Boolean Also known as: match?

Verifies that secret matches this password hash.

Uses constant-time comparison via Passlib#secure_compare to prevent timing attacks.

Parameters:

  • secret (String)

    the plaintext password to verify

Returns:

  • (Boolean)

    true if the secret matches, false otherwise



123
124
125
126
127
128
# File 'lib/passlib/password.rb', line 123

def verify(secret)
  Passlib.secure_compare(self, create_comparable(secret))
rescue NameError => error
  raise error unless error.name == :create_comparable
  raise NotImplementedError, "subclass must implement #verify or #create_comparable"
end