Major refactoring. Allow Warden::Manager to be configured through Devise, undeprecate Devise.confirm_within, Devise.pepper and friends and move Rails hooks to their own file.

This commit is contained in:
José Valim 2009-11-03 09:35:11 -02:00
parent dc86a571ae
commit 371a9bb0d0
17 changed files with 211 additions and 120 deletions

View File

@ -2,7 +2,6 @@
* Notifier is deprecated, use DeviseMailer instead. Remember to rename * Notifier is deprecated, use DeviseMailer instead. Remember to rename
app/views/notifier to app/views/devise_mailer and I18n key from app/views/notifier to app/views/devise_mailer and I18n key from
devise.notifier to devise.mailer devise.notifier to devise.mailer
* Model configuration is on Devise::Models.config and not on Devise.config anymore.
* enhancement * enhancement
* [#16] Allow devise to be more agnostic. Do not require ActiveRecord to be loaded. * [#16] Allow devise to be more agnostic. Do not require ActiveRecord to be loaded.

View File

@ -8,27 +8,50 @@ module Devise
:confirmations => :confirmable :confirmations => :confirmable
}.freeze }.freeze
STRATEGIES = [:rememberable, :authenticatable].freeze
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze
end
# Devise initialization process goes like this: class << self
# # Default way to setup Devise. Run script/generate devise_install to create
# 1) Includes in Devise::ActiveRecord and Devise::Migrations # a fresh initializer with all configuration values.
# 2) Load and config warden def setup
# 3) Load devise mapping structure yield self
# 4) Add routes extensions end
# 5) Load routes definitions
# 6) Include filters and helpers in controllers and views # Sets the sender in DeviseMailer.
# def mail_sender=(value)
Rails.configuration.after_initialize do DeviseMailer.sender = value
if defined?(ActiveRecord) end
ActiveRecord::Base.extend Devise::Models alias :sender= :mail_sender=
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Migrations
# Sets warden configuration using a block that will be invoked on warden
# initialization.
#
# Devise.initialize do |config|
# config.confirm_within = 2.days
#
# config.warden do |manager|
# # Configure warden to use other strategies, like oauth.
# manager.oauth(:twitter)
# end
# end
def warden(&block)
@warden_config = block
end
# A method used internally to setup warden manager from the Rails initialize
# block.
def configure_warden_manager(manager) #:nodoc:
manager.default_strategies *Devise::STRATEGIES
manager.failure_app = Devise::Failure
manager.silence_missing_strategies!
# If the user provided a warden hook, call it now.
@warden_config.try :call, manager
end
end end
I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'devise', 'locales', 'en.yml'))
end end
require 'devise/warden' require 'devise/warden'
require 'devise/mapping' require 'devise/mapping'
require 'devise/routes' require 'devise/rails'

View File

@ -9,10 +9,13 @@ module Devise
def self.call(env) def self.call(env)
options = env['warden.options'] options = env['warden.options']
scope = options[:scope] scope = options[:scope]
params = if env['warden'].try(:message) params = case env['warden'].try(:message)
{ env['warden'].message => true } when Symbol
else { env['warden'].message => true }
options[:params] when String
{ :message => env['warden'].message }
else
options[:params]
end end
redirect_path = if mapping = Devise.mappings[scope] redirect_path = if mapping = Devise.mappings[scope]

View File

@ -26,13 +26,6 @@ module Devise
string :password_salt, :limit => 20, :null => null string :password_salt, :limit => 20, :null => null
end end
# TODO Remove me in a next release.
#
def authenticable(*args)
ActiveSupport::Deprecation.warn "authenticable in migrations is deprecated, use authenticatable instead"
authenticatable(*args)
end
# Creates confirmation_token, confirmed_at and confirmation_sent_at. # Creates confirmation_token, confirmed_at and confirmation_sent_at.
# #
def confirmable def confirmable

View File

