devise-bootstrap 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +31 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/app/controllers/devise/confirmations_controller.rb +47 -0
  8. data/app/controllers/devise/omniauth_callbacks_controller.rb +30 -0
  9. data/app/controllers/devise/passwords_controller.rb +70 -0
  10. data/app/controllers/devise/registrations_controller.rb +137 -0
  11. data/app/controllers/devise/sessions_controller.rb +53 -0
  12. data/app/controllers/devise/unlocks_controller.rb +46 -0
  13. data/app/controllers/devise_controller.rb +176 -0
  14. data/app/helpers/devise_helper.rb +25 -0
  15. data/app/mailers/devise/mailer.rb +20 -0
  16. data/app/views/devise/confirmations/new.html.erb +12 -0
  17. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  18. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  19. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  20. data/app/views/devise/passwords/edit.html.erb +16 -0
  21. data/app/views/devise/passwords/new.html.erb +12 -0
  22. data/app/views/devise/registrations/edit.html.erb +29 -0
  23. data/app/views/devise/registrations/new.html.erb +18 -0
  24. data/app/views/devise/sessions/new.html.erb +17 -0
  25. data/app/views/devise/shared/_links.erb +25 -0
  26. data/app/views/devise/unlocks/new.html.erb +12 -0
  27. data/config/locales/en.yml +59 -0
  28. data/devise-bootstrap.gemspec +30 -0
  29. data/gemfiles/Gemfile.rails-3.2-stable +29 -0
  30. data/gemfiles/Gemfile.rails-4.0-stable +29 -0
  31. data/gemfiles/Gemfile.rails-head +29 -0
  32. data/lib/devise/bootstrap.rb +7 -0
  33. data/lib/devise/bootstrap/version.rb +5 -0
  34. data/lib/devise/devise.rb +491 -0
  35. data/lib/devise/devise/controllers/helpers.rb +213 -0
  36. data/lib/devise/devise/controllers/rememberable.rb +47 -0
  37. data/lib/devise/devise/controllers/scoped_views.rb +17 -0
  38. data/lib/devise/devise/controllers/sign_in_out.rb +103 -0
  39. data/lib/devise/devise/controllers/store_location.rb +50 -0
  40. data/lib/devise/devise/controllers/url_helpers.rb +67 -0
  41. data/lib/devise/devise/delegator.rb +16 -0
  42. data/lib/devise/devise/failure_app.rb +205 -0
  43. data/lib/devise/devise/hooks/activatable.rb +11 -0
  44. data/lib/devise/devise/hooks/csrf_cleaner.rb +5 -0
  45. data/lib/devise/devise/hooks/forgetable.rb +9 -0
  46. data/lib/devise/devise/hooks/lockable.rb +7 -0
  47. data/lib/devise/devise/hooks/proxy.rb +21 -0
  48. data/lib/devise/devise/hooks/rememberable.rb +7 -0
  49. data/lib/devise/devise/hooks/timeoutable.rb +28 -0
  50. data/lib/devise/devise/hooks/trackable.rb +9 -0
  51. data/lib/devise/devise/mailers/helpers.rb +90 -0
  52. data/lib/devise/devise/mapping.rb +172 -0
  53. data/lib/devise/devise/models.rb +119 -0
  54. data/lib/devise/devise/models/authenticatable.rb +284 -0
  55. data/lib/devise/devise/models/confirmable.rb +295 -0
  56. data/lib/devise/devise/models/database_authenticatable.rb +164 -0
  57. data/lib/devise/devise/models/lockable.rb +196 -0
  58. data/lib/devise/devise/models/omniauthable.rb +27 -0
  59. data/lib/devise/devise/models/recoverable.rb +131 -0
  60. data/lib/devise/devise/models/registerable.rb +25 -0
  61. data/lib/devise/devise/models/rememberable.rb +129 -0
  62. data/lib/devise/devise/models/timeoutable.rb +49 -0
  63. data/lib/devise/devise/models/trackable.rb +35 -0
  64. data/lib/devise/devise/models/validatable.rb +66 -0
  65. data/lib/devise/devise/modules.rb +28 -0
  66. data/lib/devise/devise/omniauth.rb +28 -0
  67. data/lib/devise/devise/omniauth/config.rb +45 -0
  68. data/lib/devise/devise/omniauth/url_helpers.rb +18 -0
  69. data/lib/devise/devise/orm/active_record.rb +3 -0
  70. data/lib/devise/devise/orm/mongoid.rb +3 -0
  71. data/lib/devise/devise/parameter_filter.rb +40 -0
  72. data/lib/devise/devise/parameter_sanitizer.rb +99 -0
  73. data/lib/devise/devise/rails.rb +56 -0
  74. data/lib/devise/devise/rails/routes.rb +496 -0
  75. data/lib/devise/devise/rails/warden_compat.rb +22 -0
  76. data/lib/devise/devise/strategies/authenticatable.rb +167 -0
  77. data/lib/devise/devise/strategies/base.rb +20 -0
  78. data/lib/devise/devise/strategies/database_authenticatable.rb +23 -0
  79. data/lib/devise/devise/strategies/rememberable.rb +55 -0
  80. data/lib/devise/devise/test_helpers.rb +132 -0
  81. data/lib/devise/devise/time_inflector.rb +14 -0
  82. data/lib/devise/devise/token_generator.rb +70 -0
  83. data/lib/devise/devise/version.rb +3 -0
  84. data/lib/devise/generators/active_record/devise_generator.rb +73 -0
  85. data/lib/devise/generators/active_record/templates/migration.rb +18 -0
  86. data/lib/devise/generators/active_record/templates/migration_existing.rb +25 -0
  87. data/lib/devise/generators/devise/devise_generator.rb +26 -0
  88. data/lib/devise/generators/devise/install_generator.rb +29 -0
  89. data/lib/devise/generators/devise/orm_helpers.rb +51 -0
  90. data/lib/devise/generators/devise/views_generator.rb +135 -0
  91. data/lib/devise/generators/mongoid/devise_generator.rb +55 -0
  92. data/lib/devise/generators/templates/README +35 -0
  93. data/lib/devise/generators/templates/devise.rb +260 -0
  94. data/lib/devise/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  95. data/lib/devise/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  96. data/lib/devise/generators/templates/markerb/unlock_instructions.markerb +7 -0
  97. data/lib/devise/generators/templates/simple_form_for/confirmations/new.html.erb +16 -0
  98. data/lib/devise/generators/templates/simple_form_for/passwords/edit.html.erb +19 -0
  99. data/lib/devise/generators/templates/simple_form_for/passwords/new.html.erb +15 -0
  100. data/lib/devise/generators/templates/simple_form_for/registrations/edit.html.erb +27 -0
  101. data/lib/devise/generators/templates/simple_form_for/registrations/new.html.erb +17 -0
  102. data/lib/devise/generators/templates/simple_form_for/sessions/new.html.erb +15 -0
  103. data/lib/devise/generators/templates/simple_form_for/unlocks/new.html.erb +16 -0
  104. metadata +250 -0
