devise 2.0.6 → 2.1.0.rc
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.
- data/.travis.yml +0 -1
- data/CHANGELOG.rdoc +12 -6
- data/Gemfile.lock +3 -3
- data/README.md +9 -15
- data/app/controllers/devise_controller.rb +2 -3
- data/app/views/devise/_links.erb +3 -25
- data/app/views/devise/confirmations/new.html.erb +1 -1
- data/app/views/devise/passwords/edit.html.erb +1 -1
- data/app/views/devise/passwords/new.html.erb +1 -1
- data/app/views/devise/registrations/new.html.erb +1 -1
- data/app/views/devise/sessions/new.html.erb +1 -1
- data/app/views/devise/shared/_links.erb +25 -3
- data/app/views/devise/unlocks/new.html.erb +1 -1
- data/config/locales/en.yml +2 -2
- data/gemfiles/Gemfile.rails-3.1.x.lock +3 -3
- data/lib/devise.rb +1 -0
- data/lib/devise/controllers/helpers.rb +1 -1
- data/lib/devise/encryptors/base.rb +5 -1
- data/lib/devise/encryptors/bcrypt.rb +14 -0
- data/lib/devise/failure_app.rb +1 -0
- data/lib/devise/models.rb +32 -2
- data/lib/devise/models/authenticatable.rb +15 -10
- data/lib/devise/models/confirmable.rb +21 -1
- data/lib/devise/models/database_authenticatable.rb +11 -5
- data/lib/devise/models/encryptable.rb +17 -9
- data/lib/devise/models/lockable.rb +17 -1
- data/lib/devise/models/omniauthable.rb +4 -0
- data/lib/devise/models/recoverable.rb +4 -0
- data/lib/devise/models/registerable.rb +4 -0
- data/lib/devise/models/rememberable.rb +6 -2
- data/lib/devise/models/timeoutable.rb +4 -0
- data/lib/devise/models/token_authenticatable.rb +5 -0
- data/lib/devise/models/trackable.rb +4 -0
- data/lib/devise/models/validatable.rb +4 -0
- data/lib/devise/param_filter.rb +2 -1
- data/lib/devise/rails/routes.rb +3 -2
- data/lib/devise/strategies/authenticatable.rb +10 -4
- data/lib/devise/version.rb +1 -1
- data/lib/generators/devise/views_generator.rb +15 -6
- data/lib/generators/templates/devise.rb +1 -1
- data/lib/generators/templates/simple_form_for/confirmations/new.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/passwords/edit.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/passwords/new.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/registrations/new.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/sessions/new.html.erb +1 -1
- data/lib/generators/templates/simple_form_for/unlocks/new.html.erb +1 -1
- data/test/generators/views_generator_test.rb +1 -1
- data/test/integration/omniauthable_test.rb +2 -2
- data/test/integration/recoverable_test.rb +9 -0
- data/test/models/authenticatable_test.rb +3 -5
- data/test/models/confirmable_test.rb +26 -0
- data/test/models/database_authenticatable_test.rb +29 -7
- data/test/models/encryptable_test.rb +6 -0
- data/test/models/lockable_test.rb +34 -0
- data/test/models/omniauthable_test.rb +7 -0
- data/test/models/recoverable_test.rb +8 -1
- data/test/models/registerable_test.rb +7 -0
- data/test/models/rememberable_test.rb +8 -1
- data/test/models/timeoutable_test.rb +4 -0
- data/test/models/token_authenticatable_test.rb +6 -0
- data/test/models/trackable_test.rb +9 -1
- data/test/models/validatable_test.rb +5 -1
- data/test/models_test.rb +70 -0
- data/test/rails_app/config/routes.rb +4 -0
- data/test/routes_test.rb +4 -0
- data/test/support/assertions.rb +15 -0
- data/test/support/helpers.rb +21 -0
- metadata +19 -34
@@ -36,6 +36,15 @@ module Devise
|
|
36
36
|
after_update :send_confirmation_instructions, :if => :reconfirmation_required?
|
37
37
|
end
|
38
38
|
|
39
|
+
def self.required_fields(klass)
|
40
|
+
required_methods = [:confirmation_token, :confirmed_at, :confirmation_sent_at]
|
41
|
+
if klass.reconfirmable
|
42
|
+
required_methods << :unconfirmed_email
|
43
|
+
end
|
44
|
+
|
45
|
+
required_methods
|
46
|
+
end
|
47
|
+
|
39
48
|
# Confirm a user by setting it's confirmed_at to actual time. If the user
|
40
49
|
# is already confirmed, add an error to email field. If the user is invalid
|
41
50
|
# add errors
|
@@ -45,7 +54,7 @@ module Devise
|
|
45
54
|
self.confirmed_at = Time.now.utc
|
46
55
|
|
47
56
|
if self.class.reconfirmable && unconfirmed_email.present?
|
48
|
-
|
57
|
+
skip_reconfirmation!
|
49
58
|
self.email = unconfirmed_email
|
50
59
|
self.unconfirmed_email = nil
|
51
60
|
|
@@ -99,6 +108,12 @@ module Devise
|
|
99
108
|
self.confirmed_at = Time.now.utc
|
100
109
|
end
|
101
110
|
|
111
|
+
# If you don't want reconfirmation to be sent, neither a code
|
112
|
+
# to be generated, call skip_reconfirmation!
|
113
|
+
def skip_reconfirmation!
|
114
|
+
@bypass_postpone = true
|
115
|
+
end
|
116
|
+
|
102
117
|
def headers_for(action)
|
103
118
|
headers = super
|
104
119
|
if action == :confirmation_instructions && pending_reconfirmation?
|
@@ -165,6 +180,11 @@ module Devise
|
|
165
180
|
generate_confirmation_token && save(:validate => false)
|
166
181
|
end
|
167
182
|
|
183
|
+
def after_password_reset
|
184
|
+
super
|
185
|
+
confirm! unless confirmed?
|
186
|
+
end
|
187
|
+
|
168
188
|
def postpone_email_change_until_confirmation
|
169
189
|
@reconfirmation_required = true
|
170
190
|
self.unconfirmed_email = self.email
|
@@ -27,6 +27,10 @@ module Devise
|
|
27
27
|
attr_accessor :password_confirmation
|
28
28
|
end
|
29
29
|
|
30
|
+
def self.required_fields(klass)
|
31
|
+
[:encrypted_password] + klass.authentication_keys
|
32
|
+
end
|
33
|
+
|
30
34
|
# Generates password encryption based on the given value.
|
31
35
|
def password=(new_password)
|
32
36
|
@password = new_password
|
@@ -36,9 +40,7 @@ module Devise
|
|
36
40
|
# Verifies whether an password (ie from sign in) is the user password.
|
37
41
|
def valid_password?(password)
|
38
42
|
return false if encrypted_password.blank?
|
39
|
-
|
40
|
-
password = ::BCrypt::Engine.hash_secret("#{password}#{self.class.pepper}", bcrypt.salt)
|
41
|
-
Devise.secure_compare(password, self.encrypted_password)
|
43
|
+
encryptor_class.compare(encrypted_password, password, self.class.stretches, authenticatable_salt, self.class.pepper)
|
42
44
|
end
|
43
45
|
|
44
46
|
# Set password and password confirmation to nil
|
@@ -96,14 +98,18 @@ module Devise
|
|
96
98
|
|
97
99
|
# A reliable way to expose the salt regardless of the implementation.
|
98
100
|
def authenticatable_salt
|
99
|
-
|
101
|
+
encrypted_password[0,29] if encrypted_password
|
100
102
|
end
|
101
103
|
|
102
104
|
protected
|
103
105
|
|
104
106
|
# Digests the password using bcrypt.
|
105
107
|
def password_digest(password)
|
106
|
-
|
108
|
+
encryptor_class.digest(password, self.class.stretches, ::BCrypt::Engine.generate_salt, self.class.pepper)
|
109
|
+
end
|
110
|
+
|
111
|
+
def encryptor_class
|
112
|
+
Devise::Encryptors::BCrypt
|
107
113
|
end
|
108
114
|
|
109
115
|
module ClassMethods
|
@@ -2,7 +2,8 @@ require 'devise/strategies/database_authenticatable'
|
|
2
2
|
|
3
3
|
module Devise
|
4
4
|
module Models
|
5
|
-
# Encryptable
|
5
|
+
# Encryptable module adds support to several encryptors wrapping
|
6
|
+
# them in a salt and pepper mechanism to increase security.
|
6
7
|
#
|
7
8
|
# == Options
|
8
9
|
#
|
@@ -24,30 +25,37 @@ module Devise
|
|
24
25
|
attr_accessor :password_confirmation
|
25
26
|
end
|
26
27
|
|
27
|
-
|
28
|
+
def self.required_fields(klass)
|
29
|
+
[:password_salt]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Generates password salt when setting the password.
|
28
33
|
def password=(new_password)
|
29
34
|
self.password_salt = self.class.password_salt if new_password.present?
|
30
35
|
super
|
31
36
|
end
|
32
37
|
|
38
|
+
# Overrides authenticatable salt to use the new password_salt
|
39
|
+
# column. authenticatable_salt is used by `valid_password?`
|
40
|
+
# and by other modules whenever there is a need for a random
|
41
|
+
# token based on the user password.
|
33
42
|
def authenticatable_salt
|
34
43
|
self.password_salt
|
35
44
|
end
|
36
45
|
|
37
|
-
# Verifies whether an incoming_password (ie from sign in) is the user password.
|
38
|
-
def valid_password?(incoming_password)
|
39
|
-
Devise.secure_compare(password_digest(incoming_password), self.encrypted_password)
|
40
|
-
end
|
41
|
-
|
42
46
|
protected
|
43
47
|
|
44
48
|
# Digests the password using the configured encryptor.
|
45
49
|
def password_digest(password)
|
46
|
-
if
|
47
|
-
|
50
|
+
if password_salt.present?
|
51
|
+
encryptor_class.digest(password, self.class.stretches, authenticatable_salt, self.class.pepper)
|
48
52
|
end
|
49
53
|
end
|
50
54
|
|
55
|
+
def encryptor_class
|
56
|
+
self.class.encryptor_class
|
57
|
+
end
|
58
|
+
|
51
59
|
module ClassMethods
|
52
60
|
Devise::Models.config(self, :encryptor)
|
53
61
|
|
@@ -22,6 +22,15 @@ module Devise
|
|
22
22
|
|
23
23
|
delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"
|
24
24
|
|
25
|
+
def self.required_fields(klass)
|
26
|
+
attributes = []
|
27
|
+
attributes << :failed_attempts if klass.lock_strategy_enabled?(:failed_attempts)
|
28
|
+
attributes << :unlock_at if klass.unlock_strategy_enabled?(:time)
|
29
|
+
attributes << :unlock_token if klass.unlock_strategy_enabled?(:email)
|
30
|
+
|
31
|
+
attributes
|
32
|
+
end
|
33
|
+
|
25
34
|
# Lock a user setting its locked_at to actual time.
|
26
35
|
def lock_access!
|
27
36
|
self.locked_at = Time.now.utc
|
@@ -88,7 +97,6 @@ module Devise
|
|
88
97
|
self.failed_attempts += 1
|
89
98
|
if attempts_exceeded?
|
90
99
|
lock_access! unless access_locked?
|
91
|
-
return :locked
|
92
100
|
else
|
93
101
|
save(:validate => false)
|
94
102
|
end
|
@@ -96,6 +104,14 @@ module Devise
|
|
96
104
|
end
|
97
105
|
end
|
98
106
|
|
107
|
+
def unauthenticated_message
|
108
|
+
if lock_strategy_enabled?(:failed_attempts) && attempts_exceeded?
|
109
|
+
:locked
|
110
|
+
else
|
111
|
+
super
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
99
115
|
protected
|
100
116
|
|
101
117
|
def attempts_exceeded?
|
@@ -24,6 +24,10 @@ module Devise
|
|
24
24
|
module Recoverable
|
25
25
|
extend ActiveSupport::Concern
|
26
26
|
|
27
|
+
def self.required_fields(klass)
|
28
|
+
[:reset_password_sent_at, :reset_password_token]
|
29
|
+
end
|
30
|
+
|
27
31
|
# Update password saving the record and clearing token. Returns true if
|
28
32
|
# the passwords are valid and the record was saved, false otherwise.
|
29
33
|
def reset_password!(new_password, new_password_confirmation)
|
@@ -5,6 +5,10 @@ module Devise
|
|
5
5
|
module Registerable
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
+
def self.required_fields(klass)
|
9
|
+
[]
|
10
|
+
end
|
11
|
+
|
8
12
|
module ClassMethods
|
9
13
|
# A convenience method that receives both parameters and session to
|
10
14
|
# initialize a user. This can be used by OAuth, for example, to send
|
@@ -24,7 +24,7 @@ module Devise
|
|
24
24
|
# * +extend_remember_period+: if true, extends the user's remember period
|
25
25
|
# when remembered via cookie. False by default.
|
26
26
|
#
|
27
|
-
# * +
|
27
|
+
# * +rememberable_options+: configuration options passed to the created cookie.
|
28
28
|
#
|
29
29
|
# == Examples
|
30
30
|
#
|
@@ -41,6 +41,10 @@ module Devise
|
|
41
41
|
|
42
42
|
attr_accessor :remember_me, :extend_remember_period
|
43
43
|
|
44
|
+
def self.required_fields(klass)
|
45
|
+
[:remember_created_at, :remember_token]
|
46
|
+
end
|
47
|
+
|
44
48
|
# Generate a new remember token and save the record without validations
|
45
49
|
# unless remember_across_browsers is true and the user already has a valid token.
|
46
50
|
def remember_me!(extend_period=false)
|
@@ -71,7 +75,7 @@ module Devise
|
|
71
75
|
def rememberable_value
|
72
76
|
if respond_to?(:remember_token)
|
73
77
|
remember_token
|
74
|
-
elsif salt = authenticatable_salt
|
78
|
+
elsif respond_to?(:authenticatable_salt) && (salt = authenticatable_salt)
|
75
79
|
salt
|
76
80
|
else
|
77
81
|
raise "authenticable_salt returned nil for the #{self.class.name} model. " \
|
@@ -20,6 +20,10 @@ module Devise
|
|
20
20
|
module Timeoutable
|
21
21
|
extend ActiveSupport::Concern
|
22
22
|
|
23
|
+
def self.required_fields(klass)
|
24
|
+
[]
|
25
|
+
end
|
26
|
+
|
23
27
|
# Checks whether the user session has expired based on configured time.
|
24
28
|
def timedout?(last_access)
|
25
29
|
return false if remember_exists_and_not_expired?
|
@@ -27,6 +27,10 @@ module Devise
|
|
27
27
|
module TokenAuthenticatable
|
28
28
|
extend ActiveSupport::Concern
|
29
29
|
|
30
|
+
def self.required_fields(klass)
|
31
|
+
[:authentication_token]
|
32
|
+
end
|
33
|
+
|
30
34
|
# Generate new authentication token (a.k.a. "single access token").
|
31
35
|
def reset_authentication_token
|
32
36
|
self.authentication_token = self.class.authentication_token
|
@@ -52,6 +56,7 @@ module Devise
|
|
52
56
|
def after_token_authentication
|
53
57
|
end
|
54
58
|
|
59
|
+
|
55
60
|
module ClassMethods
|
56
61
|
def find_for_token_authentication(conditions)
|
57
62
|
find_for_authentication(:authentication_token => conditions[token_authentication_key])
|
@@ -11,6 +11,10 @@ module Devise
|
|
11
11
|
# * last_sign_in_ip - Holds the remote ip of the previous sign in
|
12
12
|
#
|
13
13
|
module Trackable
|
14
|
+
def self.required_fields(klass)
|
15
|
+
[:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count]
|
16
|
+
end
|
17
|
+
|
14
18
|
def update_tracked_fields!(request)
|
15
19
|
old_current, new_current = self.current_sign_in_at, Time.now.utc
|
16
20
|
self.last_sign_in_at = old_current || new_current
|
@@ -17,6 +17,10 @@ module Devise
|
|
17
17
|
VALIDATIONS = [ :validates_presence_of, :validates_uniqueness_of, :validates_format_of,
|
18
18
|
:validates_confirmation_of, :validates_length_of ].freeze
|
19
19
|
|
20
|
+
def self.required_fields(klass)
|
21
|
+
[]
|
22
|
+
end
|
23
|
+
|
20
24
|
def self.included(base)
|
21
25
|
base.extend ClassMethods
|
22
26
|
assert_validations_api!(base)
|
data/lib/devise/param_filter.rb
CHANGED
@@ -33,8 +33,9 @@ module Devise
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
+
# Determine which values should be transformed to string or passed as-is to the query builder underneath
|
36
37
|
def param_requires_string_conversion?(value)
|
37
|
-
|
38
|
+
[Fixnum, TrueClass, FalseClass, Regexp].none? {|clz| value.is_a? clz }
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|
data/lib/devise/rails/routes.rb
CHANGED
@@ -184,7 +184,7 @@ module ActionDispatch::Routing
|
|
184
184
|
#
|
185
185
|
# In order to get Devise to recognize the deactivate action, your devise_for entry should look like this,
|
186
186
|
#
|
187
|
-
#
|
187
|
+
# devise_scope :owner do
|
188
188
|
# post "deactivate", :to => "registrations#deactivate", :as => "deactivate_registration"
|
189
189
|
# end
|
190
190
|
#
|
@@ -198,7 +198,8 @@ module ActionDispatch::Routing
|
|
198
198
|
options[:path_names] = (@scope[:path_names] || {}).merge(options[:path_names] || {})
|
199
199
|
options[:constraints] = (@scope[:constraints] || {}).merge(options[:constraints] || {})
|
200
200
|
options[:defaults] = (@scope[:defaults] || {}).merge(options[:defaults] || {})
|
201
|
-
options[:options] =
|
201
|
+
options[:options] = @scope[:options] || {}
|
202
|
+
options[:options][:format] = false if options[:format] == false
|
202
203
|
|
203
204
|
resources.map!(&:to_sym)
|
204
205
|
|
@@ -23,14 +23,20 @@ module Devise
|
|
23
23
|
result = resource && resource.valid_for_authentication?(&block)
|
24
24
|
|
25
25
|
case result
|
26
|
-
when
|
26
|
+
when Symbol, String
|
27
|
+
ActiveSupport::Deprecation.warn "valid_for_authentication should return a boolean value"
|
27
28
|
fail!(result)
|
28
|
-
false
|
29
|
-
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
|
32
|
+
if result
|
30
33
|
decorate(resource)
|
31
34
|
true
|
32
35
|
else
|
33
|
-
|
36
|
+
if resource
|
37
|
+
fail!(resource.unauthenticated_message)
|
38
|
+
end
|
39
|
+
false
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
data/lib/devise/version.rb
CHANGED
@@ -39,6 +39,18 @@ module Devise
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
class SharedViewsGenerator < Rails::Generators::Base #:nodoc:
|
43
|
+
include ViewPathTemplates
|
44
|
+
source_root File.expand_path("../../../../app/views/devise", __FILE__)
|
45
|
+
desc "Copies shared Devise views to your application."
|
46
|
+
hide!
|
47
|
+
|
48
|
+
# Override copy_views to just copy mailer and shared.
|
49
|
+
def copy_views
|
50
|
+
view_directory :shared
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
42
54
|
class FormForGenerator < Rails::Generators::Base #:nodoc:
|
43
55
|
include ViewPathTemplates
|
44
56
|
source_root File.expand_path("../../../../app/views/devise", __FILE__)
|
@@ -80,15 +92,12 @@ module Devise
|
|
80
92
|
end
|
81
93
|
|
82
94
|
class ViewsGenerator < Rails::Generators::Base
|
83
|
-
include ViewPathTemplates
|
84
|
-
|
85
|
-
source_root File.expand_path("../../../../app/views/devise", __FILE__)
|
86
95
|
desc "Copies Devise views to your application."
|
87
96
|
|
88
|
-
|
89
|
-
|
90
|
-
end
|
97
|
+
argument :scope, :required => false, :default => nil,
|
98
|
+
:desc => "The scope to copy views to"
|
91
99
|
|
100
|
+
invoke SharedViewsGenerator
|
92
101
|
hook_for :form_builder, :aliases => "-b",
|
93
102
|
:desc => "Form builder to be used",
|
94
103
|
:default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
|
@@ -117,7 +117,7 @@ Devise.setup do |config|
|
|
117
117
|
|
118
118
|
# Options to be passed to the created cookie. For instance, you can set
|
119
119
|
# :secure => true in order to force SSL only cookies.
|
120
|
-
# config.
|
120
|
+
# config.rememberable_options = {}
|
121
121
|
|
122
122
|
# ==> Configuration for :validatable
|
123
123
|
# Range for password length. Default is 6..128.
|