devise 1.0.11 → 1.1.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 (176) hide show
  1. data/CHANGELOG.rdoc +59 -13
  2. data/Gemfile +23 -0
  3. data/Gemfile.lock +118 -0
  4. data/README.rdoc +116 -77
  5. data/Rakefile +4 -2
  6. data/TODO +3 -2
  7. data/app/controllers/{confirmations_controller.rb → devise/confirmations_controller.rb} +1 -1
  8. data/app/controllers/{passwords_controller.rb → devise/passwords_controller.rb} +1 -1
  9. data/app/controllers/{registrations_controller.rb → devise/registrations_controller.rb} +12 -8
  10. data/app/controllers/devise/sessions_controller.rb +23 -0
  11. data/app/controllers/{unlocks_controller.rb → devise/unlocks_controller.rb} +1 -8
  12. data/app/helpers/devise_helper.rb +17 -0
  13. data/app/mailers/devise/mailer.rb +71 -0
  14. data/app/views/devise/confirmations/new.html.erb +12 -0
  15. data/app/views/devise/passwords/edit.html.erb +16 -0
  16. data/app/views/devise/passwords/new.html.erb +12 -0
  17. data/app/views/devise/registrations/edit.html.erb +25 -0
  18. data/app/views/devise/registrations/new.html.erb +18 -0
  19. data/app/views/devise/sessions/new.html.erb +17 -0
  20. data/app/views/devise/shared/_links.erb +19 -0
  21. data/app/views/devise/unlocks/new.html.erb +12 -0
  22. data/{lib/devise → config}/locales/en.yml +16 -12
  23. data/lib/devise/controllers/helpers.rb +57 -52
  24. data/lib/devise/controllers/internal_helpers.rb +19 -50
  25. data/lib/devise/controllers/scoped_views.rb +35 -0
  26. data/lib/devise/controllers/url_helpers.rb +1 -1
  27. data/lib/devise/encryptors/authlogic_sha512.rb +0 -2
  28. data/lib/devise/encryptors/base.rb +1 -1
  29. data/lib/devise/encryptors/bcrypt.rb +2 -4
  30. data/lib/devise/encryptors/clearance_sha1.rb +0 -2
  31. data/lib/devise/encryptors/sha1.rb +6 -8
  32. data/lib/devise/encryptors/sha512.rb +6 -8
  33. data/lib/devise/failure_app.rb +76 -41
  34. data/lib/devise/hooks/activatable.rb +6 -10
  35. data/lib/devise/hooks/forgetable.rb +11 -0
  36. data/lib/devise/hooks/rememberable.rb +40 -30
  37. data/lib/devise/hooks/timeoutable.rb +7 -3
  38. data/lib/devise/hooks/trackable.rb +5 -14
  39. data/lib/devise/mapping.rb +35 -62
  40. data/lib/devise/models/authenticatable.rb +126 -0
  41. data/lib/devise/models/confirmable.rb +19 -22
  42. data/lib/devise/models/database_authenticatable.rb +26 -60
  43. data/lib/devise/models/lockable.rb +43 -28
  44. data/lib/devise/models/recoverable.rb +11 -10
  45. data/lib/devise/models/rememberable.rb +56 -26
  46. data/lib/devise/models/timeoutable.rb +1 -3
  47. data/lib/devise/models/token_authenticatable.rb +14 -43
  48. data/lib/devise/models/trackable.rb +14 -0
  49. data/lib/devise/models/validatable.rb +16 -2
  50. data/lib/devise/models.rb +16 -53
  51. data/lib/devise/modules.rb +23 -0
  52. data/lib/devise/orm/active_record.rb +10 -15
  53. data/lib/devise/orm/mongoid.rb +29 -0
  54. data/lib/devise/path_checker.rb +18 -0
  55. data/lib/devise/rails/routes.rb +232 -117
  56. data/lib/devise/rails/warden_compat.rb +17 -41
  57. data/lib/devise/rails.rb +64 -9
  58. data/lib/devise/schema.rb +47 -23
  59. data/lib/devise/strategies/authenticatable.rb +123 -0
  60. data/lib/devise/strategies/base.rb +2 -3
  61. data/lib/devise/strategies/database_authenticatable.rb +7 -22
  62. data/lib/devise/strategies/rememberable.rb +21 -7
  63. data/lib/devise/strategies/token_authenticatable.rb +31 -19
  64. data/lib/devise/test_helpers.rb +6 -6
  65. data/lib/devise/version.rb +1 -1
  66. data/lib/devise.rb +174 -157
  67. data/lib/generators/active_record/devise_generator.rb +28 -0
  68. data/{generators/devise → lib/generators/active_record}/templates/migration.rb +9 -3
  69. data/lib/generators/devise/devise_generator.rb +17 -0
  70. data/lib/generators/devise/install_generator.rb +24 -0
  71. data/lib/generators/devise/orm_helpers.rb +23 -0
  72. data/{generators/devise_install → lib/generators/devise}/templates/README +9 -7
  73. data/lib/generators/devise/templates/devise.rb +142 -0
  74. data/lib/generators/devise/views_generator.rb +63 -0
  75. data/lib/generators/devise_install_generator.rb +4 -0
  76. data/lib/generators/devise_views_generator.rb +4 -0
  77. data/lib/generators/mongoid/devise_generator.rb +17 -0
  78. data/test/controllers/helpers_test.rb +48 -19
  79. data/test/controllers/internal_helpers_test.rb +12 -16
  80. data/test/controllers/url_helpers_test.rb +21 -10
  81. data/test/devise_test.rb +16 -25
  82. data/test/encryptors_test.rb +2 -3
  83. data/test/failure_app_test.rb +106 -27
  84. data/test/integration/authenticatable_test.rb +135 -131
  85. data/test/integration/confirmable_test.rb +22 -15
  86. data/test/integration/database_authenticatable_test.rb +38 -0
  87. data/test/integration/http_authenticatable_test.rb +9 -12
  88. data/test/integration/lockable_test.rb +22 -15
  89. data/test/integration/recoverable_test.rb +6 -6
  90. data/test/integration/registerable_test.rb +42 -33
  91. data/test/integration/rememberable_test.rb +84 -22
  92. data/test/integration/timeoutable_test.rb +16 -4
  93. data/test/integration/token_authenticatable_test.rb +45 -12
  94. data/test/integration/trackable_test.rb +1 -1
  95. data/test/mailers/confirmation_instructions_test.rb +11 -17
  96. data/test/mailers/reset_password_instructions_test.rb +7 -7
  97. data/test/mailers/unlock_instructions_test.rb +7 -7
  98. data/test/mapping_test.rb +22 -95
  99. data/test/models/confirmable_test.rb +12 -19
  100. data/test/models/{authenticatable_test.rb → database_authenticatable_test.rb} +24 -56
  101. data/test/models/lockable_test.rb +32 -46
  102. data/test/models/recoverable_test.rb +7 -7
  103. data/test/models/rememberable_test.rb +118 -35
  104. data/test/models/timeoutable_test.rb +1 -1
  105. data/test/models/token_authenticatable_test.rb +5 -19
  106. data/test/models/trackable_test.rb +1 -1
  107. data/test/models/validatable_test.rb +20 -27
  108. data/test/models_test.rb +12 -5
  109. data/test/orm/active_record.rb +1 -23
  110. data/test/orm/mongoid.rb +10 -0
  111. data/test/rails_app/app/active_record/admin.rb +1 -5
  112. data/test/rails_app/app/active_record/shim.rb +2 -0
  113. data/test/rails_app/app/active_record/user.rb +3 -3
  114. data/test/rails_app/app/controllers/application_controller.rb +2 -5
  115. data/test/rails_app/app/controllers/home_controller.rb +3 -0
  116. data/test/rails_app/app/controllers/publisher/registrations_controller.rb +2 -0
  117. data/test/rails_app/app/controllers/publisher/sessions_controller.rb +2 -0
  118. data/test/rails_app/app/controllers/sessions_controller.rb +6 -0
  119. data/test/rails_app/app/controllers/users_controller.rb +7 -5
  120. data/test/rails_app/app/mongoid/admin.rb +6 -0
  121. data/test/rails_app/app/mongoid/shim.rb +16 -0
  122. data/test/rails_app/app/mongoid/user.rb +10 -0
  123. data/test/rails_app/config/application.rb +35 -0
  124. data/test/rails_app/config/boot.rb +10 -107
  125. data/test/rails_app/config/environment.rb +4 -41
  126. data/test/rails_app/config/environments/development.rb +15 -13
  127. data/test/rails_app/config/environments/production.rb +25 -20
  128. data/test/rails_app/config/environments/test.rb +33 -28
  129. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  130. data/test/rails_app/config/initializers/devise.rb +95 -38
  131. data/test/rails_app/config/initializers/secret_token.rb +2 -0
  132. data/test/rails_app/config/routes.rb +47 -25
  133. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +27 -0
  134. data/test/rails_app/db/schema.rb +86 -0
  135. data/test/routes_test.rb +62 -47
  136. data/test/support/{assertions_helper.rb → assertions.rb} +2 -15
  137. data/test/support/{tests_helper.rb → helpers.rb} +18 -3
  138. data/test/support/{integration_tests_helper.rb → integration.rb} +24 -7
  139. data/test/support/webrat/integrations/rails.rb +32 -0
  140. data/test/test_helper.rb +11 -11
  141. data/test/test_helpers_test.rb +30 -15
  142. metadata +107 -66
  143. data/app/controllers/sessions_controller.rb +0 -42
  144. data/app/models/devise_mailer.rb +0 -68
  145. data/app/views/confirmations/new.html.erb +0 -12
  146. data/app/views/passwords/edit.html.erb +0 -16
  147. data/app/views/passwords/new.html.erb +0 -12
  148. data/app/views/registrations/edit.html.erb +0 -25
  149. data/app/views/registrations/new.html.erb +0 -17
  150. data/app/views/sessions/new.html.erb +0 -17
  151. data/app/views/shared/_devise_links.erb +0 -19
  152. data/app/views/unlocks/new.html.erb +0 -12
  153. data/generators/devise/USAGE +0 -5
  154. data/generators/devise/devise_generator.rb +0 -15
  155. data/generators/devise/lib/route_devise.rb +0 -32
  156. data/generators/devise/templates/model.rb +0 -9
  157. data/generators/devise_install/USAGE +0 -3
  158. data/generators/devise_install/devise_install_generator.rb +0 -15
  159. data/generators/devise_install/templates/devise.rb +0 -105
  160. data/generators/devise_views/USAGE +0 -3
  161. data/generators/devise_views/devise_views_generator.rb +0 -21
  162. data/lib/devise/models/activatable.rb +0 -16
  163. data/lib/devise/models/http_authenticatable.rb +0 -23
  164. data/lib/devise/orm/data_mapper.rb +0 -83
  165. data/lib/devise/orm/mongo_mapper.rb +0 -52
  166. data/lib/devise/strategies/http_authenticatable.rb +0 -59
  167. data/rails/init.rb +0 -2
  168. data/test/integration/rack_middleware_test.rb +0 -47
  169. data/test/orm/mongo_mapper.rb +0 -20
  170. data/test/rails_app/app/mongo_mapper/admin.rb +0 -13
  171. data/test/rails_app/app/mongo_mapper/user.rb +0 -14
  172. data/test/rails_app/config/initializers/new_rails_defaults.rb +0 -24
  173. data/test/rails_app/config/initializers/session_store.rb +0 -15
  174. /data/app/views/{devise_mailer → devise/mailer}/confirmation_instructions.html.erb +0 -0
  175. /data/app/views/{devise_mailer → devise/mailer}/reset_password_instructions.html.erb +0 -0
  176. /data/app/views/{devise_mailer → devise/mailer}/unlock_instructions.html.erb +0 -0
