devise 1.3.4 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of devise might be problematic. Click here for more details.

Files changed (74) hide show
  1. data/.travis.yml +3 -2
  2. data/CHANGELOG.rdoc +29 -0
  3. data/Gemfile +10 -7
  4. data/Gemfile.lock +47 -47
  5. data/README.rdoc +2 -2
  6. data/Rakefile +1 -1
  7. data/app/controllers/devise/confirmations_controller.rb +2 -2
  8. data/app/controllers/devise/passwords_controller.rb +10 -2
  9. data/app/controllers/devise/registrations_controller.rb +6 -4
  10. data/app/controllers/devise/unlocks_controller.rb +2 -2
  11. data/app/helpers/devise_helper.rb +1 -1
  12. data/app/mailers/devise/mailer.rb +4 -77
  13. data/config/locales/en.yml +3 -0
  14. data/lib/devise.rb +45 -13
  15. data/lib/devise/controllers/helpers.rb +5 -2
  16. data/lib/devise/controllers/internal_helpers.rb +15 -1
  17. data/lib/devise/controllers/rememberable.rb +1 -1
  18. data/lib/devise/email.rb +23 -0
  19. data/lib/devise/hooks/forgetable.rb +1 -1
  20. data/lib/devise/hooks/trackable.rb +1 -1
  21. data/lib/devise/mailers/helpers.rb +84 -0
  22. data/lib/devise/mapping.rb +23 -7
  23. data/lib/devise/models/authenticatable.rb +14 -6
  24. data/lib/devise/models/database_authenticatable.rb +18 -1
  25. data/lib/devise/models/recoverable.rb +1 -1
  26. data/lib/devise/models/rememberable.rb +7 -5
  27. data/lib/devise/models/validatable.rb +5 -7
  28. data/lib/devise/modules.rb +1 -1
  29. data/lib/devise/omniauth.rb +0 -5
  30. data/lib/devise/omniauth/config.rb +6 -0
  31. data/lib/devise/rails/routes.rb +65 -10
  32. data/lib/devise/strategies/rememberable.rb +2 -7
  33. data/lib/devise/version.rb +1 -1
  34. data/lib/generators/devise/install_generator.rb +2 -2
  35. data/lib/generators/devise/simple_form_for/confirmations/new.html.erb +15 -0
  36. data/lib/generators/devise/simple_form_for/passwords/edit.html.erb +19 -0
  37. data/lib/generators/devise/simple_form_for/passwords/new.html.erb +15 -0
  38. data/lib/generators/devise/simple_form_for/registrations/edit.html.erb +22 -0
  39. data/lib/generators/devise/simple_form_for/registrations/new.html.erb +17 -0
  40. data/lib/generators/devise/simple_form_for/sessions/new.html.erb +15 -0
  41. data/lib/generators/devise/simple_form_for/unlocks/new.html.erb +15 -0
  42. data/lib/generators/devise/views_generator.rb +61 -9
  43. data/lib/generators/templates/devise.rb +13 -3
  44. data/test/controllers/internal_helpers_test.rb +9 -2
  45. data/test/generators/views_generator_test.rb +10 -0
  46. data/test/helpers/devise_helper_test.rb +43 -0
  47. data/test/integration/authenticatable_test.rb +74 -5
  48. data/test/integration/confirmable_test.rb +39 -1
  49. data/test/integration/database_authenticatable_test.rb +22 -0
  50. data/test/integration/http_authenticatable_test.rb +8 -0
  51. data/test/integration/lockable_test.rb +62 -4
  52. data/test/integration/omniauthable_test.rb +1 -3
  53. data/test/integration/recoverable_test.rb +66 -6
  54. data/test/integration/registerable_test.rb +1 -1
  55. data/test/integration/rememberable_test.rb +20 -1
  56. data/test/integration/trackable_test.rb +17 -0
  57. data/test/mapping_test.rb +5 -0
  58. data/test/models/database_authenticatable_test.rb +56 -1
  59. data/test/models/encryptable_test.rb +1 -1
  60. data/test/models/recoverable_test.rb +14 -3
  61. data/test/models/rememberable_test.rb +8 -0
  62. data/test/models/token_authenticatable_test.rb +0 -6
  63. data/test/models/validatable_test.rb +17 -4
  64. data/test/models_test.rb +4 -0
  65. data/test/omniauth/url_helpers_test.rb +4 -0
  66. data/test/rails_app/app/controllers/home_controller.rb +9 -0
  67. data/test/rails_app/app/controllers/users_controller.rb +6 -1
  68. data/test/rails_app/app/views/home/admin_dashboard.html.erb +1 -0
  69. data/test/rails_app/app/views/home/join.html.erb +1 -0
  70. data/test/rails_app/app/views/home/user_dashboard.html.erb +1 -0
  71. data/test/rails_app/config/initializers/devise.rb +6 -0
  72. data/test/rails_app/config/routes.rb +30 -2
  73. data/test/routes_test.rb +54 -0
  74. metadata +21 -4