@@ -0,0 +1,22 @@
1
+ module Warden::Mixins::Common
2
+ def request
3
+ @request ||= ActionDispatch::Request.new(env)
4
+ end
5
+
6
+ # Deprecate: Remove this check once we move to Rails 4 only.
7
+ NULL_STORE =
8
+ defined?(ActionController::RequestForgeryProtection::ProtectionMethods::NullSession::NullSessionHash) ?
9
+ ActionController::RequestForgeryProtection::ProtectionMethods::NullSession::NullSessionHash : nil
10
+
11
+ def reset_session!
12
+ # Calling reset_session on NULL_STORE causes it fail.
13
+ # This is a bug that needs to be fixed in Rails.
14
+ unless NULL_STORE && request.session.is_a?(NULL_STORE)
15
+ request.reset_session
16
+ end
17
+ end
18
+
19
+ def cookies
20
+ request.cookie_jar
21
+ end
22
+ end
@@ -0,0 +1,167 @@
1
+ require 'devise/strategies/base'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # This strategy should be used as basis for authentication strategies. It retrieves
6
+ # parameters both from params or from http authorization headers. See database_authenticatable
7
+ # for an example.
8
+ class Authenticatable < Base
9
+ attr_accessor :authentication_hash, :authentication_type, :password
10
+
11
+ def store?
12
+ super && !mapping.to.skip_session_storage.include?(authentication_type)
13
+ end
14
+
15
+ def valid?
16
+ valid_for_params_auth? || valid_for_http_auth?
17
+ end
18
+
19
+ private
20
+
21
+ # Receives a resource and check if it is valid by calling valid_for_authentication?
22
+ # An optional block that will be triggered while validating can be optionally
23
+ # given as parameter. Check Devise::Models::Authenticable.valid_for_authentication?
24
+ # for more information.
25
+ #
26
+ # In case the resource can't be validated, it will fail with the given
27
+ # unauthenticated_message.
28
+ def validate(resource, &block)
29
+ result = resource && resource.valid_for_authentication?(&block)
30
+
31
+ if result
32
+ decorate(resource)
33
+ true
34
+ else
35
+ if resource
36
+ fail!(resource.unauthenticated_message)
37
+ end
38
+ false
39
+ end
40
+ end
41
+
42
+ # Get values from params and set in the resource.
43
+ def decorate(resource)
44
+ resource.remember_me = remember_me? if resource.respond_to?(:remember_me=)
45
+ end
46
+
47
+ # Should this resource be marked to be remembered?
48
+ def remember_me?
49
+ valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
50
+ end
51
+
52
+ # Check if this is a valid strategy for http authentication by:
53
+ #
54
+ # * Validating if the model allows params authentication;
55
+ # * If any of the authorization headers were sent;
56
+ # * If all authentication keys are present;
57
+ #
58
+ def valid_for_http_auth?
59
+ http_authenticatable? && request.authorization && with_authentication_hash(:http_auth, http_auth_hash)
60
+ end
61
+
62
+ # Check if this is a valid strategy for params authentication by:
63
+ #
64
+ # * Validating if the model allows params authentication;
65
+ # * If the request hits the sessions controller through POST;
66
+ # * If the params[scope] returns a hash with credentials;
67
+ # * If all authentication keys are present;
68
+ #
69
+ def valid_for_params_auth?
70
+ params_authenticatable? && valid_params_request? &&
71
+ valid_params? && with_authentication_hash(:params_auth, params_auth_hash)
72
+ end
73
+
74
+ # Check if the model accepts this strategy as http authenticatable.
75
+ def http_authenticatable?
76
+ mapping.to.http_authenticatable?(authenticatable_name)
77
+ end
78
+
79
+ # Check if the model accepts this strategy as params authenticatable.
80
+ def params_authenticatable?
81
+ mapping.to.params_authenticatable?(authenticatable_name)
82
+ end
83
+
84
+ # Extract the appropriate subhash for authentication from params.
85
+ def params_auth_hash
86
+ params[scope]
87
+ end
88
+
89
+ # Extract a hash with attributes:values from the http params.
90
+ def http_auth_hash
91
+ keys = [http_authentication_key, :password]
92
+ Hash[*keys.zip(decode_credentials).flatten]
93
+ end
94
+
95
+ # By default, a request is valid if the controller set the proper env variable.
96
+ def valid_params_request?
97
+ !!env["devise.allow_params_authentication"]
98
+ end
99
+
100
+ # If the request is valid, finally check if params_auth_hash returns a hash.
101
+ def valid_params?
102
+ params_auth_hash.is_a?(Hash)
103
+ end
104
+
105
+ # Check if password is present.
106
+ def valid_password?
107
+ password.present?
108
+ end
109
+
110
+ # Helper to decode credentials from HTTP.
111
+ def decode_credentials
112
+ return [] unless request.authorization && request.authorization =~ /^Basic (.*)/m
113
+ Base64.decode64($1).split(/:/, 2)
114
+ end
115
+
116
+ # Sets the authentication hash and the password from params_auth_hash or http_auth_hash.
117
+ def with_authentication_hash(auth_type, auth_values)
118
+ self.authentication_hash, self.authentication_type = {}, auth_type
119
+ self.password = auth_values[:password]
120
+
121
+ parse_authentication_key_values(auth_values, authentication_keys) &&
122
+ parse_authentication_key_values(request_values, request_keys)
123
+ end
124
+
125
+ def authentication_keys
126
+ @authentication_keys ||= mapping.to.authentication_keys
127
+ end
128
+
129
+ def http_authentication_key
130
+ @http_authentication_key ||= mapping.to.http_authentication_key || case authentication_keys
131
+ when Array then authentication_keys.first
132
+ when Hash then authentication_keys.keys.first
133
+ end
134
+ end
135
+
136
+ def request_keys
137
+ @request_keys ||= mapping.to.request_keys
138
+ end
139
+
140
+ def request_values
141
+ keys = request_keys.respond_to?(:keys) ? request_keys.keys : request_keys
142
+ values = keys.map { |k| self.request.send(k) }
143
+ Hash[keys.zip(values)]
144
+ end
145
+
146
+ def parse_authentication_key_values(hash, keys)
147
+ keys.each do |key, enforce|
148
+ value = hash[key].presence
149
+ if value
150
+ self.authentication_hash[key] = value
151
+ else
152
+ return false unless enforce == false
153
+ end
154
+ end
155
+ true
156
+ end
157
+
158
+ # Holds the authenticatable name for this class. Devise::Strategies::DatabaseAuthenticatable
159
+ # becomes simply :database.
160
+ def authenticatable_name
161
+ @authenticatable_name ||=
162
+ ActiveSupport::Inflector.underscore(self.class.name.split("::").last).
163
+ sub("_authenticatable", "").to_sym
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,20 @@
1
+ module Devise
2
+ module Strategies
3
+ # Base strategy for Devise. Responsible for verifying correct scope and mapping.
4
+ class Base < ::Warden::Strategies::Base
5
+ # Whenever CSRF cannot be verified, we turn off any kind of storage
6
+ def store?
7
+ !env["devise.skip_storage"]
8
+ end
9
+
10
+ # Checks if a valid scope was given for devise and find mapping based on this scope.
11
+ def mapping
12
+ @mapping ||= begin
13
+ mapping = Devise.mappings[scope]
14
+ raise "Could not find mapping for #{scope}" unless mapping
15
+ mapping
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,23 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Default strategy for signing in a user, based on their email and password in the database.
6
+ class DatabaseAuthenticatable < Authenticatable
7
+ def authenticate!
8
+ resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
+ encrypted = false
10
+
11
+ if validate(resource){ encrypted = true; resource.valid_password?(password) }
12
+ resource.after_database_authentication
13
+ success!(resource)
14
+ end
15
+
16
+ mapping.to.new.password = password if !encrypted && Devise.paranoid
17
+ fail(:not_found_in_database) unless resource
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ Warden::Strategies.add(:database_authenticatable, Devise::Strategies::DatabaseAuthenticatable)
@@ -0,0 +1,55 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Remember the user through the remember token. This strategy is responsible
6
+ # to verify whether there is a cookie with the remember token, and to
7
+ # recreate the user from this cookie if it exists. Must be called *before*
8
+ # authenticatable.
9
+ class Rememberable < Authenticatable
10
+ # A valid strategy for rememberable needs a remember token in the cookies.
11
+ def valid?
12
+ @remember_cookie = nil
13
+ remember_cookie.present?
14
+ end
15
+
16
+ # To authenticate a user we deserialize the cookie and attempt finding
17
+ # the record in the database. If the attempt fails, we pass to another
18
+ # strategy handle the authentication.
19
+ def authenticate!
20
+ resource = mapping.to.serialize_from_cookie(*remember_cookie)
21
+
22
+ unless resource
23
+ cookies.delete(remember_key)
24
+ return pass
25
+ end
26
+
27
+ if validate(resource)
28
+ success!(resource)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def decorate(resource)
35
+ super
36
+ resource.extend_remember_period = mapping.to.extend_remember_period if resource.respond_to?(:extend_remember_period=)
37
+ end
38
+
39
+ def remember_me?
40
+ true
41
+ end
42
+
43
+ def remember_key
44
+ mapping.to.rememberable_options.fetch(:key, "remember_#{scope}_token")
45
+ end
46
+
47
+ def remember_cookie
48
+ @remember_cookie ||= cookies.signed[remember_key]
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+
55
+ Warden::Strategies.add(:rememberable, Devise::Strategies::Rememberable)
@@ -0,0 +1,132 @@
1
+ module Devise
2
+ # Devise::TestHelpers provides a facility to test controllers in isolation
3
+ # when using ActionController::TestCase allowing you to quickly sign_in or
4
+ # sign_out a user. Do not use Devise::TestHelpers in integration tests.
5
+ #
6
+ # Notice you should not test Warden specific behavior (like Warden callbacks)
7
+ # using Devise::TestHelpers since it is a stub of the actual behavior. Such
8
+ # callbacks should be tested in your integration suite instead.
9
+ module TestHelpers
10
+ def self.included(base)
11
+ base.class_eval do
12
+ setup :setup_controller_for_warden, :warden if respond_to?(:setup)
13
+ end
14
+ end
15
+
16
+ # Override process to consider warden.
17
+ def process(*)
18
+ # Make sure we always return @response, a la ActionController::TestCase::Behaviour#process, even if warden interrupts
19
+ _catch_warden { super } || @response
20
+ end
21
+
22
+ # We need to setup the environment variables and the response in the controller.
23
+ def setup_controller_for_warden #:nodoc:
24
+ @request.env['action_controller.instance'] = @controller
25
+ end
26
+
27
+ # Quick access to Warden::Proxy.
28
+ def warden #:nodoc:
29
+ @warden ||= begin
30
+ manager = Warden::Manager.new(nil) do |config|
31
+ config.merge! Devise.warden_config
32
+ end
33
+ @request.env['warden'] = Warden::Proxy.new(@request.env, manager)
34
+ end
35
+ end
36
+
37
+ # sign_in a given resource by storing its keys in the session.
38
+ # This method bypass any warden authentication callback.
39
+ #
40
+ # Examples:
41
+ #
42
+ # sign_in :user, @user # sign_in(scope, resource)
43
+ # sign_in @user # sign_in(resource)
44
+ #
45
+ def sign_in(resource_or_scope, resource=nil)
46
+ scope ||= Devise::Mapping.find_scope!(resource_or_scope)
47
+ resource ||= resource_or_scope
48
+ warden.instance_variable_get(:@users).delete(scope)
49
+ warden.session_serializer.store(resource, scope)
50
+ end
51
+
52
+ # Sign out a given resource or scope by calling logout on Warden.
53
+ # This method bypass any warden logout callback.
54
+ #
55
+ # Examples:
56
+ #
57
+ # sign_out :user # sign_out(scope)
58
+ # sign_out @user # sign_out(resource)
59
+ #
60
+ def sign_out(resource_or_scope)
61
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
62
+ @controller.instance_variable_set(:"@current_#{scope}", nil)
63
+ user = warden.instance_variable_get(:@users).delete(scope)
64
+ warden.session_serializer.delete(scope, user)
65
+ end
66
+
67
+ protected
68
+
69
+ # Catch warden continuations and handle like the middleware would.
70
+ # Returns nil when interrupted, otherwise the normal result of the block.
71
+ def _catch_warden(&block)
72
+ result = catch(:warden, &block)
73
+
74
+ env = @controller.request.env
75
+
76
+ result ||= {}
77
+
78
+ # Set the response. In production, the rack result is returned
79
+ # from Warden::Manager#call, which the following is modelled on.
80
+ case result
81
+ when Array
82
+ if result.first == 401 && intercept_401?(env) # does this happen during testing?
83
+ _process_unauthenticated(env)
84
+ else
85
+ result
86
+ end
87
+ when Hash
88
+ _process_unauthenticated(env, result)
89
+ else
90
+ result
91
+ end
92
+ end
93
+
94
+ def _process_unauthenticated(env, options = {})
95
+ options[:action] ||= :unauthenticated
96
+ proxy = env['warden']
97
+ result = options[:result] || proxy.result
98
+
99
+ ret = case result
100
+ when :redirect
101
+ body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
102
+ [proxy.status, proxy.headers, [body]]
103
+ when :custom
104
+ proxy.custom_response
105
+ else
106
+ env["PATH_INFO"] = "/#{options[:action]}"
107
+ env["warden.options"] = options
108
+ Warden::Manager._run_callbacks(:before_failure, env, options)
109
+
110
+ status, headers, response = Devise.warden_config[:failure_app].call(env).to_a
111
+ @controller.response.headers.merge!(headers)
112
+ @controller.send :render, status: status, text: response.body,
113
+ content_type: headers["Content-Type"], location: headers["Location"]
114
+ nil # causes process return @response
115
+ end
116
+
117
+ # ensure that the controller response is set up. In production, this is
118
+ # not necessary since warden returns the results to rack. However, at
119
+ # testing time, we want the response to be available to the testing
120
+ # framework to verify what would be returned to rack.
121
+ if ret.is_a?(Array)
122
+ # ensure the controller response is set to our response.
123
+ @controller.response ||= @response
124
+ @response.status = ret.first
125
+ @response.headers = ret.second
126
+ @response.body = ret.third
127
+ end
128
+
129
+ ret
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,14 @@
1
+ require "active_support/core_ext/module/delegation"
2
+
3
+ module Devise
4
+ class TimeInflector
5
+ include ActionView::Helpers::DateHelper
6
+
7
+ class << self
8
+ attr_reader :instance
9
+ delegate :time_ago_in_words, to: :instance
10
+ end
11
+
12
+ @instance = new
13
+ end
14
+ end
@@ -0,0 +1,70 @@
1
+ # Deprecate: Copied verbatim from Rails source, remove once we move to Rails 4 only.
2
+ require 'thread_safe'
3
+ require 'openssl'
4
+ require 'securerandom'
5
+
6
+ module Devise
7
+ class TokenGenerator
8
+ def initialize(key_generator, digest="SHA256")
9
+ @key_generator = key_generator
10
+ @digest = digest
11
+ end
12
+
13
+ def digest(klass, column, value)
14
+ value.present? && OpenSSL::HMAC.hexdigest(@digest, key_for(column), value.to_s)
15
+ end
16
+
17
+ def generate(klass, column)
18
+ key = key_for(column)
19
+
20
+ loop do
21
+ raw = Devise.friendly_token
22
+ enc = OpenSSL::HMAC.hexdigest(@digest, key, raw)
23
+ break [raw, enc] unless klass.to_adapter.find_first({ column => enc })
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def key_for(column)
30
+ @key_generator.generate_key("Devise #{column}")
31
+ end
32
+ end
33
+
34
+ # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
35
+ # It can be used to derive a number of keys for various purposes from a given secret.
36
+ # This lets Rails applications have a single secure secret, but avoid reusing that
37
+ # key in multiple incompatible contexts.
38
+ class KeyGenerator
39
+ def initialize(secret, options = {})
40
+ @secret = secret
41
+ # The default iterations are higher than required for our key derivation uses
42
+ # on the off chance someone uses this for password storage
43
+ @iterations = options[:iterations] || 2**16
44
+ end
45
+
46
+ # Returns a derived key suitable for use. The default key_size is chosen
47
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
48
+ # i.e. OpenSSL::Digest::SHA1#block_length
49
+ def generate_key(salt, key_size=64)
50
+ OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
51
+ end
52
+ end
53
+
54
+ # CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
55
+ # re-executing the key generation process when it's called using the same salt and
56
+ # key_size
57
+ class CachingKeyGenerator
58
+ def initialize(key_generator)
59
+ @key_generator = key_generator
60
+ @cache_keys = ThreadSafe::Cache.new
61
+ end
62
+
63
+ # Returns a derived key suitable for use. The default key_size is chosen
64
+ # to be compatible with the default settings of ActiveSupport::MessageVerifier.
65
+ # i.e. OpenSSL::Digest::SHA1#block_length
66
+ def generate_key(salt, key_size=64)
67
+ @cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size)
68
+ end
69
+ end
70
+ end