Compare commits

..

23 Commits

Author SHA1 Message Date
José Valim
65947b6696 Release v3.2.2 2013-11-25 12:00:21 +01:00
Vasiliy Ermolovich
0028dc6a4f Merge pull request #2751 from fgo/patch-1
Fix spelling in README [ci skip]
2013-11-22 00:57:03 -08:00
Francis Go
f438209669 Fix spelling in README 2013-11-22 19:55:16 +11:00
José Valim
c85ecbb9ac Merge pull request #2750 from louman/master
Timeoutable - fixes missing caller
2013-11-21 08:38:12 -08:00
Marcus Mansur
b16899f7bf fixes timeoutable specs to cover sign_out_all_scopes false 2013-11-21 14:02:45 -02:00
Marcus Mansur
bd83483ba6 fixes missing caller 2013-11-21 13:26:34 -02:00
José Valim
0514e60bc4 Merge pull request #2749 from csexton/master
Keep the query string and path in store_location_for
2013-11-20 13:13:12 -08:00
Christopher Sexton
7afc096fa4 Keep the query string and path in store_location_for
Persist the URI's query when saving to the session.

Fixes #2742
2013-11-20 15:47:20 -05:00
Vasiliy Ermolovich
bb2ff3553b require rails generator base class in devise generators
closes #2743
2013-11-15 15:56:09 +03:00
José Valim
1390945e5c Improve default omniauth sample 2013-11-14 09:09:05 +01:00
José Valim
f36efc0cc9 Ensure multiple leading / are also removed, thanks @homakov 2013-11-13 15:01:23 +01:00
José Valim
3b598ec235 Release v3.2.1 2013-11-13 14:15:13 +01:00
José Valim
95ec62ea76 Ensure encryption on authentication 2013-11-13 13:45:34 +01:00
José Valim
9a412c139f Update CHANGELOG 2013-11-13 13:32:59 +01:00
José Valim
0582467032 Ensure we only store paths in store_location_for (thanks to @homakov for the tip) 2013-11-13 13:30:24 +01:00
José Valim
221be6d6ef Update bundled rails app 2013-11-13 13:29:25 +01:00
José Valim
ed86361b92 Merge pull request #2728 from edelpero/master
Adds yield around resource on devise controllers
2013-11-08 23:22:53 -08:00
José Valim
e303de9756 Merge pull request #2729 from matthewrudy/store-location-helper
Add store_location_for helper
2013-11-08 23:22:39 -08:00
Matthew Rudy Jacobs
268e486dbb Add store_location_for helper
This is used as a complement to `stored_location_for`.

Example:

Before authorizing with Omniauth;

  store_location_for(:user, dashboard_path)
  redirect_to user_omniauth_authorize_path(:facebook)

In our Omniauth callback

  sign_in(user)
  redirect_to stored_location_for(:user) || root_path
2013-11-09 00:59:00 +00:00
Ezequiel Delpero
989071144e Adds yield around resource on devise controllers
If you want to add a new behavior to your devise
controllers but you don't want to override devise's
default workflow, just pass a block around resource.

This would give you for example, the ability to
trigger background jobs after user signs in.
2013-11-08 20:43:08 -03:00
Rafael Mendonça França
25726becdd Merge pull request #2731 from plataformatec/lm-password-digest
Bring `password_digest` back.
2013-11-08 10:28:11 -08:00
Lucas Mazza
bf5bcd52cb Bring password_digest back.
This method is part of the protected API and is used by custom
encryption engines (like `devise-encryptable`) to hook the custom
encryption logic in the models.

Fixes #2730
2013-11-08 16:22:31 -02:00
José Valim
e26ea51fe5 Improve error message for wrongly nested omniauth callback 2013-11-07 14:30:32 +01:00
31 changed files with 204 additions and 69 deletions

View File