@@ -27,8 +27,10 @@ en:
27
27
  passwords:
28
28
  send_instructions: 'You will receive an email with instructions about how to reset your password in a few minutes.'
29
29
  updated: 'Your password was changed successfully. You are now signed in.'
30
+ send_paranoid_instructions: "If your e-mail exists on our database, you will receive a password recovery link on your e-mail"
30
31
  confirmations:
31
32
  send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
33
+ send_paranoid_instructions: 'If your e-mail exists on our database, you will receive an email with instructions about how to confirm your account in a few minutes.'
32
34
  confirmed: 'Your account was successfully confirmed. You are now signed in.'
33
35
  registrations:
34
36
  signed_up: 'Welcome! You have signed up successfully.'
@@ -38,6 +40,7 @@ en:
38
40
  unlocks:
39
41
  send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
40
42
  unlocked: 'Your account was successfully unlocked. You are now signed in.'
43
+ send_paranoid_instructions: 'If your account exists, you will receive an email with instructions about how to unlock it in a few minutes.'
41
44
  omniauth_callbacks:
42
45
  success: 'Successfully authorized from %{kind} account.'
43
46
  failure: 'Could not authorize you from %{kind} because "%{reason}".'
data/lib/devise.rb CHANGED
@@ -3,6 +3,7 @@ require 'active_support/core_ext/numeric/time'
3
3
  require 'active_support/dependencies'
4
4
  require 'orm_adapter'
5
5
  require 'set'
6
+ require 'securerandom'
6
7
 
7
8
  module Devise
8
9
  autoload :FailureApp, 'devise/failure_app'
@@ -10,6 +11,7 @@ module Devise
10
11
  autoload :PathChecker, 'devise/path_checker'
11
12
  autoload :Schema, 'devise/schema'
12
13
  autoload :TestHelpers, 'devise/test_helpers'
14
+ autoload :Email, 'devise/email'
13
15
 
14
16
  module Controllers
15
17
  autoload :Helpers, 'devise/controllers/helpers'
@@ -29,6 +31,10 @@ module Devise
29
31
  autoload :Sha1, 'devise/encryptors/sha1'
30
32
  end
31
33
 
34
+ module Mailers
35
+ autoload :Helpers, 'devise/mailers/helpers'
36
+ end
37
+
32
38
  module Strategies
33
39
  autoload :Base, 'devise/strategies/base'
34
40
  autoload :Authenticatable, 'devise/strategies/authenticatable'
@@ -42,6 +48,9 @@ module Devise
42
48
  STRATEGIES = ActiveSupport::OrderedHash.new
43
49
  URL_HELPERS = ActiveSupport::OrderedHash.new
44
50
 
51
+ # Strategies that do not require user input.
52
+ NO_INPUT = []
53
+
45
54
  # True values used to check params
46
55
  TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE']
47
56
 
@@ -74,6 +83,11 @@ module Devise
74
83
  # False by default for backwards compatibility.
75
84
  mattr_accessor :case_insensitive_keys
76
85
  @@case_insensitive_keys = false
86
+
87
+ # Keys that should have whitespace stripped.
88
+ # False by default for backwards compatibility.
89
+ mattr_accessor :strip_whitespace_keys
90
+ @@strip_whitespace_keys = false
77
91
 
78
92
  # If http authentication is enabled by default.
79
93
  mattr_accessor :http_authenticatable
@@ -91,9 +105,11 @@ module Devise
91
105
  mattr_accessor :http_authentication_realm
92
106
  @@http_authentication_realm = "Application"
93
107
 
94
- # Email regex used to validate email formats. Adapted from authlogic.
108
+ # Email regex used to validate email formats. Based on RFC 822 and
109
+ # retrieved from Sixarm email validation gem
110
+ # (https://github.com/SixArm/sixarm_ruby_email_address_validation).
95
111
  mattr_accessor :email_regexp
