devise 3.2.0 → 3.2.1

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.

@@ -1,3 +1,15 @@
1
+ ### 3.2.1
2
+
3
+ Security announcement: http://blog.plataformatec.com.br/2013/11/e-mail-enumeration-in-devise-in-paranoid-mode
4
+
5
+ * enhancements
6
+ * Add `store_location_for` helper and ensure it is safe (by @matthewrudy and @homakov)
7
+ * Add `yield` around resource methods in Devise controllers (by @edelpero)
8
+
9
+ * bug fix
10
+ * Bring `password_digest` back to fix compatibility with `devise-encryptable`
11
+ * Avoid e-mail enumeration on sign in when in paranoid mode
12
+
1
13
  ### 3.2.0
2
14
 
3
15
  * enhancements
@@ -12,7 +12,7 @@ GIT
12
12
  PATH
13
13
  remote: .
14
14
  specs:
15
- devise (3.2.0)
15
+ devise (3.2.1)
16
16
  bcrypt-ruby (~> 3.0)
17
17
  orm_adapter (~> 0.1)
18
18
  railties (>= 3.2.6, < 5)
@@ -7,6 +7,7 @@ class Devise::ConfirmationsController < DeviseController
7
7
  # POST /resource/confirmation
8
8
  def create
9
9
  self.resource = resource_class.send_confirmation_instructions(resource_params)
10
+ yield resource if block_given?
10
11
 
11
12
  if successfully_sent?(resource)
12
13
  respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
@@ -18,6 +19,7 @@ class Devise::ConfirmationsController < DeviseController
18
19
  # GET /resource/confirmation?confirmation_token=abcdef
19
20
  def show
20
21
  self.resource = resource_class.confirm_by_token(params[:confirmation_token])
22
+ yield resource if block_given?
21
23
 
22
24
  if resource.errors.empty?
23
25
  set_flash_message(:notice, :confirmed) if is_flashing_format?
@@ -11,6 +11,7 @@ class Devise::PasswordsController < DeviseController
11
11
  # POST /resource/password
12
12
  def create
13
13
  self.resource = resource_class.send_reset_password_instructions(resource_params)
14
+ yield resource if block_given?
14
15
 
15
16
  if successfully_sent?(resource)
16
17
  respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
@@ -28,6 +29,7 @@ class Devise::PasswordsController < DeviseController
28
29
  # PUT /resource/password
29
30
  def update
30
31
  self.resource = resource_class.reset_password_by_token(resource_params)
32
+ yield resource if block_given?
31
33
 
32
34
  if resource.errors.empty?
33
35
  resource.unlock_access! if unlockable?(resource)
@@ -13,6 +13,7 @@ class Devise::RegistrationsController < DeviseController
13
13
  build_resource(sign_up_params)
14
14
 
15
15
  if resource.save
16
+ yield resource if block_given?
16
17
  if resource.active_for_authentication?
17
18
  set_flash_message :notice, :signed_up if is_flashing_format?
18
19
  sign_up(resource_name, resource)
@@ -41,6 +42,7 @@ class Devise::RegistrationsController < DeviseController
41
42
  prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
42
43
 
43
44
  if update_resource(resource, account_update_params)
45
+ yield resource if block_given?
44
46
  if is_flashing_format?
45
47
  flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
46
48
  :update_needs_confirmation : :updated
@@ -59,6 +61,7 @@ class Devise::RegistrationsController < DeviseController
59
61
  resource.destroy
60
62
  Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
61
63
  set_flash_message :notice, :destroyed if is_flashing_format?
64
+ yield resource if block_given?
62
65
  respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
63
66
  end
64
67
 
@@ -15,6 +15,7 @@ class Devise::SessionsController < DeviseController
15
15
  self.resource = warden.authenticate!(auth_options)
16
16
  set_flash_message(:notice, :signed_in) if is_flashing_format?
17
17
  sign_in(resource_name, resource)
18
+ yield resource if block_given?
18
19
  respond_with resource, :location => after_sign_in_path_for(resource)
19
20
  end
20
21
 
