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.

Files changed (54) hide show
  1. data/CHANGELOG.rdoc +11 -0
  2. data/README.rdoc +3 -2
  3. data/TODO +0 -1
  4. data/app/controllers/confirmations_controller.rb +18 -7
  5. data/app/controllers/passwords_controller.rb +18 -7
  6. data/app/controllers/registrations_controller.rb +55 -0
  7. data/app/controllers/sessions_controller.rb +17 -5
  8. data/app/controllers/unlocks_controller.rb +18 -7
  9. data/app/models/devise_mailer.rb +3 -2
  10. data/app/views/registrations/edit.html.erb +25 -0
  11. data/app/views/registrations/new.html.erb +17 -0
  12. data/app/views/sessions/new.html.erb +10 -12
  13. data/app/views/shared/_devise_links.erb +5 -1
  14. data/generators/devise_install/templates/devise.rb +3 -1
  15. data/lib/devise.rb +15 -8
  16. data/lib/devise/controllers/helpers.rb +1 -1
  17. data/lib/devise/controllers/internal_helpers.rb +15 -6
  18. data/lib/devise/controllers/url_helpers.rb +7 -7
  19. data/lib/devise/locales/en.yml +6 -0
  20. data/lib/devise/mapping.rb +9 -15
  21. data/lib/devise/models.rb +9 -16
  22. data/lib/devise/models/authenticatable.rb +38 -8
  23. data/lib/devise/models/lockable.rb +42 -24
  24. data/lib/devise/models/registerable.rb +8 -0
  25. data/lib/devise/models/timeoutable.rb +1 -1
  26. data/lib/devise/orm/active_record.rb +3 -2
  27. data/lib/devise/orm/data_mapper.rb +3 -3
  28. data/lib/devise/orm/mongo_mapper.rb +3 -3
  29. data/lib/devise/rails/routes.rb +9 -6
  30. data/lib/devise/strategies/authenticatable.rb +11 -1
  31. data/lib/devise/strategies/base.rb +5 -13
  32. data/lib/devise/strategies/http_authenticatable.rb +49 -0
  33. data/lib/devise/strategies/rememberable.rb +1 -1
  34. data/lib/devise/strategies/token_authenticatable.rb +9 -10
  35. data/lib/devise/version.rb +1 -1
  36. data/test/devise_test.rb +1 -1
  37. data/test/integration/authenticatable_test.rb +59 -43
  38. data/test/integration/http_authenticatable_test.rb +44 -0
  39. data/test/integration/registerable_test.rb +130 -0
  40. data/test/integration/token_authenticatable_test.rb +4 -4
  41. data/test/mailers/confirmation_instructions_test.rb +9 -0
  42. data/test/mapping_test.rb +14 -10
  43. data/test/models/authenticatable_test.rb +31 -9
  44. data/test/models/lockable_test.rb +8 -8
  45. data/test/models_test.rb +1 -1
  46. data/test/rails_app/app/active_record/admin.rb +1 -1
  47. data/test/rails_app/app/active_record/user.rb +4 -2
  48. data/test/rails_app/config/routes.rb +3 -2
  49. data/test/routes_test.rb +35 -0
  50. data/test/support/integration_tests_helper.rb +5 -1
  51. data/test/support/tests_helper.rb +35 -1
  52. metadata +9 -4
  53. data/lib/devise/controllers/common.rb +0 -24
  54. 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 without setting password and password confirmation fields.
73
+ # Build a devise resource.
63
74
  def build_resource
64
- self.resource ||= begin
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
- if Devise.scoped_views
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
- [:session, :password, :confirmation, :unlock].each do |module_name|
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 module_name == :password
26
- actions << :destroy_ if module_name == :session
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}(resource, *args)
31
- resource = Devise::Mapping.find_scope!(resource)
32
- send("#{action}\#{resource}_#{module_name}_#{path_or_url}", *args)
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
@@ -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
+
@@ -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.values.find { |m| return m if klass <= m.to }
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
- @path_names = options.delete(:path_names) || {}
66
- @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
68
+
69
+ @path_prefix = "/#{options.delete(:path_prefix)}/".squeeze("/")
67
70
  @route_options = options || {}
68
71
 
69
- setup_path_names
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
@@ -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 following configuration values in a hash: :pepper,
54
- # :stretches, :confirm_within and :remember_for. Please check your Devise
55
- # initialiazer for a complete description on those values.
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
- # TODO Remove me
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, modules) do
71
- modules.each do |m|
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, :old_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
- # Update record attributes when :old_password matches, otherwise returns
67
- # error on :old_password.
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
- if valid_password?(params[:old_password])
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
- self.class.add_error_on(self, :old_password, :invalid, false)
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. Attributes are by default
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 [:both, :email].include?(self.class.unlock_strategy)
32
+ if unlock_strategy_enabled?(:email)
20
33
  generate_unlock_token
21
- self.send_unlock_instructions
34
+ send_unlock_instructions
22
35
  end
23
36
  end
24
37
 
25
- # calls lock and save the model
38
+ # Lock an user also saving the record.
26
39
  def lock!
27
- self.lock
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
- self.locked_at && !lock_expired?
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 self.unlock_token.present?
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
- self.lock if self.failed_attempts > self.class.maximum_attempts
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 [:both, :time].include?(self.class.unlock_strategy)
100
- self.locked_at && self.locked_at < self.class.unlock_in.ago
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
@@ -0,0 +1,8 @@
1
+ module Devise
2
+ module Models
3
+ # Registerable is responsible for everything related to registering a new
4
+ # resource (ie user sign up).
5
+ module Registerable
6
+ end
7
+ end
8
+ end
@@ -9,7 +9,7 @@ module Devise
9
9
  #
10
10
  # Configuration:
11
11
  #
12
- # timeout: the time you want to timeout the user session without activity.
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