devise 1.4.9 → 1.5.0.rc1

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 (70) hide show
  1. data/.travis.yml +1 -1
  2. data/CHANGELOG.rdoc +21 -0
  3. data/Gemfile +5 -3
  4. data/README.rdoc +25 -13
  5. data/app/controllers/devise/confirmations_controller.rb +2 -3
  6. data/app/controllers/devise/passwords_controller.rb +2 -3
  7. data/app/controllers/devise/registrations_controller.rb +2 -13
  8. data/app/controllers/devise/sessions_controller.rb +2 -2
  9. data/app/controllers/devise/unlocks_controller.rb +2 -3
  10. data/config/locales/en.yml +1 -1
  11. data/devise.gemspec +1 -1
  12. data/lib/devise.rb +6 -4
  13. data/lib/devise/controllers/helpers.rb +43 -27
  14. data/lib/devise/controllers/internal_helpers.rb +14 -8
  15. data/lib/devise/delegator.rb +16 -0
  16. data/lib/devise/encryptors/authlogic_sha512.rb +1 -1
  17. data/lib/devise/encryptors/clearance_sha1.rb +1 -1
  18. data/lib/devise/encryptors/restful_authentication_sha1.rb +1 -1
  19. data/lib/devise/encryptors/sha1.rb +1 -1
  20. data/lib/devise/encryptors/sha512.rb +1 -1
  21. data/lib/devise/failure_app.rb +2 -1
  22. data/lib/devise/hooks/timeoutable.rb +3 -1
  23. data/lib/devise/mailers/helpers.rb +0 -5
  24. data/lib/devise/mapping.rb +70 -44
  25. data/lib/devise/models/authenticatable.rb +14 -24
  26. data/lib/devise/models/confirmable.rb +3 -3
  27. data/lib/devise/models/database_authenticatable.rb +11 -1
  28. data/lib/devise/models/lockable.rb +7 -11
  29. data/lib/devise/models/recoverable.rb +3 -3
  30. data/lib/devise/models/trackable.rb +2 -2
  31. data/lib/devise/omniauth.rb +5 -4
  32. data/lib/devise/omniauth/config.rb +27 -5
  33. data/lib/devise/param_filter.rb +41 -0
  34. data/lib/devise/rails.rb +0 -11
  35. data/lib/devise/rails/routes.rb +10 -7
  36. data/lib/devise/strategies/authenticatable.rb +1 -11
  37. data/lib/devise/version.rb +1 -1
  38. data/lib/generators/active_record/templates/migration.rb +7 -1
  39. data/lib/generators/active_record/templates/migration_existing.rb +3 -3
  40. data/lib/generators/devise/views_generator.rb +30 -4
  41. data/lib/generators/templates/devise.rb +0 -1
  42. data/lib/generators/templates/markerb/confirmation_instructions.markerb +5 -0
  43. data/lib/generators/templates/markerb/reset_password_instructions.markerb +8 -0
  44. data/lib/generators/templates/markerb/unlock_instructions.markerb +7 -0
  45. data/test/controllers/helpers_test.rb +20 -11
  46. data/test/devise_test.rb +1 -1
  47. data/test/generators/active_record_generator_test.rb +16 -6
  48. data/test/generators/views_generator_test.rb +11 -4
  49. data/test/integration/authenticatable_test.rb +25 -3
  50. data/test/integration/confirmable_test.rb +27 -3
  51. data/test/integration/lockable_test.rb +17 -6
  52. data/test/integration/omniauthable_test.rb +6 -9
  53. data/test/integration/recoverable_test.rb +21 -2
  54. data/test/integration/registerable_test.rb +18 -1
  55. data/test/integration/timeoutable_test.rb +9 -0
  56. data/test/integration/trackable_test.rb +11 -0
  57. data/test/mailers/confirmation_instructions_test.rb +5 -0
  58. data/test/mailers/reset_password_instructions_test.rb +5 -0
  59. data/test/mailers/unlock_instructions_test.rb +5 -0
  60. data/test/models/database_authenticatable_test.rb +2 -19
  61. data/test/omniauth/config_test.rb +56 -0
  62. data/test/omniauth/my_other_strategy.rb +5 -0
  63. data/test/omniauth/omniauth-my_strategy.rb +5 -0
  64. data/test/omniauth/url_helpers_test.rb +4 -4
  65. data/test/rails_app/config/environments/development.rb +0 -1
  66. data/test/rails_app/config/initializers/devise.rb +2 -2
  67. data/test/rails_app/config/routes.rb +4 -4
  68. data/test/rails_app/lib/shared_admin.rb +1 -0
  69. data/test/support/helpers.rb +27 -0
  70. metadata +54 -77
