devise 1.1.pre4 → 1.1.rc0

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 (122) hide show
  1. data/CHANGELOG.rdoc +31 -2
  2. data/Gemfile +15 -6
  3. data/README.rdoc +12 -16
  4. data/Rakefile +2 -2
  5. data/TODO +2 -1
  6. data/app/controllers/devise/confirmations_controller.rb +1 -1
  7. data/app/controllers/devise/passwords_controller.rb +2 -3
  8. data/app/controllers/devise/registrations_controller.rb +5 -5
  9. data/app/controllers/devise/sessions_controller.rb +5 -27
  10. data/app/controllers/devise/unlocks_controller.rb +9 -1
  11. data/app/models/devise/mailer.rb +17 -11
  12. data/app/views/devise/confirmations/new.html.erb +1 -1
  13. data/app/views/devise/passwords/edit.html.erb +1 -1
  14. data/app/views/devise/passwords/new.html.erb +1 -1
  15. data/app/views/devise/registrations/edit.html.erb +2 -2
  16. data/app/views/devise/registrations/new.html.erb +2 -2
  17. data/app/views/devise/sessions/new.html.erb +2 -2
  18. data/app/views/devise/shared/_links.erb +5 -5
  19. data/app/views/devise/unlocks/new.html.erb +1 -1
  20. data/config/locales/en.yml +4 -9
  21. data/lib/devise.rb +83 -42
  22. data/lib/devise/controllers/helpers.rb +6 -18
  23. data/lib/devise/controllers/internal_helpers.rb +11 -12
  24. data/lib/devise/controllers/scoped_views.rb +2 -2
  25. data/lib/devise/controllers/url_helpers.rb +1 -1
  26. data/lib/devise/failure_app.rb +56 -16
  27. data/lib/devise/hooks/activatable.rb +18 -6
  28. data/lib/devise/hooks/rememberable.rb +36 -27
  29. data/lib/devise/hooks/timeoutable.rb +1 -1
  30. data/lib/devise/hooks/trackable.rb +4 -2
  31. data/lib/devise/mapping.rb +19 -14
  32. data/lib/devise/models.rb +12 -3
  33. data/lib/devise/models/authenticatable.rb +19 -95
  34. data/lib/devise/models/confirmable.rb +14 -20
  35. data/lib/devise/models/database_authenticatable.rb +99 -0
  36. data/lib/devise/models/lockable.rb +53 -39
  37. data/lib/devise/models/recoverable.rb +3 -3
  38. data/lib/devise/models/rememberable.rb +5 -10
  39. data/lib/devise/models/token_authenticatable.rb +18 -25
  40. data/lib/devise/models/validatable.rb +14 -9
  41. data/lib/devise/modules.rb +7 -8
  42. data/lib/devise/orm/active_record.rb +1 -1
  43. data/lib/devise/orm/data_mapper.rb +20 -7
  44. data/lib/devise/orm/mongoid.rb +40 -0
  45. data/lib/devise/rails.rb +26 -3
  46. data/lib/devise/rails/routes.rb +18 -16
  47. data/lib/devise/rails/warden_compat.rb +2 -2
  48. data/lib/devise/schema.rb +45 -18
  49. data/lib/devise/strategies/authenticatable.rb +92 -21
  50. data/lib/devise/strategies/base.rb +6 -3
  51. data/lib/devise/strategies/database_authenticatable.rb +20 -0
  52. data/lib/devise/strategies/rememberable.rb +10 -6
  53. data/lib/devise/strategies/token_authenticatable.rb +28 -19
  54. data/lib/devise/test_helpers.rb +5 -1
  55. data/lib/devise/version.rb +1 -1
  56. data/lib/generators/devise/devise_generator.rb +15 -5
  57. data/lib/generators/devise/templates/migration.rb +2 -2
  58. data/lib/generators/devise_install/templates/devise.rb +37 -16
  59. data/lib/generators/devise_views/devise_views_generator.rb +51 -4
  60. data/test/controllers/helpers_test.rb +16 -8
  61. data/test/controllers/internal_helpers_test.rb +6 -1
  62. data/test/controllers/url_helpers_test.rb +10 -10
  63. data/test/devise_test.rb +13 -17
  64. data/test/encryptors_test.rb +2 -0
  65. data/test/failure_app_test.rb +72 -23
  66. data/test/integration/confirmable_test.rb +4 -4
  67. data/test/integration/{authenticatable_test.rb → database_authenticatable_test.rb} +35 -17
  68. data/test/integration/http_authenticatable_test.rb +3 -3
  69. data/test/integration/lockable_test.rb +28 -8
  70. data/test/integration/recoverable_test.rb +3 -3
  71. data/test/integration/registerable_test.rb +6 -4
  72. data/test/integration/rememberable_test.rb +11 -4
  73. data/test/integration/timeoutable_test.rb +4 -4
  74. data/test/integration/token_authenticatable_test.rb +46 -10
  75. data/test/integration/trackable_test.rb +2 -2
  76. data/test/mailers/confirmation_instructions_test.rb +5 -5
  77. data/test/mailers/reset_password_instructions_test.rb +5 -5
  78. data/test/mailers/unlock_instructions_test.rb +5 -5
  79. data/test/mapping_test.rb +15 -14
  80. data/test/models/confirmable_test.rb +9 -32
  81. data/test/models/{authenticatable_test.rb → database_authenticatable_test.rb} +2 -34
  82. data/test/models/lockable_test.rb +48 -66
  83. data/test/models/recoverable_test.rb +8 -8
  84. data/test/models/rememberable_test.rb +6 -28
  85. data/test/models/timeoutable_test.rb +1 -1
  86. data/test/models/token_authenticatable_test.rb +1 -8
  87. data/test/models/trackable_test.rb +1 -1
  88. data/test/models/validatable_test.rb +2 -2
  89. data/test/models_test.rb +16 -2
  90. data/test/orm/active_record.rb +1 -22
  91. data/test/orm/data_mapper.rb +1 -0
  92. data/test/orm/mongoid.rb +10 -0
  93. data/test/rails_app/app/active_record/admin.rb +1 -5
  94. data/test/rails_app/app/controllers/application_controller.rb +2 -0
  95. data/test/rails_app/app/controllers/sessions_controller.rb +1 -1
  96. data/test/rails_app/app/data_mapper/admin.rb +13 -0
  97. data/test/rails_app/app/data_mapper/user.rb +24 -0
  98. data/test/rails_app/app/mongoid/admin.rb +15 -0
  99. data/test/rails_app/app/mongoid/user.rb +21 -0
  100. data/test/rails_app/config/application.rb +10 -5
  101. data/test/rails_app/config/boot.rb +5 -1
  102. data/test/rails_app/config/initializers/devise.rb +1 -1
  103. data/test/rails_app/config/routes.rb +4 -1
  104. data/test/rails_app/db/migrate/20100401102949_create_tables.rb +27 -0
  105. data/test/rails_app/db/schema.rb +86 -0
  106. data/test/routes_test.rb +3 -3
  107. data/test/support/assertions.rb +2 -0
  108. data/test/support/helpers.rb +2 -0
  109. data/test/support/integration.rb +4 -7
  110. data/test/support/webrat/integrations/rails.rb +2 -1
  111. data/test/test_helper.rb +5 -2
  112. data/test/test_helpers_test.rb +4 -4
  113. metadata +36 -21
  114. data/lib/devise/models/http_authenticatable.rb +0 -19
  115. data/lib/devise/orm/mongo_mapper.rb +0 -49
  116. data/lib/devise/strategies/http_authenticatable.rb +0 -47
  117. data/test/models/http_authenticatable_test.rb +0 -19
  118. data/test/orm/mongo_mapper.rb +0 -12
  119. data/test/rails_app/app/mongo_mapper/admin.rb +0 -10
  120. data/test/rails_app/app/mongo_mapper/user.rb +0 -11
  121. data/test/rails_app/config/initializers/cookie_verification_secret.rb +0 -7
  122. data/test/rails_app/config/initializers/session_store.rb +0 -15