@ -6,7 +6,7 @@ module Devise
# #
# The line above creates: # The line above creates:
# #
# 1) An accessor called Devise::Models.stretches, which value is used by default; # 1) An accessor called Devise.stretches, which value is used by default;
# #
# 2) Some class methods for your model Model.stretches and Model.stretches= # 2) Some class methods for your model Model.stretches and Model.stretches=
# which have higher priority than Devise.stretches; # which have higher priority than Devise.stretches;
@ -17,17 +17,8 @@ module Devise
# inside the given class. # inside the given class.
# #
def self.config(mod, accessor, default=nil) #:nodoc: def self.config(mod, accessor, default=nil) #:nodoc:
mattr_accessor accessor Devise.send :mattr_accessor, accessor
send(:"#{accessor}=", default) Devise.send :"#{accessor}=", default
# TODO Remove me in a next release
Devise.class_eval <<-METHOD, __FILE__, __LINE__
def self.#{accessor}=(value)
ActiveSupport::Deprecation.warn "Devise.#{accessor}= is deprecated, " <<
"use Devise::Models.#{accessor}= instead."
Devise::Models.#{accessor} = value
end
METHOD
mod.class_eval <<-METHOD, __FILE__, __LINE__ mod.class_eval <<-METHOD, __FILE__, __LINE__
def #{accessor} def #{accessor}
@ -42,7 +33,7 @@ module Devise
elsif superclass.respond_to?(:#{accessor}) elsif superclass.respond_to?(:#{accessor})
superclass.#{accessor} superclass.#{accessor}
else else
Devise::Models.#{accessor} Devise.#{accessor}
end end
end end
@ -111,13 +102,6 @@ module Devise
def devise(*modules) def devise(*modules)
options = modules.extract_options! options = modules.extract_options!
# TODO Remove me in a next release
if modules.include?(:authenticable)
modules.delete(:authenticable)
modules.unshift(:authenticatable)
ActiveSupport::Deprecation.warn "devise :authenticate is deprecated, use authenticatable instead"
end
modules = Devise::ALL if modules.include?(:all) modules = Devise::ALL if modules.include?(:all)
modules -= Array(options.delete(:except)) modules -= Array(options.delete(:except))
modules = [:authenticatable] | modules modules = [:authenticatable] | modules

17
lib/devise/rails.rb Normal file
View File

@ -0,0 +1,17 @@
require 'devise/rails/routes'
require 'devise/rails/warden_compat'
Rails.configuration.after_initialize do
if defined?(ActiveRecord)
ActiveRecord::Base.extend Devise::Models
ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Devise::Migrations
end
# Adds Warden Manager to Rails middleware stack, configuring default devise
# strategy and also the failure app.
Rails.configuration.middleware.use Warden::Manager do |manager|
Devise.configure_warden_manager(manager)
end
I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'locales', 'en.yml'))
end

View File

@ -0,0 +1,26 @@
# Taken from RailsWarden, thanks to Hassox. http://github.com/hassox/rails_warden
module Warden::Mixins::Common
# Gets the rails request object by default if it's available
def request
return @request if @request
if env['action_controller.rescue.request']
@request = env['action_controller.rescue.request']
else
Rack::Request.new(env)
end
end
def raw_session
request.session
end
def reset_session!
raw_session.inspect # why do I have to inspect it to get it to clear?
raw_session.clear
end
# Proxy to request cookies
def cookies
request.cookies
end
end

View File

