devise 1.0.11 → 1.1.pre

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 (121) hide show
  1. data/CHANGELOG.rdoc +6 -72
  2. data/Gemfile +18 -0
  3. data/README.rdoc +51 -46
  4. data/Rakefile +5 -4
  5. data/app/controllers/{confirmations_controller.rb → devise/confirmations_controller.rb} +2 -2
  6. data/app/controllers/{passwords_controller.rb → devise/passwords_controller.rb} +4 -3
  7. data/app/controllers/{registrations_controller.rb → devise/registrations_controller.rb} +11 -7
  8. data/app/controllers/{sessions_controller.rb → devise/sessions_controller.rb} +11 -8
  9. data/app/controllers/{unlocks_controller.rb → devise/unlocks_controller.rb} +2 -10
  10. data/app/models/devise/mailer.rb +55 -0
  11. data/app/views/{confirmations → devise/confirmations}/new.html.erb +1 -1
  12. data/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  13. data/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  14. data/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  15. data/app/views/{passwords → devise/passwords}/edit.html.erb +1 -1
  16. data/app/views/{passwords → devise/passwords}/new.html.erb +1 -1
  17. data/app/views/{registrations → devise/registrations}/edit.html.erb +1 -1
  18. data/app/views/{registrations → devise/registrations}/new.html.erb +1 -1
  19. data/app/views/{sessions → devise/sessions}/new.html.erb +1 -1
  20. data/app/views/{shared/_devise_links.erb → devise/shared/_links.erb} +0 -0
  21. data/app/views/{unlocks → devise/unlocks}/new.html.erb +1 -1
  22. data/{lib/devise → config}/locales/en.yml +7 -1
  23. data/lib/devise.rb +6 -29
  24. data/lib/devise/controllers/helpers.rb +16 -43
  25. data/lib/devise/controllers/internal_helpers.rb +10 -36
  26. data/lib/devise/controllers/scoped_views.rb +35 -0
  27. data/lib/devise/failure_app.rb +7 -14
  28. data/lib/devise/hooks/rememberable.rb +3 -6
  29. data/lib/devise/hooks/trackable.rb +1 -1
  30. data/lib/devise/mapping.rb +17 -18
  31. data/lib/devise/models.rb +4 -20
  32. data/lib/devise/models/{database_authenticatable.rb → authenticatable.rb} +16 -28
  33. data/lib/devise/models/confirmable.rb +25 -23
  34. data/lib/devise/models/http_authenticatable.rb +3 -7
  35. data/lib/devise/models/lockable.rb +40 -35
  36. data/lib/devise/models/recoverable.rb +4 -8
  37. data/lib/devise/models/rememberable.rb +6 -9
  38. data/lib/devise/models/timeoutable.rb +1 -3
  39. data/lib/devise/models/token_authenticatable.rb +4 -5
  40. data/lib/devise/models/validatable.rb +10 -1
  41. data/lib/devise/orm/mongo_mapper.rb +10 -23
  42. data/lib/devise/rails.rb +11 -9
  43. data/lib/devise/rails/routes.rb +113 -107
  44. data/lib/devise/rails/warden_compat.rb +3 -41
  45. data/lib/devise/schema.rb +13 -21
  46. data/lib/devise/strategies/{database_authenticatable.rb → authenticatable.rb} +3 -3
  47. data/lib/devise/strategies/http_authenticatable.rb +4 -22
  48. data/lib/devise/test_helpers.rb +1 -8
  49. data/lib/devise/version.rb +1 -1
  50. data/lib/generators/devise/devise_generator.rb +57 -0
  51. data/{generators → lib/generators}/devise/templates/migration.rb +1 -1
  52. data/lib/generators/devise_install/devise_install_generator.rb +25 -0
  53. data/{generators → lib/generators}/devise_install/templates/README +4 -8
  54. data/{generators → lib/generators}/devise_install/templates/devise.rb +0 -3
  55. data/lib/generators/devise_views/devise_views_generator.rb +11 -0
  56. data/test/controllers/helpers_test.rb +15 -9
  57. data/test/devise_test.rb +1 -6
  58. data/test/encryptors_test.rb +0 -3
  59. data/test/failure_app_test.rb +6 -1
  60. data/test/integration/authenticatable_test.rb +25 -85
  61. data/test/integration/http_authenticatable_test.rb +2 -10
  62. data/test/integration/lockable_test.rb +3 -22
  63. data/test/integration/recoverable_test.rb +1 -1
  64. data/test/integration/registerable_test.rb +31 -36
  65. data/test/integration/rememberable_test.rb +6 -24
  66. data/test/integration/token_authenticatable_test.rb +2 -4
  67. data/test/integration/trackable_test.rb +1 -1
  68. data/test/mailers/confirmation_instructions_test.rb +4 -10
  69. data/test/mailers/unlock_instructions_test.rb +1 -1
  70. data/test/mapping_test.rb +12 -24
  71. data/test/models/authenticatable_test.rb +3 -3
  72. data/test/models/confirmable_test.rb +29 -29
  73. data/test/models/http_authenticatable_test.rb +19 -0
  74. data/test/models/lockable_test.rb +45 -44
  75. data/test/models/recoverable_test.rb +7 -7
  76. data/test/models/rememberable_test.rb +7 -10
  77. data/test/models/validatable_test.rb +19 -24
  78. data/test/models_test.rb +2 -16
  79. data/test/orm/active_record.rb +3 -4
  80. data/test/orm/mongo_mapper.rb +2 -10
  81. data/test/rails_app/app/active_record/admin.rb +1 -1
  82. data/test/rails_app/app/active_record/user.rb +3 -3
  83. data/test/rails_app/app/controllers/application_controller.rb +1 -7
  84. data/test/rails_app/app/controllers/sessions_controller.rb +6 -0
  85. data/test/rails_app/app/controllers/users_controller.rb +0 -4
  86. data/test/rails_app/app/mongo_mapper/admin.rb +4 -7
  87. data/test/rails_app/app/mongo_mapper/user.rb +5 -8
  88. data/test/rails_app/config/application.rb +32 -0
  89. data/test/rails_app/config/boot.rb +7 -108
  90. data/test/rails_app/config/environment.rb +4 -41
  91. data/test/rails_app/config/environments/development.rb +15 -13
  92. data/test/rails_app/config/environments/production.rb +25 -20
  93. data/test/rails_app/config/environments/test.rb +23 -22
  94. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  95. data/test/rails_app/config/initializers/cookie_verification_secret.rb +7 -0
  96. data/test/rails_app/config/initializers/devise.rb +0 -3
  97. data/test/rails_app/config/initializers/session_store.rb +2 -2
  98. data/test/rails_app/config/routes.rb +17 -21
  99. data/test/routes_test.rb +30 -47
  100. data/test/support/{assertions_helper.rb → assertions.rb} +0 -15
  101. data/test/support/{tests_helper.rb → helpers.rb} +16 -3
  102. data/test/support/{integration_tests_helper.rb → integration.rb} +8 -4
  103. data/test/support/webrat/integrations/rails.rb +31 -0
  104. data/test/test_helper.rb +8 -7
  105. data/test/test_helpers_test.rb +9 -9
  106. metadata +53 -128
  107. data/app/models/devise_mailer.rb +0 -68
  108. data/app/views/devise_mailer/confirmation_instructions.html.erb +0 -5
  109. data/app/views/devise_mailer/reset_password_instructions.html.erb +0 -8
  110. data/app/views/devise_mailer/unlock_instructions.html.erb +0 -7
  111. data/generators/devise/USAGE +0 -5
  112. data/generators/devise/devise_generator.rb +0 -15
  113. data/generators/devise/lib/route_devise.rb +0 -32
  114. data/generators/devise/templates/model.rb +0 -9
  115. data/generators/devise_install/USAGE +0 -3
  116. data/generators/devise_install/devise_install_generator.rb +0 -15
  117. data/generators/devise_views/USAGE +0 -3
  118. data/generators/devise_views/devise_views_generator.rb +0 -21
  119. data/rails/init.rb +0 -2
  120. data/test/integration/rack_middleware_test.rb +0 -47
  121. data/test/rails_app/config/initializers/new_rails_defaults.rb +0 -24