@@ -18,21 +18,12 @@ module Devise
18
18
  # mapping.to #=> User
19
19
  # # is the class to be loaded from routes, given in the route as :class_name.
20
20
  #
21
- # mapping.for #=> [:authenticatable]
21
+ # mapping.modules #=> [:authenticatable]
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
26
-
27
- # Loop through all mappings looking for a map that matches with the requested
28
- # path (ie /users/sign_in). If a path prefix is given, it's taken into account.
29
- def self.find_by_path(path)
30
- Devise.mappings.each_value do |mapping|
31
- route = path.split("/")[mapping.as_position]
32
- return mapping if route && mapping.as == route.to_sym
33
- end
34
- nil
35
- end
25
+ attr_reader :singular, :plural, :path, :controllers, :path_names, :class_name
26
+ alias :name :singular
36
27
 
37
28
  # Receives an object and find a scope for it. If a scope cannot be found,
38
29
  # raises an error. If a symbol is given, it's considered to be the scope.
@@ -49,82 +40,64 @@ module Devise
49
40
  raise "Could not find a valid mapping for #{duck}"
50
41
  end
51
42
 
52
- # Default url options which can be used as prefix.
53
- def self.default_url_options
54
- {}
55
- end
56
-
57
43
  def initialize(name, options) #:nodoc:
