From f26e6a269f11f7396a6d9bad7b47f87b0f98f29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 20 Dec 2009 12:31:02 +0100 Subject: [PATCH] Decouple serializers from Devise modules. You can use any serializer at any time, as long the API required by the serializer is satisfied. Since session serializer is encrypted, it simply stores the id and the resource klass. Cookie is not encrypted, so it creates a random token and store it in the database, so in order to do the token cleanup, you need provide remember_me! and forget_me! methods, as in :rememberable. --- lib/devise.rb | 9 +++-- lib/devise/models.rb | 2 +- lib/devise/models/authenticatable.rb | 16 +-------- lib/devise/models/rememberable.rb | 3 +- lib/devise/models/session_serializer.rb | 19 ++++++++++ lib/devise/serializers/authenticatable.rb | 11 ------ lib/devise/serializers/base.rb | 11 ++---- lib/devise/serializers/cookie.rb | 43 +++++++++++++++++++++++ lib/devise/serializers/rememberable.rb | 30 ---------------- lib/devise/serializers/session.rb | 22 ++++++++++++ 10 files changed, 95 insertions(+), 71 deletions(-) create mode 100644 lib/devise/models/session_serializer.rb delete mode 100644 lib/devise/serializers/authenticatable.rb create mode 100644 lib/devise/serializers/cookie.rb delete mode 100644 lib/devise/serializers/rememberable.rb create mode 100644 lib/devise/serializers/session.rb diff --git a/lib/devise.rb b/lib/devise.rb index 3f4aeab1..e3b3c2af 100644 --- a/lib/devise.rb +++ b/lib/devise.rb @@ -35,7 +35,7 @@ module Devise } STRATEGIES = [:authenticatable] - SERIALIZERS = [:authenticatable, :rememberable] + SERIALIZERS = [:session, :cookie] TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'] # Maps the messages types that are used in flash message. This array is not @@ -166,6 +166,9 @@ rescue require 'warden' end -# Set the default_scope to nil, so it's overwritten when the first route is declared. +# Clear some Warden default configuration which will be overwritten +Warden::Strategies.clear! +Warden::Serializers.clear! Warden::Manager.default_scope = nil -require 'devise/rails' + +require 'devise/rails' \ No newline at end of file diff --git a/lib/devise/models.rb b/lib/devise/models.rb index c7b08459..ebc8bb44 100644 --- a/lib/devise/models.rb +++ b/lib/devise/models.rb @@ -4,7 +4,7 @@ module Devise autoload :Confirmable, 'devise/models/confirmable' autoload :Recoverable, 'devise/models/recoverable' autoload :Rememberable, 'devise/models/rememberable' - autoload :SessionSerializer, 'devise/models/authenticatable' + autoload :SessionSerializer, 'devise/models/session_serializer' autoload :Timeoutable, 'devise/models/timeoutable' autoload :Trackable, 'devise/models/trackable' autoload :Validatable, 'devise/models/validatable' diff --git a/lib/devise/models/authenticatable.rb b/lib/devise/models/authenticatable.rb index 9cb89ba1..b57f0b23 100644 --- a/lib/devise/models/authenticatable.rb +++ b/lib/devise/models/authenticatable.rb @@ -1,22 +1,8 @@ require 'devise/strategies/authenticatable' -require 'devise/serializers/authenticatable' +require 'devise/models/session_serializer' module Devise module Models - module SessionSerializer - # 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 - raise "#{self} cannot serialize from #{klass} session since it's not its ancestors" unless klass <= self - klass.find(:first, :conditions => { :id => id }) - end - end - # Authenticable Module, responsible for encrypting password and validating # authenticity of a user while signing in. # diff --git a/lib/devise/models/rememberable.rb b/lib/devise/models/rememberable.rb index 7b2ea6ca..0d27c7d0 100644 --- a/lib/devise/models/rememberable.rb +++ b/lib/devise/models/rememberable.rb @@ -1,5 +1,4 @@ -require 'digest/sha1' -require 'devise/serializers/rememberable' +require 'devise/serializers/cookie' module Devise module Models diff --git a/lib/devise/models/session_serializer.rb b/lib/devise/models/session_serializer.rb new file mode 100644 index 00000000..f8789b2d --- /dev/null +++ b/lib/devise/models/session_serializer.rb @@ -0,0 +1,19 @@ +require 'devise/serializers/session' + +module Devise + module Models + module SessionSerializer + # 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 + raise "#{self} cannot serialize from #{klass} session since it's not one of its ancestors" unless klass <= self + klass.find(:first, :conditions => { :id => id }) + end + end + end +end \ No newline at end of file diff --git a/lib/devise/serializers/authenticatable.rb b/lib/devise/serializers/authenticatable.rb deleted file mode 100644 index 7d75bffc..00000000 --- a/lib/devise/serializers/authenticatable.rb +++ /dev/null @@ -1,11 +0,0 @@ -require 'devise/serializers/base' - -module Devise - module Serializers - class Authenticatable < Warden::Serializers::Session - include Devise::Serializers::Base - end - end -end - -Warden::Serializers.add(:authenticatable, Devise::Serializers::Authenticatable) diff --git a/lib/devise/serializers/base.rb b/lib/devise/serializers/base.rb index 86908855..c65eacea 100644 --- a/lib/devise/serializers/base.rb +++ b/lib/devise/serializers/base.rb @@ -7,24 +7,17 @@ module Devise attr_reader :scope def serialize(record) - record.class.send(:"serialize_into_#{serialization_type}", record) + record.class.send(:"serialize_into_#{klass_type}", record) end def deserialize(keys) - mapping.to.send(:"serialize_from_#{serialization_type}", keys) + mapping.to.send(:"serialize_from_#{klass_type}", keys) end def fetch(scope) @scope = scope 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 diff --git a/lib/devise/serializers/cookie.rb b/lib/devise/serializers/cookie.rb new file mode 100644 index 00000000..db17c00a --- /dev/null +++ b/lib/devise/serializers/cookie.rb @@ -0,0 +1,43 @@ +require 'devise/serializers/base' + +module Devise + module Serializers + # This is a cookie serializer which stores the information if a :remember_me + # is sent in the params and if the model responds to remember_me! as well. + # As in Session serializer, the invoked methods are: + # + # User.serialize_into_cookie(@user) + # User.serialize_from_cookie(*args) + # + # An implementation for such methods can be found at Devise::Models::Rememberable. + # + # Differently from session, this approach is based in a token which is stored in + # the database. So if you want to sign out all clients at once, you just need to + # clean up the token column. + # + class Cookie < 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) + if record && record.respond_to?(:forget_me!) + record.forget_me! + super + end + end + end + end +end + +Warden::Serializers.add(:cookie, Devise::Serializers::Cookie) diff --git a/lib/devise/serializers/rememberable.rb b/lib/devise/serializers/rememberable.rb deleted file mode 100644 index cb16ec1a..00000000 --- a/lib/devise/serializers/rememberable.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'devise/serializers/base' - -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) - if record && record.respond_to?(:forget_me!) - record.forget_me! - super - end - end - end - end -end - -Warden::Serializers.add(:rememberable, Devise::Serializers::Rememberable) diff --git a/lib/devise/serializers/session.rb b/lib/devise/serializers/session.rb new file mode 100644 index 00000000..3dd37d50 --- /dev/null +++ b/lib/devise/serializers/session.rb @@ -0,0 +1,22 @@ +require 'devise/serializers/base' + +module Devise + module Serializers + # This serializer stores sign in information in th client session. It just + # extends Warden own serializer to move all the serialization logic to a + # class. For example, if a @user resource is given, it will call the following + # two methods to serialize and deserialize a record: + # + # User.serialize_into_session(@user) + # User.serialize_from_session(*args) + # + # This can be used any strategy and the default implementation is available + # at Devise::Models::SessionSerializer. + # + class Session < Warden::Serializers::Session + include Devise::Serializers::Base + end + end +end + +Warden::Serializers.add(:session, Devise::Serializers::Session)