@@ -0,0 +1,35 @@
1
+ module Devise
2
+ module Controllers
3
+ module ScopedViews
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def scoped_views
8
+ defined?(@scoped_views) ? @scoped_views : Devise.scoped_views
9
+ end
10
+
11
+ def scoped_views=(value)
12
+ @scoped_views = value
13
+ end
14
+ end
15
+
16
+ protected
17
+
18
+ # Render a view for the specified scope. Turned off by default.
19
+ # Accepts just :controller as option.
20
+ def render_with_scope(action, options={})
21
+ controller_name = options.delete(:controller) || self.controller_name
22
+
23
+ if self.class.scoped_views
24
+ begin
25
+ render :template => "#{devise_mapping.as}/#{controller_name}/#{action}"
26
+ rescue ActionView::MissingTemplate
27
+ render :template => "#{controller_path}/#{action}"
28
+ end
29
+ else
30
+ render :template => "#{controller_path}/#{action}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -22,8 +22,12 @@ module Devise
22
22
  options = @env['warden.options']
23
23
  scope = options[:scope]
24
24
 
25
- redirect_path = redirect_path_for(scope)
26
- query_string = query_string_for(options)
25
+ redirect_path = if mapping = Devise.mappings[scope]
26
+ "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
27
+ else
28
+ "/#{default_url}"
29
+ end
30
+ query_string = query_string_for(options)
27
31
  store_location!(scope)
