devise 1.1.3 → 1.2.0

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 (157) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.rdoc +94 -0
  4. data/Gemfile +24 -13
  5. data/Gemfile.lock +119 -75
  6. data/MIT-LICENSE +1 -1
  7. data/README.rdoc +144 -101
  8. data/Rakefile +3 -24
  9. data/app/controllers/devise/confirmations_controller.rb +1 -1
  10. data/app/controllers/devise/omniauth_callbacks_controller.rb +26 -0
  11. data/app/controllers/devise/passwords_controller.rb +1 -1
  12. data/app/controllers/devise/registrations_controller.rb +60 -7
  13. data/app/controllers/devise/sessions_controller.rb +6 -4
  14. data/app/controllers/devise/unlocks_controller.rb +1 -1
  15. data/app/helpers/devise_helper.rb +10 -2
  16. data/app/mailers/devise/mailer.rb +27 -10
  17. data/app/views/devise/confirmations/new.html.erb +1 -1
  18. data/app/views/devise/passwords/edit.html.erb +2 -2
  19. data/app/views/devise/passwords/new.html.erb +1 -1
  20. data/app/views/devise/registrations/edit.html.erb +1 -1
  21. data/app/views/devise/registrations/new.html.erb +1 -1
  22. data/app/views/devise/sessions/new.html.erb +1 -1
  23. data/app/views/devise/shared/_links.erb +6 -0
  24. data/app/views/devise/unlocks/new.html.erb +1 -1
  25. data/config/locales/en.yml +11 -2
  26. data/devise.gemspec +25 -0
  27. data/lib/devise/controllers/helpers.rb +119 -116
  28. data/lib/devise/controllers/internal_helpers.rb +29 -8
  29. data/lib/devise/controllers/rememberable.rb +52 -0
  30. data/lib/devise/controllers/scoped_views.rb +4 -6
  31. data/lib/devise/controllers/url_helpers.rb +3 -5
  32. data/lib/devise/encryptors/authlogic_sha512.rb +1 -1
  33. data/lib/devise/encryptors/base.rb +1 -1
  34. data/lib/devise/encryptors/restful_authentication_sha1.rb +4 -4
  35. data/lib/devise/failure_app.rb +46 -10
  36. data/lib/devise/hooks/activatable.rb +2 -2
  37. data/lib/devise/hooks/forgetable.rb +1 -3
  38. data/lib/devise/hooks/rememberable.rb +5 -42
  39. data/lib/devise/hooks/timeoutable.rb +1 -1
  40. data/lib/devise/mapping.rb +18 -7
  41. data/lib/devise/models/authenticatable.rb +73 -31
  42. data/lib/devise/models/confirmable.rb +16 -20
  43. data/lib/devise/models/database_authenticatable.rb +27 -37
  44. data/lib/devise/models/encryptable.rb +72 -0
  45. data/lib/devise/models/lockable.rb +27 -21
  46. data/lib/devise/models/omniauthable.rb +23 -0
  47. data/lib/devise/models/recoverable.rb +13 -3
  48. data/lib/devise/models/registerable.rb +13 -0
  49. data/lib/devise/models/rememberable.rb +39 -34
  50. data/lib/devise/models/timeoutable.rb +20 -3
  51. data/lib/devise/models/token_authenticatable.rb +22 -10
  52. data/lib/devise/models/validatable.rb +16 -4
  53. data/lib/devise/models.rb +4 -16
  54. data/lib/devise/modules.rb +15 -8
  55. data/lib/devise/omniauth/config.rb +18 -0
  56. data/lib/devise/omniauth/url_helpers.rb +33 -0
  57. data/lib/devise/omniauth.rb +32 -0
  58. data/lib/devise/orm/active_record.rb +2 -0
  59. data/lib/devise/orm/mongoid.rb +4 -2
  60. data/lib/devise/rails/routes.rb +67 -20
  61. data/lib/devise/rails/warden_compat.rb +101 -15
  62. data/lib/devise/rails.rb +33 -44
  63. data/lib/devise/schema.rb +16 -16
  64. data/lib/devise/strategies/authenticatable.rb +48 -7
  65. data/lib/devise/strategies/database_authenticatable.rb +1 -1
  66. data/lib/devise/strategies/rememberable.rb +6 -5
  67. data/lib/devise/strategies/token_authenticatable.rb +6 -2
  68. data/lib/devise/test_helpers.rb +13 -3
  69. data/lib/devise/version.rb +1 -1
  70. data/lib/devise.rb +162 -50
  71. data/lib/generators/active_record/devise_generator.rb +2 -2
  72. data/lib/generators/active_record/templates/migration.rb +2 -0
  73. data/lib/generators/devise/devise_generator.rb +3 -1
  74. data/lib/generators/devise/orm_helpers.rb +1 -1
  75. data/lib/generators/devise/views_generator.rb +8 -45
  76. data/lib/generators/mongoid/devise_generator.rb +2 -2
  77. data/lib/generators/templates/devise.rb +82 -39
  78. data/test/controllers/helpers_test.rb +78 -72
  79. data/test/controllers/internal_helpers_test.rb +29 -8
  80. data/test/controllers/url_helpers_test.rb +2 -1
  81. data/test/devise_test.rb +10 -0
  82. data/test/failure_app_test.rb +86 -22
  83. data/test/generators/active_record_generator_test.rb +24 -0
  84. data/test/generators/devise_generator_test.rb +33 -0
  85. data/test/generators/install_generator_test.rb +13 -0
  86. data/test/generators/mongoid_generator_test.rb +22 -0
  87. data/test/generators/views_generator_test.rb +35 -0
  88. data/test/indifferent_hash.rb +33 -0
  89. data/test/integration/authenticatable_test.rb +165 -62
  90. data/test/integration/database_authenticatable_test.rb +22 -0
  91. data/test/integration/http_authenticatable_test.rb +12 -2
  92. data/test/integration/omniauthable_test.rb +138 -0
  93. data/test/integration/recoverable_test.rb +39 -20
  94. data/test/integration/registerable_test.rb +60 -4
  95. data/test/integration/rememberable_test.rb +72 -29
  96. data/test/integration/timeoutable_test.rb +10 -1
  97. data/test/integration/token_authenticatable_test.rb +55 -6
  98. data/test/mailers/confirmation_instructions_test.rb +4 -0
  99. data/test/mailers/reset_password_instructions_test.rb +4 -0
  100. data/test/mailers/unlock_instructions_test.rb +4 -0
  101. data/test/mapping_test.rb +37 -3
  102. data/test/models/confirmable_test.rb +32 -15
  103. data/test/models/database_authenticatable_test.rb +14 -71
  104. data/test/models/encryptable_test.rb +65 -0
  105. data/test/models/lockable_test.rb +48 -11
  106. data/test/models/recoverable_test.rb +28 -2
  107. data/test/models/rememberable_test.rb +186 -125
  108. data/test/models/token_authenticatable_test.rb +19 -1
  109. data/test/models_test.rb +12 -5
  110. data/test/omniauth/url_helpers_test.rb +54 -0
  111. data/test/orm/mongoid.rb +3 -2
  112. data/test/rails_app/Rakefile +10 -0
  113. data/test/rails_app/app/active_record/admin.rb +4 -1
  114. data/test/rails_app/app/active_record/user.rb +5 -4
  115. data/test/rails_app/app/controllers/{sessions_controller.rb → admins/sessions_controller.rb} +1 -1
  116. data/test/rails_app/app/controllers/application_controller.rb +0 -1
  117. data/test/rails_app/app/controllers/home_controller.rb +9 -0
  118. data/test/rails_app/app/controllers/users/omniauth_callbacks_controller.rb +14 -0
  119. data/test/rails_app/app/mongoid/admin.rb +4 -1
  120. data/test/rails_app/app/mongoid/shim.rb +16 -3
  121. data/test/rails_app/app/mongoid/user.rb +5 -5
  122. data/test/rails_app/app/views/admins/index.html.erb +1 -0
  123. data/test/rails_app/app/views/admins/sessions/new.html.erb +2 -0
  124. data/test/rails_app/app/views/home/index.html.erb +1 -0
  125. data/test/rails_app/app/views/home/private.html.erb +1 -0
  126. data/test/rails_app/app/views/layouts/application.html.erb +24 -0
  127. data/test/rails_app/app/views/users/index.html.erb +1 -0
  128. data/test/rails_app/app/views/users/mailer/confirmation_instructions.erb +1 -0
  129. data/test/rails_app/app/views/users/sessions/new.html.erb +1 -0
  130. data/test/rails_app/config/application.rb +5 -0
  131. data/test/rails_app/config/boot.rb +2 -2
  132. data/test/rails_app/config/database.yml +18 -0
  133. data/test/rails_app/config/initializers/devise.rb +71 -31
  134. data/test/rails_app/config/routes.rb +14 -6
  135. data/test/rails_app/config.ru +4 -0
  136. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +21 -17
  137. data/test/rails_app/db/schema.rb +17 -51
  138. data/test/rails_app/lib/shared_admin.rb +9 -0
  139. data/test/rails_app/lib/shared_user.rb +23 -0
  140. data/test/rails_app/public/404.html +26 -0
  141. data/test/rails_app/public/422.html +26 -0
  142. data/test/rails_app/public/500.html +26 -0
  143. data/test/rails_app/public/favicon.ico +0 -0
  144. data/test/rails_app/script/rails +10 -0
  145. data/test/routes_test.rb +42 -9
  146. data/test/schema_test.rb +33 -0
  147. data/test/support/integration.rb +4 -4
  148. data/test/support/locale/en.yml +4 -0
  149. data/test/support/webrat/integrations/rails.rb +11 -19
  150. data/test/test_helper.rb +8 -2
  151. data/test/test_helpers_test.rb +64 -2
  152. metadata +106 -30
  153. data/TODO +0 -3
  154. data/lib/devise/encryptors/bcrypt.rb +0 -19
  155. data/lib/generators/devise_install_generator.rb +0 -4
  156. data/lib/generators/devise_views_generator.rb +0 -4
  157. data/test/support/test_silencer.rb +0 -5