@@ -40,7 +40,7 @@ module Devise
40
40
  # Resets reset password token and send reset password instructions by email
41
41
  def send_reset_password_instructions
42
42
  generate_reset_password_token! if should_generate_token?
43
- ::Devise.mailer.reset_password_instructions(self).deliver
43
+ self.devise_mailer.reset_password_instructions(self).deliver
44
44
  end
45
45
 
46
46
  # Checks if the reset password token sent is within the limit time.
@@ -64,7 +64,7 @@ module Devise
64
64
  # reset_password_period_valid? # will always return false
65
65
  #
66
66
  def reset_password_period_valid?
67
- return true unless respond_to?(:reset_password_sent_at)
67
+ return true unless respond_to?(:reset_password_sent_at)
68
68
  reset_password_sent_at && reset_password_sent_at.utc >= self.class.reset_password_within.ago
69
69
  end
70
70
 
@@ -121,7 +121,7 @@ module Devise
121
121
  recoverable = find_or_initialize_with_error_by(:reset_password_token, attributes[:reset_password_token])
122
122
  if recoverable.persisted?
123
123
  if recoverable.reset_password_period_valid?
124
- recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
124
+ recoverable.reset_password!(attributes[:password], attributes[:password_confirmation])
125
125
  else
126
126
  recoverable.errors.add(:reset_password_token, :expired)
127
127
  end
@@ -12,11 +12,11 @@ module Devise
12
12
  #
13
13
  module Trackable
14
14
  def update_tracked_fields!(request)
15
- old_current, new_current = self.current_sign_in_at, Time.now
15
+ old_current, new_current = self.current_sign_in_at, Time.now.utc
16
16
  self.last_sign_in_at = old_current || new_current
17
17
  self.current_sign_in_at = new_current
18
18
 
19
- old_current, new_current = self.current_sign_in_ip, request.remote_ip
19
+ old_current, new_current = self.current_sign_in_ip, request.ip
20
20
  self.last_sign_in_ip = old_current || new_current
21
21
  self.current_sign_in_ip = new_current
22
22
 
@@ -1,12 +1,13 @@
1
1
  begin
2
- require "omniauth/core"
2
+ require "omniauth"
3
+ require 'omniauth/version'
3
4
  rescue LoadError => e
4
- warn "Could not load 'omniauth/core'. Please ensure you have the oa-core gem installed and listed in your Gemfile."
5
+ warn "Could not load 'omniauth'. Please ensure you have the omniauth gem >= 1.0.0 installed and listed in your Gemfile."
5
6
  raise
6
7
  end
7
8
 
8
- unless OmniAuth.config.respond_to? :test_mode
9
- raise "You are using an old OmniAuth version, please ensure you have 0.2.0.beta version or later installed."
9
+ unless OmniAuth::VERSION =~ /^1\./
10
+ raise "You are using an old OmniAuth version, please ensure you have 1.0.0.pr2 version or later installed."
10
11
  end
11
12
 
12
13
  # Clean up the default path_prefix. It will be automatically set by Devise.
@@ -2,23 +2,45 @@ module Devise
2
2
  module OmniAuth
3
3
  class Config
4
4
  attr_accessor :strategy
5
- attr_reader :args
5
+ attr_reader :args, :options, :provider
6
6
 
7
7
  def initialize(provider, args)
8
8
  @provider = provider
9
9
  @args = args
10
10
  @strategy = nil
11
+ @options = @args.last.is_a?(Hash) ? @args.last : {}
11
12
  end