28
32
 
29
33
  headers = {}
@@ -50,23 +54,12 @@ module Devise
50
54
  Rack::Utils.build_query(params)
51
55
  end
52
56
 
53
- # Build the path based on current scope.
54
- def redirect_path_for(scope)
55
- if mapping = Devise.mappings[scope]
56
- "#{mapping.parsed_path}/#{mapping.path_names[:sign_in]}"
57
- else
58
- "/#{default_url}"
59
- end
60
- end
61
-
62
57
  # Stores requested uri to redirect the user after signing in. We cannot use
63
58
  # scoped session provided by warden here, since the user is not authenticated
64
59
  # yet, but we still need to store the uri based on scope, so different scopes
65
60
  # would never use the same uri to redirect.
66
61
  def store_location!(scope)
67
- if request && request.get? && !request.xhr?
68
- session[:"#{scope}.return_to"] = request.request_uri
69
- end
62
+ session[:"#{scope}.return_to"] = request.request_uri if request && request.get?
70
63
  end
71
64
  end
72
65
  end
@@ -22,12 +22,9 @@ end
22
22
  # Before logout hook to forget the user in the given scope, only if rememberable
23
23
  # is activated for this scope. Also clear remember token to ensure the user
24
24
  # won't be remembered again.
25
- # Notice that we forget the user if the record is frozen. This usually means the
26
- # user was just deleted.
27
- Warden::Manager.before_logout do |record, warden, options|
28
- scope = options[:scope]
25
+ Warden::Manager.before_logout do |record, warden, scope|
29
26
  if record.respond_to?(:forget_me!)
30
- record.forget_me! unless record.frozen?
31
- warden.response.delete_cookie "remember_#{scope}_token", :path => "/"
27
+ record.forget_me!
28
+ warden.response.delete_cookie "remember_#{scope}_token"
32
29
  end
33
30
  end
@@ -13,6 +13,6 @@ Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
13
13
  record.sign_in_count ||= 0