58
- @as = (options.delete(:as) || name).to_sym
59
- @klass = (options.delete(:class_name) || name.to_s.classify).to_s
60
- @name = (options.delete(:scope) || name.to_s.singularize).to_sym
44
+ @plural = (options[:as] ? "#{options[:as]}_#{name}" : name).to_sym
45
+ @singular = (options[:singular] || @plural.to_s.singularize).to_sym
61
46
 
62
- @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
63
- @route_options = options || {}
47
+ @class_name = (options[:class_name] || name.to_s.classify).to_s
48
+ @ref = ActiveSupport::Dependencies.ref(@class_name)
64
49
 
65
- @path_names = Hash.new { |h,k| h[k] = k.to_s }
66
- @path_names.merge!(options.delete(:path_names) || {})
50
+ @path = (options[:path] || name).to_s
51
+ @path_prefix = options[:path_prefix]
67
52
 
68
- @sign_out_via = (options.delete(:sign_out_via) || :get)
53
+ mod = options[:module] || "devise"
54
+ @controllers = Hash.new { |h,k| h[k] = "#{mod}/#{k}" }
55
+ @controllers.merge!(options[:controllers] || {})
56
+
57
+ @path_names = Hash.new { |h,k| h[k] = k.to_s }
58
+ @path_names.merge!(:registration => "")
59
+ @path_names.merge!(options[:path_names] || {})
69
60
  end