@@ -23,6 +24,7 @@ class Devise::SessionsController < DeviseController
23
24
  redirect_path = after_sign_out_path_for(resource_name)
24
25
  signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
25
26
  set_flash_message :notice, :signed_out if signed_out && is_flashing_format?
27
+ yield resource if block_given?
26
28
 
27
29
  # We actually need to hardcode this as Rails default responder doesn't
28
30
  # support returning empty response on GET request
@@ -9,6 +9,7 @@ class Devise::UnlocksController < DeviseController
9
9
  # POST /resource/unlock
10
10
  def create
11
11
  self.resource = resource_class.send_unlock_instructions(resource_params)
12
+ yield resource if block_given?
12
13
 
13
14
  if successfully_sent?(resource)
14
15
  respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
@@ -20,6 +21,7 @@ class Devise::UnlocksController < DeviseController
20
21
  # GET /resource/unlock?unlock_token=abcdef
21
22
  def show
22
23
  self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
24
+ yield resource if block_given?
23
25
 
24
26
  if resource.errors.empty?
25
27
  set_flash_message :notice, :unlocked if is_flashing_format?
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- devise (3.2.0)
4
+ devise (3.2.1)
5
5
  bcrypt-ruby (~> 3.0)
6
6
  orm_adapter (~> 0.1)
7
7
  railties (>= 3.2.6, < 5)
@@ -21,6 +21,7 @@ module Devise
21
21
  autoload :Rememberable, 'devise/controllers/rememberable'
22
22
  autoload :ScopedViews, 'devise/controllers/scoped_views'
23
23
  autoload :SignInOut, 'devise/controllers/sign_in_out'
24
+ autoload :StoreLocation, 'devise/controllers/store_location'
24
25
  autoload :UrlHelpers, 'devise/controllers/url_helpers'
25
26
  end
26
27
 
@@ -4,6 +4,7 @@ module Devise
4
4
  module Helpers
5
5
  extend ActiveSupport::Concern
6
6
  include Devise::Controllers::SignInOut
7
+ include Devise::Controllers::StoreLocation
7
8
 
8
9
  included do
9
10
  helper_method :warden, :signed_in?, :devise_controller?
@@ -97,23 +98,6 @@ module Devise
97
98
  request.env["devise.allow_params_authentication"] = true
98
99
  end
99
100
 
100
- # Returns and delete (if it's navigational format) the url stored in the session for
101
- # the given scope. Useful for giving redirect backs after sign up:
102
- #
103
- # Example:
104
- #
105
- # redirect_to stored_location_for(:user) || root_path
106
- #
107
- def stored_location_for(resource_or_scope)
108
- scope = Devise::Mapping.find_scope!(resource_or_scope)
109
-
110
- if is_navigational_format?
111
- session.delete("#{scope}_return_to")
112
- else
113
- session["#{scope}_return_to"]
114
- end
115
- end
116
-
117
101
  # The scope root url to be used when he's signed in. By default, it first
118
102
  # tries to find a resource_root_path, otherwise it uses the root_path.
119
103
  def signed_in_root_path(resource_or_scope)
@@ -0,0 +1,47 @@
1
+ require "uri"
2
+
3
+ module Devise
4
+ module Controllers
5
+ # Provide the ability to store a location.
6
+ # Used to redirect back to a desired path after sign in.
7
+ # Included by default in all controllers.
8
+ module StoreLocation
9
+ # Returns and delete (if it's navigational format) the url stored in the session for
10
+ # the given scope. Useful for giving redirect backs after sign up:
11
+ #
12
+ # Example:
13
+ #
14
+ # redirect_to stored_location_for(:user) || root_path
15
+ #
16
+ def stored_location_for(resource_or_scope)
17
+ session_key = stored_location_key_for(resource_or_scope)
18
+
19
+ if is_navigational_format?
20
+ session.delete(session_key)
21
+ else
22
+ session[session_key]
23
+ end
24
+ end
25
+
26
+ # Stores the provided location to redirect the user after signing in.
27
+ # Useful in combination with the `stored_location_for` helper.
28
+ #
29
+ # Example:
30
+ #
31
+ # store_location_for(:user, dashboard_path)
32
+ # redirect_to user_omniauth_authorize_path(:facebook)
33
+ #
34
+ def store_location_for(resource_or_scope, location)
35
+ session_key = stored_location_key_for(resource_or_scope)
36
+ session[session_key] = URI.parse(location).path if location
37
+ end
38
+
39
+ private
40
+
41
+ def stored_location_key_for(resource_or_scope)
42
+ scope = Devise::Mapping.find_scope!(resource_or_scope)
43
+ "#{scope}_return_to"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -13,6 +13,8 @@ module Devise
13
13
  include Rails.application.routes.url_helpers