14
14
  record.sign_in_count += 1
15
15
 
16
- record.save(false)
16
+ record.save(:validate => false)
17
17
  end
18
18
  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 :name, :as, :path_names, :path_prefix, :route_options, :sign_out_via
25
+ attr_reader :name, :as, :path_names, :path_prefix
26
26
 
27
27
  # Loop through all mappings looking for a map that matches with the requested
28
28
  # path (ie /users/sign_in). If a path prefix is given, it's taken into account.
@@ -34,19 +34,26 @@ module Devise
34
34
  nil
35
35
  end
36
36
 
37
+ # Find a mapping by a given class. It takes into account single table inheritance as well.
38
+ def self.find_by_class(klass)
39
+ Devise.mappings.each_value do |mapping|
40
+ return mapping if klass <= mapping.to
41
+ end
42
+ nil
43
+ end
44
+
37
45
  # Receives an object and find a scope for it. If a scope cannot be found,
38
46
  # raises an error. If a symbol is given, it's considered to be the scope.
39
47
  def self.find_scope!(duck)
40
48
  case duck
41
49
  when String, Symbol
42
- return duck
43
- when Class
44
- Devise.mappings.each_value { |m| return m.name if duck <= m.to }
50
+ duck
45
51
  else
46
- Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
52
+ klass = duck.is_a?(Class) ? duck : duck.class
53
+ mapping = Devise::Mapping.find_by_class(klass)
54
+ raise "Could not find a valid mapping for #{duck}" unless mapping
55
+ mapping.name
47
56
  end
48
-
49
- raise "Could not find a valid mapping for #{duck}"
50
57
  end
51
58
 
52
59
  # Default url options which can be used as prefix.
@@ -59,13 +66,9 @@ module Devise
59
66
  @klass = (options.delete(:class_name) || name.to_s.classify).to_s
60
67
  @name = (options.delete(:scope) || name.to_s.singularize).to_sym
61
68
 
62
- @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
63
- @route_options = options || {}
64
-
65
- @path_names = Hash.new { |h,k| h[k] = k.to_s }
69
+ @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
70
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
66
71
  @path_names.merge!(options.delete(:path_names) || {})
67
-
68
- @sign_out_via = (options.delete(:sign_out_via) || :get)
69
72
  end
70
73
 
71
74
  # Return modules for the mapping.
@@ -98,17 +101,13 @@ module Devise
98
101
 
99
102
  # Returns the parsed path taking into account the relative url root and raw path.
100
103
  def parsed_path
101
- (ActionController::Base.relative_url_root.to_s + raw_path).tap do |path|
104
+ returning (ActionController::Base.relative_url_root.to_s + raw_path) do |path|
102
105
  self.class.default_url_options.each do |key, value|
103
106
  path.gsub!(key.inspect, value.to_param)
104
107
  end
105
108
  end
106
109
  end
107
110
 
108
- def authenticatable?
109
- @authenticatable ||= self.for.any? { |m| m.to_s =~ /authenticatable/ }
110
- end
111
-
112
111
  # Create magic predicates for verifying what module is activated by this map.
113
112
  # Example:
114
113
  #
@@ -1,7 +1,7 @@
1
1
  module Devise
2
2
  module Models
3
3
  autoload :Activatable, 'devise/models/activatable'
4
- autoload :DatabaseAuthenticatable, 'devise/models/database_authenticatable'
4
+ autoload :Authenticatable, 'devise/models/authenticatable'
5
5
  autoload :Confirmable, 'devise/models/confirmable'
6
6
  autoload :Lockable, 'devise/models/lockable'
7
7
  autoload :Recoverable, 'devise/models/recoverable'
@@ -57,12 +57,7 @@ module Devise
57
57
  #
58
58
  def devise(*modules)
59
59
  raise "You need to give at least one Devise module" if modules.empty?