@ -5,33 +5,6 @@ rescue
require 'warden' require 'warden'
end end
# Taken from RailsWarden, thanks to Hassox. http://github.com/hassox/rails_warden
module Warden::Mixins::Common
# Gets the rails request object by default if it's available
def request
return @request if @request
if env['action_controller.rescue.request']
@request = env['action_controller.rescue.request']
else
Rack::Request.new(env)
end
end
def raw_session
request.session
end
def reset_session!
raw_session.inspect # why do I have to inspect it to get it to clear?
raw_session.clear
end
# Proxy to request cookies
def cookies
request.cookies
end
end
# Session Serialization in. This block determines how the user will be stored # Session Serialization in. This block determines how the user will be stored
# in the session. If you're using a complex object like an ActiveRecord model, # in the session. If you're using a complex object like an ActiveRecord model,
# it is not a good idea to store the complete object. An ID is sufficient. # it is not a good idea to store the complete object. An ID is sufficient.
@ -43,19 +16,5 @@ Warden::Manager.serialize_from_session do |klass, id|
klass.find(id) klass.find(id)
end end
# Be a good citizen and always set the controller action, even if Devise is
# never calling the failure app through warden.
Warden::Manager.before_failure do |env, opts|
env['warden'].request.params['action'] = 'new'
end
# Setup devise strategies for Warden # Setup devise strategies for Warden
require 'devise/strategies/base' require 'devise/strategies/base'
# Adds Warden Manager to Rails middleware stack, configuring default devise
# strategy and also the controller who will manage not authenticated users.
Rails.configuration.middleware.use Warden::Manager do |manager|
manager.default_strategies :rememberable, :authenticatable
manager.failure_app = Devise::Failure
manager.silence_missing_strategies!
end

72
test/devise_test.rb Normal file
View File

@ -0,0 +1,72 @@
require 'test/test_helper'
module Devise
def self.clean_warden_config!
@warden_config = nil
end
end
class DeviseTest < ActiveSupport::TestCase
class MockManager
attr_accessor :failure_app
attr_reader :default_strategies, :silence_missing_strategies
def silence_missing_strategies!
@silence_missing_strategies = true
end
def default_strategies(*args)
if args.empty?
@default_strategies
else
@default_strategies = args
end
end
end
test 'DeviseMailer.sender can be configured through Devise' do
swap DeviseMailer, :sender => "foo@bar" do
assert_equal "foo@bar", DeviseMailer.sender
Devise.mail_sender = "bar@foo"
assert_equal "bar@foo", DeviseMailer.sender
end
end
test 'model options can be configured through Devise' do
swap Devise, :confirm_within => 113, :pepper => "foo" do
assert_equal 113, Devise.confirm_within
assert_equal "foo", Devise.pepper
end
end
test 'setup block yields self' do
Devise.setup do |config|
assert_equal Devise, config
end
end
test 'warden manager configuration' do
manager = MockManager.new
Devise.configure_warden_manager(manager)
assert_equal Devise::Failure, manager.failure_app
assert_equal [:rememberable, :authenticatable], manager.default_strategies
assert manager.silence_missing_strategies
end
test 'warden manager user configuration through a block' do
begin
@executed = false
Devise.warden do |manager|
@executed = true
assert_kind_of MockManager, manager
end
manager = MockManager.new
Devise.configure_warden_manager(manager)
assert @executed
ensure
Devise.clean_warden_config!
end
end
end

View File

@ -59,7 +59,7 @@ class ConfirmationTest < ActionController::IntegrationTest
end end
test 'not confirmed user and setup to block without confirmation should not be able to sign in' do test 'not confirmed user and setup to block without confirmation should not be able to sign in' do
Devise::Models.confirm_within = 0 Devise.confirm_within = 0
user = sign_in_as_user(:confirm => false) user = sign_in_as_user(:confirm => false)
assert_redirected_to new_user_session_path(:unconfirmed => true) assert_redirected_to new_user_session_path(:unconfirmed => true)
@ -67,7 +67,7 @@ class ConfirmationTest < ActionController::IntegrationTest
end end
test 'not confirmed user but configured with some days to confirm should be able to sign in' do test 'not confirmed user but configured with some days to confirm should be able to sign in' do
Devise::Models.confirm_within = 1 Devise.confirm_within = 1
user = sign_in_as_user(:confirm => false) user = sign_in_as_user(:confirm => false)
assert_response :success assert_response :success

View File

@ -3,7 +3,7 @@ require 'test/test_helper'
class RememberMeTest < ActionController::IntegrationTest class RememberMeTest < ActionController::IntegrationTest
def create_user_and_remember(add_to_token='') def create_user_and_remember(add_to_token='')
Devise::Models.remember_for = 1 Devise.remember_for = 1
user = create_user user = create_user
user.remember_me! user.remember_me!
cookies['remember_token'] = User.serialize_into_cookie(user) + add_to_token cookies['remember_token'] = User.serialize_into_cookie(user) + add_to_token
@ -39,7 +39,7 @@ class RememberMeTest < ActionController::IntegrationTest
test 'do not remember with token expired' do test 'do not remember with token expired' do
user = create_user_and_remember user = create_user_and_remember
Devise::Models.remember_for = 0 Devise.remember_for = 0
get users_path get users_path
assert_response :success assert_response :success
assert_not warden.authenticated?(:user) assert_not warden.authenticated?(:user)