12
13
 
13
14
  # open_id strategy can have configurable name
14
15
  def strategy_name
15
- options = @args.last.is_a?(Hash) && @args.last
16
- options && options[:name] ? options[:name] : @provider
16
+ options[:name] || @provider
17
17
  end
18
18
 
19
19
  def strategy_class
20
- ::OmniAuth::Strategies.const_get("#{::OmniAuth::Utils.camelize(@provider.to_s)}")
20
+ find_strategy || require_strategy
21
+ end
22
+
23
+ def find_strategy
24
+ ::OmniAuth.strategies.find do |strategy_class|
25
+ strategy_class.to_s =~ /#{::OmniAuth::Utils.camelize(strategy_name)}$/ ||
26
+ strategy_class.default_options[:name] == strategy_name
27
+ end
28
+ end
29
+
30
+ def require_strategy
31
+ if [:facebook, :github, :twitter].include?(provider.to_sym)
32
+ require "omniauth/strategies/#{provider}"
33
+ elsif options[:require]
34
+ require options[:require]
35
+ else
36
+ require "omniauth-#{provider}"
37
+ end
38
+ find_strategy || autoload_strategy
39
+ end
40
+
41
+ def autoload_strategy
42
+ ::OmniAuth::Strategies.const_get(::OmniAuth::Utils.camelize(provider.to_s))
21
43
  end
22
44
  end
23
45
  end
24
- end
46
+ end
@@ -0,0 +1,41 @@
1
+ module Devise
2
+ class ParamFilter
3
+ def initialize(case_insensitive_keys, strip_whitespace_keys)
4
+ @case_insensitive_keys = case_insensitive_keys || []
5
+ @strip_whitespace_keys = strip_whitespace_keys || []
6
+ end
7
+
8
+ def filter(conditions)
9
+ conditions = stringify_params(conditions.dup)
10
+
11
+ @case_insensitive_keys.each do |k|
12
+ value = conditions[k]
13
+ next unless value.respond_to?(:downcase)
14
+ conditions[k] = value.downcase
15
+ end
16
+
17
+ @strip_whitespace_keys.each do |k|
18
+ value = conditions[k]
19
+ next unless value.respond_to?(:strip)
20
+ conditions[k] = value.strip
21
+ end
22
+
23
+ conditions
24
+ end
25
+
26
+ # Force keys to be string to avoid injection on mongoid related database.
27
+ def stringify_params(conditions)
28
+ return conditions unless conditions.is_a?(Hash)
29
+ conditions.each do |k, v|
30
+ conditions[k] = v.to_s if param_requires_string_conversion?(v)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ # Determine which values should be transformed to string or passed as-is to the query builder underneath
37
+ def param_requires_string_conversion?(value)
38
+ true unless value.is_a?(TrueClass) || value.is_a?(FalseClass) || value.is_a?(Fixnum)
39
+ end
40
+ end
41
+ end
@@ -17,17 +17,6 @@ module Devise
17
17
  Devise.include_helpers(Devise::Controllers)
18
18
  end
19
19
 
20
- initializer "devise.auth_keys" do
21
- if Devise.authentication_keys.size > 1
22
- puts "[DEVISE] You are configuring Devise to use more than one authentication key. " \
23
- "In previous versions, we automatically added #{Devise.authentication_keys[1..-1].inspect} " \
24
- "as scope to your e-mail validation, but this was changed now. If you were relying in such " \
25
- "behavior, you should remove :validatable from your models and add the validations manually. " \
26
- "To get rid of this warning, you can comment config.authentication_keys in your initializer " \
27
- "and pass the current values as key to the devise call in your model."
28
- end
29
- end
30
-
31
20
  initializer "devise.omniauth" do |app|
32
21
  Devise.omniauth_configs.each do |provider, config|
33
22
  app.middleware.use config.strategy_class, *config.args do |strategy|
@@ -49,23 +49,23 @@ module ActionDispatch::Routing
49
49
  #
50
50
  # You can configure your routes with some options:
51
51
  #