@@ -1,3 +1,22 @@
### 3.2.2
* bug fix
* Ensure timeoutable works when `sign_out_all_scopes` is false (by @louman)
* Keep the query string when storing location (by @csexton)
* Require rails generator base class in devise generators
### 3.2.1
Security announcement: http://blog.plataformatec.com.br/2013/11/e-mail-enumeration-in-devise-in-paranoid-mode
* enhancements
* Add `store_location_for` helper and ensure it is safe (by @matthewrudy and @homakov)
* Add `yield` around resource methods in Devise controllers (by @edelpero)
* bug fix
* Bring `password_digest` back to fix compatibility with `devise-encryptable`
* Avoid e-mail enumeration on sign in when in paranoid mode
### 3.2.0
* enhancements

View File

@@ -12,7 +12,7 @@ GIT
PATH
remote: .
specs:
devise (3.2.0)
devise (3.2.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)

View File

@@ -180,7 +180,7 @@ Besides :stretches, you can define :pepper, :encryptor, :confirm_within, :rememb
When you customize your own views, you may end up adding new attributes to forms. Rails 4 moved the parameter sanitization from the model to the controller, causing Devise to handle this concern at the controller as well.
There are just three actions in Devise that allows any set of parameters to be passed down to the model, therefore requiring sanitization. Their names and the permited parameters by default are:
There are just three actions in Devise that allows any set of parameters to be passed down to the model, therefore requiring sanitization. Their names and the permitted parameters by default are:
* `sign_in` (`Devise::SessionsController#new`) - Permits only the authentication keys (like `email`)
* `sign_up` (`Devise::RegistrationsController#create`) - Permits authentication keys plus `password` and `password_confirmation`

View File

@@ -7,6 +7,7 @@ class Devise::ConfirmationsController < DeviseController
# POST /resource/confirmation
def create
self.resource = resource_class.send_confirmation_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, :location => after_resending_confirmation_instructions_path_for(resource_name))
@@ -18,6 +19,7 @@ class Devise::ConfirmationsController < DeviseController
# GET /resource/confirmation?confirmation_token=abcdef
def show
self.resource = resource_class.confirm_by_token(params[:confirmation_token])
yield resource if block_given?
if resource.errors.empty?
set_flash_message(:notice, :confirmed) if is_flashing_format?

View File

@@ -11,6 +11,7 @@ class Devise::PasswordsController < DeviseController
# POST /resource/password
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
@@ -28,6 +29,7 @@ class Devise::PasswordsController < DeviseController
# PUT /resource/password
def update
self.resource = resource_class.reset_password_by_token(resource_params)
yield resource if block_given?
if resource.errors.empty?
resource.unlock_access! if unlockable?(resource)

View File

@@ -13,6 +13,7 @@ class Devise::RegistrationsController < DeviseController
build_resource(sign_up_params)
if resource.save
yield resource if block_given?
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
@@ -41,6 +42,7 @@ class Devise::RegistrationsController < DeviseController
prev_unconfirmed_email = resource.unconfirmed_email if resource.respond_to?(:unconfirmed_email)
if update_resource(resource, account_update_params)
yield resource if block_given?
if is_flashing_format?
flash_key = update_needs_confirmation?(resource, prev_unconfirmed_email) ?
:update_needs_confirmation : :updated
@@ -59,6 +61,7 @@ class Devise::RegistrationsController < DeviseController
resource.destroy
Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name)
set_flash_message :notice, :destroyed if is_flashing_format?
yield resource if block_given?
respond_with_navigational(resource){ redirect_to after_sign_out_path_for(resource_name) }
end

View File

@@ -15,6 +15,7 @@ class Devise::SessionsController < DeviseController
self.resource = warden.authenticate!(auth_options)
set_flash_message(:notice, :signed_in) if is_flashing_format?
sign_in(resource_name, resource)
yield resource if block_given?
respond_with resource, :location => after_sign_in_path_for(resource)
end
@@ -23,6 +24,7 @@ class Devise::SessionsController < DeviseController
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out && is_flashing_format?
yield resource if block_given?
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request

View File

@@ -9,6 +9,7 @@ class Devise::UnlocksController < DeviseController
# POST /resource/unlock
def create
self.resource = resource_class.send_unlock_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, :location => after_sending_unlock_instructions_path_for(resource))
@@ -20,6 +21,7 @@ class Devise::UnlocksController < DeviseController
# GET /resource/unlock?unlock_token=abcdef
def show
self.resource = resource_class.unlock_access_by_token(params[:unlock_token])
yield resource if block_given?
if resource.errors.empty?
set_flash_message :notice, :unlocked if is_flashing_format?