96
- @@email_regexp = /\A([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})\z/i
112
+ @@email_regexp = Devise::Email::EXACT_PATTERN
97
113
 
98
114
  # Range validation for password length
99
115
  mattr_accessor :password_length
@@ -172,7 +188,7 @@ module Devise
172
188
  mattr_accessor :reset_password_keys
173
189
  @@reset_password_keys = [ :email ]
174
190
 
175
- # Time interval you can reset your password with a reset password key
191
+ # Time interval you can reset your password with a reset password key
176
192
  mattr_accessor :reset_password_within
177
193
  @@reset_password_within = nil
178
194
 
@@ -225,15 +241,30 @@ module Devise
225
241
  @@warden_config = nil
226
242
  @@warden_config_block = nil
227
243
 
244
+ # When true, enter in paranoid mode to avoid user enumeration.
245
+ mattr_accessor :paranoid
246
+ @@paranoid = false
247
+
228
248
  # Default way to setup Devise. Run rails generate devise_install to create
229
249
  # a fresh initializer with all configuration values.
230
250
  def self.setup
231
251
  yield self
232
252
  end
233
253
 
254
+ class Getter
255
+ def initialize name
256
+ @name = name
257
+ end
258
+
259
+ def get
260
+ ActiveSupport::Dependencies.constantize(@name)
261
+ end
262
+ end
263
+
234
264
  def self.ref(arg)
235
265
  if defined?(ActiveSupport::Dependencies::ClassCache)
236
- ActiveSupport::Dependencies::Reference.store(arg)
266
+ ActiveSupport::Dependencies::reference(arg)
267
+ Getter.new(arg)
237
268
  else
238
269
  ActiveSupport::Dependencies.ref(arg)
239
270
  end
@@ -245,11 +276,7 @@ module Devise
245
276
 
246
277
  # Get the mailer class from the mailer reference object.
247
278
  def self.mailer
248
- if defined?(ActiveSupport::Dependencies::ClassCache)
249
- @@mailer_ref.get "Devise::Mailer"
250
- else
251
- @@mailer_ref.get
252
- end
279
+ @@mailer_ref.get
253
280
  end
254
281
 
255
282
  # Set the mailer reference object to access the mailer.
@@ -290,13 +317,17 @@ module Devise
290
317
  options.assert_valid_keys(:strategy, :model, :controller, :route)
291
318
 
292
319
  if strategy = options[:strategy]
293
- STRATEGIES[module_name] = (strategy == true ? module_name : strategy)
320
+ strategy = (strategy == true ? module_name : strategy)
321
+ STRATEGIES[module_name] = strategy
294
322
  end
295
323
 
296
324
  if controller = options[:controller]
297
- CONTROLLERS[module_name] = (controller == true ? module_name : controller)
325
+ controller = (controller == true ? module_name : controller)
326
+ CONTROLLERS[module_name] = controller
298
327
  end
299
328
 
329
+ NO_INPUT << strategy if strategy && controller != :sessions
330
+
300
331
  if route = options[:route]
301
332
  case route
302
333
  when TrueClass
@@ -346,7 +377,8 @@ module Devise
346
377
  #
347
378
  def self.omniauth(provider, *args)
348
379
  @@helpers << Devise::OmniAuth::UrlHelpers
349
- @@omniauth_configs[provider] = Devise::OmniAuth::Config.new(provider, args)
380
+ config = Devise::OmniAuth::Config.new(provider, args)
381
+ @@omniauth_configs[config.strategy_name.to_sym] = config
350
382
  end
351
383
 
352
384
  # Include helpers in the given scope to AC and AV.
@@ -385,7 +417,7 @@ module Devise
385
417
 
386
418
  # Generate a friendly string randomically to be used as token.
387
419
  def self.friendly_token
388
- ActiveSupport::SecureRandom.base64(15).tr('+/=', 'xyz')
420
+ SecureRandom.base64(15).tr('+/=', 'xyz')
389
421
  end
390
422
 
391
423
  # constant-time comparison algorithm to prevent timing attacks
@@ -5,7 +5,7 @@ module Devise
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- helper_method :warden, :signed_in?, :devise_controller?, :anybody_signed_in?
8
+ helper_method :warden, :signed_in?, :devise_controller?
9
9
  end
10
10
 
11
11
  # Define authentication filters and accessor helpers based on mappings.