@@ -17,17 +17,15 @@ module Devise
17
17
 
18
18
  # Render a view for the specified scope. Turned off by default.
19
19
  # Accepts just :controller as option.
20
- def render_with_scope(action, options={})
21
- controller_name = options.delete(:controller) || self.controller_name
22
-
20
+ def render_with_scope(action, path=self.controller_path)
23
21
  if self.class.scoped_views?
24
22
  begin
25
- render :template => "#{devise_mapping.plural}/#{controller_name}/#{action}"
23
+ render :template => "#{devise_mapping.scoped_path}/#{path.split("/").last}/#{action}"
26
24
  rescue ActionView::MissingTemplate
27
- render :template => "#{controller_path}/#{action}"
25
+ render :template => "#{path}/#{action}"
28
26
  end
29
27
  else
30
- render :template => "#{controller_path}/#{action}"
28
+ render :template => "#{path}/#{action}"
31
29
  end
32
30
  end
33
31
  end
@@ -19,13 +19,11 @@ module Devise
19
19
  # Those helpers are added to your ApplicationController.
20
20
  module UrlHelpers
21
21
 
22
- Devise::ROUTES.values.uniq.each do |module_name|
22
+ Devise::URL_HELPERS.each do |module_name, actions|
23
23
  [:path, :url].each do |path_or_url|