14
14
  include Rails.application.routes.mounted_helpers
15
15
 
16
+ include Devise::Controllers::StoreLocation
17
+
16
18
  delegate :flash, :to => :request
17
19
 
18
20
  def self.call(env)
@@ -189,7 +191,7 @@ module Devise
189
191
  # yet, but we still need to store the uri based on scope, so different scopes
190
192
  # would never use the same uri to redirect.
191
193
  def store_location!
192
- session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
194
+ store_location_for(scope, attempted_path) if request.get? && !http_auth?
193
195
  end
194
196
 
195
197
  def is_navigational_format?
@@ -39,7 +39,7 @@ module Devise
39
39
  # Generates password encryption based on the given value.
40
40
  def password=(new_password)
41
41
  @password = new_password
42
- self.encrypted_password = Devise.bcrypt(self.class, @password) if @password.present?
42
+ self.encrypted_password = password_digest(@password) if @password.present?
43
43
  end
44
44
 
45
45
  # Verifies whether an password (ie from sign in) is the user password.
@@ -135,6 +135,15 @@ module Devise
135
135
 
136
136
  protected
137
137
 
138
+ # Digests the password using bcrypt. Custom encryption should override
139
+ # this method to apply their own algorithm.
140
+ #
141
+ # See https://github.com/plataformatec/devise-encryptable for examples
142
+ # of other encryption engines.
143
+ def password_digest(password)
144
+ Devise.bcrypt(self.class, password)
145
+ end
146
+
138
147
  module ClassMethods
139
148
  Devise::Models.config(self, :pepper, :stretches)
140
149
 
@@ -387,8 +387,23 @@ module ActionDispatch::Routing
387
387
 
388
388
  def devise_omniauth_callback(mapping, controllers) #:nodoc:
389
389
  if mapping.fullpath =~ /:[a-zA-Z_]/
390
- raise "[DEVISE] Nesting omniauth callbacks under scopes with dynamic segments " \
391
- "is not supported. Please, use Devise.omniauth_path_prefix instead."
390
+ raise <<-ERROR
391
+ Devise does not support scoping omniauth callbacks under a dynamic segment
392
+ and you have set #{mapping.fullpath.inspect}. You can work around by passing
393
+ `skip: :omniauth_callbacks` and manually defining the routes. Here is an example:
394
+
395
+ match "/users/auth/:provider",
396
+ :constraints => { :provider => /\Agoogle|facebook\z/ },
397
+ :to => "devise/omniauth_callbacks#passthru",
398
+ :as => :omniauth_authorize,
399
+ :via => [:get, :post]
400
+
401
+ match "/users/auth/:action/callback",
402
+ :constraints => { :action => /\Agoogle|facebook\z/ },
403
+ :to => "devise/omniauth_callbacks",
404
+ :as => :omniauth_callback,
405
+ :via => [:get, :post]
406
+ ERROR
392
407
  end
393
408
 
394
409
  path, @scope[:path] = @scope[:path], nil
@@ -5,13 +5,16 @@ module Devise
5
5
  # Default strategy for signing in a user, based on his email and password in the database.
6
6
  class DatabaseAuthenticatable < Authenticatable
7
7
  def authenticate!
8
- resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
- return fail(:not_found_in_database) unless resource
8
+ resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
9
+ encrypted = false
10
10
 
11
- if validate(resource){ resource.valid_password?(password) }
11
+ if validate(resource){ encrypted = true; resource.valid_password?(password) }
12
12
  resource.after_database_authentication