60
- options = modules.extract_options!
61
-
62
- if modules.delete(:authenticatable)
63
- ActiveSupport::Deprecation.warn ":authenticatable as module is deprecated. Please give :database_authenticatable instead.", caller
64
- modules << :database_authenticatable
65
- end
60
+ options = modules.extract_options!
66
61
 
67
62
  @devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
68
63
 
@@ -94,24 +89,13 @@ module Devise
94
89
  if value.present?
95
90
  record.send(:"#{attribute}=", value)
96
91
  else
97
- error, skip_default = :blank, true
92
+ error = :blank
98
93
  end
99
94
 
100
- add_error_on(record, attribute, error, !skip_default)
95
+ record.errors.add(attribute, error)
101
96
  end
102
97
 
103
98
  record
104
99
  end
105
-
106
- # Wraps add error logic in a method that works for different frameworks.
107
- def add_error_on(record, attribute, error, add_default=true)
108
- options = add_default ? { :default => error.to_s.gsub("_", " ") } : {}
109
-
110
- begin
111
- record.errors.add(attribute, error, options)
112
- rescue ArgumentError
113
- record.errors.add(attribute, error.to_s.gsub("_", " "))
114
- end
115
- end
116
100
  end
117
101
  end
@@ -1,4 +1,4 @@
1
- require 'devise/strategies/database_authenticatable'
1
+ require 'devise/strategies/authenticatable'
2
2
 
3
3
  module Devise
4
4
  module Models
@@ -26,20 +26,12 @@ module Devise
26
26
  # User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
27
27
  # User.find(1).valid_password?('password123') # returns true/false
28
28
  #
29
- module DatabaseAuthenticatable
30
- def self.included(base)
31
- base.class_eval do
32
- extend ClassMethods
29
+ module Authenticatable
30
+ extend ActiveSupport::Concern
33
31
 
34
- attr_reader :password, :current_password
35
- attr_accessor :password_confirmation
36
- end
37
- end
38
-
39
- # TODO Remove me in next release
40
- def old_password
41
- ActiveSupport::Deprecation.warn "old_password is deprecated, please use current_password instead", caller
42
- @old_password
32
+ included do
33
+ attr_reader :password, :current_password
34
+ attr_accessor :password_confirmation
43
35
  end
44
36
 
45
37
  # Regenerates password salt and encrypted password each time password is set,
@@ -55,7 +47,13 @@ module Devise
55
47
 
56
48
  # Verifies whether an incoming_password (ie from sign in) is the user password.
57
49
  def valid_password?(incoming_password)
58
- Devise.secure_compare(password_digest(incoming_password), self.encrypted_password)
50
+ password_digest(incoming_password) == self.encrypted_password
51
+ end
52
+
53
+ # Verifies whether an +incoming_authentication_token+ (i.e. from single access URL)
54
+ # is the user authentication token.
55
+ def valid_authentication_token?(incoming_auth_token)
56
+ incoming_auth_token == self.authentication_token
59
57
  end
60
58
 
61
59
  # Checks if a resource is valid upon authentication.
@@ -74,16 +72,13 @@ module Devise
74
72
  def update_with_password(params={})
75
73
  current_password = params.delete(:current_password)
76
74
 
77
- if params[:password].blank?
78
- params.delete(:password)
79
- params.delete(:password_confirmation) if params[:password_confirmation].blank?
80
- end
75
+ params.delete(:password) if params[:password].blank?
76
+ params.delete(:password_confirmation) if params[:password_confirmation].blank?
81
77
 
82
78
  result = if valid_password?(current_password)
83
79
  update_attributes(params)
84
80
  else
85
- message = current_password.blank? ? :blank : :invalid
86
- self.class.add_error_on(self, :current_password, message, false)
81
+ self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
87
82
  self.attributes = params
88
83
  false
89
84
  end
@@ -94,13 +89,6 @@ module Devise
94
89
 