View File

@ -1,6 +1,6 @@
require 'test/test_helper' require 'test/test_helper'
class MapTest < ActiveSupport::TestCase class MappingTest < ActiveSupport::TestCase
test 'store options' do test 'store options' do
mapping = Devise.mappings[:user] mapping = Devise.mappings[:user]

View File

@ -66,28 +66,28 @@ class AuthenticatableTest < ActiveSupport::TestCase
test 'should fallback to devise pepper default configuring' do test 'should fallback to devise pepper default configuring' do
begin begin
Devise::Models.pepper = '' Devise.pepper = ''
user = new_user user = new_user
assert_equal encrypt_password(user), user.encrypted_password assert_equal encrypt_password(user), user.encrypted_password
Devise::Models.pepper = 'new_pepper' Devise.pepper = 'new_pepper'
user = new_user user = new_user
assert_equal encrypt_password(user, 'new_pepper'), user.encrypted_password assert_equal encrypt_password(user, 'new_pepper'), user.encrypted_password
Devise::Models.pepper = '123456' Devise.pepper = '123456'
user = new_user user = new_user
assert_equal encrypt_password(user, '123456'), user.encrypted_password assert_equal encrypt_password(user, '123456'), user.encrypted_password
ensure ensure
Devise::Models.pepper = nil Devise.pepper = nil
end end
end end
test 'should fallback to devise stretches default configuring' do test 'should fallback to devise stretches default configuring' do
begin begin
default_stretches = Devise::Models.stretches default_stretches = Devise.stretches
Devise::Models.stretches = 1 Devise.stretches = 1
user = new_user user = new_user
assert_equal encrypt_password(user, nil, nil), user.encrypted_password assert_equal encrypt_password(user, nil, nil), user.encrypted_password
ensure ensure
Devise::Models.stretches = default_stretches Devise.stretches = default_stretches
end end
end end

View File

@ -179,20 +179,20 @@ class ConfirmableTest < ActiveSupport::TestCase
test 'confirm time should fallback to devise confirm in default configuration' do test 'confirm time should fallback to devise confirm in default configuration' do
begin begin
confirm_within = Devise::Models.confirm_within confirm_within = Devise.confirm_within
Devise::Models.confirm_within = 1.day Devise.confirm_within = 1.day
user = new_user user = new_user
user.confirmation_sent_at = 2.days.ago user.confirmation_sent_at = 2.days.ago
assert_not user.active? assert_not user.active?
Devise::Models.confirm_within = 3.days Devise.confirm_within = 3.days
assert user.active? assert user.active?
ensure ensure
Devise::Models.confirm_within = confirm_within Devise.confirm_within = confirm_within
end end
end end
test 'should be active when confirmation sent at is not overpast' do test 'should be active when confirmation sent at is not overpast' do
Devise::Models.confirm_within = 5.days Devise.confirm_within = 5.days
user = create_user user = create_user
user.confirmation_sent_at = 4.days.ago user.confirmation_sent_at = 4.days.ago
assert user.active? assert user.active?
@ -208,21 +208,21 @@ class ConfirmableTest < ActiveSupport::TestCase
end end
test 'should not be active when confirmation was sent within the limit' do test 'should not be active when confirmation was sent within the limit' do
Devise::Models.confirm_within = 5.days Devise.confirm_within = 5.days
user = create_user user = create_user
user.confirmation_sent_at = 5.days.ago user.confirmation_sent_at = 5.days.ago
assert_not user.active? assert_not user.active?
end end
test 'should be active when confirm in is zero' do test 'should be active when confirm in is zero' do
Devise::Models.confirm_within = 0.days Devise.confirm_within = 0.days
user = create_user user = create_user
user.confirmation_sent_at = Date.today user.confirmation_sent_at = Date.today
assert_not user.active? assert_not user.active?
end end
test 'should not be active when confirmation was sent before confirm in time' do test 'should not be active when confirmation was sent before confirm in time' do
Devise::Models.confirm_within = 4.days Devise.confirm_within = 4.days
user = create_user user = create_user
user.confirmation_sent_at = 5.days.ago user.confirmation_sent_at = 5.days.ago
assert_not user.active? assert_not user.active?