@@ -218,9 +218,12 @@ module Devise
218
218
  session.keys.grep(/^devise\./).each { |k| session.delete(k) }
219
219
  end
220
220
 
221
- # Overwrite Rails' handle unverified request to sign out all scopes.
221
+ # Overwrite Rails' handle unverified request to sign out all scopes,
222
+ # clear run strategies and remove cached variables.
222
223
  def handle_unverified_request
223
224
  sign_out_all_scopes
225
+ warden.clear_strategies_cache!
226
+ Devise.mappings.each { |_,m| instance_variable_set("@current_#{m.name}", nil) }
224
227
  super # call the default behaviour which resets the session
225
228
  end
226
229
  end
@@ -91,13 +91,27 @@ MESSAGE
91
91
  # Example:
92
92
  # before_filter :require_no_authentication, :only => :new
93
93
  def require_no_authentication
94
- if warden.authenticated?(resource_name)
94
+ no_input = devise_mapping.no_input_strategies
95
+ args = no_input.dup.push :scope => resource_name
96
+ if no_input.present? && warden.authenticate?(*args)
95
97
  resource = warden.user(resource_name)
96
98
  flash[:alert] = I18n.t("devise.failure.already_authenticated")
97
99
  redirect_to after_sign_in_path_for(resource)
98
100
  end
99
101
  end
100
102
 
103
+ # Helper for use to validate if an resource is errorless. If we are on paranoid mode, we always should assume it is
104
+ # and return false.
105
+ def successful_and_sane?(resource)
106
+ if Devise.paranoid
107
+ set_flash_message :notice, :send_paranoid_instructions if is_navigational_format?
108
+ resource.errors.clear
109
+ false
110
+ else
111
+ resource.errors.empty?
112
+ end
113
+ end
114
+
101
115
  # Sets the flash message with :key, using I18n. By default you are able
102
116
  # to setup your messages using specific resource scope, and if no one is
103
117
  # found we look to default scope.
@@ -29,7 +29,7 @@ module Devise
29
29
  # Forgets the given resource by deleting a cookie
30
30
  def forget_me(resource)
31
31
  scope = Devise::Mapping.find_scope!(resource)
32
- resource.forget_me! unless resource.frozen?
32
+ resource.forget_me!
33
33
  cookies.delete("remember_#{scope}_token", forget_cookie_values(resource))
34
34
  end
35
35
 
@@ -0,0 +1,23 @@
1
+ # This e-mail validation regexes were retrieved from SixArm Ruby
2
+ # e-mail validation gem (https://github.com/SixArm/sixarm_ruby_email_address_validation)
3
+ # As said on https://github.com/SixArm/sixarm_ruby_email_address_validation/blob/master/LICENSE.txt,
4
+ # we added it using Ruby license terms.
5
+
6
+ module Devise
7
+ module Email
8
+ QTEXT = Regexp.new '[^\\x0d\\x22\\x5c\\x80-\\xff]', nil, 'n'
9
+ DTEXT = Regexp.new '[^\\x0d\\x5b-\\x5d\\x80-\\xff]', nil, 'n'
10
+ ATOM = Regexp.new '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+', nil, 'n'
11
+ QUOTED_PAIR = Regexp.new '\\x5c[\\x00-\\x7f]', nil, 'n'
12
+ DOMAIN_LITERAL = Regexp.new "\\x5b(?:#{DTEXT}|#{QUOTED_PAIR})*\\x5d", nil, 'n'
13
+ QUOTED_STRING = Regexp.new "\\x22(?:#{QTEXT}|#{QUOTED_PAIR})*\\x22", nil, 'n'
14
+ DOMAIN_REF = ATOM
15
+ SUB_DOMAIN = "(?:#{DOMAIN_REF}|#{DOMAIN_LITERAL})"
16
+ WORD = "(?:#{ATOM}|#{QUOTED_STRING})"
17
+ DOMAIN = "#{SUB_DOMAIN}(?:\\x2e#{SUB_DOMAIN})*"
18
+ LOCAL_PART = "#{WORD}(?:\\x2e#{WORD})*"
19
+ SPEC = "#{LOCAL_PART}\\x40#{DOMAIN}"
20
+ PATTERN = Regexp.new "#{SPEC}", nil, 'n'
21
+ EXACT_PATTERN = Regexp.new "\\A#{SPEC}\\z", nil, 'n'
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
  # Before logout hook to forget the user in the given scope, if it responds
