devise 0.9.2 → 1.0.0
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/CHANGELOG.rdoc +11 -0
- data/README.rdoc +3 -2
- data/TODO +0 -1
- data/app/controllers/confirmations_controller.rb +18 -7
- data/app/controllers/passwords_controller.rb +18 -7
- data/app/controllers/registrations_controller.rb +55 -0
- data/app/controllers/sessions_controller.rb +17 -5
- data/app/controllers/unlocks_controller.rb +18 -7
- data/app/models/devise_mailer.rb +3 -2
- data/app/views/registrations/edit.html.erb +25 -0
- data/app/views/registrations/new.html.erb +17 -0
- data/app/views/sessions/new.html.erb +10 -12
- data/app/views/shared/_devise_links.erb +5 -1
- data/generators/devise_install/templates/devise.rb +3 -1
- data/lib/devise.rb +15 -8
- data/lib/devise/controllers/helpers.rb +1 -1
- data/lib/devise/controllers/internal_helpers.rb +15 -6
- data/lib/devise/controllers/url_helpers.rb +7 -7
- data/lib/devise/locales/en.yml +6 -0
- data/lib/devise/mapping.rb +9 -15
- data/lib/devise/models.rb +9 -16
- data/lib/devise/models/authenticatable.rb +38 -8
- data/lib/devise/models/lockable.rb +42 -24
- data/lib/devise/models/registerable.rb +8 -0
- data/lib/devise/models/timeoutable.rb +1 -1
- data/lib/devise/orm/active_record.rb +3 -2
- data/lib/devise/orm/data_mapper.rb +3 -3
- data/lib/devise/orm/mongo_mapper.rb +3 -3
- data/lib/devise/rails/routes.rb +9 -6
- data/lib/devise/strategies/authenticatable.rb +11 -1
- data/lib/devise/strategies/base.rb +5 -13
- data/lib/devise/strategies/http_authenticatable.rb +49 -0
- data/lib/devise/strategies/rememberable.rb +1 -1
- data/lib/devise/strategies/token_authenticatable.rb +9 -10
- data/lib/devise/version.rb +1 -1
- data/test/devise_test.rb +1 -1
- data/test/integration/authenticatable_test.rb +59 -43
- data/test/integration/http_authenticatable_test.rb +44 -0
- data/test/integration/registerable_test.rb +130 -0
- data/test/integration/token_authenticatable_test.rb +4 -4
- data/test/mailers/confirmation_instructions_test.rb +9 -0
- data/test/mapping_test.rb +14 -10
- data/test/models/authenticatable_test.rb +31 -9
- data/test/models/lockable_test.rb +8 -8
- data/test/models_test.rb +1 -1
- data/test/rails_app/app/active_record/admin.rb +1 -1
- data/test/rails_app/app/active_record/user.rb +4 -2
- data/test/rails_app/config/routes.rb +3 -2
- data/test/routes_test.rb +35 -0
- data/test/support/integration_tests_helper.rb +5 -1
- data/test/support/tests_helper.rb +35 -1
- metadata +9 -4
- data/lib/devise/controllers/common.rb +0 -24
- data/test/support/model_tests_helper.rb +0 -36
@@ -176,7 +176,7 @@ module Devise
|
|
176
176
|
# before_filter :authenticate_admin! # Tell devise to use :admin map
|
177
177
|
#
|
178
178
|
Devise.mappings.each_key do |mapping|
|
179
|
-
class_eval <<-METHODS, __FILE__, __LINE__
|
179
|
+
class_eval <<-METHODS, __FILE__, __LINE__ + 1
|
180
180
|
def authenticate_#{mapping}!
|
181
181
|
warden.authenticate!(:scope => :#{mapping})
|
182
182
|
end
|
@@ -7,6 +7,7 @@ module Devise
|
|
7
7
|
|
8
8
|
def self.included(base)
|
9
9
|
base.class_eval do
|
10
|
+
extend ScopedViews
|
10
11
|
unloadable
|
11
12
|
|
12
13
|
helper_method :resource, :scope_name, :resource_name, :resource_class, :devise_mapping, :devise_controller?
|
@@ -17,6 +18,16 @@ module Devise
|
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
21
|
+
module ScopedViews
|
22
|
+
def scoped_views
|
23
|
+
defined?(@scoped_views) ? @scoped_views : Devise.scoped_views
|
24
|
+
end
|
25
|
+
|
26
|
+
def scoped_views=(value)
|
27
|
+
@scoped_views = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
20
31
|
# Gets the actual resource stored in the instance variable
|
21
32
|
def resource
|
22
33
|
instance_variable_get(:"@#{resource_name}")
|
@@ -59,12 +70,9 @@ module Devise
|
|
59
70
|
instance_variable_set(:"@#{resource_name}", new_resource)
|
60
71
|
end
|
61
72
|
|
62
|
-
# Build a devise resource
|
73
|
+
# Build a devise resource.
|
63
74
|
def build_resource
|
64
|
-
self.resource ||=
|
65
|
-
attributes = params[resource_name].try(:except, :password, :password_confirmation)
|
66
|
-
resource_class.new(attributes || {})
|
67
|
-
end
|
75
|
+
self.resource ||= resource_class.new(params[resource_name] || {})
|
68
76
|
end
|
69
77
|
|
70
78
|
# Helper for use in before_filters where no authentication is required.
|
@@ -104,7 +112,8 @@ module Devise
|
|
104
112
|
# Accepts just :controller as option.
|
105
113
|
def render_with_scope(action, options={})
|
106
114
|
controller_name = options.delete(:controller) || self.controller_name
|
107
|
-
|
115
|
+
|
116
|
+
if self.class.scoped_views
|
108
117
|
begin
|
109
118
|
render :template => "#{controller_name}/#{devise_mapping.as}/#{action}"
|
110
119
|
rescue ActionView::MissingTemplate
|
@@ -19,17 +19,17 @@ module Devise
|
|
19
19
|
# Those helpers are added to your ApplicationController.
|
20
20
|
module UrlHelpers
|
21
21
|
|
22
|
-
|
22
|
+
Devise::ROUTES.each do |module_name|
|
23
23
|
[:path, :url].each do |path_or_url|
|
24
24
|
actions = [ nil, :new_ ]
|
25
|
-
actions << :edit_ if
|
26
|
-
actions << :destroy_ if
|
25
|
+
actions << :edit_ if [:password, :registration].include?(module_name)
|
26
|
+
actions << :destroy_ if [:session].include?(module_name)
|
27
27
|
|
28
28
|
actions.each do |action|
|
29
|
-
class_eval <<-URL_HELPERS
|
30
|
-
def #{action}#{module_name}_#{path_or_url}(
|
31
|
-
|
32
|
-
send("#{action}\#{
|
29
|
+
class_eval <<-URL_HELPERS, __FILE__, __LINE__ + 1
|
30
|
+
def #{action}#{module_name}_#{path_or_url}(resource_or_scope, *args)
|
31
|
+
scope = Devise::Mapping.find_scope!(resource_or_scope)
|
32
|
+
send("#{action}\#{scope}_#{module_name}_#{path_or_url}", *args)
|
33
33
|
end
|
34
34
|
URL_HELPERS
|
35
35
|
end
|
data/lib/devise/locales/en.yml
CHANGED
@@ -19,6 +19,11 @@ en:
|
|
19
19
|
link: "Didn't receive confirmation instructions?"
|
20
20
|
send_instructions: 'You will receive an email with instructions about how to confirm your account in a few minutes.'
|
21
21
|
confirmed: 'Your account was successfully confirmed. You are now signed in.'
|
22
|
+
registrations:
|
23
|
+
link: 'Sign up'
|
24
|
+
signed_up: 'You have signed up successfully.'
|
25
|
+
updated: 'You updated your account successfully.'
|
26
|
+
destroyed: 'Bye! Your account was successfully cancelled. We hope to see you again soon.'
|
22
27
|
unlocks:
|
23
28
|
link: "Didn't receive unlock instructions?"
|
24
29
|
send_instructions: 'You will receive an email with instructions about how to unlock your account in a few minutes.'
|
@@ -27,3 +32,4 @@ en:
|
|
27
32
|
confirmation_instructions: 'Confirmation instructions'
|
28
33
|
reset_password_instructions: 'Reset password instructions'
|
29
34
|
unlock_instructions: 'Unlock Instructions'
|
35
|
+
|
data/lib/devise/mapping.rb
CHANGED
@@ -36,7 +36,10 @@ module Devise
|
|
36
36
|
|
37
37
|
# Find a mapping by a given class. It takes into account single table inheritance as well.
|
38
38
|
def self.find_by_class(klass)
|
39
|
-
Devise.mappings.
|
39
|
+
Devise.mappings.each_value do |mapping|
|
40
|
+
return mapping if klass <= mapping.to
|
41
|
+
end
|
42
|
+
nil
|
40
43
|
end
|
41
44
|
|
42
45
|
# Receives an object and find a scope for it. If a scope cannot be found,
|
@@ -62,11 +65,12 @@ module Devise
|
|
62
65
|
@as = (options.delete(:as) || name).to_sym
|
63
66
|
@klass = (options.delete(:class_name) || name.to_s.classify).to_s
|
64
67
|
@name = (options.delete(:scope) || name.to_s.singularize).to_sym
|
65
|
-
|
66
|
-
@path_prefix
|
68
|
+
|
69
|
+
@path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
|
67
70
|
@route_options = options || {}
|
68
71
|
|
69
|
-
|
72
|
+
@path_names = Hash.new { |h,k| h[k] = k.to_s }
|
73
|
+
@path_names.merge!(options.delete(:path_names) || {})
|
70
74
|
end
|
71
75
|
|
72
76
|
# Return modules for the mapping.
|
@@ -115,7 +119,7 @@ module Devise
|
|
115
119
|
#
|
116
120
|
def self.register(*modules)
|
117
121
|
modules.each do |m|
|
118
|
-
class_eval <<-METHOD, __FILE__, __LINE__
|
122
|
+
class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
119
123
|
def #{m}?
|
120
124
|
self.for.include?(:#{m})
|
121
125
|
end
|
@@ -123,15 +127,5 @@ module Devise
|
|
123
127
|
end
|
124
128
|
end
|
125
129
|
Devise::Mapping.register *ALL
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
# Configure default path names, allowing the user overwrite defaults by
|
130
|
-
# passing a hash in :path_names.
|
131
|
-
def setup_path_names
|
132
|
-
[:sign_in, :sign_out, :password, :confirmation].each do |path_name|
|
133
|
-
@path_names[path_name] ||= path_name.to_s
|
134
|
-
end
|
135
|
-
end
|
136
130
|
end
|
137
131
|
end
|
data/lib/devise/models.rb
CHANGED
@@ -6,6 +6,7 @@ module Devise
|
|
6
6
|
autoload :Lockable, 'devise/models/lockable'
|
7
7
|
autoload :Recoverable, 'devise/models/recoverable'
|
8
8
|
autoload :Rememberable, 'devise/models/rememberable'
|
9
|
+
autoload :Registerable, 'devise/models/registerable'
|
9
10
|
autoload :Timeoutable, 'devise/models/timeoutable'
|
10
11
|
autoload :Trackable, 'devise/models/trackable'
|
11
12
|
autoload :Validatable, 'devise/models/validatable'
|
@@ -28,7 +29,7 @@ module Devise
|
|
28
29
|
#
|
29
30
|
def self.config(mod, *accessors) #:nodoc:
|
30
31
|
accessors.each do |accessor|
|
31
|
-
mod.class_eval <<-METHOD, __FILE__, __LINE__
|
32
|
+
mod.class_eval <<-METHOD, __FILE__, __LINE__ + 1
|
32
33
|
def #{accessor}
|
33
34
|
if defined?(@#{accessor})
|
34
35
|
@#{accessor}
|
@@ -50,26 +51,18 @@ module Devise
|
|
50
51
|
#
|
51
52
|
# devise :authenticatable, :confirmable, :recoverable
|
52
53
|
#
|
53
|
-
# You can also give the
|
54
|
-
#
|
55
|
-
#
|
54
|
+
# You can also give any of the devise configuration values in form of a hash,
|
55
|
+
# with specific values for this model. Please check your Devise initializer
|
56
|
+
# for a complete description on those values.
|
56
57
|
#
|
57
58
|
def devise(*modules)
|
58
59
|
raise "You need to give at least one Devise module" if modules.empty?
|
59
60
|
options = modules.extract_options!
|
60
61
|
|
61
|
-
|
62
|
-
if modules.delete(:all)
|
63
|
-
ActiveSupport::Deprecation.warn "devise :all is deprecated. List your modules instead", caller
|
64
|
-
modules += Devise.all
|
65
|
-
end
|
66
|
-
|
67
|
-
modules -= Array(options.delete(:except))
|
68
|
-
modules = Devise::ALL & modules.uniq
|
62
|
+
@devise_modules = Devise::ALL & modules.map(&:to_sym).uniq
|
69
63
|
|
70
|
-
Devise.orm_class.included_modules_hook(self
|
71
|
-
|
72
|
-
devise_modules << m.to_sym
|
64
|
+
Devise.orm_class.included_modules_hook(self) do
|
65
|
+
devise_modules.each do |m|
|
73
66
|
include Devise::Models.const_get(m.to_s.classify)
|
74
67
|
end
|
75
68
|
|
@@ -116,4 +109,4 @@ module Devise
|
|
116
109
|
end
|
117
110
|
end
|
118
111
|
end
|
119
|
-
end
|
112
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'devise/strategies/authenticatable'
|
2
|
+
require 'devise/strategies/http_authenticatable'
|
2
3
|
|
3
4
|
module Devise
|
4
5
|
module Models
|
@@ -31,11 +32,17 @@ module Devise
|
|
31
32
|
base.class_eval do
|
32
33
|
extend ClassMethods
|
33
34
|
|
34
|
-
attr_reader :password, :
|
35
|
+
attr_reader :password, :current_password
|
35
36
|
attr_accessor :password_confirmation
|
36
37
|
end
|
37
38
|
end
|
38
39
|
|
40
|
+
# TODO Remove me in next release
|
41
|
+
def old_password
|
42
|
+
ActiveSupport::Deprecation.warn "old_password is deprecated, please use current_password instead", caller
|
43
|
+
@old_password
|
44
|
+
end
|
45
|
+
|
39
46
|
# Regenerates password salt and encrypted password each time password is set,
|
40
47
|
# and then trigger any "after_changed_password"-callbacks.
|
41
48
|
def password=(new_password)
|
@@ -63,15 +70,35 @@ module Devise
|
|
63
70
|
valid_password?(attributes[:password])
|
64
71
|
end
|
65
72
|
|
66
|
-
#
|
67
|
-
|
73
|
+
# Set password and password confirmation to nil
|
74
|
+
def clean_up_passwords
|
75
|
+
self.password = self.password_confirmation = nil
|
76
|
+
end
|
77
|
+
|
78
|
+
# Update record attributes when :current_password matches, otherwise returns
|
79
|
+
# error on :current_password. It also automatically rejects :password and
|
80
|
+
# :password_confirmation if they are blank.
|
68
81
|
def update_with_password(params={})
|
69
|
-
|
82
|
+
# TODO Remove me in next release
|
83
|
+
if params[:old_password].present?
|
84
|
+
params[:current_password] ||= params[:old_password]
|
85
|
+
ActiveSupport::Deprecation.warn "old_password is deprecated, please use current_password instead", caller
|
86
|
+
end
|
87
|
+
|
88
|
+
params.delete(:password) if params[:password].blank?
|
89
|
+
params.delete(:password_confirmation) if params[:password_confirmation].blank?
|
90
|
+
|
91
|
+
result = if valid_password?(params[:current_password])
|
70
92
|
update_attributes(params)
|
71
93
|
else
|
72
|
-
|
94
|
+
message = params[:current_password].blank? ? :blank : :invalid
|
95
|
+
self.class.add_error_on(self, :current_password, message, false)
|
96
|
+
self.attributes = params
|
73
97
|
false
|
74
98
|
end
|
99
|
+
|
100
|
+
clean_up_passwords unless result
|
101
|
+
result
|
75
102
|
end
|
76
103
|
|
77
104
|
protected
|
@@ -82,12 +109,10 @@ module Devise
|
|
82
109
|
end
|
83
110
|
|
84
111
|
module ClassMethods
|
85
|
-
|
86
112
|
Devise::Models.config(self, :pepper, :stretches, :encryptor, :authentication_keys)
|
87
113
|
|
88
114
|
# Authenticate a user based on configured attribute keys. Returns the
|
89
|
-
# authenticated user if it's valid or nil.
|
90
|
-
# :email and :password, but the latter is always required.
|
115
|
+
# authenticated user if it's valid or nil.
|
91
116
|
def authenticate(attributes={})
|
92
117
|
return unless authentication_keys.all? { |k| attributes[k].present? }
|
93
118
|
conditions = attributes.slice(*authentication_keys)
|
@@ -95,6 +120,11 @@ module Devise
|
|
95
120
|
resource if resource.try(:valid_for_authentication?, attributes)
|
96
121
|
end
|
97
122
|
|
123
|
+
# Authenticate an user using http.
|
124
|
+
def authenticate_with_http(username, password)
|
125
|
+
authenticate(authentication_keys.first => username, :password => password)
|
126
|
+
end
|
127
|
+
|
98
128
|
# Returns the class for the configured encryptor.
|
99
129
|
def encryptor_class
|
100
130
|
@encryptor_class ||= ::Devise::Encryptors.const_get(encryptor.to_s.classify)
|
@@ -3,9 +3,22 @@ require 'devise/models/activatable'
|
|
3
3
|
module Devise
|
4
4
|
module Models
|
5
5
|
|
6
|
+
# Handles blocking a user access after a certain number of attempts.
|
7
|
+
# Lockable accepts two different strategies to unlock a user after it's
|
8
|
+
# blocked: email and time. The former will send an email to the user when
|
9
|
+
# the lock happens, containing a link to unlock it's account. The second
|
10
|
+
# will unlock the user automatically after some configured time (ie 2.hours).
|
11
|
+
# It's also possible to setup lockable to use both email and time strategies.
|
12
|
+
#
|
13
|
+
# Configuration:
|
14
|
+
#
|
15
|
+
# maximum_attempts: how many attempts should be accepted before blocking the user.
|
16
|
+
# unlock_strategy: unlock the user account by :time, :email or :both.
|
17
|
+
# unlock_in: the time you want to lock the user after to lock happens. Only
|
18
|
+
# available when unlock_strategy is :time or :both.
|
19
|
+
#
|
6
20
|
module Lockable
|
7
21
|
include Devise::Models::Activatable
|
8
|
-
include Devise::Models::Authenticatable
|
9
22
|
|
10
23
|
def self.included(base)
|
11
24
|
base.class_eval do
|
@@ -16,19 +29,19 @@ module Devise
|
|
16
29
|
# Lock an user setting it's locked_at to actual time.
|
17
30
|
def lock
|
18
31
|
self.locked_at = Time.now
|
19
|
-
if
|
32
|
+
if unlock_strategy_enabled?(:email)
|
20
33
|
generate_unlock_token
|
21
|
-
|
34
|
+
send_unlock_instructions
|
22
35
|
end
|
23
36
|
end
|
24
37
|
|
25
|
-
#
|
38
|
+
# Lock an user also saving the record.
|
26
39
|
def lock!
|
27
|
-
|
40
|
+
lock
|
28
41
|
save(false)
|
29
42
|
end
|
30
43
|
|
31
|
-
# Unlock an user by cleaning locket_at and failed_attempts
|
44
|
+
# Unlock an user by cleaning locket_at and failed_attempts.
|
32
45
|
def unlock!
|
33
46
|
if_locked do
|
34
47
|
self.locked_at = nil
|
@@ -38,9 +51,9 @@ module Devise
|
|
38
51
|
end
|
39
52
|
end
|
40
53
|
|
41
|
-
# Verifies whether a user is locked or not
|
54
|
+
# Verifies whether a user is locked or not.
|
42
55
|
def locked?
|
43
|
-
|
56
|
+
locked_at && !lock_expired?
|
44
57
|
end
|
45
58
|
|
46
59
|
# Send unlock instructions by email
|
@@ -48,10 +61,10 @@ module Devise
|
|
48
61
|
::DeviseMailer.deliver_unlock_instructions(self)
|
49
62
|
end
|
50
63
|
|
51
|
-
# Resend the unlock instructions if the user is locked
|
64
|
+
# Resend the unlock instructions if the user is locked.
|
52
65
|
def resend_unlock!
|
53
66
|
if_locked do
|
54
|
-
generate_unlock_token unless
|
67
|
+
generate_unlock_token unless unlock_token.present?
|
55
68
|
save(false)
|
56
69
|
send_unlock_instructions
|
57
70
|
end
|
@@ -63,6 +76,16 @@ module Devise
|
|
63
76
|
super && !locked?
|
64
77
|
end
|
65
78
|
|
79
|
+
# Overwrites invalid_message from Devise::Models::Authenticatable to define
|
80
|
+
# the correct reason for blocking the sign in.
|
81
|
+
def inactive_message
|
82
|
+
if locked?
|
83
|
+
:locked
|
84
|
+
else
|
85
|
+
super
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
66
89
|
# Overwrites valid_for_authentication? from Devise::Models::Authenticatable
|
67
90
|
# for verifying whether an user is allowed to sign in or not. If the user
|
68
91
|
# is locked, it should never be allowed.
|
@@ -71,22 +94,12 @@ module Devise
|
|
71
94
|
self.failed_attempts = 0
|
72
95
|
else
|
73
96
|
self.failed_attempts += 1
|
74
|
-
|
97
|
+
lock if failed_attempts > self.class.maximum_attempts
|
75
98
|
end
|
76
99
|
save(false) if changed?
|
77
100
|
result
|
78
101
|
end
|
79
102
|
|
80
|
-
# Overwrites invalid_message from Devise::Models::Authenticatable to define
|
81
|
-
# the correct reason for blocking the sign in.
|
82
|
-
def inactive_message
|
83
|
-
if locked?
|
84
|
-
:locked
|
85
|
-
else
|
86
|
-
super
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
103
|
protected
|
91
104
|
|
92
105
|
# Generates unlock token
|
@@ -96,8 +109,8 @@ module Devise
|
|
96
109
|
|
97
110
|
# Tells if the lock is expired if :time unlock strategy is active
|
98
111
|
def lock_expired?
|
99
|
-
if
|
100
|
-
|
112
|
+
if unlock_strategy_enabled?(:time)
|
113
|
+
locked_at && locked_at < self.class.unlock_in.ago
|
101
114
|
else
|
102
115
|
false
|
103
116
|
end
|
@@ -114,6 +127,11 @@ module Devise
|
|
114
127
|
end
|
115
128
|
end
|
116
129
|
|
130
|
+
# Is the unlock enabled for the given unlock strategy?
|
131
|
+
def unlock_strategy_enabled?(strategy)
|
132
|
+
[:both, strategy].include?(self.class.unlock_strategy)
|
133
|
+
end
|
134
|
+
|
117
135
|
module ClassMethods
|
118
136
|
# Attempt to find a user by it's email. If a record is found, send new
|
119
137
|
# unlock instructions to it. If not user is found, returns a new user
|
@@ -139,4 +157,4 @@ module Devise
|
|
139
157
|
end
|
140
158
|
end
|
141
159
|
end
|
142
|
-
end
|
160
|
+
end
|
@@ -9,7 +9,7 @@ module Devise
|
|
9
9
|
#
|
10
10
|
# Configuration:
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# timeout_in: the time you want to timeout the user session without activity.
|
13
13
|
module Timeoutable
|
14
14
|
def self.included(base)
|
15
15
|
base.extend ClassMethods
|