@@ -3,13 +3,25 @@ Warden::Manager.after_set_user do |record, warden, options|
3
3
  if record && record.respond_to?(:active?) && !record.active?
4
4
  scope = options[:scope]
5
5
  warden.logout(scope)
6
+ throw :warden, :scope => scope, :message => record.inactive_message
7
+ end
8
+ end
6
9
 
7
- # If winning strategy was set, this is being called after authenticate and
8
- # there is no need to force a redirect.
9
- if warden.winning_strategy
10
- warden.winning_strategy.fail!(record.inactive_message)
11
- else
12
- throw :warden, :scope => scope, :message => record.inactive_message
10
+ module Devise
11
+ module Hooks
12
+ # Overwrite Devise base strategy to only authenticate an user if it's active.
13
+ # If you have an strategy that does not use Devise::Strategy::Base, don't worry
14
+ # because the hook above will still avoid it to authenticate.
15
+ module Activatable
16
+ def success!(resource)
17
+ if resource.respond_to?(:active?) && !resource.active?
18
+ fail!(resource.inactive_message)
19
+ else
20
+ super
21
+ end
22
+ end
13
23
  end
14
24
  end
15
25
  end
26
+
27
+ Devise::Strategies::Base.send :include, Devise::Hooks::Activatable
@@ -1,32 +1,41 @@
1
- # After authenticate hook to verify if the user in the given scope asked to be
2
- # remembered while he does not sign out. Generates a new remember token for
3
- # that specific user and adds a cookie with this user info to sign in this user
4
- # automatically without asking for credentials. Refer to rememberable strategy
5
- # for more info.
6
- Warden::Manager.prepend_after_authentication do |record, warden, options|
7
- scope = options[:scope]
8
- remember_me = warden.params[scope].try(:fetch, :remember_me, nil)
1
+ # Before logout hook to forget the user in the given scope, if it responds
2
+ # to forget_me! Also clear remember token to ensure the user won't be
3
+ # remembered again. Notice that we forget the user unless the record is frozen.
4
+ # This avoids forgetting deleted users.
5
+ Warden::Manager.before_logout do |record, warden, scope|
6
+ if record.respond_to?(:forget_me!)
7
+ record.forget_me! unless record.frozen?
8
+ warden.cookies.delete "remember_#{scope}_token"
9
+ end
10
+ end
11
+
12
+ module Devise
13
+ module Hooks
14
+ # Overwrite success! in authentication strategies allowing users to be remembered.
15
+ # We choose to implement this as an strategy hook instead of a Devise hook to avoid users
16
+ # giving a remember_me access in strategies that should not create remember me tokens.
17
+ module Rememberable #:nodoc:
18
+ def success!(resource)
19
+ super
9
20
 