52
- # * :class_name => setup a different class to be looked up by devise,
53
- # if it cannot be correctly find by the route name.
52
+ # * :class_name => setup a different class to be looked up by devise, if it cannot be
53
+ # properly found by the route name.
54
54
  #
55
55
  # devise_for :users, :class_name => 'Account'
56
56
  #
57
57
  # * :path => allows you to setup path name that will be used, as rails routes does.
58
- # The following route configuration would setup your route as /accounts instead of /users:
58
+ # The following route configuration would setup your route as /accounts instead of /users:
59
59
  #
60
60
  # devise_for :users, :path => 'accounts'
61
61
  #
62
- # * :singular => setup the singular name for the given resource. This is used as the instance variable name in
63
- # controller, as the name in routes and the scope given to warden.
62
+ # * :singular => setup the singular name for the given resource. This is used as the instance variable
63
+ # name in controller, as the name in routes and the scope given to warden.
64
64
  #
65
65
  # devise_for :users, :singular => :user
66
66
  #
67
67
  # * :path_names => configure different path names to overwrite defaults :sign_in, :sign_out, :sign_up,
68
- # :password, :confirmation, :unlock.
68
+ # :password, :confirmation, :unlock.
69
69
  #
70
70
  # devise_for :users, :path_names => { :sign_in => 'login', :sign_out => 'logout', :password => 'secret', :confirmation => 'verification' }
71
71
  #
@@ -74,6 +74,9 @@ module ActionDispatch::Routing
74
74
  #
75
75
  # devise_for :users, :controllers => { :sessions => "users/sessions" }
76
76
  #
77
+ # * :failure_app => a rack app which is invoked whenever there is a failure. Strings representing a given
78
+ # are also allowed as parameter.
79
+ #
77
80
  # * :sign_out_via => the HTTP method(s) accepted for the :sign_out action (default: :get),
78
81
  # if you wish to restrict this to accept only :post or :delete requests you should do:
79
82
  #
@@ -354,7 +357,7 @@ module ActionDispatch::Routing
354
357
  path_prefix = "/#{mapping.path}/auth".squeeze("/")
355
358
 
356
359
  if ::OmniAuth.config.path_prefix && ::OmniAuth.config.path_prefix != path_prefix
357
- warn "[DEVISE] You can only add :omniauthable behavior to one model."
360
+ raise "You can only add :omniauthable behavior to one Devise model"
358
361
  else
359
362
  ::OmniAuth.config.path_prefix = path_prefix
360
363
  end
@@ -85,17 +85,7 @@ module Devise
85
85
 
86
86
  # By default, a request is valid if the controller is allowed and the VERB is POST.
87
87
  def valid_request?
88
- if env["devise.allow_params_authentication"]
89
- true
90
- elsif request.post? && mapping.controllers[:sessions] == params[:controller]
91
- ActiveSupport::Deprecation.warn "It seems that you are using a custom SessionsController. " \
92
- "In order for it to work from Devise 1.4.6 forward, you need to add the following:" \
93
- "\n\n prepend_before_filter :allow_params_authentication!, :only => :create\n\n" \
94
- "This will ensure your controller can authenticate from params for the create action.", caller
95
- true
96
- else
97
- false
98
- end
88
+ !!env["devise.allow_params_authentication"]
99
89
  end
100
90
 
101
91
  # If the request is valid, finally check if params_auth_hash returns a hash.
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "1.4.9".freeze
2
+ VERSION = "1.5.0.rc1".freeze
3
3
  end
@@ -1,5 +1,9 @@
1
1
  class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
2
+ <% if ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR >= 1 -%>
3
+ def change
4
+ <% else -%>
2
5
  def self.up
6
+ <% end -%>
3
7
  create_table(:<%= table_name %>) do |t|
4
8
  t.database_authenticatable :null => false
5
9
  t.recoverable
@@ -11,7 +15,7 @@ class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
11
15
  # t.lockable :lock_strategy => :<%= Devise.lock_strategy %>, :unlock_strategy => :<%= Devise.unlock_strategy %>
12
16
  # t.token_authenticatable
13
17
 