View File

@ -3,7 +3,7 @@ require 'test/test_helper'
class RememberableTest < ActiveSupport::TestCase class RememberableTest < ActiveSupport::TestCase
def setup def setup
Devise::Models.remember_for = 1 Devise.remember_for = 1
end end
test 'should respond to remember_me attribute' do test 'should respond to remember_me attribute' do
@ -83,37 +83,37 @@ class RememberableTest < ActiveSupport::TestCase
test 'remember for should fallback to devise remember for default configuration' do test 'remember for should fallback to devise remember for default configuration' do
begin begin
remember_for = Devise::Models.remember_for remember_for = Devise.remember_for
user = create_user user = create_user
Devise::Models.remember_for = 1.day Devise.remember_for = 1.day
user.remember_me! user.remember_me!
assert_not user.remember_expired? assert_not user.remember_expired?
Devise::Models.remember_for = 0.days Devise.remember_for = 0.days
user.remember_me! user.remember_me!
assert user.remember_expired? assert user.remember_expired?
ensure ensure
Devise::Models.remember_for = remember_for Devise.remember_for = remember_for
end end
end end
test 'remember expires at should sum date of creation with remember for configuration' do test 'remember expires at should sum date of creation with remember for configuration' do
Devise::Models.remember_for = 3.days Devise.remember_for = 3.days
user = create_user user = create_user
user.remember_me! user.remember_me!
assert_equal 3.days.from_now.to_date, user.remember_expires_at.to_date assert_equal 3.days.from_now.to_date, user.remember_expires_at.to_date
Devise::Models.remember_for = 5.days Devise.remember_for = 5.days
assert_equal 5.days.from_now.to_date, user.remember_expires_at.to_date assert_equal 5.days.from_now.to_date, user.remember_expires_at.to_date
end end
test 'remember should be expired if remember_for is zero' do test 'remember should be expired if remember_for is zero' do
Devise::Models.remember_for = 0.days Devise.remember_for = 0.days
user = create_user user = create_user
user.remember_me! user.remember_me!
assert user.remember_expired? assert user.remember_expired?
end end
test 'remember should be expired if it was created before limit time' do test 'remember should be expired if it was created before limit time' do
Devise::Models.remember_for = 1.day Devise.remember_for = 1.day
user = create_user user = create_user
user.remember_me! user.remember_me!
user.update_attribute(:remember_created_at, 2.days.ago) user.update_attribute(:remember_created_at, 2.days.ago)
@ -121,7 +121,7 @@ class RememberableTest < ActiveSupport::TestCase
end end
test 'remember should not be expired if it was created whitin the limit time' do test 'remember should not be expired if it was created whitin the limit time' do
Devise::Models.remember_for = 30.days Devise.remember_for = 30.days
user = create_user user = create_user
user.remember_me! user.remember_me!
user.update_attribute(:remember_created_at, 30.days.ago + 2.minutes) user.update_attribute(:remember_created_at, 30.days.ago + 2.minutes)

View File

@ -33,4 +33,19 @@ class ActiveSupport::TestCase
def create_user(attributes={}) def create_user(attributes={})
User.create!(valid_attributes(attributes)) User.create!(valid_attributes(attributes))
end end
# Execute the block setting the given values and restoring old values after
# the block is executed.
def swap(object, new_values)
old_values = {}
new_values.each do |key, value|
old_values[key] = object.send key
object.send :"#{key}=", value
end
yield
ensure
old_values.each do |key, value|
object.send :"#{key}=", value
end
end
end end