13
13
  success!(resource)
14
14
  end
15
+
16
+ mapping.to.new.password = password if !encrypted && Devise.paranoid
17
+ fail(:not_found_in_database) unless resource
15
18
  end
16
19
  end
17
20
  end
@@ -1,3 +1,3 @@
1
1
  module Devise
2
- VERSION = "3.2.0".freeze
2
+ VERSION = "3.2.1".freeze
3
3
  end
@@ -187,6 +187,23 @@ class ControllerAuthenticatableTest < ActionController::TestCase
187
187
  assert_nil @controller.session[:"user_return_to"]
188
188
  end
189
189
 
190
+ test 'store location for stores a location to redirect back to' do
191
+ assert_nil @controller.stored_location_for(:user)
192
+ @controller.store_location_for(:user, "/foo.bar")
193
+ assert_equal "/foo.bar", @controller.stored_location_for(:user)
194
+ end
195
+
196
+ test 'store location for accepts a resource as argument' do
197
+ @controller.store_location_for(User.new, "/foo.bar")
198
+ assert_equal "/foo.bar", @controller.stored_location_for(User.new)
199
+ end
200
+
201
+ test 'store location for stores only paths' do
202
+ assert_nil @controller.stored_location_for(:user)
203
+ @controller.store_location_for(:user, "//host/foo.bar")
204
+ assert_equal "/foo.bar", @controller.stored_location_for(:user)
205
+ end
206
+
190
207
  test 'after sign in path defaults to root path if none by was specified for the given scope' do
191
208
  assert_equal root_path, @controller.after_sign_in_path_for(:user)
192
209
  end
@@ -93,6 +93,11 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
93
93
  assert_present user.encrypted_password
94
94
  end
95
95
 
96
+ test 'should support custom encryption methods' do
97
+ user = UserWithCustomEncryption.new(:password => '654321')
98
+ assert_equal user.encrypted_password, '123456'
99
+ end
100
+
96
101
  test 'allow authenticatable_salt to work even with nil encrypted password' do
97
102
  user = User.new
98
103
  user.encrypted_password = nil
@@ -2,7 +2,13 @@ unless defined?(DEVISE_ORM)
2
2
  DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
3
3
  end
4
4
 
5
+ module Devise
6
+ # Detection for minor differences between Rails 3.2 and 4 in tests.
7
+ def self.rails4?
8
+ Rails.version.start_with? '4'
9
+ end
10
+ end
11
+
5
12
  # Set up gems listed in the Gemfile.
6
13
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
7
-
8
14
  require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
@@ -22,10 +22,6 @@ RailsApp::Application.configure do
22
22
  # Only use best-standards-support built into browsers.
23
23
  config.action_dispatch.best_standards_support = :builtin
24
24
 
25
- # Log the query plan for queries taking more than this (works
26
- # with SQLite, MySQL, and PostgreSQL).
27
- config.active_record.auto_explain_threshold_in_seconds = 0.5
28
-
29
25
  # Raise an error on page load if there are pending migrations
30
26
  config.active_record.migration_error = :page_load
31
27
 
@@ -72,10 +72,6 @@ RailsApp::Application.configure do
72
72
  # Send deprecation notices to registered listeners.
73
73
  config.active_support.deprecation = :notify
74
74
 
75
- # Log the query plan for queries taking more than this (works
76
- # with SQLite, MySQL, and PostgreSQL).
77
- # config.active_record.auto_explain_threshold_in_seconds = 0.5
78
-
79
75
  # Disable automatic flushing of the log to improve performance.
80
76
  # config.autoflush_log = false
81
77
 
@@ -1,3 +1,4 @@
1
+ # encoding: UTF-8
1
2
  # This file is auto-generated from the current state of the database. Instead
2
3
  # of editing this file, please use the migrations feature of Active Record to
3
4
  # incrementally modify your database, and then regenerate this schema definition.
@@ -8,40 +9,43 @@
8
9
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9
10
  # you'll amass, the slower it'll run and the greater likelihood for issues).
10
11
  #
11
- # It's strongly recommended to check this file into your version control system.
12
+ # It's strongly recommended that you check this file into your version control system.
12
13
 