14
- <% for attribute in attributes -%>
18
+ <% attributes.each do |attribute| -%>
15
19
  t.<%= attribute.type %> :<%= attribute.name %>
16
20
  <% end -%>
17
21
 
@@ -25,7 +29,9 @@ class DeviseCreate<%= table_name.camelize %> < ActiveRecord::Migration
25
29
  # add_index :<%= table_name %>, :authentication_token, :unique => true
26
30
  end
27
31
 
32
+ <% unless ::Rails::VERSION::MAJOR == 3 && ::Rails::VERSION::MINOR >= 1 -%>
28
33
  def self.down
29
34
  drop_table :<%= table_name %>
30
35
  end
36
+ <% end -%>
31
37
  end
@@ -5,13 +5,13 @@ class AddDeviseTo<%= table_name.camelize %> < ActiveRecord::Migration
5
5
  t.recoverable
6
6
  t.rememberable
7
7
  t.trackable
8
-
8
+
9
9
  # t.encryptable
10
10
  # t.confirmable
11
11
  # t.lockable :lock_strategy => :<%= Devise.lock_strategy %>, :unlock_strategy => :<%= Devise.unlock_strategy %>
12
12
  # t.token_authenticatable
13
13
 
14
- <% for attribute in attributes -%>
14
+ <% attributes.each do |attribute| -%>
15
15
  t.<%= attribute.type %> :<%= attribute.name %>
16
16
  <% end -%>
17
17
 
@@ -29,6 +29,6 @@ class AddDeviseTo<%= table_name.camelize %> < ActiveRecord::Migration
29
29
  def self.down
30
30
  # By default, we don't want to make any assumption about how to roll back a migration when your
31
31
  # model already existed. Please edit below which fields you would like to remove in this migration.
32
- raise ActiveRecord::IrreversibleMigration
32
+ raise ActiveRecord::IrreversibleMigration
33
33
  end
34
34
  end
@@ -23,8 +23,8 @@ module Devise
23
23
 
24
24
  protected
25
25
 
26
- def view_directory(name)
27
- directory name.to_s, "#{target_path}/#{name}"
26
+ def view_directory(name, _target_path = nil)
27
+ directory name.to_s, _target_path || "#{target_path}/#{name}"
28
28
  end
29
29
 
30
30
  def target_path
@@ -39,7 +39,6 @@ module Devise
39
39
 
40
40
  # Override copy_views to just copy mailer and shared.
41
41
  def copy_views
42
- view_directory :mailer
43
42
  view_directory :shared
44
43
  end
45
44
  end
@@ -56,6 +55,30 @@ module Devise
56
55
  desc "Copies simple form enabled views to your application."
57
56
  end
58
57
 
58
+ class ErbGenerator < Rails::Generators::Base #:nodoc:
59
+ include ViewPathTemplates
60
+ source_root File.expand_path("../../../../app/views/devise", __FILE__)
61
+ desc "Copies Devise mail erb views to your application."
62
+
63
+ def copy_views
64
+ view_directory :mailer
65
+ end
66
+ end
67
+
68
+ class MarkerbGenerator < Rails::Generators::Base #:nodoc:
69
+ include ViewPathTemplates
70
+ source_root File.expand_path("../../templates", __FILE__)
71
+ desc "Copies Devise mail markerb views to your application."
72
+
73
+ def copy_views
74
+ view_directory :markerb, target_path
75
+ end
76
+
77
+ def target_path
78
+ "app/views/#{scope || :devise}/mailer"
79
+ end
80
+ end
81
+
59
82
  class ViewsGenerator < Rails::Generators::Base
60
83
  desc "Copies Devise views to your application."
61
84
 
@@ -63,10 +86,13 @@ module Devise
63
86
  :desc => "The scope to copy views to"
64
87
 
65
88
  invoke SharedViewsGenerator
66
-
67
89
  hook_for :form_builder, :aliases => "-b",
68
90
  :desc => "Form builder to be used",
69
91
  :default => defined?(SimpleForm) ? "simple_form_for" : "form_for"
92
+
93
+ hook_for :markerb, :desc => "Generate markerb instead of erb mail views",
94
+ :default => defined?(Markerb) ? :markerb : :erb,
95
+ :type => :boolean
70
96
  end