View File

@@ -1,7 +1,7 @@
PATH
remote: ..
specs:
devise (3.2.0)
devise (3.2.2)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)

View File

@@ -21,6 +21,7 @@ module Devise
autoload :Rememberable, 'devise/controllers/rememberable'
autoload :ScopedViews, 'devise/controllers/scoped_views'
autoload :SignInOut, 'devise/controllers/sign_in_out'
autoload :StoreLocation, 'devise/controllers/store_location'
autoload :UrlHelpers, 'devise/controllers/url_helpers'
end

View File

@@ -4,6 +4,7 @@ module Devise
module Helpers
extend ActiveSupport::Concern
include Devise::Controllers::SignInOut
include Devise::Controllers::StoreLocation
included do
helper_method :warden, :signed_in?, :devise_controller?
@@ -97,23 +98,6 @@ module Devise
request.env["devise.allow_params_authentication"] = true
end
# Returns and delete (if it's navigational format) the url stored in the session for
# the given scope. Useful for giving redirect backs after sign up:
#
# Example:
#
# redirect_to stored_location_for(:user) || root_path
#
def stored_location_for(resource_or_scope)
scope = Devise::Mapping.find_scope!(resource_or_scope)
if is_navigational_format?
session.delete("#{scope}_return_to")
else
session["#{scope}_return_to"]
end
end
# The scope root url to be used when he's signed in. By default, it first
# tries to find a resource_root_path, otherwise it uses the root_path.
def signed_in_root_path(resource_or_scope)

View File

@@ -0,0 +1,50 @@
require "uri"
module Devise
module Controllers
# Provide the ability to store a location.
# Used to redirect back to a desired path after sign in.
# Included by default in all controllers.
module StoreLocation
# Returns and delete (if it's navigational format) the url stored in the session for
# the given scope. Useful for giving redirect backs after sign up:
#
# Example:
#
# redirect_to stored_location_for(:user) || root_path
#
def stored_location_for(resource_or_scope)
session_key = stored_location_key_for(resource_or_scope)
if is_navigational_format?
session.delete(session_key)
else
session[session_key]
end
end
# Stores the provided location to redirect the user after signing in.
# Useful in combination with the `stored_location_for` helper.
#
# Example:
#
# store_location_for(:user, dashboard_path)
# redirect_to user_omniauth_authorize_path(:facebook)
#
def store_location_for(resource_or_scope, location)
session_key = stored_location_key_for(resource_or_scope)
if location
uri = URI.parse(location)
session[session_key] = [uri.path.sub(/\A\/+/, '/'), uri.query].compact.join('?')
end
end
private
def stored_location_key_for(resource_or_scope)
scope = Devise::Mapping.find_scope!(resource_or_scope)
"#{scope}_return_to"
end
end
end
end

View File