10
- if Devise::TRUE_VALUES.include?(remember_me) &&
11
- warden.authenticated?(scope) && record.respond_to?(:remember_me!)
12
- record.remember_me!
21
+ if succeeded? && resource.respond_to?(:remember_me!) && remember_me?
22
+ resource.remember_me!
13
23
 
14
- warden.response.set_cookie "remember_#{scope}_token", {
15
- :value => record.class.serialize_into_cookie(record),
16
- :expires => record.remember_expires_at,
17
- :path => "/"
18
- }
24
+ cookies.signed["remember_#{scope}_token"] = {
25
+ :value => resource.class.serialize_into_cookie(resource),
26
+ :expires => resource.remember_expires_at,
27
+ :path => "/"
28
+ }
29
+ end
30
+ end
31
+
32
+ protected
33
+
34
+ def remember_me?
35
+ valid_params? && Devise::TRUE_VALUES.include?(params_auth_hash[:remember_me])
36
+ end
37
+ end
19
38
  end
20
39
  end
21
40
 
22
- # Before logout hook to forget the user in the given scope, only if rememberable
23
- # is activated for this scope. Also clear remember token to ensure the user
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, scope|
28
- if record.respond_to?(:forget_me!)
29
- record.forget_me! unless record.frozen?
30
- warden.response.delete_cookie "remember_#{scope}_token"
31
- end
32
- end
41
+ Devise::Strategies::Authenticatable.send :include, Devise::Hooks::Rememberable
@@ -1,4 +1,4 @@
1
- # Each time a record is set we check whether it's session has already timed out
1
+ # Each time a record is set we check whether its session has already timed out
2
2
  # or not, based on last request time. If so, the record is logged out and
