diff --git a/lib/devise.rb b/lib/devise.rb index 14157bc5..c059d5b6 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -9,6 +9,7 @@ module Devise }.freeze STRATEGIES = [:authenticatable].freeze + SERIALIZERS = [:authenticatable, :rememberable].freeze TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].freeze # Maps the messages types that comes from warden to a flash type. @@ -104,8 +105,10 @@ module Devise # block. def configure_warden_manager(manager) #:nodoc: manager.default_strategies *Devise::STRATEGIES + manager.default_serializers *Devise::SERIALIZERS manager.failure_app = Devise::Failure manager.silence_missing_strategies! + manager.silence_missing_serializers! # If the user provided a warden hook, call it now. @warden_config.try :call, manager @@ -118,5 +121,14 @@ module Devise end end -require 'devise/warden' +begin + require 'warden' +rescue + gem 'warden' + require 'warden' +end + +require 'devise/strategies/base' +require 'devise/serializers/base' + require 'devise/rails' \ No newline at end of file diff --git a/lib/devise/hooks/rememberable.rb b/lib/devise/hooks/rememberable.rb deleted file mode 100644 index 4293dc9d..00000000 --- a/lib/devise/hooks/rememberable.rb +++ /dev/null @@ -1,29 +0,0 @@ -# After authenticate hook to verify if the user in the given scope asked to be -# remembered while he does not sign out. Generates a new remember token for -# that specific user and adds a cookie with this user info to sign in this user -# automatically without asking for credentials. Refer to rememberable strategy -# for more info. -Warden::Manager.after_authentication do |record, warden, options| - scope = options[:scope] - remember_me = warden.params[scope].try(:fetch, :remember_me, nil) - - if Devise::TRUE_VALUES.include?(remember_me) && record.respond_to?(:remember_me!) - record.remember_me! - - warden.response.set_cookie "remember_#{scope}_token", { - :value => record.class.serialize_into_cookie(record), - :expires => record.remember_expires_at, - :path => "/" - } - end -end - -# Before logout hook to forget the user in the given scope, only if rememberable -# is activated for this scope. Also clear remember token to ensure the user -# won't be remembered again. -Warden::Manager.before_logout do |record, warden, scope| - if record.respond_to?(:forget_me!) - record.forget_me! - warden.response.delete_cookie "remember_#{scope}_token" - end -end diff --git a/lib/devise/middlewares/rememberable.rb b/lib/devise/middlewares/rememberable.rb deleted file mode 100644 index ae5b2e3b..00000000 --- a/lib/devise/middlewares/rememberable.rb +++ /dev/null @@ -1,35 +0,0 @@ -module Devise - module Middlewares - class Rememberable - def initialize(app) - @app = app - end - - def call(env) - auth = env['warden'] - scopes = select_cookies(auth.request) - scopes.each do |scope, token| - mapping = Devise.mappings[scope] - next unless mapping && mapping.for.include?(:rememberable) - user = mapping.to.serialize_from_cookie(token) - auth.set_user(user, :scope => scope) if user - end - - @app.call(env) - end - - protected - - def select_cookies(request) - scopes = {} - matching = /remember_(#{Devise.mappings.keys.join("|")})_token/ - request.cookies.each do |key, value| - if key.to_s =~ matching - scopes[$1.to_sym] = value - end - end - scopes - end - end - end -end diff --git a/lib/devise/models/authenticatable.rb b/lib/devise/models/authenticatable.rb index 7bbf1e01..8e8c8d1a 100644 --- a/lib/devise/models/authenticatable.rb +++ b/lib/devise/models/authenticatable.rb @@ -1,4 +1,5 @@ require 'devise/strategies/authenticatable' +require 'devise/serializers/authenticatable' module Devise module Models @@ -77,6 +78,17 @@ module Devise end perishable end + + # Hook to serialize user into session. Overwrite if you want. + def serialize_into_session(record) + [record.class, record.id] + end + + # Hook to serialize user from session. Overwrite if you want. + def serialize_from_session(keys) + klass, id = keys + klass.find_by_id(id) + end end Devise::Models.config(self, :pepper) diff --git a/lib/devise/models/rememberable.rb b/lib/devise/models/rememberable.rb index e4c7022e..9e92b8a1 100644 --- a/lib/devise/models/rememberable.rb +++ b/lib/devise/models/rememberable.rb @@ -1,6 +1,5 @@ require 'digest/sha1' -require 'devise/hooks/rememberable' -require 'devise/middlewares/rememberable' +require 'devise/serializers/rememberable' module Devise module Models diff --git a/lib/devise/rails.rb b/lib/devise/rails.rb index f7817ef7..b0b29aba 100644 --- a/lib/devise/rails.rb +++ b/lib/devise/rails.rb @@ -10,8 +10,5 @@ Rails.configuration.after_initialize do Devise.configure_warden_manager(manager) end - # If using a rememberable module, include the middleware that log users. - Rails.configuration.middleware.use Devise::Middlewares::Rememberable - I18n.load_path.unshift File.expand_path(File.join(File.dirname(__FILE__), 'locales', 'en.yml')) end diff --git a/lib/devise/serializers/authenticatable.rb b/lib/devise/serializers/authenticatable.rb new file mode 100644 index 00000000..c292e5ed --- /dev/null +++ b/lib/devise/serializers/authenticatable.rb @@ -0,0 +1,9 @@ +module Devise + module Serializers + class Authenticatable < Warden::Serializers::Session + include Devise::Serializers::Base + end + end +end + +Warden::Serializers.add(:authenticatable, Devise::Serializers::Authenticatable) \ No newline at end of file diff --git a/lib/devise/serializers/base.rb b/lib/devise/serializers/base.rb new file mode 100644 index 00000000..721787fc --- /dev/null +++ b/lib/devise/serializers/base.rb @@ -0,0 +1,41 @@ +module Devise + module Serializers + module Base + include Devise::Strategies::Base + attr_reader :scope + + def serialize(record) + record.class.send(:"serialize_into_#{serialization_type}", record) + end + + def deserialize(keys) + mapping.to.send(:"serialize_from_#{serialization_type}", keys) + end + + def store(user, scope) + @scope = scope + return unless valid? + super + end + + def fetch(scope) + @scope = scope + return unless valid? + super + end + + def delete(scope, user=nil) + @scope = scope + return unless valid? + super + end + + def serialization_type + @serialization_type ||= begin + warden = self.class.ancestors.find{ |k| k < Warden::Serializers::Base && k != self.class } + warden.name.split("::").last.underscore + end + end + end + end +end \ No newline at end of file diff --git a/lib/devise/serializers/rememberable.rb b/lib/devise/serializers/rememberable.rb new file mode 100644 index 00000000..1352df7d --- /dev/null +++ b/lib/devise/serializers/rememberable.rb @@ -0,0 +1,26 @@ +module Devise + module Serializers + class Rememberable < Warden::Serializers::Cookie + include Devise::Serializers::Base + + def store(record, scope) + remember_me = params[scope].try(:fetch, :remember_me, nil) + if Devise::TRUE_VALUES.include?(remember_me) && record.respond_to?(:remember_me!) + record.remember_me! + super + end + end + + def default_options(record) + super.merge!(:expires => record.remember_expires_at) + end + + def delete(scope, record=nil) + record.forget_me! if record && record.respond_to?(:forget_me!) + super + end + end + end +end + +Warden::Serializers.add(:rememberable, Devise::Serializers::Rememberable) \ No newline at end of file diff --git a/lib/devise/strategies/authenticatable.rb b/lib/devise/strategies/authenticatable.rb index 307638b9..fa302be1 100644 --- a/lib/devise/strategies/authenticatable.rb +++ b/lib/devise/strategies/authenticatable.rb @@ -2,7 +2,8 @@ module Devise module Strategies # Default strategy for signing in a user, based on his email and password. # Redirects to sign_in page if it's not authenticated - class Authenticatable < Devise::Strategies::Base + class Authenticatable < Warden::Strategies::Base + include Devise::Strategies::Base # Authenticate a user based on email and password params, returning to warden # success and the authenticated user if everything is okay. Otherwise redirect diff --git a/lib/devise/strategies/base.rb b/lib/devise/strategies/base.rb index 15a9a2f3..70d0a854 100644 --- a/lib/devise/strategies/base.rb +++ b/lib/devise/strategies/base.rb @@ -2,22 +2,23 @@ module Devise module Strategies # Base strategy for Devise. Responsible for verifying correct scope and # mapping. - class Base < Warden::Strategies::Base + module Base # Validate strategy. By default will raise an error if no scope or an # invalid mapping is found. def valid? - mapping.for.include?(self.class.name.split("::").last.underscore.to_sym) + mapping.for.include?(klass_type) end # Checks if a valid scope was given for devise and find mapping based on # this scope. def mapping - @mapping ||= begin - raise "You need to give a scope for Devise authentication" unless scope - raise "You need to give a valid Devise mapping" unless mapping = Devise.mappings[scope] - mapping - end + @mapping ||= Devise.mappings[scope] + end + + # Store this class type. + def klass_type + @klass_type ||= self.class.name.split("::").last.underscore.to_sym end end end diff --git a/lib/devise/warden.rb b/lib/devise/warden.rb deleted file mode 100644 index 8024de46..00000000 --- a/lib/devise/warden.rb +++ /dev/null @@ -1,20 +0,0 @@ -begin - require 'warden' -rescue - gem 'warden' - require 'warden' -end - -# 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, -# it is not a good idea to store the complete object. An ID is sufficient. -Warden::Manager.serialize_into_session{ |user| [user.class, user.id] } - -# Session Serialization out. This block gets the user out of the session. -# It should be the reverse of serializing the object into the session -Warden::Manager.serialize_from_session do |klass, id| - klass.find_by_id(id) -end - -# Setup devise strategies for Warden -require 'devise/strategies/base' diff --git a/test/devise_test.rb b/test/devise_test.rb index 9baca4a9..3ab0e61c 100644 --- a/test/devise_test.rb +++ b/test/devise_test.rb @@ -15,6 +15,10 @@ class DeviseTest < ActiveSupport::TestCase @silence_missing_strategies = true end + def silence_missing_serializers! + @silence_missing_serializers = true + end + def default_strategies(*args) if args.empty? @default_strategies @@ -22,6 +26,14 @@ class DeviseTest < ActiveSupport::TestCase @default_strategies = args end end + + def default_serializers(*args) + if args.empty? + @default_serializers + else + @default_serializers = args + end + end end test 'DeviseMailer.sender can be configured through Devise' do diff --git a/test/integration/rememberable_test.rb b/test/integration/rememberable_test.rb index 0ef048cf..d06b8e48 100644 --- a/test/integration/rememberable_test.rb +++ b/test/integration/rememberable_test.rb @@ -6,7 +6,7 @@ class RememberMeTest < ActionController::IntegrationTest Devise.remember_for = 1 user = create_user user.remember_me! - cookies['remember_user_token'] = User.serialize_into_cookie(user) + add_to_token + cookies['warden.user.user.key'] = User.serialize_into_cookie(user) + add_to_token user end