@@ -13,6 +13,8 @@ module Devise
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include Devise::Controllers::StoreLocation
delegate :flash, :to => :request
def self.call(env)
@@ -189,7 +191,7 @@ module Devise
# yet, but we still need to store the uri based on scope, so different scopes
# would never use the same uri to redirect.
def store_location!
session["#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
store_location_for(scope, attempted_path) if request.get? && !http_auth?
end
def is_navigational_format?

View File

@@ -12,7 +12,7 @@ Warden::Manager.after_set_user do |record, warden, options|
proxy = Devise::Hooks::Proxy.new(warden)
if record.timedout?(last_request_at) && !env['devise.skip_timeout']
Devise.sign_out_all_scopes ? proxy.sign_out : sign_out(scope)
Devise.sign_out_all_scopes ? proxy.sign_out : proxy.sign_out(scope)
if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout
record.reset_authentication_token!

View File

@@ -39,7 +39,7 @@ module Devise
# Generates password encryption based on the given value.
def password=(new_password)
@password = new_password
self.encrypted_password = Devise.bcrypt(self.class, @password) if @password.present?
self.encrypted_password = password_digest(@password) if @password.present?
end
# Verifies whether an password (ie from sign in) is the user password.
@@ -135,6 +135,15 @@ module Devise
protected
# Digests the password using bcrypt. Custom encryption should override
# this method to apply their own algorithm.
#
# See https://github.com/plataformatec/devise-encryptable for examples
# of other encryption engines.
def password_digest(password)
Devise.bcrypt(self.class, password)
end
module ClassMethods
Devise::Models.config(self, :pepper, :stretches)

View File

@@ -387,8 +387,23 @@ module ActionDispatch::Routing
def devise_omniauth_callback(mapping, controllers) #:nodoc:
if mapping.fullpath =~ /:[a-zA-Z_]/
raise "[DEVISE] Nesting omniauth callbacks under scopes with dynamic segments " \
"is not supported. Please, use Devise.omniauth_path_prefix instead."
raise <<-ERROR
Devise does not support scoping omniauth callbacks under a dynamic segment
and you have set #{mapping.fullpath.inspect}. You can work around by passing
`skip: :omniauth_callbacks` and manually defining the routes. Here is an example:
match "/users/auth/:provider",
:constraints => { :provider => /\A(google|facebook)\z/ },
:to => "devise/omniauth_callbacks#passthru",
:as => :omniauth_authorize,
:via => [:get, :post]
match "/users/auth/:action/callback",
:constraints => { :action => /\A(google|facebook)\z/ },
:to => "devise/omniauth_callbacks",
:as => :omniauth_callback,
:via => [:get, :post]
ERROR
end
path, @scope[:path] = @scope[:path], nil

View File

@@ -5,13 +5,16 @@ module Devise
# Default strategy for signing in a user, based on his email and password in the database.
class DatabaseAuthenticatable < Authenticatable
def authenticate!
resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
return fail(:not_found_in_database) unless resource
resource = valid_password? && mapping.to.find_for_database_authentication(authentication_hash)
encrypted = false
if validate(resource){ resource.valid_password?(password) }
if validate(resource){ encrypted = true; resource.valid_password?(password) }
resource.after_database_authentication
success!(resource)
end
mapping.to.new.password = password if !encrypted && Devise.paranoid
fail(:not_found_in_database) unless resource
end
end
end

View File

@@ -1,3 +1,3 @@
module Devise
VERSION = "3.2.0".freeze
VERSION = "3.2.2".freeze
end

View File

@@ -1,3 +1,5 @@
require 'rails/generators/named_base'
module Devise
module Generators
class DeviseGenerator < Rails::Generators::NamedBase

View File

@@ -1,3 +1,4 @@
require 'rails/generators/base'
require 'securerandom'
module Devise

View File

@@ -1,3 +1,5 @@
require 'rails/generators/base'
module Devise
module Generators
# Include this module in your generator to generate Devise views.

View File

@@ -1,3 +1,4 @@
require 'rails/generators/named_base'
require 'generators/devise/orm_helpers'
module Mongoid

View File

@@ -187,6 +187,29 @@ class ControllerAuthenticatableTest < ActionController::TestCase
assert_nil @controller.session[:"user_return_to"]
end
test 'store location for stores a location to redirect back to' do
assert_nil @controller.stored_location_for(:user)
@controller.store_location_for(:user, "/foo.bar")
assert_equal "/foo.bar", @controller.stored_location_for(:user)
end
test 'store location for accepts a resource as argument' do
@controller.store_location_for(User.new, "/foo.bar")
assert_equal "/foo.bar", @controller.stored_location_for(User.new)
end
test 'store location for stores paths' do
@controller.store_location_for(:user, "//host/foo.bar")
assert_equal "/foo.bar", @controller.stored_location_for(:user)
@controller.store_location_for(:user, "///foo.bar")
assert_equal "/foo.bar", @controller.stored_location_for(:user)
end
test 'store location for stores query string' do
@controller.store_location_for(:user, "/foo?bar=baz")
assert_equal "/foo?bar=baz", @controller.stored_location_for(:user)
end
test 'after sign in path defaults to root path if none by was specified for the given scope' do
assert_equal root_path, @controller.after_sign_in_path_for(:user)
end

View File

@@ -35,14 +35,19 @@ class SessionTimeoutTest < ActionDispatch::IntegrationTest
assert warden.authenticated?(:user)
end
test 'time out user session after default limit time' do
user = sign_in_as_user
get expire_user_path(user)
assert_not_nil last_request_at
test 'time out user session after default limit time when sign_out_all_scopes is false' do
swap Devise, sign_out_all_scopes: false do
sign_in_as_admin
get users_path
assert_redirected_to users_path
assert_not warden.authenticated?(:user)
user = sign_in_as_user
get expire_user_path(user)
assert_not_nil last_request_at
get users_path
assert_redirected_to users_path
assert_not warden.authenticated?(:user)
assert warden.authenticated?(:admin)
end
end
test 'time out all sessions after default limit time when sign_out_all_scopes is true' do

View File

@@ -93,6 +93,11 @@ class DatabaseAuthenticatableTest < ActiveSupport::TestCase
assert_present user.encrypted_password
end
test 'should support custom encryption methods' do
user = UserWithCustomEncryption.new(:password => '654321')
assert_equal user.encrypted_password, '123456'
end
test 'allow authenticatable_salt to work even with nil encrypted password' do
user = User.new
user.encrypted_password = nil

View File

@@ -2,7 +2,13 @@ unless defined?(DEVISE_ORM)
DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
end
module Devise
# Detection for minor differences between Rails 3.2 and 4 in tests.
def self.rails4?
Rails.version.start_with? '4'
end
end
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__)
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])