3
3
  # redirected to the sign in page. Also, each time the request comes and the
4
4
  # record is set, we set the last request time inside it's scoped session to
@@ -1,7 +1,9 @@
1
1
  # After each sign in, update sign in time, sign in count and sign in IP.
2
+ # This is only triggered when the user is explicitly set (with set_user)
3
+ # and on authentication. Retrieving the user from session (:fetch) does
4
+ # not trigger it.
2
5
  Warden::Manager.after_set_user :except => :fetch do |record, warden, options|
3
- scope = options[:scope]
4
- if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(scope)
6
+ if record.respond_to?(:update_tracked_fields!) && warden.authenticated?(options[:scope])
5
7
  record.update_tracked_fields!(warden.request)
6
8
  end
7
9
  end
@@ -34,26 +34,19 @@ 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
-
45
37
  # Receives an object and find a scope for it. If a scope cannot be found,
46
38
  # raises an error. If a symbol is given, it's considered to be the scope.
47
39
  def self.find_scope!(duck)
48
40
  case duck
49
41
  when String, Symbol
50
- duck
42
+ return duck
43
+ when Class
44
+ Devise.mappings.each_value { |m| return m.name if duck <= m.to }
51
45
  else
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
46
+ Devise.mappings.each_value { |m| return m.name if duck.is_a?(m.to) }
56
47
  end
48
+
49
+ raise "Could not find a valid mapping for #{duck}"
57
50
  end
58
51
 
59
52
  def initialize(name, options) #:nodoc:
@@ -84,6 +77,14 @@ module Devise
84
77
  klass
85
78
  end
86
79
 
80
+ def strategies
81
+ @strategies ||= STRATEGIES.values_at(*self.modules).compact.uniq.reverse
82
+ end
83
+
84
+ def routes
85
+ @routes ||= ROUTES.values_at(*self.modules).compact.uniq
86
+ end
87
+
87
88
  # Keep a list of allowed controllers for this mapping. It's useful to ensure
88
89
  # that an Admin cannot access the registrations controller unless it has
89
90
  # :registerable in the model.
@@ -104,6 +105,10 @@ module Devise
104
105
  path_prefix + as.to_s
105
106
  end
106
107
 
108
+ def authenticatable?
109
+ @authenticatable ||= self.modules.any? { |m| m.to_s =~ /authenticatable/ }
110
+ end
111
+
107
112
  # Create magic predicates for verifying what module is activated by this map.
108
113
  # Example:
109
114
  #
@@ -111,7 +116,7 @@ module Devise
111
116
  # self.modules.include?(:confirmable)
112
117
  # end
113
118
  #
114
- def self.register(m)
119
+ def self.add_module(m)
115
120
  class_eval <<-METHOD, __FILE__, __LINE__ + 1
116
121
  def #{m}?