2
2
  # to forget_me! Also clear remember token to ensure the user won't be
3
- # remembered again. Notice that we forget the user unless the record is frozen.
3
+ # remembered again. Notice that we forget the user unless the record is not persisted.
4
4
  # This avoids forgetting deleted users.
5
5
  Warden::Manager.before_logout do |record, warden, options|
6
6
  if record.respond_to?(:forget_me!)
@@ -3,7 +3,7 @@
3
3
  # and on authentication. Retrieving the user from session (:fetch) does
4
4
  # not trigger it.
5
5
  Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
6
- if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope])
6
+ if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope]) && !warden.request.env['devise.skip_trackable']
7
7
  record.update_tracked_fields!(warden.request)
8
8
  end
9
9
  end
@@ -0,0 +1,84 @@
1
+ module Devise
2
+ module Mailers
3
+ module Helpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Devise::Controllers::ScopedViews
8
+ attr_reader :scope_name, :resource
9
+ end
10
+
11
+ protected
12
+
13
+ # Configure default email options
14
+ def devise_mail(record, action)
15
+ initialize_from_record(record)
16
+ mail headers_for(action)
17
+ end
18
+
19
+ def initialize_from_record(record)
20
+ @scope_name = Devise::Mapping.find_scope!(record)
21
+ @resource = instance_variable_set("@#{devise_mapping.name}", record)
22
+ end
23
+
24
+ def devise_mapping
25
+ @devise_mapping ||= Devise.mappings[scope_name]
26
+ end
27
+
28
+ def headers_for(action)
29
+ headers = {
30
+ :subject => translate(devise_mapping, action),
31
+ :from => mailer_sender(devise_mapping),
32
+ :to => resource.email,
33
+ :template_path => template_paths
34
+ }
35
+
36
+ if resource.respond_to?(:headers_for)
37
+ headers.merge!(resource.headers_for(action))
38
+ end
39
+
40
+ unless headers.key?(:reply_to)
41
+ headers[:reply_to] = headers[:from]
42
+ end
43
+
44
+ headers
45
+ end
46
+
47
+ def mailer_sender(mapping)
48
+ if Devise.mailer_sender.is_a?(Proc)
49
+ Devise.mailer_sender.call(mapping.name)
50
+ else
51
+ Devise.mailer_sender
52
+ end
53
+ end
54
+
55
+ def template_paths
56
+ template_path = [self.class.mailer_name]
57
+ template_path.unshift "#{@devise_mapping.scoped_path}/mailer" if self.class.scoped_views?
58
+ template_path
59
+ end
60
+
61
+ # Setup a subject doing an I18n lookup. At first, it attemps to set a subject
62
+ # based on the current mapping:
63
+ #
64
+ # en:
65
+ # devise:
66
+ # mailer:
67
+ # confirmation_instructions:
68
+ # user_subject: '...'
69
+ #
70
+ # If one does not exist, it fallbacks to ActionMailer default:
71
+ #
72
+ # en:
73
+ # devise:
74
+ # mailer:
75
+ # confirmation_instructions:
76
+ # subject: '...'
77
+ #
78
+ def translate(mapping, key)
79
+ I18n.t(:"#{mapping.name}_subject", :scope => [:devise, :mailer, key],
80
+ :default => [:subject, key.to_s.humanize])
81
+ end
82
+ end
83
+ end
84
+ end
@@ -22,7 +22,7 @@ module Devise
22
22
  # # is the modules included in the class
23
23
  #
24
24
  class Mapping #:nodoc:
25
- attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via
25
+ attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via, :format
26
26
  alias :name :singular
27
27
 
28
28
  # Receives an object and find a scope for it. If a scope cannot be found,
@@ -58,12 +58,20 @@ module Devise
58
58
  mod = options[:module] || "devise"
59
59
  @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
60
60
  @controllers.merge!(options[:controllers] || {})
61
+ @controllers.each { |k,v| @controllers[k] = v.to_s }
61
62
 
62
63
  @path_names = Hash.new { |h,k| h[k] = k.to_s }
63
64
  @path_names.merge!(:registration => "")
64
65
  @path_names.merge!(options[:path_names] || {})
66
+
67
+ @constraints = Hash.new { |h,k| h[k] = k.to_s }
68
+ @constraints.merge!(options[:constraints] || {})
69
+
70
+ @defaults = Hash.new { |h,k| h[k] = k.to_s }
71
+ @defaults.merge!(options[:defaults] || {})
65
72
 