95
90
  protected
96
91
 
97
- # Checks whether a password is needed or not. For validations only.
98
- # Passwords are always required if it's a new record, or if the password
99
- # or confirmation are being set somewhere.
100
- def password_required?
101
- new_record? || !password.nil? || !password_confirmation.nil?
102
- end
103
-
104
92
  # Digests the password using the configured encryptor.
105
93
  def password_digest(password)
106
94
  self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
@@ -29,15 +29,12 @@ module Devise
29
29
  # User.find(1).send_confirmation_instructions # manually send instructions
30
30
  # User.find(1).resend_confirmation! # generates a new token and resent it
31
31
  module Confirmable
32
+ extend ActiveSupport::Concern
32
33
  include Devise::Models::Activatable
33
34
 
34
- def self.included(base)
35
- base.class_eval do
36
- extend ClassMethods
37
-
38
- before_create :generate_confirmation_token, :if => :confirmation_required?
39
- after_create :send_confirmation_instructions, :if => :confirmation_required?
40
- end
35
+ included do
36
+ before_create :generate_confirmation_token, :if => :confirmation_required?
37
+ after_create :send_confirmation_instructions, :if => :confirmation_required?
41
38
  end
42
39
 
43
40
  # Confirm a user by setting it's confirmed_at to actual time. If the user
@@ -46,7 +43,7 @@ module Devise
46
43
  unless_confirmed do
47
44
  self.confirmation_token = nil
48
45
  self.confirmed_at = Time.now
49
- save(false)
46
+ save(:validate => false)
50
47
  end
51
48
  end
52
49
 
@@ -57,13 +54,18 @@ module Devise
57
54
 
58
55
  # Send confirmation instructions by email
59
56
  def send_confirmation_instructions
60
- generate_confirmation_token! if self.confirmation_token.nil?
61
- ::DeviseMailer.deliver_confirmation_instructions(self)
57
+ ::Devise::Mailer.confirmation_instructions(self).deliver
62
58
  end
63
59
 
64
- # Resend confirmation token. This method does not need to generate a new token.
65
- def resend_confirmation_token
66
- unless_confirmed { send_confirmation_instructions }
60
+ # Remove confirmation date and send confirmation instructions, to ensure
61
+ # after sending these instructions the user won't be able to sign in without
62
+ # confirming it's account
63
+ def resend_confirmation!
64
+ unless_confirmed do
65
+ generate_confirmation_token
66
+ save(:validate => false)
67
+ send_confirmation_instructions
68
+ end
67
69
  end
68
70
 
69
71
  # Overwrites active? from Devise::Models::Activatable for confirmation
@@ -71,12 +73,16 @@ module Devise
71
73
  # is already confirmed, it should never be blocked. Otherwise we need to
72
74
  # calculate if the confirm time has not expired for this user.
73
75
  def active?
74
- super && (!confirmation_required? || confirmed? || confirmation_period_valid?)
76
+ super && (confirmed? || confirmation_period_valid?)
75
77
  end
76
78
 
77
79
  # The message to be shown if the account is inactive.
78
80
  def inactive_message
79
- !confirmed? ? :unconfirmed : super
81
+ if !confirmed?
82
+ :unconfirmed
83
+ else
84
+ super
85
+ end
80
86
  end
81
87
 
82
88
  # If you don't want confirmation to be sent on create, neither a code
@@ -122,7 +128,7 @@ module Devise
122
128
  unless confirmed?
123
129
  yield
124
130
  else
125
- self.class.add_error_on(self, :email, :already_confirmed)
131
+ self.errors.add(:email, :already_confirmed)
126
132
  false
127
133
  end
128
134
  end
@@ -135,10 +141,6 @@ module Devise
135
141
  self.confirmation_sent_at = Time.now.utc
136
142
  end
137
143
 
138
- def generate_confirmation_token!
139
- generate_confirmation_token && save(false)
140
- end
141
-
142
144
  module ClassMethods