70
61
 
71
62
  # Return modules for the mapping.
72
- def for
73
- @for ||= to.devise_modules
63
+ def modules
64
+ @modules ||= to.respond_to?(:devise_modules) ? to.devise_modules : []
74
65
  end
75
66
 
76
- # Reload mapped class each time when cache_classes is false.
67
+ # Gives the class the mapping points to.
77
68
  def to
78
- return @to if @to
79
- klass = @klass.constantize
80
- @to = klass if Rails.configuration.cache_classes
81
- klass
69
+ @ref.get
82
70
  end
83
71
 
84
- # Check if the respective controller has a module in the mapping class.
85
- def allows?(controller)
86
- (self.for & CONTROLLERS[controller.to_sym]).present?
72
+ def strategies
73
+ @strategies ||= STRATEGIES.values_at(*self.modules).compact.uniq.reverse
87
74
  end
88
75
 
89
- # Return in which position in the path prefix devise should find the as mapping.
90
- def as_position
91
- self.path_prefix.count("/")
76
+ def routes
77
+ @routes ||= ROUTES.values_at(*self.modules).compact.uniq
92
78
  end
93
79
 
94
- # Returns the raw path using path_prefix and as.
95
- def raw_path
96
- path_prefix + as.to_s
97
- end
98
-
99
- # Returns the parsed path taking into account the relative url root and raw path.
100
- def parsed_path
101
- (ActionController::Base.relative_url_root.to_s + raw_path).tap do |path|
102
- self.class.default_url_options.each do |key, value|
103
- path.gsub!(key.inspect, value.to_param)
104
- end
105
- end
80
+ def authenticatable?
81
+ @authenticatable ||= self.modules.any? { |m| m.to_s =~ /authenticatable/ }
106
82
  end
107
83
 
108
- def authenticatable?
109
- @authenticatable ||= self.for.any? { |m| m.to_s =~ /authenticatable/ }
84
+ def fullpath
85
+ "#{@path_prefix}/#{@path}".squeeze("/")
110
86
  end
111
87
 
112
88
  # Create magic predicates for verifying what module is activated by this map.
113
89
  # Example:
114
90
  #
115
91
  # def confirmable?
116
- # self.for.include?(:confirmable)
92
+ # self.modules.include?(:confirmable)
117
93
  # end
118
94
  #