66
73
  @sign_out_via = options[:sign_out_via] || Devise.sign_out_via
74
+ @format = options[:format]
67
75
  end
68
76
 
69
77
  # Return modules for the mapping.
@@ -73,17 +81,17 @@ module Devise
73
81
 
74
82
  # Gives the class the mapping points to.
75
83
  def to
76
- if defined?(ActiveSupport::Dependencies::ClassCache)
77
- @ref.get @class_name
78
- else
79
- @ref.get
80
- end
84
+ @ref.get
81
85
  end
82
86
 
83
87
  def strategies
84
88
  @strategies ||= STRATEGIES.values_at(*self.modules).compact.uniq.reverse
85
89
  end
86
90
 
91
+ def no_input_strategies
92
+ self.strategies & Devise::NO_INPUT
93
+ end
94
+
87
95
  def routes
88
96
  @routes ||= ROUTES.values_at(*self.modules).compact.uniq
89
97
  end
@@ -95,7 +103,15 @@ module Devise
95
103
  def fullpath
96
104
  "/#{@path_prefix}/#{@path}".squeeze("/")
97
105
  end
98
-
106
+
107
+ def constraints
108
+ @constraints
109
+ end
110
+
111
+ def defaults
112
+ @defaults
113
+ end
114
+
99
115
  # Create magic predicates for verifying what module is activated by this map.
100
116
  # Example:
101
117
  #
@@ -76,7 +76,8 @@ module Devise
76
76
  def authenticatable_salt
77
77
  end
78
78
 
79
- %w(to_xml to_json).each do |method|
79
+ # TODO: to_xml does not call serializable_hash. Hopefully someone will fix this in AR.
80
+ %w(to_xml serializable_hash).each do |method|
80
81
  class_eval <<-RUBY, __FILE__, __LINE__
81
82
  def #{method}(options={})
82
83
  if self.class.respond_to?(:accessible_attributes)
@@ -90,7 +91,7 @@ module Devise
90
91
  end
91
92
 
92
93
  module ClassMethods
93
- Devise::Models.config(self, :authentication_keys, :request_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
94
+ Devise::Models.config(self, :authentication_keys, :request_keys, :strip_whitespace_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
94
95
 
95
96
  def params_authenticatable?(strategy)
96
97
  params_authenticatable.is_a?(Array) ?
@@ -113,8 +114,9 @@ module Devise
113
114
  # end
114
115
  #
115
116
  def find_for_authentication(conditions)
116
- filter_auth_params(conditions)
117
+ conditions = filter_auth_params(conditions.dup)
117
118
  (case_insensitive_keys || []).each { |k| conditions[k].try(:downcase!) }
119
+ (strip_whitespace_keys || []).each { |k| conditions[k].try(:strip!) }
118
120
  to_adapter.find_first(conditions)
119
121
  end
120
122
 
@@ -126,14 +128,15 @@ module Devise
126
128
  # Find an initialize a group of attributes based on a list of required attributes.
127
129
  def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
128
130
  (case_insensitive_keys || []).each { |k| attributes[k].try(:downcase!) }
129
-
131
+ (strip_whitespace_keys || []).each { |k| attributes[k].try(:strip!) }
132
+
130
133
  attributes = attributes.slice(*required_attributes)
131
134
  attributes.delete_if { |key, value| value.blank? }
132
135
 
133
136
  if attributes.size == required_attributes.size
134
137
  record = to_adapter.find_first(filter_auth_params(attributes))
135
138
  end
136
-
139
+
137
140
  unless record
138
141
  record = new
139
142
 
@@ -152,9 +155,14 @@ module Devise
152
155
  # Force keys to be string to avoid injection on mongoid related database.
153
156
  def filter_auth_params(conditions)
154
157
  conditions.each do |k, v|
155
- conditions[k] = v.to_s
158
+ conditions[k] = v.to_s if auth_param_requires_string_conversion?(v)
156
159
  end if conditions.is_a?(Hash)
157
160
  end
161
+
162
+ # Determine which values should be transformed to string or passed as-is to the query builder underneath
163
+ def auth_param_requires_string_conversion?(value)
164
+ true unless value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Fixnum)
165
+ end
158
166
 
159
167
  # Generate a token by looping and ensuring does not already exist.
160
168
  def generate_token(column)