117
122
  self.modules.include?(:#{m})
@@ -38,7 +38,7 @@ module Devise
38
38
 
39
39
  # Include the chosen devise modules in your model:
40
40
  #
41
- # devise :authenticatable, :confirmable, :recoverable
41
+ # devise :database_authenticatable, :confirmable, :recoverable
42
42
  #
43
43
  # You can also give any of the devise configuration values in form of a hash,
44
44
  # with specific values for this model. Please check your Devise initializer
@@ -46,12 +46,21 @@ module Devise
46
46
  #
47
47
  def devise(*modules)
48
48
  raise "You need to give at least one Devise module" if modules.empty?
49
-
50
49
  options = modules.extract_options!
50
+
51
+ if modules.delete(:authenticatable)
52
+ ActiveSupport::Deprecation.warn ":authenticatable as module is deprecated. Please give :database_authenticatable instead.", caller
53
+ modules << :database_authenticatable
54
+ end
55
+
56
+ if modules.delete(:http_authenticatable)
57
+ ActiveSupport::Deprecation.warn ":http_authenticatable as module is deprecated and is on by default. Revert by setting :http_authenticatable => false.", caller
58
+ end
59
+
51
60
  @devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
52
61
 
53
62
  devise_modules_hook! do
54
- devise_modules.each { |m| include Devise::Models.const_get(m.to_s.classify) }
63
+ @devise_modules.each { |m| include Devise::Models.const_get(m.to_s.classify) }
55
64
  options.each { |key, value| send(:"#{key}=", value) }
56
65
  end
57
66
  end
@@ -1,118 +1,42 @@
1
- require 'devise/strategies/authenticatable'
2
-
3
1
  module Devise
4
2
  module Models
5
- # Authenticable Module, responsible for encrypting password and validating
6
- # authenticity of a user while signing in.
3
+ # Authenticable module. Holds common settings for authentication.
7
4
  #
8
5
  # Configuration:
9
6
  #
10
7
  # You can overwrite configuration values by setting in globally in Devise,
11
8
  # using devise method or overwriting the respective instance method.
12
9
  #
13
- # pepper: encryption key used for creating encrypted password. Each time
14
- # password changes, it's gonna be encrypted again, and this key
15
- # is added to the password and salt to create a secure hash.
16
- # Always use `rake secret' to generate a new key.
17
- #
18
- # stretches: defines how many times the password will be encrypted.
19
- #
20
- # encryptor: the encryptor going to be used. By default :sha1.
21
- #
22
- # authentication_keys: parameters used for authentication. By default [:email]
10
+ # authentication_keys: parameters used for authentication. By default [:email].
23
11
  #
24
- # Examples:
12
+ # http_authenticatable: if this model allows http authentication. By default true.
13
+ # It also accepts an array specifying the strategies that should allow http.
25
14
  #
26
- # User.authenticate('email@test.com', 'password123') # returns authenticated user or nil
27
- # User.find(1).valid_password?('password123') # returns true/false
15
+ # params_authenticatable: if this model allows authentication through request params. By default true.
16
+ # It also accepts an array specifying the strategies that should allow params authentication.
28
17
  #
29
18
  module Authenticatable
30
19
  extend ActiveSupport::Concern
31
20
 
32
- included do
33
- attr_reader :password, :current_password
34
- attr_accessor :password_confirmation
35
- end
36
-
37
- # Regenerates password salt and encrypted password each time password is set,
38
- # and then trigger any "after_changed_password"-callbacks.
39
- def password=(new_password)
40
- @password = new_password
41
-
42
- if @password.present?
43
- self.password_salt = self.class.encryptor_class.salt
44
- self.encrypted_password = password_digest(@password)
45
- end
46
- end
47
-
48
- # Verifies whether an incoming_password (ie from sign in) is the user password.
49
- def valid_password?(incoming_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
57
- end
58
-
59
- # Checks if a resource is valid upon authentication.
60
- def valid_for_authentication?(attributes)
61
- valid_password?(attributes[:password])
62
- end
63
-
64
- # Set password and password confirmation to nil
65
- def clean_up_passwords
66
- self.password = self.password_confirmation = nil
21
+ # Yields the given block. This method is overwritten by other modules to provide
22
+ # hooks around authentication.
23
+ def valid_for_authentication?
24
+ yield
67
25
  end
68
26
 
69
- # Update record attributes when :current_password matches, otherwise returns
70
- # error on :current_password. It also automatically rejects :password and
71
- # :password_confirmation if they are blank.
72
- def update_with_password(params={})
73
- current_password = params.delete(:current_password)
74
-
75
- params.delete(:password) if params[:password].blank?
76
- params.delete(:password_confirmation) if params[:password_confirmation].blank?
77
-
78
- result = if valid_password?(current_password)
79
- update_attributes(params)
80
- else
81
- self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
82
- self.attributes = params
83
- false
84
- end
85
-
86
- clean_up_passwords unless result
87
- result
88
- end
89
-
90
- protected
91
-
92
- # Digests the password using the configured encryptor.
93
- def password_digest(password)
94
- self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
95
- end
96
-
97
27
  module ClassMethods
98
- Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
28
+ Devise::Models.config(self, :authentication_keys, :http_authenticatable, :params_authenticatable)
99
29
 
100
- # Authenticate a user based on configured attribute keys. Returns the
101
- # authenticated user if it's valid or nil.
102
- def authenticate(attributes={})
103
- return unless authentication_keys.all? { |k| attributes[k].present? }
104
- conditions = attributes.slice(*authentication_keys)
105
- resource = find_for_authentication(conditions)
106
- resource if resource.try(:valid_for_authentication?, attributes)
30
+ def params_authenticatable?(strategy)
31
+ params_authenticatable.is_a?(Array) ?
32
+ params_authenticatable.include?(strategy) : params_authenticatable
107
33
  end
108
34
 
109
- # Returns the class for the configured encryptor.
110
- def encryptor_class
111
- @encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
35
+ def http_authenticatable?(strategy)
36
+ http_authenticatable.is_a?(Array) ?
37
+ http_authenticatable.include?(strategy) : http_authenticatable
112
38
  end
113
39
 
114
- protected
115
-
116
40
  # Find first record based on conditions given (ie by the sign in form).
117
41
  # Overwrite to add customized conditions, create a join, or maybe use a
118
42
  # namedscope to filter records while authenticating.
@@ -120,7 +44,7 @@ module Devise
120
44
  #
121
45
  # def self.find_for_authentication(conditions={})
122
46
  # conditions[:active] = true
123
- # find(:first, :conditions => conditions)
47
+ # super
124
48
  # end
125
49
  #
126
50
  def find_for_authentication(conditions)
@@ -129,4 +53,4 @@ module Devise
129
53
  end
130
54
  end
131
55
  end
132
- end
56
+ end
@@ -49,7 +49,7 @@ module Devise
49
49
 
50
50
  # Verifies whether a user is confirmed or not
51
51
  def confirmed?
52
- !new_record? && !confirmed_at.nil?
52
+ persisted? && !confirmed_at.nil?
53
53
  end
54
54
 
55
55
  # Send confirmation instructions by email
@@ -57,15 +57,9 @@ module Devise
57
57
  ::Devise::Mailer.confirmation_instructions(self).deliver
58
58
  end
59
59
 
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
60
+ # Resend confirmation token. This method does not need to generate a new token.
61
+ def resend_confirmation_token
62
+ unless_confirmed { send_confirmation_instructions }
69
63
  end
70
64
 
71
65
  # Overwrites active? from Devise::Models::Activatable for confirmation
@@ -78,11 +72,7 @@ module Devise
78
72
 
79
73
  # The message to be shown if the account is inactive.
80
74
  def inactive_message
81
- if !confirmed?
82
- :unconfirmed
83
- else
84
- super
85
- end
75
+ !confirmed? ? :unconfirmed : super
86
76
  end
87
77
 
88
78
  # If you don't want confirmation to be sent on create, neither a code
@@ -137,7 +127,7 @@ module Devise
137
127
  # this token is being generated
138
128
  def generate_confirmation_token
139
129
  self.confirmed_at = nil
140
- self.confirmation_token = Devise.friendly_token
130
+ self.confirmation_token = self.class.confirmation_token
141
131
  self.confirmation_sent_at = Time.now.utc
142
132
  end
143
133
 
@@ -148,7 +138,7 @@ module Devise
148
138
  # Options must contain the user email
149
139
  def send_confirmation_instructions(attributes={})
150
140
  confirmable = find_or_initialize_with_error_by(:email, attributes[:email], :not_found)
151
- confirmable.resend_confirmation! unless confirmable.new_record?
141
+ confirmable.resend_confirmation_token if confirmable.persisted?
152
142
  confirmable
153
143
  end
154
144
 
@@ -156,12 +146,16 @@ module Devise
156
146
  # If no user is found, returns a new user with an error.
157
147
  # If the user is already confirmed, create an error for the user
158
148
  # Options must have the confirmation_token
159
- def confirm!(attributes={})
160
- confirmable = find_or_initialize_with_error_by(:confirmation_token, attributes[:confirmation_token])
161
- confirmable.confirm! unless confirmable.new_record?
149
+ def confirm_by_token(confirmation_token)
150
+ confirmable = find_or_initialize_with_error_by(:confirmation_token, confirmation_token)
151
+ confirmable.confirm! if confirmable.persisted?
162
152
  confirmable
163
153
  end
164
154
 
155
+ def confirmation_token
156
+ Devise.friendly_token
157
+ end
158
+
165
159
  Devise::Models.config(self, :confirm_within)
166
160
  end
167
161
  end
@@ -0,0 +1,99 @@
1
+ require 'devise/models/authenticatable'
2
+ require 'devise/strategies/database_authenticatable'
3
+
4
+ module Devise
5
+ module Models
6
+ # Authenticable Module, responsible for encrypting password and validating
7
+ # authenticity of a user while signing in.
8
+ #
9
+ # Configuration:
10
+ #
11
+ # You can overwrite configuration values by setting in globally in Devise,
12
+ # using devise method or overwriting the respective instance method.
13
+ #
14
+ # pepper: encryption key used for creating encrypted password. Each time
15
+ # password changes, it's gonna be encrypted again, and this key
16
+ # is added to the password and salt to create a secure hash.
17
+ # Always use `rake secret' to generate a new key.
18
+ #
19
+ # stretches: defines how many times the password will be encrypted.
20
+ #
21
+ # encryptor: the encryptor going to be used. By default :sha1.
22
+ #
23
+ # Examples:
24
+ #
25
+ # User.find(1).valid_password?('password123') # returns true/false
26
+ #
27
+ module DatabaseAuthenticatable
28
+ extend ActiveSupport::Concern
29
+ include Devise::Models::Authenticatable
30
+
31
+ included do
32
+ attr_reader :password, :current_password
33
+ attr_accessor :password_confirmation
34
+ end
35
+
36
+ # Regenerates password salt and encrypted password each time password is set,
37
+ # and then trigger any "after_changed_password"-callbacks.
38
+ def password=(new_password)
39
+ @password = new_password
40
+
41
+ if @password.present?
42
+ self.password_salt = self.class.encryptor_class.salt
43
+ self.encrypted_password = password_digest(@password)
44
+ end
45
+ end
46
+
47
+ # Verifies whether an incoming_password (ie from sign in) is the user password.
48
+ def valid_password?(incoming_password)
49
+ password_digest(incoming_password) == self.encrypted_password
50
+ end
51
+
52
+ # Set password and password confirmation to nil
53
+ def clean_up_passwords
54
+ self.password = self.password_confirmation = nil
55
+ end
56
+
57
+ # Update record attributes when :current_password matches, otherwise returns
58
+ # error on :current_password. It also automatically rejects :password and
59
+ # :password_confirmation if they are blank.
60
+ def update_with_password(params={})
61
+ current_password = params.delete(:current_password)
62
+
63
+ params.delete(:password) if params[:password].blank?
64
+ params.delete(:password_confirmation) if params[:password_confirmation].blank?
65
+
66
+ result = if valid_password?(current_password)
67
+ update_attributes(params)
68
+ else
69
+ self.errors.add(:current_password, current_password.blank? ? :blank : :invalid)
70
+ self.attributes = params
71
+ false
72
+ end
73
+
74
+ clean_up_passwords
75
+ result
76
+ end
77
+
78
+ protected
79
+
80
+ # Digests the password using the configured encryptor.
81
+ def password_digest(password)
82
+ self.class.encryptor_class.digest(password, self.class.stretches, self.password_salt, self.class.pepper)
83
+ end
84
+
85
+ module ClassMethods
86
+ Devise::Models.config(self, :pepper, :stretches, :encryptor)
87
+
88
+ # Returns the class for the configured encryptor.
89
+ def encryptor_class
90
+ @encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
91
+ end
92
+
93
+ def find_for_database_authentication(*args)
94
+ find_for_authentication(*args)
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end