24
- actions = [ nil, :new_ ]
25
- actions << :edit_ if [:password, :registration].include?(module_name)
26
- actions << :destroy_ if [:session].include?(module_name)
27
-
28
24
  actions.each do |action|
25
+ action = action ? "#{action}_" : ""
26
+
29
27
  class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
30
28
  def #{action}#{module_name}_#{path_or_url}(resource_or_scope, *args)
31
29
  scope = Devise::Mapping.find_scope!(resource_or_scope)
@@ -4,7 +4,7 @@ module Devise
4
4
  module Encryptors
5
5
  # = AuthlogicSha512
6
6
  # Simulates Authlogic's default encryption mechanism.
7
- # Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to silumate
7
+ # Warning: it uses Devise's stretches configuration to port Authlogic's one. Should be set to 20 in the initializer to simulate
8
8
  # the default behavior.
9
9
  class AuthlogicSha512 < Base
10
10
  # Gererates a default password digest based on salt, pepper and the
@@ -13,7 +13,7 @@ module Devise
13
13
  end
14
14
 
15
15
  def self.salt(stretches)
16
- Devise.friendly_token
16
+ Devise.friendly_token[0,20]
17
17
  end
18
18
  end
19
19
  end
@@ -5,10 +5,10 @@ module Devise
5
5
  # = RestfulAuthenticationSha1
