diff --git a/lib/warden.rb b/lib/warden.rb index 11b1f46..5f7b81a 100644 --- a/lib/warden.rb +++ b/lib/warden.rb @@ -6,6 +6,7 @@ require 'warden/proxy' require 'warden/manager' require 'warden/errors' +require 'warden/session_serializer' require 'warden/strategies' require 'warden/strategies/base' require 'warden/serializers' diff --git a/lib/warden/manager.rb b/lib/warden/manager.rb index 4990236..fb60078 100644 --- a/lib/warden/manager.rb +++ b/lib/warden/manager.rb @@ -52,6 +52,32 @@ def _run_callbacks(*args) #:nodoc: self.class._run_callbacks(*args) end + class << self + # Prepares the user to serialize into the session. + # Any object that can be serialized into the session in some way can be used as a "user" object + # Generally however complex object should not be stored in the session. + # If possible store only a "key" of the user object that will allow you to reconstitute it. + # + # Example: + # Warden::Manager.serialize_into_session{ |user| user.id } + # + # :api: public + def serialize_into_session(&block) + Warden::SessionSerializer.send :define_method, :serialize, &block + end + + # Reconstitues the user from the session. + # Use the results of user_session_key to reconstitue the user from the session on requests after the initial login + # + # Example: + # Warden::Manager.serialize_from_session{ |id| User.get(id) } + # + # :api: public + def serialize_from_session(&block) + Warden::SessionSerializer.send :define_method, :deserialize, &block + end + end + private # When a request is unauthentiated, here's where the processing occurs. @@ -62,7 +88,7 @@ def process_unauthenticated(result, env) case action when :redirect - [env['warden']._status, env['warden'].headers, [env['warden'].message || "You are being redirected to #{env['warden'].headers['Location']}"]] + [env['warden'].status, env['warden'].headers, [env['warden'].message || "You are being redirected to #{env['warden'].headers['Location']}"]] when :custom env['warden'].custom_response else diff --git a/lib/warden/manager_deprecation.rb b/lib/warden/manager_deprecation.rb index e15b538..50fe1bd 100644 --- a/lib/warden/manager_deprecation.rb +++ b/lib/warden/manager_deprecation.rb @@ -9,54 +9,5 @@ def default_scope def default_scope=(scope) warn "[DEPRECATION] Warden::Manager.default_scope= is deprecated. Please set it in the Warden::Manager instance." end - - # Prepares the user to serialize into the session. - # Any object that can be serialized into the session in some way can be used as a "user" object - # Generally however complex object should not be stored in the session. - # If possible store only a "key" of the user object that will allow you to reconstitute it. - # - # Example: - # Warden::Manager.serialize_into_session{ |user| user.id } - # - # Deprecation: - # This method was deprecated in favor of serializer in Session. You can set it while setting the middleware: - # - # use Warden::Manager do |manager| - # manager.serializers.update(:session) do - # def serialize(user) - # user.id - # end - # end - # end - # - # :api: public - def serialize_into_session(&block) - warn "[DEPRECATION] serialize_into_session is deprecated. Please overwrite the serialize method in Warden::Serializers::Session." - Warden::Serializers::Session.send :define_method, :serialize, &block - end - - # Reconstitues the user from the session. - # Use the results of user_session_key to reconstitue the user from the session on requests after the initial login - # - # Example: - # Warden::Manager.serialize_from_session{ |id| User.get(id) } - # - # Deprecation: - # This method was deprecated in favor of serializer in Session. You can set it while setting the middleware: - # - # use Warden::Manager do |manager| - # manager.serializers.update(:session) do - # def deserialize(id) - # User.get(id) - # end - # end - # end - # - # :api: public - def serialize_from_session(&block) - warn "[DEPRECATION] serialize_from_session is deprecated. Please overwrite the deserialize method in Warden::Serializers::Session." - Warden::Serializers::Session.send :define_method, :deserialize, &block - end - end end \ No newline at end of file diff --git a/lib/warden/proxy.rb b/lib/warden/proxy.rb index 54c91a1..d767ec2 100644 --- a/lib/warden/proxy.rb +++ b/lib/warden/proxy.rb @@ -3,7 +3,7 @@ module Warden class UserNotSet < RuntimeError; end class Proxy - # An accessor to the wining strategy + # An accessor to the winning strategy # :api: private attr_accessor :winning_strategy @@ -15,27 +15,21 @@ class Proxy include ::Warden::Mixins::Common # :api: private - def_delegators :winning_strategy, :headers, :_status, :custom_response + def_delegators :winning_strategy, :headers, :status, :custom_response def initialize(env, manager) #:nodoc: @env, @users = env, {} + @strategies = Hash.new { |h,k| h[k] = {} } @manager, @config = manager, manager.config errors # setup the error object in the session end - # Add warden cookies to the response streamed back. - def respond!(args) - return args if warden_cookies.empty? - response = Rack::Response.new(args[2], args[0], args[1]) - - warden_cookies.each do |key, value| - if value.is_a?(Hash) - response.set_cookie key, value - else - response.delete_cookie key - end - end - response.to_a + # Points to a SessionSerializer instance repsonsible for handling + # everything related with storing, fetching and removing the user + # session. + # :api: public + def session_serializer + @session_serializer ||= Warden::SessionSerializer.new(@env) end # Check to see if there is an authenticated user for the given scope. @@ -102,16 +96,10 @@ def authenticate!(*args) # env['warden'].set_user(@user) # env['warden'].stored? #=> true # env['warden'].stored?(:default) #=> true - # env['warden'].stored?(:default, :session) #=> true - # env['warden'].stored?(:default, :cookie) #=> false # # :api: public - def stored?(scope = @config.default_scope, serializer = nil) - if serializer - _find_serializer(serializer).stored?(scope) - else - serializers.any? { |s| s.stored?(scope) } - end + def stored?(scope = @config.default_scope) + session_serializer.stored?(scope) end # Manually set the user into the session and auth proxy @@ -124,8 +112,9 @@ def stored?(scope = @config.default_scope, serializer = nil) def set_user(user, opts = {}) return unless user scope = (opts[:scope] ||= @config.default_scope) - _store_user(user, scope) unless opts[:store] == false + @users[scope] = user + session_serializer.store(user, scope) unless opts[:store] == false opts[:event] ||= :set_user manager._run_callbacks(:after_set_user, user, self, opts) @@ -144,7 +133,8 @@ def set_user(user, opts = {}) # # :api: public def user(scope = @config.default_scope) - @users[scope] ||= set_user(_fetch_user(scope), :scope => scope, :event => :fetch) + @users[scope] ||= set_user(session_serializer.fetch(scope), + :scope => scope, :event => :fetch) end # Provides a scoped session data for authenticated users. @@ -191,7 +181,7 @@ def logout(*scopes) manager._run_callbacks(:before_logout, user, self, :scope => scope) raw_session.delete("warden.user.#{scope}.session") - _delete_user(user, scope) + session_serializer.delete(scope, user) end reset_session! if reset_session @@ -222,32 +212,32 @@ def custom_failure? !!@custom_failure end - # Retrieve and initializer serializers. + # Add warden cookies to the response streamed back. # :api: private - def serializers # :nodoc: - @serializers ||= begin - @config.default_serializers.inject([]) do |array, s| - unless klass = Warden::Serializers[s] - raise "Invalid serializer #{s}" unless @config.silence_missing_serializers? - array - else - array << klass.new(@env) - end + # TODO Test me since I was removed. + def respond!(args) + return args if warden_cookies.empty? + response = Rack::Response.new(args[2], args[0], args[1]) + + warden_cookies.each do |key, value| + if value.is_a?(Hash) + response.set_cookie key, value + else + response.delete_cookie key end end + response.to_a end private - # :api: private def _perform_authentication(*args) - scope = scope_from_args(args) - opts = opts_from_args(args) + opts = args.last.is_a?(Hash) ? args.pop : {} + scope = opts[:scope] || @config.default_scope # Look for an existing user in the session for this scope. - # If there was no user in the session. See if we can get one from the request + # If there was no user in the session. See if we can get one from the request. return scope, opts if user(scope) - _run_strategies_for(scope, args) if winning_strategy && winning_strategy.user @@ -257,61 +247,32 @@ def _perform_authentication(*args) [scope, opts] end - # :api: private - def scope_from_args(args) # :nodoc: - Hash === args.last ? args.last.fetch(:scope, @config.default_scope) : @config.default_scope - end - - # :api: private - def opts_from_args(args) # :nodoc: - Hash === args.last ? args.pop : {} - end - - # :api: private + # Run the strategies for a given scope def _run_strategies_for(scope, args) #:nodoc: strategies = args.empty? ? @config.default_strategies : args - strategies.each do |s| - unless klass = Warden::Strategies[s] - raise "Invalid strategy #{s}" unless args.empty? && @config.silence_missing_strategies? - next - end + strategies.each do |name| + strategy = _fetch_strategy(name, scope) + next unless strategy && strategy.valid? - strategy = klass.new(@env, scope) self.winning_strategy = strategy - next unless strategy.valid? - strategy._run! break if strategy.halted? end end - # Does the work of storing the user in stores. - # :api: private - def _store_user(user, scope) # :nodoc: - return unless user - serializers.each { |s| s.store(user, scope) } - end + # Fetchs strategies and keep them in a hash cache. + def _fetch_strategy(name, scope) + return @strategies[scope][name] if @strategies[scope].key?(name) - # Does the work of fetching the user from the first store. - # :api: private - def _fetch_user(scope) # :nodoc: - serializers.each do |s| - user = s.fetch(scope) - return user if user + @strategies[scope][name] = if klass = Warden::Strategies[name] + klass.new(@env, scope) + elsif @config.silence_missing_strategies? + nil + else + raise "Invalid strategy #{name}" end - nil - end - - # Does the work of deleteing the user in all stores. - # :api: private - def _delete_user(user, scope) # :nodoc: - serializers.each { |s| s.delete(scope, user) } end - # :api: private - def _find_serializer(name) # :nodoc: - serializers.find { |s| s.class == ::Warden::Serializers[name] } - end end # Proxy end # Warden diff --git a/lib/warden/serializers.rb b/lib/warden/serializers.rb deleted file mode 100644 index 66bf974..0000000 --- a/lib/warden/serializers.rb +++ /dev/null @@ -1,20 +0,0 @@ -# encoding: utf-8 -require 'warden/declarable' - -module Warden - module Serializers - extend Warden::Declarable - - class << self - def check_validity!(label, serializer) - [:fetch, :store, :stored?, :delete].each do |method| - next if serializer.method_defined?(method) - raise NoMethodError, "#{method} is not declared in the #{label.inspect} serializer" - end - end - - alias :_serializers :_declarations - end # << self - - end # Serializers -end # Warden diff --git a/lib/warden/serializers/base.rb b/lib/warden/serializers/base.rb deleted file mode 100644 index dde05b3..0000000 --- a/lib/warden/serializers/base.rb +++ /dev/null @@ -1,38 +0,0 @@ -# encoding: utf-8 -module Warden - module Serializers - # A serializer is a place where you put the logic to serializing and deserializing an user. All serializers - # inherits from Warden::Serializers::Base. - # - # The Warden::Serializers.add method is a simple way to provide custom serializers. In order to do so, - # you _must_ declare @store@, @fetch@, @delete@ and @stored?@ methods. - # - # The parameters for Warden::Serializers.add method is: - # The label is the name given to a serializer. Use the label to refer to the serializer when authenticating - # The optional serializer argument if set _must_ be a class that inherits from Warden::Serializers::Base - # The block acts as a convinient way to declare your serializer. Inside is the class definition of a serializer. - # - # Check Session and Cookie serializers for more information. - # - class Base - attr_accessor :env - include ::Warden::Mixins::Common - - def initialize(env) - @env = env - end - - def key_for(scope) - "warden.user.#{scope}.key" - end - - def serialize(user) - user - end - - def deserialize(key) - key - end - end # Base - end # Serializers -end # Warden \ No newline at end of file diff --git a/lib/warden/serializers/cookie.rb b/lib/warden/serializers/cookie.rb deleted file mode 100644 index ac3aa32..0000000 --- a/lib/warden/serializers/cookie.rb +++ /dev/null @@ -1,34 +0,0 @@ -# encoding: utf-8 -module Warden - module Serializers - # A cookie serializer provided by Warden. You need to implement the serialize and deserialize - # methods in order to use it. - class Cookie < Base - def store(user, scope) - warden_cookies[key_for(scope)] = default_options(user) - end - - def fetch(scope) - key = request.cookies[key_for(scope)] - return nil unless key - user = deserialize(key) - delete(scope) unless user - user - end - - def stored?(scope) - !!request.cookies[key_for(scope)] - end - - def delete(scope, user=nil) - warden_cookies[key_for(scope)] = nil - end - - def default_options(user) - { :value => serialize(user), :expires => (Time.now + 7 * 24 * 3600), :path => "/" } - end - end # Cookie - - Serializers.add(:cookie, Cookie) - end # Serializers -end # Warden \ No newline at end of file diff --git a/lib/warden/serializers/session.rb b/lib/warden/serializers/session.rb deleted file mode 100644 index 05ab633..0000000 --- a/lib/warden/serializers/session.rb +++ /dev/null @@ -1,30 +0,0 @@ -# encoding: utf-8 -module Warden - module Serializers - # A session serializer provided by Warden. You need to implement the serialize and deserialize - # methods in order to use it. - class Session < Base - def store(user, scope) - session[key_for(scope)] = serialize(user) - end - - def fetch(scope) - key = session[key_for(scope)] - return nil unless key - user = deserialize(key) - delete(scope) unless user - user - end - - def stored?(scope) - !!session[key_for(scope)] - end - - def delete(scope, user=nil) - session.delete(key_for(scope)) - end - end # Session - - Serializers.add(:session, Session) - end # Serializers -end # Warden \ No newline at end of file diff --git a/lib/warden/session_serializer.rb b/lib/warden/session_serializer.rb new file mode 100644 index 0000000..5d64237 --- /dev/null +++ b/lib/warden/session_serializer.rb @@ -0,0 +1,44 @@ +# encoding: utf-8 +module Warden + class SessionSerializer + attr_reader :env + include ::Warden::Mixins::Common + + def initialize(env) + @env = env + end + + def key_for(scope) + "warden.user.#{scope}.key" + end + + def serialize(user) + user + end + + def deserialize(key) + key + end + + def store(user, scope) + return unless user + session[key_for(scope)] = serialize(user) + end + + def fetch(scope) + key = session[key_for(scope)] + return nil unless key + user = deserialize(key) + delete(scope) unless user + user + end + + def stored?(scope) + !!session[key_for(scope)] + end + + def delete(scope, user=nil) + session.delete(key_for(scope)) + end + end # SessionSerializer +end # Warden \ No newline at end of file diff --git a/lib/warden/strategies/base.rb b/lib/warden/strategies/base.rb index 10610a5..59e1a03 100644 --- a/lib/warden/strategies/base.rb +++ b/lib/warden/strategies/base.rb @@ -34,13 +34,9 @@ class Base #:api: private attr_accessor :result, :custom_response - # Setup for redirection - # :api: private - attr_reader :_status - - # Accessor for the rack env + # Accessors for the rack env # :api: public - attr_reader :env, :scope + attr_reader :env, :scope, :status include ::Warden::Mixins::Common # :api: private @@ -127,7 +123,7 @@ def fail!(message = "Failed to Login") # :api: public def redirect!(url, params = {}, opts = {}) halt! - @_status = opts[:permanent] ? 301 : 302 + @status = opts[:permanent] ? 301 : 302 headers["Location"] = url headers["Location"] << "?" << Rack::Utils.build_query(params) unless params.empty? headers["Content-Type"] = opts[:content_type] || 'text/plain' diff --git a/spec/warden/proxy_spec.rb b/spec/warden/proxy_spec.rb index 16837bc..dd16660 100644 --- a/spec/warden/proxy_spec.rb +++ b/spec/warden/proxy_spec.rb @@ -226,30 +226,6 @@ end setup_rack(app).call(@env) end - - it "returns based on the given store" do - @env['rack.session'].delete("warden.user.default.key") - @env["HTTP_COOKIE"] = "warden.user.default.key=user;" - app = lambda do |env| - env['warden'].stored?(:default, :session).should be_false - env['warden'].stored?(:default, :cookie).should be_true - valid_response - end - setup_rack(app, :default_serializers => [:session, :cookie]).call(@env) - end - end - - it "should not raise errors with missing serializers" do - env = env_with_params('/') - app = lambda do |env| - env['warden'].authenticate - env['warden'].serializers.size.should == 1 - env['warden'].serializers.first.should be_kind_of(Warden::Serializers[:session]) - valid_response - end - lambda { - setup_rack(app, :silence_missing_serializers => true, :default_serializers => [:session, :unknown]).call(env) - }.should_not raise_error end describe "set user" do @@ -265,18 +241,6 @@ setup_rack(app).call(env) end - it "should store the user into cookies" do - env = env_with_params("/") - app = lambda do |env| - env['warden'].authenticate(:pass) - env['warden'].should be_authenticated - env['warden'].user.should == "Valid User" - valid_response - end - response = setup_rack(app, :default_serializers => [:cookie]).call(env) - response[1]['Set-Cookie'].split(";").first.should == "warden.user.default.key=Valid+User" - end - it "should not store the user if the :store option is set to false" do env = env_with_params("/") app = lambda do |e| @@ -313,17 +277,7 @@ setup_rack(app).call(@env) end - it "should load user from others serializers" do - @env["HTTP_COOKIE"] = "warden.user.default.key=user;" - app = lambda do |env| - env['warden'].user.should == "user" - valid_response - end - setup_rack(app, :default_serializers => [:session, :cookie]).call(@env) - end - describe "previously logged in" do - before(:each) do @env['rack.session']['warden.user.default.key'] = "A Previous User" @env['warden.spec.strategies'] = [] @@ -411,13 +365,6 @@ setup_rack(app).call(@env) end - it "should clear the cookie serializer when logging out" do - @app = setup_rack(@app, :default_serializers => [:cookie]) - @env['warden.spec.which_logout'] = :default - response = @app.call(@env) - response[1]['Set-Cookie'].first.split(";").first.should == "warden.user.default.key=" - end - it "should clear out the session by calling reset_session! so that plugins can setup their own session clearing" do @env['rack.session'].should_not be_nil app = lambda do |e| diff --git a/spec/warden/serializers/cookie_spec.rb b/spec/warden/serializers/cookie_spec.rb deleted file mode 100644 index bf81650..0000000 --- a/spec/warden/serializers/cookie_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Warden::Serializers::Cookie do - before(:each) do - @env = env_with_params - @cookie = Warden::Serializers::Cookie.new(@env) - end - - def set_cookie! - response = Rack::Response.new - @cookie.warden_cookies.each do |key, value| - if value.is_a?(Hash) - response.set_cookie key, value - else - response.delete_cookie key - end - end - @env['HTTP_COOKIE'] = response.headers['Set-Cookie'] - end - - it "should store data for the default scope" do - @cookie.store("user", :default) - @cookie.warden_cookies.should have_key("warden.user.default.key") - @cookie.warden_cookies["warden.user.default.key"][:value].should == "user" - end - - it "should check if a data is stored or not" do - @cookie.should_not be_stored(:default) - @cookie.store("user", :default) - set_cookie! - @cookie.should be_stored(:default) - end - - it "should load an user from store" do - @cookie.fetch(:default).should be_nil - @cookie.store("user", :default) - set_cookie! - @cookie.fetch(:default).should == "user" - end - - it "should store data based on the scope" do - @cookie.store("user", :default) - set_cookie! - @cookie.fetch(:default).should == "user" - @cookie.fetch(:another).should be_nil - end - - it "should delete data from store" do - @cookie.store("user", :default) - set_cookie! - @cookie.fetch(:default).should == "user" - @cookie.delete(:default) - @cookie.warden_cookies.should have_key("warden.user.default.key") - @cookie.warden_cookies["warden.user.default.key"].should be_nil - end - - it "should delete information from store if user cannot be retrieved" do - @cookie.store("user", :default) - set_cookie! - @cookie.instance_eval "def deserialize(key); nil; end" - @cookie.fetch(:default) - @cookie.warden_cookies.should have_key("warden.user.default.key") - @cookie.warden_cookies["warden.user.default.key"].should be_nil - end -end diff --git a/spec/warden/serializers_spec.rb b/spec/warden/serializers_spec.rb deleted file mode 100644 index f620bf0..0000000 --- a/spec/warden/serializers_spec.rb +++ /dev/null @@ -1,102 +0,0 @@ -require File.dirname(__FILE__) + '/../spec_helper' - -describe Warden::Serializers do - it "should let me add a serializer via a block" do - Warden::Serializers.add(:serializer1) do - def fetch; end - def store; end - def stored?; end - def delete; end - end - Warden::Serializers[:serializer1].ancestors.should include(Warden::Serializers::Base) - end - - it "should raise an error if I add a serializer via a block, that does not have an autheniticate! method" do - lambda do - Warden::Serializers.add(:serializer2) do - end - end.should raise_error - end - - it "should allow me to get access to a particular serializer" do - Warden::Serializers.add(:serializer3) do - def fetch; end - def store; end - def stored?; end - def delete; end - end - serializer = Warden::Serializers[:serializer3] - serializer.should_not be_nil - serializer.ancestors.should include(Warden::Serializers::Base) - end - - it "should allow me to add a serializer with the required methods" do - class MySerializer < Warden::Serializers::Base - def fetch; end - def store; end - def stored?; end - def delete; end - end - lambda do - Warden::Serializers.add(:serializer4, MySerializer) - end.should_not raise_error - end - - it "should not allow a serializer that does not have any required method" do - class MyOtherSerializer - end - lambda do - Warden::Serializers.add(:serializer5, MyOtherSerializer) - end.should raise_error - end - - it "should allow me to change a class when providing a block and class" do - class MySerializer < Warden::Serializers::Base - end - - Warden::Serializers.add(:foo, MySerializer) do - def fetch; end - def store; end - def stored?; end - def delete; end - end - - Warden::Serializers[:foo].ancestors.should include(MySerializer) - end - - it "should allow me to update a previously given serializer" do - class MySerializer < Warden::Serializers::Base - def fetch; end - def store; end - def stored?; end - def delete; end - end - - Warden::Serializers.add(:serializer6, MySerializer) - - new_module = Module.new - Warden::Serializers.update(:serializer6) do - include new_module - end - - Warden::Serializers[:serializer6].ancestors.should include(new_module) - end - - it "should allow me to clear the Serializers" do - old_serializers = Warden::Serializers._serializers.dup - - begin - Warden::Serializers.add(:foobar) do - def fetch; end - def store; end - def stored?; end - def delete; end - end - Warden::Serializers[:foobar].should_not be_nil - Warden::Serializers.clear! - Warden::Serializers[:foobar].should be_nil - else - Warden::Serializers._serializers.replace(old_serializers) - end - end -end diff --git a/spec/warden/serializers/session_spec.rb b/spec/warden/session_serializer_spec.rb similarity index 90% rename from spec/warden/serializers/session_spec.rb rename to spec/warden/session_serializer_spec.rb index 454d711..d5fbdd6 100644 --- a/spec/warden/serializers/session_spec.rb +++ b/spec/warden/session_serializer_spec.rb @@ -1,10 +1,10 @@ -require File.dirname(__FILE__) + '/../../spec_helper' +require File.dirname(__FILE__) + '/../spec_helper' -describe Warden::Serializers::Session do +describe Warden::SessionSerializer do before(:each) do @env = env_with_params @env['rack.session'] ||= {} - @session = Warden::Serializers::Session.new(@env) + @session = Warden::SessionSerializer.new(@env) end it "should store data for the default scope" do