13
- ActiveRecord::Schema.define(:version => 20100401102949) do
14
+ ActiveRecord::Schema.define(version: 20100401102949) do
14
15
 
15
- create_table "admins", :force => true do |t|
16
+ create_table "admins", force: true do |t|
16
17
  t.string "email"
17
- t.string "encrypted_password", :limit => 128
18
- t.string "password_salt"
19
- t.string "remember_token"
20
- t.datetime "remember_created_at"
18
+ t.string "encrypted_password"
21
19
  t.string "reset_password_token"
22
- t.integer "failed_attempts", :default => 0
23
- t.string "unlock_token"
20
+ t.datetime "reset_password_sent_at"
21
+ t.datetime "remember_created_at"
22
+ t.string "confirmation_token"
23
+ t.datetime "confirmed_at"
24
+ t.datetime "confirmation_sent_at"
25
+ t.string "unconfirmed_email"
24
26
  t.datetime "locked_at"
27
+ t.boolean "active", default: false
25
28
  t.datetime "created_at"
26
29
  t.datetime "updated_at"
27
30
  end
28
31
 
29
- create_table "users", :force => true do |t|
32
+ create_table "users", force: true do |t|
30
33
  t.string "username"
31
34
  t.string "facebook_token"
32
- t.string "email", :default => "", :null => false
33
- t.string "encrypted_password", :limit => 128, :default => "", :null => false
34
- t.string "confirmation_token"
35
- t.datetime "confirmed_at"
36
- t.datetime "confirmation_sent_at"
35
+ t.string "email", default: "", null: false
36
+ t.string "encrypted_password", default: "", null: false
37
37
  t.string "reset_password_token"
38
+ t.datetime "reset_password_sent_at"
38
39
  t.datetime "remember_created_at"
39
- t.integer "sign_in_count", :default => 0
40
+ t.integer "sign_in_count", default: 0
40
41
  t.datetime "current_sign_in_at"
41
42
  t.datetime "last_sign_in_at"
42
43
  t.string "current_sign_in_ip"
43
44
  t.string "last_sign_in_ip"
44
- t.integer "failed_attempts", :default => 0
45
+ t.string "confirmation_token"
46
+ t.datetime "confirmed_at"
47
+ t.datetime "confirmation_sent_at"
48
+ t.integer "failed_attempts", default: 0
45
49
  t.string "unlock_token"
46
50
  t.datetime "locked_at"
47
51
  t.datetime "created_at"
@@ -4,13 +4,6 @@ DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
4
4
  $:.unshift File.dirname(__FILE__)
5
5
  puts "\n==> Devise.orm = #{DEVISE_ORM.inspect}"
6
6
 
7
- module Devise
8
- # Detection for minor differences between Rails 3.2 and 4 in tests.
9
- def self.rails4?
10
- Rails.version.start_with? '4'
11
- end
12
- end
13
-
14
7
  require "rails_app/config/environment"
15
8
  require "rails/test_help"
16
9
  require "orm/#{DEVISE_ORM}"
@@ -12,6 +12,13 @@ class UserWithValidation < User
12
12
  validates_presence_of :username
13
13
  end
14
14
 
15
+ class UserWithCustomEncryption < User
16
+ protected
17
+ def password_digest(password)
18
+ password.reverse
19
+ end
20
+ end
21
+
15
22
  class UserWithVirtualAttributes < User
16
23
  devise :case_insensitive_keys => [ :email, :email_confirmation ]
17
24
  validates :email, :presence => true, :confirmation => {:on => :create}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-11-06 00:00:00.000000000 Z
13
+ date: 2013-11-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: warden
@@ -144,6 +144,7 @@ files:
144
144
  - lib/devise/controllers/rememberable.rb
145
145
  - lib/devise/controllers/scoped_views.rb
146
146
  - lib/devise/controllers/sign_in_out.rb
147
+ - lib/devise/controllers/store_location.rb
147
148
  - lib/devise/controllers/url_helpers.rb
148
149
  - lib/devise/delegator.rb
149
150
  - lib/devise/failure_app.rb