6
6
  # Simulates Restful Authentication's default encryption mechanism.
7
7
  # Warning: it uses Devise's pepper to port the concept of REST_AUTH_SITE_KEY
8
- # Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
9
- # the initializer to silumate the default behavior.
8
+ # Warning: it uses Devise's stretches configuration to port the concept of REST_AUTH_DIGEST_STRETCHES. Should be set to 10 in
9
+ # the initializer to simulate the default behavior.
10
10
  class RestfulAuthenticationSha1 < Base
11
-
11
+
12
12
  # Gererates a default password digest based on salt, pepper and the
13
13
  # incoming password.
14
14
  def self.digest(password, stretches, salt, pepper)
@@ -19,4 +19,4 @@ module Devise
19
19
 
20
20
  end
21
21
  end
22
- end
22
+ end
@@ -33,7 +33,7 @@ module Devise
33
33
 
34
34
  def http_auth
35
35
  self.status = 401
36
- self.headers["WWW-Authenticate"] = %(Basic realm=#{Devise.http_authentication_realm.inspect})
36
+ self.headers["WWW-Authenticate"] = %(Basic realm=#{Devise.http_authentication_realm.inspect}) if http_auth_header?
37
37
  self.content_type = request.format.to_s
38
38
  self.response_body = http_auth_body
39
39
  end
@@ -41,12 +41,12 @@ module Devise
41
41
  def recall
42
42
  env["PATH_INFO"] = attempted_path
43
43
  flash.now[:alert] = i18n_message(:invalid)
44
- self.response = recall_controller.action(warden_options[:recall]).call(env)
44
+ self.response = recall_app(warden_options[:recall]).call(env)
45
45
  end
46
46
 
47
47
  def redirect
48
48
  store_location!
49
- flash[:alert] = i18n_message unless flash[:notice]
49
+ flash[:alert] = i18n_message
50
50
  redirect_to redirect_url
51
51
  end
52
52
 
@@ -64,20 +64,44 @@ module Devise
64
64
  end
65
65
 
66
66
  def redirect_url
67
- send(:"new_#{scope}_session_path")
67
+ if request_format == :html
68
+ send(:"new_#{scope}_session_path")
69
+ else
70
+ send(:"new_#{scope}_session_path", :format => request_format)
71
+ end
68
72
  end
69
73
 
74
+ # Choose whether we should respond in a http authentication fashion,
75
+ # including 401 and optional headers.
76
+ #
77
+ # This method allows the user to explicitly disable http authentication
78
+ # on ajax requests in case they want to redirect on failures instead of
79
+ # handling the errors on their own. This is useful in case your ajax API
80
+ # is the same as your public API and uses a format like JSON (so you
81
+ # cannot mark JSON as a navigational format).
70
82
  def http_auth?
71
- !Devise.navigational_formats.include?(request.format.to_sym) || (request.xhr? && Devise.http_authenticatable_on_xhr)
83
+ if request.xhr?
84
+ Devise.http_authenticatable_on_xhr
85
+ else
86
+ !(request_format && Devise.navigational_formats.include?(request_format))
87
+ end
88
+ end
89
+
90
+ # It does not make sense to send authenticate headers in ajax requests
91
+ # or if the user disabled them.
92
+ def http_auth_header?
93
+ Devise.mappings[scope].to.http_authenticatable && !request.xhr?
72
94
  end
73
95
 
74
96
  def http_auth_body
75
- method = :"to_#{request.format.to_sym}"
97
+ return i18n_message unless request_format
98
+ method = "to_#{request_format}"
76
99
  {}.respond_to?(method) ? { :error => i18n_message }.send(method) : i18n_message
77
100
  end
78
101
 
79
- def recall_controller
80
- "#{params[:controller].camelize}Controller".constantize
102
+ def recall_app(app)
103
+ controller, action = app.split("#")
104
+ "#{controller.camelize}Controller".constantize.action(action)
81
105
  end
82
106
 
83
107
  def warden
@@ -89,7 +113,7 @@ module Devise
89
113
  end
90
114
 
91
115
  def scope
92
- @scope ||= warden_options[:scope]
116
+ @scope ||= warden_options[:scope] || Devise.default_scope
93
117
  end
94
118
 
95
119
  def attempted_path
@@ -101,7 +125,19 @@ module Devise
101
125
  # yet, but we still need to store the uri based on scope, so different scopes
102
126
  # would never use the same uri to redirect.
103
127
  def store_location!
104
- session[:"#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
128
+ session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
129
+ end
130
+
131
+ MIME_REFERENCES = Mime::HTML.respond_to?(:ref)
132
+
133
+ def request_format
134
+ @request_format ||= if request.format.respond_to?(:ref)
135
+ request.format.ref
136
+ elsif MIME_REFERENCES
137
+ request.format
138
+ else # Rails < 3.0.4
139
+ request.format.to_sym
140
+ end
105
141
  end
106
142
  end
107
143
  end
@@ -1,9 +1,9 @@
1
1
  # Deny user access whenever his account is not active yet. All strategies that inherits from
2
- # Devise::Strategies::Authenticatable and uses the validate already check if the user is active?
2
+ # Devise::Strategies::Authenticatable and uses the validate already check if the user is active_for_authentication?
3
3
  # before actively signing him in. However, we need this as hook to validate the user activity
4
4
  # in each request and in case the user is using other strategies beside Devise ones.
5
5
  Warden::Manager.after_set_user do |record, warden, options|
6
- if record && record.respond_to?(:active?) && !record.active?
6
+ if record && record.respond_to?(:active_for_authentication?) && !record.active_for_authentication?
7
7
  scope = options[:scope]
8
8
  warden.logout(scope)
9
9
  throw :warden, :scope => scope, :message => record.inactive_message
@@ -4,8 +4,6 @@
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!)
7
- record.forget_me! unless record.frozen?
8
- cookie_options = record.cookie_domain? ? { :domain => record.cookie_domain } : {}
9
- warden.cookies.delete("remember_#{options[:scope]}_token", cookie_options)
7
+ Devise::Controllers::Rememberable::Proxy.new(warden).forget_me(record)
10
8
  end
11
9
  end
@@ -1,43 +1,6 @@
1
- module Devise
2
- module Hooks
3
- # Overwrite success! in authentication strategies allowing users to be remembered.
4
- # We choose to implement this as an strategy hook instead of a warden hook to allow a specific
5
- # strategy (like token authenticatable or facebook authenticatable) to turn off remember_me?
6
- # cookies.
7
- module Rememberable #:nodoc:
8
- def success!(resource)
9
- super
10
-
11
- if succeeded? && resource.respond_to?(:remember_me!) && remember_me?
12
- resource.remember_me!(extend_remember_period?)
13
-
14
- configuration = {
15
- :value => resource.class.serialize_into_cookie(resource),
16
- :expires => resource.remember_expires_at,
17
- :path => "/"
18
- }
19
-
20
- configuration[:domain] = resource.cookie_domain if resource.cookie_domain?
21
- cookies.signed["remember_#{scope}_token"] = configuration
22
- end
23
- end
24
-
25
- protected
26
-
27
- def succeeded?
28
- @result == :success
29
- end
30
-
31
- def extend_remember_period?
32
- false
33
- end
34
-
35
- def remember_me?
36
- valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
37
- end
38
- end
1
+ Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
2
+ scope = options[:scope]
3
+ if record.respond_to?(:remember_me) && record.remember_me && warden.authenticated?(scope)
4
+ Devise::Controllers::Rememberable::Proxy.new(warden).remember_me(record)
39
5
  end
40
- end
41
-
42
- Devise::Strategies::Authenticatable.send :include, Devise::Hooks::Rememberable
43
-
6
+ end
@@ -6,7 +6,7 @@
6
6
  Warden::Manager.after_set_user do |record, warden, options|
7
7
  scope = options[:scope]
8
8
 
9
- if record && record.respond_to?(:timedout?) && warden.authenticated?(scope)
9
+ if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false
10
10
  last_request_at = warden.session(scope)['last_request_at']
11
11
 
12
12
  if record.timedout?(last_request_at)
@@ -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, :plural, :path, :controllers, :path_names, :class_name
25
+ attr_reader :singular, :scoped_path, :path, :controllers, :path_names, :class_name, :sign_out_via
26
26
  alias :name :singular
27
27
 
28
28
  # Receives an object and find a scope for it. If a scope cannot be found,
@@ -37,15 +37,20 @@ module Devise
37
37
  Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
38
38
  end
39
39
 
40
- raise "Could not find a valid mapping for #{duck}"
40
+ raise "Could not find a valid mapping for #{duck.inspect}"
41
+ end
42
+
43
+ def self.find_by_path!(path, path_type=:fullpath)
44
+ Devise.mappings.each_value { |m| return m if path.include?(m.send(path_type)) }
45
+ raise "Could not find a valid mapping for path #{path.inspect}"
41
46
  end
42
47
 
43
48
  def initialize(name, options) #:nodoc:
44
- @plural = (options[:as] ? "#{options[:as]}_#{name}" : name).to_sym
45
- @singular = (options[:singular] || @plural.to_s.singularize).to_sym
49
+ @scoped_path = options[:as] ? "#{options[:as]}/#{name}" : name.to_s
50
+ @singular = (options[:singular] || @scoped_path.tr('/', '_').singularize).to_sym
46
51
 
47
52
  @class_name = (options[:class_name] || name.to_s.classify).to_s
48
- @ref = ActiveSupport::Dependencies.ref(@class_name)
53
+ @ref = Devise.ref(@class_name)
49
54
 
50
55
  @path = (options[:path] || name).to_s
51
56
  @path_prefix = options[:path_prefix]
@@ -57,6 +62,8 @@ module Devise
57
62
  @path_names = Hash.new { |h,k| h[k] = k.to_s }
58
63
  @path_names.merge!(:registration => "")
59
64
  @path_names.merge!(options[:path_names] || {})
65
+
66
+ @sign_out_via = options[:sign_out_via] || Devise.sign_out_via
60
67
  end
61
68
 
62
69
  # Return modules for the mapping.
@@ -66,7 +73,11 @@ module Devise
66
73
 
67
74
  # Gives the class the mapping points to.
68
75
  def to
69
- @ref.get
76
+ if defined?(ActiveSupport::Dependencies::ClassCache)
77
+ @ref.get @class_name
78
+ else
79
+ @ref.get
80
+ end
70
81
  end
71
82
 
72
83
  def strategies
@@ -82,7 +93,7 @@ module Devise
82
93
  end
83
94
 
84
95
  def fullpath
85
- "#{@path_prefix}/#{@path}".squeeze("/")
96
+ "/#{@path_prefix}/#{@path}".squeeze("/")
86
97
  end
87
98
 
88
99
  # Create magic predicates for verifying what module is activated by this map.
@@ -2,34 +2,41 @@ require 'devise/hooks/activatable'
2
2
 
3
3
  module Devise
4
4
  module Models
5
- # Authenticable module. Holds common settings for authentication.
5
+ # Authenticatable module. Holds common settings for authentication.
6
6
  #
7
- # == Configuration:
7
+ # == Options
8
8
  #
9
- # You can overwrite configuration values by setting in globally in Devise,
10
- # using devise method or overwriting the respective instance method.
9
+ # Authenticatable adds the following options to devise_for:
11
10
  #
12
- # authentication_keys: parameters used for authentication. By default [:email].
11
+ # * +authentication_keys+: parameters used for authentication. By default [:email].
13
12
  #
14
- # http_authenticatable: if this model allows http authentication. By default true.
15
- # It also accepts an array specifying the strategies that should allow http.
13
+ # * +request_keys+: parameters from the request object used for authentication.
14
+ # By specifying a symbol (which should be a request method), it will automatically be
15
+ # passed to find_for_authentication method and considered in your model lookup.
16
16
  #
17
- # params_authenticatable: if this model allows authentication through request params. By default true.
18
- # It also accepts an array specifying the strategies that should allow params authentication.
17
+ # For instance, if you set :request_keys to [:subdomain], :subdomain will be considered
18
+ # as key on authentication. This can also be a hash where the value is a boolean expliciting
19
+ # if the value is required or not.
19
20
  #
20
- # == Active?
21
+ # * +http_authenticatable+: if this model allows http authentication. By default true.
22
+ # It also accepts an array specifying the strategies that should allow http.
21
23
  #
22
- # Before authenticating an user and in each request, Devise checks if your model is active by
23
- # calling model.active?. This method is overwriten by other devise modules. For instance,
24
- # :confirmable overwrites .active? to only return true if your model was confirmed.
24
+ # * +params_authenticatable+: if this model allows authentication through request params. By default true.
25
+ # It also accepts an array specifying the strategies that should allow params authentication.
26
+ #
27
+ # == active_for_authentication?
28
+ #
29
+ # Before authenticating a user and in each request, Devise checks if your model is active by
30
+ # calling model.active_for_authentication?. This method is overwriten by other devise modules. For instance,
31
+ # :confirmable overwrites .active_for_authentication? to only return true if your model was confirmed.
25
32
  #
26
33
  # You overwrite this method yourself, but if you do, don't forget to call super:
27
34
  #
28
- # def active?
35
+ # def active_for_authentication?
29
36
  # super && special_condition_is_valid?
30
37
  # end
31
38
  #
32
- # Whenever active? returns false, Devise asks the reason why your model is inactive using
39
+ # Whenever active_for_authentication? returns false, Devise asks the reason why your model is inactive using
33
40
  # the inactive_message method. You can overwrite it as well:
34
41
  #
35
42
  # def inactive_message
@@ -48,10 +55,10 @@ module Devise
48
55
  # find_for_authentication are the methods used in a Warden::Strategy to check
49
56
  # if a model should be signed in or not.
50
57
  #
51
- # However, you should not overwrite this method, you should overwrite active? and
52
- # inactive_message instead.
58
+ # However, you should not overwrite this method, you should overwrite active_for_authentication?
59
+ # and inactive_message instead.
53
60
  def valid_for_authentication?
54
- if active?
61
+ if active_for_authentication?
55
62
  block_given? ? yield : true
56
63
  else
57
64
  inactive_message
@@ -59,15 +66,30 @@ module Devise
59
66
  end
60
67
 
61
68
  def active?
62
- true
69
+ ActiveSupport::Deprecation.warn "[DEVISE] active? is deprecated, please use active_for_authentication? instead.", caller
70
+ active_for_authentication?
71
+ end
72
+
73
+ def active_for_authentication?
74
+ my_methods = self.class.instance_methods(false)
75
+ if my_methods.include?("active?") || my_methods.include?(:active?)
76
+ ActiveSupport::Deprecation.warn "[DEVISE] Overriding active? is deprecated to avoid conflicts. " \
77
+ "Please use active_for_authentication? instead.", caller
78
+ active?
79
+ else
80
+ true
81
+ end
63
82
  end
64
83
 
65
84
  def inactive_message
66
85
  :inactive
67
86
  end
68
87
 
88
+ def authenticatable_salt
89
+ end
90
+
69
91
  module ClassMethods
70
- Devise::Models.config(self, :authentication_keys, :http_authenticatable, :params_authenticatable)
92
+ Devise::Models.config(self, :authentication_keys, :request_keys, :case_insensitive_keys, :http_authenticatable, :params_authenticatable)
71
93
 
72
94
  def params_authenticatable?(strategy)
73
95
  params_authenticatable.is_a?(Array) ?
@@ -90,34 +112,54 @@ module Devise
90
112
  # end
91
113
  #
92
114
  def find_for_authentication(conditions)
93
- find(:first, :conditions => conditions)
115
+ filter_auth_params(conditions)
116
+ (case_insensitive_keys || []).each { |k| conditions[k].try(:downcase!) }
117
+ to_adapter.find_first(conditions)
94
118
  end
95
119
 
96
120
  # Find an initialize a record setting an error if it can't be found.
97
121
  def find_or_initialize_with_error_by(attribute, value, error=:invalid) #:nodoc:
98
- if value.present?
99
- conditions = { attribute => value }
100
- record = find(:first, :conditions => conditions)
101
- end
122
+ find_or_initialize_with_errors([attribute], { attribute => value }, error)
123
+ end
102
124
 
125
+ # Find an initialize a group of attributes based on a list of required attributes.
126
+ def find_or_initialize_with_errors(required_attributes, attributes, error=:invalid) #:nodoc:
127
+ (case_insensitive_keys || []).each { |k| attributes[k].try(:downcase!) }
128
+
129
+ attributes = attributes.slice(*required_attributes)
130
+ attributes.delete_if { |key, value| value.blank? }
131
+
132
+ if attributes.size == required_attributes.size
133
+ record = to_adapter.find_first(filter_auth_params(attributes))
134
+ end
135
+
103
136
  unless record
104
137
  record = new
105
- if value.present?
106
- record.send(:"#{attribute}=", value)
107
- else
108
- error = :blank
138
+
139
+ required_attributes.each do |key|
140
+ value = attributes[key]
141
+ record.send("#{key}=", value)
142
+ record.errors.add(key, value.present? ? error : :blank)
109
143
  end
110
- record.errors.add(attribute, error)
111
144
  end
112
145
 
113
146
  record
114
147
  end
115
148
 
149
+ protected
150
+
151
+ # Force keys to be string to avoid injection on mongoid related database.
152
+ def filter_auth_params(conditions)
153
+ conditions.each do |k, v|
154
+ conditions[k] = v.to_s
155
+ end if conditions.is_a?(Hash)
156
+ end
157
+
116
158
  # Generate a token by looping and ensuring does not already exist.
117
159
  def generate_token(column)
118
160
  loop do
119
161
  token = Devise.friendly_token
120
- break token unless find(:first, :conditions => { column => token })
162
+ break token unless to_adapter.find_first({ column => token })
121
163
  end
122
164
  end
123
165
  end
@@ -3,28 +3,24 @@ module Devise
3
3
  # Confirmable is responsible to verify if an account is already confirmed to
4
4
  # sign in, and to send emails with confirmation instructions.
5
5
  # Confirmation instructions are sent to the user email after creating a
6
- # record, after updating it's email and also when manually requested by
7
- # a new confirmation instruction request.
8
- # Whenever the user update it's email, his account is automatically unconfirmed,
9
- # it means it won't be able to sign in again without confirming the account
10
- # again through the email that was sent.
6
+ # record and when manually requested by a new confirmation instruction request.
11
7
  #
12
- # Configuration:
8
+ # == Options
13
9
  #
14
- # confirm_within: the time you want the user will have to confirm it's account
15
- # without blocking his access. When confirm_within is zero, the
16
- # user won't be able to sign in without confirming. You can
17
- # use this to let your user access some features of your
18
- # application without confirming the account, but blocking it
19
- # after a certain period (ie 7 days). By default confirm_within is
20
- # zero, it means users always have to confirm to sign in.
10
+ # Confirmable adds the following options to devise_for:
21
11
  #
22
- # Examples:
12
+ # * +confirm_within+: the time you want to allow the user to access his account
13
+ # before confirming it. After this period, the user access is denied. You can
14
+ # use this to let your user access some features of your application without
15
+ # confirming the account, but blocking it after a certain period (ie 7 days).
16
+ # By default confirm_within is zero, it means users always have to confirm to sign in.
17
+ #
18
+ # == Examples
23
19
  #
24
20
  # User.find(1).confirm! # returns true unless it's already confirmed
25
21
  # User.find(1).confirmed? # true/false
26
22
  # User.find(1).send_confirmation_instructions # manually send instructions
27
- # User.find(1).resend_confirmation! # generates a new token and resent it
23
+ #
28
24
  module Confirmable
29
25
  extend ActiveSupport::Concern
30
26
 
@@ -59,11 +55,11 @@ module Devise
59
55
  unless_confirmed { send_confirmation_instructions }
60
56
  end
61
57
 
62
- # Overwrites active? from Devise::Models::Activatable for confirmation
63
- # by verifying whether an user is active to sign in or not. If the user
58
+ # Overwrites active_for_authentication? for confirmation
59
+ # by verifying whether a user is active to sign in or not. If the user
64
60
  # is already confirmed, it should never be blocked. Otherwise we need to
65
61
  # calculate if the confirm time has not expired for this user.
66
- def active?
62
+ def active_for_authentication?
67
63
  super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
68
64
  end
69
65
 
@@ -137,7 +133,7 @@ module Devise
137
133
  # with an email not found error.
138
134
  # Options must contain the user email
139
135
  def send_confirmation_instructions(attributes={})
140
- confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
136
+ confirmable = find_or_initialize_with_errors(confirmation_keys, attributes, :not_found)
141
137
  confirmable.resend_confirmation_token if confirmable.persisted?
142
138
  confirmable
143
139
  end
@@ -157,7 +153,7 @@ module Devise
157
153
  generate_token(:confirmation_token)
158
154
  end
159
155
 
160
- Devise::Models.config(self, :confirm_within)
156
+ Devise::Models.config(self, :confirm_within, :confirmation_keys)
161
157
  end
162
158
  end
163
159
  end