119
- def self.register(*modules)
120
- modules.each do |m|
121
- class_eval <<-METHOD, __FILE__, __LINE__ + 1
122
- def #{m}?
123
- self.for.include?(:#{m})
124
- end
125
- METHOD
126
- end
95
+ def self.add_module(m)
96
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
97
+ def #{m}?
98
+ self.modules.include?(:#{m})
99
+ end
100
+ METHOD
127
101
  end
128
- Devise::Mapping.register *ALL
129
102
  end
130
103
  end
@@ -0,0 +1,126 @@
1
+ require 'devise/hooks/activatable'
2
+
3
+ module Devise
4
+ module Models
5
+ # Authenticable module. Holds common settings for authentication.
6
+ #
7
+ # == Configuration:
8
+ #
9
+ # You can overwrite configuration values by setting in globally in Devise,
10
+ # using devise method or overwriting the respective instance method.
11
+ #
12
+ # authentication_keys: parameters used for authentication. By default [:email].
13
+ #
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.
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.
19
+ #
20
+ # == Active?
21
+ #
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.
25
+ #
26
+ # You overwrite this method yourself, but if you do, don't forget to call super:
27
+ #
28
+ # def active?
29
+ # super && special_condition_is_valid?
30
+ # end
31
+ #
32
+ # Whenever active? returns false, Devise asks the reason why your model is inactive using
33
+ # the inactive_message method. You can overwrite it as well:
34
+ #
35
+ # def inactive_message
36
+ # special_condition_is_valid? ? super : :special_condition_is_not_valid
37
+ # end
38
+ #
39
+ module Authenticatable
40
+ extend ActiveSupport::Concern
41
+
42
+ included do
43
+ class_attribute :devise_modules, :instance_writer => false
44
+ self.devise_modules ||= []
45
+ end
46
+
47
+ # Check if the current object is valid for authentication. This method and
48
+ # find_for_authentication are the methods used in a Warden::Strategy to check
49
+ # if a model should be signed in or not.
50
+ #
51
+ # However, you should not overwrite this method, you should overwrite active? and
52
+ # inactive_message instead.
53
+ def valid_for_authentication?
54
+ if active?
55
+ block_given? ? yield : true
56
+ else
57
+ inactive_message
58
+ end
59
+ end
60
+
61
+ def active?
62
+ true
63
+ end
64
+
65
+ def inactive_message
66
+ :inactive
67
+ end
68
+
69
+ module ClassMethods
70
+ Devise::Models.config(self, :authentication_keys, :http_authenticatable, :params_authenticatable)
71
+
72
+ def params_authenticatable?(strategy)
73
+ params_authenticatable.is_a?(Array) ?
74
+ params_authenticatable.include?(strategy) : params_authenticatable
75
+ end
76
+
77
+ def http_authenticatable?(strategy)
78
+ http_authenticatable.is_a?(Array) ?
79
+ http_authenticatable.include?(strategy) : http_authenticatable
80
+ end
81
+
82
+ # Find first record based on conditions given (ie by the sign in form).
83
+ # Overwrite to add customized conditions, create a join, or maybe use a
84
+ # namedscope to filter records while authenticating.
85
+ # Example:
86
+ #
87
+ # def self.find_for_authentication(conditions={})
88
+ # conditions[:active] = true
89
+ # super
90
+ # end
91
+ #
92
+ def find_for_authentication(conditions)
93
+ find(:first, :conditions => conditions)
94
+ end
95
+
96
+ # Find an initialize a record setting an error if it can't be found.
97
+ 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
102
+
103
+ unless record
104
+ record = new
105
+ if value.present?
106
+ record.send(:"#{attribute}=", value)
107
+ else
108
+ error = :blank
109
+ end
110
+ record.errors.add(attribute, error)
111
+ end
112
+
113
+ record
114
+ end
115
+
116
+ # Generate a token by looping and ensuring does not already exist.
117
+ def generate_token(column)
118
+ loop do
119
+ token = Devise.friendly_token
120
+ break token unless find(:first, :conditions => { column => token })
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -1,8 +1,5 @@
1
- require 'devise/models/activatable'
2
-
3
1
  module Devise
4
2
  module Models
5
-
6
3
  # Confirmable is responsible to verify if an account is already confirmed to
7
4
  # sign in, and to send emails with confirmation instructions.
8
5
  # Confirmation instructions are sent to the user email after creating a
@@ -29,15 +26,11 @@ module Devise
29
26
  # User.find(1).send_confirmation_instructions # manually send instructions
30
27
  # User.find(1).resend_confirmation! # generates a new token and resent it
31
28
  module Confirmable
32
- include Devise::Models::Activatable
33
-
34
- def self.included(base)
35
- base.class_eval do
36
- extend ClassMethods
29
+ extend ActiveSupport::Concern
37
30
 
38
- before_create :generate_confirmation_token, :if => :confirmation_required?
39
- after_create :send_confirmation_instructions, :if => :confirmation_required?
40
- end
31
+ included do
32
+ before_create :generate_confirmation_token, :if => :confirmation_required?
33
+ after_create :send_confirmation_instructions, :if => :confirmation_required?
41
34
  end
42
35
 
43
36
  # Confirm a user by setting it's confirmed_at to actual time. If the user
@@ -46,19 +39,19 @@ module Devise
46
39
  unless_confirmed do
47
40
  self.confirmation_token = nil
48
41
  self.confirmed_at = Time.now
49
- save(false)
42
+ save(:validate => false)
50
43
  end
51
44
  end
52
45
 
53
46
  # Verifies whether a user is confirmed or not
54
47
  def confirmed?
55
- !new_record? && !confirmed_at.nil?
48
+ !!confirmed_at
56
49
  end
57
50
 
58
51
  # Send confirmation instructions by email
59
52
  def send_confirmation_instructions
60
53
  generate_confirmation_token! if self.confirmation_token.nil?
61
- ::DeviseMailer.deliver_confirmation_instructions(self)
54
+ ::Devise.mailer.confirmation_instructions(self).deliver
62
55
  end
63
56
 
64
57
  # Resend confirmation token. This method does not need to generate a new token.
@@ -82,15 +75,14 @@ module Devise
82
75
  # If you don't want confirmation to be sent on create, neither a code
83
76
  # to be generated, call skip_confirmation!
84
77
  def skip_confirmation!
85
- self.confirmed_at = Time.now
86
- @skip_confirmation = true
78
+ self.confirmed_at = Time.now
87
79
  end
88
80
 
89
81
  protected
90
82
 
91
83
  # Callback to overwrite if confirmation is required or not.
92
84
  def confirmation_required?
93
- !@skip_confirmation
85
+ !confirmed?
94
86
  end
95
87
 
96
88
  # Checks if the confirmation for the user is within the limit time.
@@ -122,7 +114,7 @@ module Devise
122
114
  unless confirmed?
123
115
  yield
124
116
  else
125
- self.class.add_error_on(self, :email, :already_confirmed)
117
+ self.errors.add(:email, :already_confirmed)
126
118
  false
127
119
  end
128
120
  end
@@ -131,12 +123,12 @@ module Devise
131
123
  # this token is being generated
132
124
  def generate_confirmation_token
133
125
  self.confirmed_at = nil
134
- self.confirmation_token = Devise.friendly_token
126
+ self.confirmation_token = self.class.confirmation_token
135
127
  self.confirmation_sent_at = Time.now.utc
136
128
  end
137
129
 
138
130
  def generate_confirmation_token!
139
- generate_confirmation_token && save(false)
131
+ generate_confirmation_token && save(:validate => false)
140
132
  end
141
133
 
142
134
  module ClassMethods
@@ -146,7 +138,7 @@ module Devise
146
138
  # Options must contain the user email
147
139
  def send_confirmation_instructions(attributes={})
148
140
  confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
149
- confirmable.resend_confirmation_token unless confirmable.new_record?
141
+ confirmable.resend_confirmation_token if confirmable.persisted?
150
142
  confirmable
151
143
  end
152
144
 
@@ -156,10 +148,15 @@ module Devise
156
148
  # Options must have the confirmation_token
157
149
  def confirm_by_token(confirmation_token)
158
150
  confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
159
- confirmable.confirm! unless confirmable.new_record?
151
+ confirmable.confirm! if confirmable.persisted?
160
152
  confirmable
161
153
  end
162
154
 
155
+ # Generate a token checking if one does not already exist in the database.
156
+ def confirmation_token
157
+ generate_token(:confirmation_token)
158
+ end
159
+
163
160
  Devise::Models.config(self, :confirm_within)
164
161
  end
165
162
  end
@@ -19,27 +19,16 @@ module Devise
19
19
  #
20
20
  # encryptor: the encryptor going to be used. By default :sha1.
21
21
  #
22
- # authentication_keys: parameters used for authentication. By default [:email]
23
- #
24
22
  # Examples:
25
23
  #
26
- # User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
27
24
  # User.find(1).valid_password?('password123') # returns true/false
28
25
  #
29
26
  module DatabaseAuthenticatable
30
- def self.included(base)
31
- base.class_eval do
32
- extend ClassMethods
33
-
34
- attr_reader :password, :current_password
35
- attr_accessor :password_confirmation
36
- end
37
- end
27
+ extend ActiveSupport::Concern
38
28
 
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
29
+ included do
30
+ attr_reader :password, :current_password
31
+ attr_accessor :password_confirmation
43
32
  end
44
33
 
45
34
  # Regenerates password salt and encrypted password each time password is set,
@@ -48,19 +37,14 @@ module Devise
48
37
  @password = new_password
49
38
 
50
39
  if @password.present?
51
- self.password_salt = self.class.encryptor_class.salt
40
+ self.password_salt = self.class.password_salt
52
41
  self.encrypted_password = password_digest(@password)
53
42
  end
54
43
  end
55
44
 
56
45
  # Verifies whether an incoming_password (ie from sign in) is the user password.
57
46
  def valid_password?(incoming_password)
58
- Devise.secure_compare(password_digest(incoming_password), self.encrypted_password)
59
- end
60
-
61
- # Checks if a resource is valid upon authentication.
62
- def valid_for_authentication?(attributes)
63
- valid_password?(attributes[:password])
47
+ password_digest(incoming_password) == self.encrypted_password
64
48
  end
65
49
 
66
50
  # Set password and password confirmation to nil
@@ -82,61 +66,43 @@ module Devise
82
66
  result = if valid_password?(current_password)
83
67
  update_attributes(params)
84
68
  else
85
- message = current_password.blank? ? :blank : :invalid
86
- self.class.add_error_on(self, :current_password, message, false)
69
+ self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
87
70
  self.attributes = params
88
71
  false
89
72
  end
90
73
 
91
- clean_up_passwords unless result
74
+ clean_up_passwords
92
75
  result
93
76
  end
94
77
 
95
- protected
78
+ def after_database_authentication
79
+ end
96
80
 
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
81
+ protected
103
82
 
104
- # Digests the password using the configured encryptor.
105
- def password_digest(password)
106
- self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
107
- end
83
+ # Digests the password using the configured encryptor.
84
+ def password_digest(password)
85
+ self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
86
+ end
108
87
 
109
88
  module ClassMethods
110
- Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
111
-
112
- # Authenticate a user based on configured attribute keys. Returns the
113
- # authenticated user if it's valid or nil.
114
- def authenticate(attributes={})
115
- return unless authentication_keys.all? { |k| attributes[k].present? }
116
- conditions = attributes.slice(*authentication_keys)
117
- resource = find_for_authentication(conditions)
118
- resource if resource.try(:valid_for_authentication?, attributes)
119
- end
89
+ Devise::Models.config(self, :pepper, :stretches, :encryptor)
120
90
 
121
91
  # Returns the class for the configured encryptor.
122
92
  def encryptor_class
123
93
  @encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
124
94
  end
125
95
 
126
- protected
127
-
128
- # Find first record based on conditions given (ie by the sign in form).
129
- # Overwrite to add customized conditions, create a join, or maybe use a
130
- # namedscope to filter records while authenticating.
131
- # Example:
132
- #
133
- # def self.find_for_authentication(conditions={})
134
- # conditions[:active] = true
135
- # find(:first, :conditions => conditions)
136
- # end
137
- #
138
- def find_for_authentication(conditions)
139
- find(:first, :conditions => conditions)
96
+ def password_salt
97
+ self.encryptor_class.salt(self.stretches)
98
+ end
99
+
100
+ # We assume this method already gets the sanitized values from the
101
+ # DatabaseAuthenticatable strategy. If you are using this method on
102
+ # your own, be sure to sanitize the conditions hash to only include
103
+ # the proper fields.
104
+ def find_for_database_authentication(conditions)
105
+ find_for_authentication(conditions)
140
106
  end
141
107
  end
142
108
  end
@@ -1,8 +1,5 @@
1
- require 'devise/models/activatable'
2
-
3
1
  module Devise
4
2
  module Models
5
-
6
3
  # Handles blocking a user access after a certain number of attempts.
7
4
  # Lockable accepts two different strategies to unlock a user after it's
8
5
  # blocked: email and time. The former will send an email to the user when
@@ -13,39 +10,35 @@ module Devise
13
10
  # Configuration:
14
11
  #
15
12
  # maximum_attempts: how many attempts should be accepted before blocking the user.
16
- # unlock_strategy: unlock the user account by :time, :email or :both.
13
+ # lock_strategy: lock the user account by :failed_attempts or :none.
14
+ # unlock_strategy: unlock the user account by :time, :email, :both or :none.
17
15
  # unlock_in: the time you want to lock the user after to lock happens. Only
18
16
  # available when unlock_strategy is :time or :both.
19
17
  #
20
18
  module Lockable
21
- include Devise::Models::Activatable
19
+ extend ActiveSupport::Concern
22
20
 
23
- def self.included(base)
24
- base.class_eval do
25
- extend ClassMethods
26
- end
27
- end
21
+ delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
28
22
 
29
23
  # Lock an user setting it's locked_at to actual time.
30
24
  def lock_access!
31
- return true if access_locked?
32
25
  self.locked_at = Time.now
33
26
 
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
38
31
 
39
- save(false)
32
+ save(:validate => false)
40
33
  end
41
34
 
42
35
  # Unlock an user by cleaning locket_at and failed_attempts.
43
36
  def unlock_access!
44
37
  if_access_locked do
45
38
  self.locked_at = nil
46
- self.failed_attempts = 0
47
- self.unlock_token = nil
48
- save(false)
39
+ self.failed_attempts = 0 if respond_to?(:failed_attempts=)
40
+ self.unlock_token = nil if respond_to?(:unlock_token=)
41
+ save(:validate => false)
49
42
  end
50
43
  end
51
44
 
@@ -56,7 +49,7 @@ module Devise
56
49
 
57
50
  # Send unlock instructions by email
58
51
  def send_unlock_instructions
59
- ::DeviseMailer.deliver_unlock_instructions(self)
52
+ ::Devise.mailer.unlock_instructions(self).deliver
60
53
  end
61
54
 
62
55
  # Resend the unlock instructions if the user is locked.
@@ -79,27 +72,40 @@ module Devise
79
72
  # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
80
73
  # for verifying whether an user is allowed to sign in or not. If the user
81
74
  # is locked, it should never be allowed.
82
- def valid_for_authentication?(attributes)
83
- if result = super
75
+ def valid_for_authentication?
76
+ return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
77
+
78
+ case (result = super)
79
+ when Symbol
80
+ return result
81
+ when TrueClass
84
82
  self.failed_attempts = 0
85
- else
83
+ when FalseClass
86
84
  self.failed_attempts += 1
87
- lock_access! if failed_attempts > self.class.maximum_attempts
85
+ if attempts_exceeded?
86
+ lock_access!
87
+ return :locked
88
+ end
88
89
  end
89
- save(false) if changed?
90
+
91
+ save(:validate => false) if changed?
90
92
  result
91
93
  end
92
94
 
93
95
  protected
94
96
 
97
+ def attempts_exceeded?
98
+ self.failed_attempts > self.class.maximum_attempts
99
+ end
100
+
95
101
  # Generates unlock token
96
102
  def generate_unlock_token
97
- self.unlock_token = Devise.friendly_token
103
+ self.unlock_token = self.class.unlock_token
98
104
  end
99
105
 
100
106
  # Tells if the lock is expired if :time unlock strategy is active
101
107
  def lock_expired?
102
- if self.class.unlock_strategy_enabled?(:time)
108
+ if unlock_strategy_enabled?(:time)
103
109
  locked_at && locked_at < self.class.unlock_in.ago
104
110
  else
105
111
  false
@@ -112,7 +118,7 @@ module Devise
112
118
  if access_locked?
113
119
  yield
114
120
  else
115
- self.class.add_error_on(self, :email, :not_locked)
121
+ self.errors.add(:email, :not_locked)
116
122
  false
117
123
  end
118
124
  end
@@ -124,7 +130,7 @@ module Devise
124
130
  # Options must contain the user email
125
131
  def send_unlock_instructions(attributes={})
126
132
  lockable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
127
- lockable.resend_unlock_token unless lockable.new_record?
133
+ lockable.resend_unlock_token if lockable.persisted?
128
134
  lockable
129
135
  end
130
136
 
@@ -134,7 +140,7 @@ module Devise
134
140
  # Options must have the unlock_token
135
141
  def unlock_access_by_token(unlock_token)
136
142
  lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
137
- lockable.unlock_access! unless lockable.new_record?
143
+ lockable.unlock_access! if lockable.persisted?
138
144
  lockable
139
145
  end
140
146
 
@@ -143,7 +149,16 @@ module Devise
143
149
  [:both, strategy].include?(self.unlock_strategy)
144
150
  end
145
151
 
146
- Devise::Models.config(self, :maximum_attempts, :unlock_strategy, :unlock_in)
152
+ # Is the lock enabled for the given lock strategy?
153
+ def lock_strategy_enabled?(strategy)
154
+ self.lock_strategy == strategy
155
+ end
156
+
157
+ def unlock_token
158
+ Devise.friendly_token
159
+ end
160
+
161
+ Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in)
147
162
  end
148
163
  end
149
164
  end