143
145
  # Attempt to find a user by it's email. If a record is found, send new
144
146
  # confirmation instructions to it. If not user is found, returns a new user
@@ -146,7 +148,7 @@ module Devise
146
148
  # Options must contain the user email
147
149
  def send_confirmation_instructions(attributes={})
148
150
  confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
149
- confirmable.resend_confirmation_token unless confirmable.new_record?
151
+ confirmable.resend_confirmation! unless confirmable.new_record?
150
152
  confirmable
151
153
  end
152
154
 
@@ -154,8 +156,8 @@ module Devise
154
156
  # If no user is found, returns a new user with an error.
155
157
  # If the user is already confirmed, create an error for the user
156
158
  # Options must have the confirmation_token
157
- def confirm_by_token(confirmation_token)
158
- confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
159
+ def confirm!(attributes={})
160
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, attributes[:confirmation_token])
159
161
  confirmable.confirm! unless confirmable.new_record?
160
162
  confirmable
161
163
  end
@@ -3,16 +3,12 @@ require 'devise/strategies/http_authenticatable'
3
3
  module Devise
4
4
  module Models
5
5
  # Adds HttpAuthenticatable behavior to your model. It expects that your
6
- # model class responds to authenticate method
7
- # (which for example is defined in authenticatable).
6
+ # model class responds to authenticate and authentication_keys methods
7
+ # (which for example are defined in authenticatable).
8
8
  module HttpAuthenticatable
9
- def self.included(base)
10
- base.extend ClassMethods
11
- end
9
+ extend ActiveSupport::Concern
12
10
 
13
11
  module ClassMethods
14
- Devise::Models.config(self, :authentication_keys)
15
-
16
12
  # Authenticate an user using http.
17
13
  def authenticate_with_http(username, password)
18
14
  authenticate(authentication_keys.first => username, :password => password)
@@ -18,62 +18,67 @@ module Devise
18
18
  # available when unlock_strategy is :time or :both.
19
19
  #
20
20
  module Lockable
21
+ extend ActiveSupport::Concern
21
22
  include Devise::Models::Activatable
22
23
 
23
- def self.included(base)
24
- base.class_eval do
25
- extend ClassMethods
26
- end
27
- end
28
-
29
24
  # Lock an user setting it's locked_at to actual time.
30
- def lock_access!
31
- return true if access_locked?
25
+ def lock
32
26
  self.locked_at = Time.now
33
-
34
- if self.class.unlock_strategy_enabled?(:email)
27
+ if unlock_strategy_enabled?(:email)
35
28
  generate_unlock_token
36
29
  send_unlock_instructions
37
30
  end
31
+ end
38
32
 
39
- save(false)
33
+ # Lock an user also saving the record.
34
+ def lock!
35
+ lock
36
+ save(:validate => false)
40
37
  end
41
38
 
42
39
  # Unlock an user by cleaning locket_at and failed_attempts.
43
- def unlock_access!
44
- if_access_locked do
40
+ def unlock!
41
+ if_locked do
45
42
  self.locked_at = nil
46
43
  self.failed_attempts = 0
47
44
  self.unlock_token = nil
48
- save(false)
45
+ save(:validate => false)
49
46
  end
50
47
  end
51
48
 
52
49
  # Verifies whether a user is locked or not.
53
- def access_locked?
50
+ def locked?
54
51
  locked_at && !lock_expired?
55
52
  end
56
53
 
57
54
  # Send unlock instructions by email
58
55
  def send_unlock_instructions
59
- ::DeviseMailer.deliver_unlock_instructions(self)
56
+ ::Devise::Mailer.unlock_instructions(self).deliver
60
57
  end
61
58
 
62
59
  # Resend the unlock instructions if the user is locked.