View File

@@ -22,10 +22,6 @@ RailsApp::Application.configure do
# Only use best-standards-support built into browsers.
config.action_dispatch.best_standards_support = :builtin
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL).
config.active_record.auto_explain_threshold_in_seconds = 0.5
# Raise an error on page load if there are pending migrations
config.active_record.migration_error = :page_load

View File

@@ -72,10 +72,6 @@ RailsApp::Application.configure do
# Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify
# Log the query plan for queries taking more than this (works
# with SQLite, MySQL, and PostgreSQL).
# config.active_record.auto_explain_threshold_in_seconds = 0.5
# Disable automatic flushing of the log to improve performance.
# config.autoflush_log = false

View File

@@ -1,3 +1,4 @@
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -8,40 +9,43 @@
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended to check this file into your version control system.
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(:version => 20100401102949) do
ActiveRecord::Schema.define(version: 20100401102949) do
create_table "admins", :force => true do |t|
create_table "admins", force: true do |t|
t.string "email"
t.string "encrypted_password", :limit => 128
t.string "password_salt"
t.string "remember_token"
t.datetime "remember_created_at"
t.string "encrypted_password"
t.string "reset_password_token"
t.integer "failed_attempts", :default => 0
t.string "unlock_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
t.datetime "locked_at"
t.boolean "active", default: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "users", :force => true do |t|
create_table "users", force: true do |t|
t.string "username"
t.string "facebook_token"
t.string "email", :default => "", :null => false
t.string "encrypted_password", :limit => 128, :default => "", :null => false
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", :default => 0
t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.integer "failed_attempts", :default => 0
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.integer "failed_attempts", default: 0
t.string "unlock_token"
t.datetime "locked_at"
t.datetime "created_at"

View File

@@ -4,13 +4,6 @@ DEVISE_ORM = (ENV["DEVISE_ORM"] || :active_record).to_sym
$:.unshift File.dirname(__FILE__)
puts "\n==> Devise.orm = #{DEVISE_ORM.inspect}"
module Devise
# Detection for minor differences between Rails 3.2 and 4 in tests.
def self.rails4?
Rails.version.start_with? '4'
end
end
require "rails_app/config/environment"
require "rails/test_help"
require "orm/#{DEVISE_ORM}"

View File

@@ -12,6 +12,13 @@ class UserWithValidation < User
validates_presence_of :username
end
class UserWithCustomEncryption < User
protected
def password_digest(password)
password.reverse
end
end
class UserWithVirtualAttributes < User
devise :case_insensitive_keys => [ :email, :email_confirmation ]
validates :email, :presence => true, :confirmation => {:on => :create}