71
97
  end
72
98
  end
@@ -203,7 +203,6 @@ Devise.setup do |config|
203
203
  # change the failure app, you can configure them inside the config.warden block.
204
204
  #
205
205
  # config.warden do |manager|
206
- # manager.failure_app = AnotherApp
207
206
  # manager.intercept_401 = false
208
207
  # manager.default_strategies(:scope => :user).unshift :some_external_strategy
209
208
  # end
@@ -0,0 +1,5 @@
1
+ Welcome <%= @resource.email %>!
2
+
3
+ You can confirm your account through the link below:
4
+
5
+ <%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
@@ -0,0 +1,8 @@
1
+ Hello <%= @resource.email %>!
2
+
3
+ Someone has requested a link to change your password, and you can do this through the link below.
4
+
5
+ <%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @resource.reset_password_token) %>
6
+
7
+ If you didn't request this, please ignore this email.
8
+ Your password won't change until you access the link above and create a new one.
@@ -0,0 +1,7 @@
1
+ Hello <%= @resource.email %>!
2
+
3
+ Your account has been locked due to an excessive amount of unsuccessful sign in attempts.
4
+
5
+ Click the link below to unlock your account:
6
+
7
+ <%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @resource.unlock_token) %>
@@ -128,6 +128,26 @@ class ControllerAuthenticatableTest < ActionController::TestCase
128
128
  @controller.sign_in(user, :bypass => true)
129
129
  end
130
130
 
131
+ test 'sign out clears up any signed in user from all scopes' do
132
+ user = User.new
133
+ @mock_warden.expects(:user).times(Devise.mappings.size)
134
+ @mock_warden.expects(:logout).with().returns(true)
135
+ @controller.instance_variable_set(:@current_user, user)
136
+ @controller.instance_variable_set(:@current_admin, user)
137
+ @controller.sign_out
138
+ assert_equal nil, @controller.instance_variable_get(:@current_user)
139
+ assert_equal nil, @controller.instance_variable_get(:@current_admin)
140
+ end
141
+
142
+ test 'sign out clears up any signed in user by scope' do
143
+ user = User.new
144
+ @mock_warden.expects(:user).with(:user).returns(user)
145
+ @mock_warden.expects(:logout).with(:user).returns(true)
146
+ @controller.instance_variable_set(:@current_user, user)
147
+ @controller.sign_out(:user)
148
+ assert_equal nil, @controller.instance_variable_get(:@current_user)
149
+ end
150
+
131
151
  test 'sign out proxy to logout on warden' do
132
152
  @mock_warden.expects(:user).with(:user).returns(true)
133
153
  @mock_warden.expects(:logout).with(:user).returns(true)
@@ -208,17 +228,6 @@ class ControllerAuthenticatableTest < ActionController::TestCase
208
228
  @controller.sign_in_and_redirect(admin)
209
229
  end
210
230
 
211
- test 'redirect_location returns the stored location if set' do
212
- user = User.new
213
- @controller.session[:"user_return_to"] = "/foo.bar"
214
- assert_equal '/foo.bar', @controller.redirect_location('user', user)
215
- end
216
-
217
- test 'redirect_location returns the after sign in path by default' do
218
- user = User.new
219
- assert_equal @controller.after_sign_in_path_for(:user), @controller.redirect_location('user', user)
220
- end
221
-
222
231
  test 'sign out and redirect uses the configured after sign out path when signing out only the current scope' do
223
232
  swap Devise, :sign_out_all_scopes => false do
224
233
  @mock_warden.expects(:user).with(:admin).returns(true)
@@ -25,7 +25,7 @@ class DeviseTest < ActiveSupport::TestCase
25
25
  end
26
26
 
27
27
  test 'stores warden configuration' do
28
- assert_equal Devise::FailureApp, Devise.warden_config.failure_app
28
+ assert_kind_of Devise::Delegator, Devise.warden_config.failure_app
29
29
  assert_equal :user, Devise.warden_config.default_scope
30
30
  end
31
31