63
- def resend_unlock_token
64
- if_access_locked { send_unlock_instructions }
60
+ def resend_unlock!
61
+ if_locked do
62
+ generate_unlock_token unless unlock_token.present?
63
+ save(:validate => false)
64
+ send_unlock_instructions
65
+ end
65
66
  end
66
67
 
67
68
  # Overwrites active? from Devise::Models::Activatable for locking purposes
68
69
  # by verifying whether an user is active to sign in or not based on locked?
69
70
  def active?
70
- super && !access_locked?
71
+ super && !locked?
71
72
  end
72
73
 
73
74
  # Overwrites invalid_message from Devise::Models::Authenticatable to define
74
75
  # the correct reason for blocking the sign in.
75
76
  def inactive_message
76
- access_locked? ? :locked : super
77
+ if locked?
78
+ :locked
79
+ else
80
+ super
81
+ end
77
82
  end
78
83
 
79
84
  # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
@@ -84,9 +89,9 @@ module Devise
84
89
  self.failed_attempts = 0
85
90
  else
86
91
  self.failed_attempts += 1
87
- lock_access! if failed_attempts > self.class.maximum_attempts
92
+ lock if failed_attempts > self.class.maximum_attempts
88
93
  end
89
- save(false) if changed?
94
+ save(:validate => false) if changed?
90
95
  result
91
96
  end
92
97
 
@@ -99,7 +104,7 @@ module Devise
99
104
 
100
105
  # Tells if the lock is expired if :time unlock strategy is active
101
106
  def lock_expired?
102
- if self.class.unlock_strategy_enabled?(:time)
107
+ if unlock_strategy_enabled?(:time)
103
108
  locked_at && locked_at < self.class.unlock_in.ago
104
109
  else
105
110
  false
@@ -108,15 +113,20 @@ module Devise
108
113
 
109
114
  # Checks whether the record is locked or not, yielding to the block
110
115
  # if it's locked, otherwise adds an error to email.
111
- def if_access_locked
112
- if access_locked?
116
+ def if_locked
117
+ if locked?
113
118
  yield
114
119
  else
115
- self.class.add_error_on(self, :email, :not_locked)
120
+ self.errors.add(:email, :not_locked)
116
121
  false
117
122
  end
118
123
  end
119
124
 
125
+ # Is the unlock enabled for the given unlock strategy?
126
+ def unlock_strategy_enabled?(strategy)
127
+ [:both, strategy].include?(self.class.unlock_strategy)
128
+ end
129
+
120
130
  module ClassMethods
121
131
  # Attempt to find a user by it's email. If a record is found, send new
122
132
  # unlock instructions to it. If not user is found, returns a new user
@@ -124,7 +134,7 @@ module Devise
124
134
  # Options must contain the user email
125
135
  def send_unlock_instructions(attributes={})
126
136
  lockable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
127
- lockable.resend_unlock_token unless lockable.new_record?
137
+ lockable.resend_unlock! unless lockable.new_record?
128
138
  lockable
129
139
  end
130
140
 
@@ -132,17 +142,12 @@ module Devise
132
142
  # If no user is found, returns a new user with an error.
133
143
  # If the user is not locked, creates an error for the user
134
144
  # Options must have the unlock_token
135
- def unlock_access_by_token(unlock_token)
136
- lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
137
- lockable.unlock_access! unless lockable.new_record?
145
+ def unlock!(attributes={})
146
+ lockable = find_or_initialize_with_error_by(:unlock_token, attributes[:unlock_token])
147
+ lockable.unlock! unless lockable.new_record?
138
148
  lockable
139
149
  end
140
150
 
141
- # Is the unlock enabled for the given unlock strategy?
142
- def unlock_strategy_enabled?(strategy)
143
- [:both, strategy].include?(self.unlock_strategy)
144
- end
145
-
146
151
  Devise::Models.config(self, :maximum_attempts, :unlock_strategy, :unlock_in)
147
152
  end
148
153
  end