ruby-warden - initial gem2deb package.
authorFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 13 Mar 2014 13:31:32 +0000 (14:31 +0100)
committerFrantišek Dvořák <valtri@civ.zcu.cz>
Thu, 13 Mar 2014 13:31:32 +0000 (14:31 +0100)
49 files changed:
ruby-warden/Gemfile [new file with mode: 0644]
ruby-warden/History.rdoc [new file with mode: 0644]
ruby-warden/LICENSE [new file with mode: 0644]
ruby-warden/README.textile [new file with mode: 0644]
ruby-warden/Rakefile [new file with mode: 0644]
ruby-warden/debian/changelog [new file with mode: 0644]
ruby-warden/debian/compat [new file with mode: 0644]
ruby-warden/debian/control [new file with mode: 0644]
ruby-warden/debian/copyright [new file with mode: 0644]
ruby-warden/debian/ruby-tests.rb [new file with mode: 0644]
ruby-warden/debian/ruby-warden.docs [new file with mode: 0644]
ruby-warden/debian/rules [new file with mode: 0755]
ruby-warden/debian/source/format [new file with mode: 0644]
ruby-warden/debian/watch [new file with mode: 0644]
ruby-warden/lib/warden.rb [new file with mode: 0644]
ruby-warden/lib/warden/config.rb [new file with mode: 0644]
ruby-warden/lib/warden/errors.rb [new file with mode: 0644]
ruby-warden/lib/warden/hooks.rb [new file with mode: 0644]
ruby-warden/lib/warden/manager.rb [new file with mode: 0644]
ruby-warden/lib/warden/mixins/common.rb [new file with mode: 0644]
ruby-warden/lib/warden/proxy.rb [new file with mode: 0644]
ruby-warden/lib/warden/session_serializer.rb [new file with mode: 0644]
ruby-warden/lib/warden/strategies.rb [new file with mode: 0644]
ruby-warden/lib/warden/strategies/base.rb [new file with mode: 0644]
ruby-warden/lib/warden/test/helpers.rb [new file with mode: 0644]
ruby-warden/lib/warden/test/warden_helpers.rb [new file with mode: 0644]
ruby-warden/lib/warden/version.rb [new file with mode: 0644]
ruby-warden/metadata.yml [new file with mode: 0644]
ruby-warden/spec/helpers/request_helper.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/failz.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/invalid.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/pass.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/pass_with_message.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/password.rb [new file with mode: 0644]
ruby-warden/spec/helpers/strategies/single.rb [new file with mode: 0644]
ruby-warden/spec/spec_helper.rb [new file with mode: 0644]
ruby-warden/spec/warden/authenticated_data_store_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/config_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/errors_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/hooks_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/manager_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/proxy_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/scoped_session_serializer.rb [new file with mode: 0644]
ruby-warden/spec/warden/session_serializer_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/strategies/base_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/strategies_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/test/helpers_spec.rb [new file with mode: 0644]
ruby-warden/spec/warden/test/test_mode_spec.rb [new file with mode: 0644]
ruby-warden/warden.gemspec [new file with mode: 0644]

diff --git a/ruby-warden/Gemfile b/ruby-warden/Gemfile
new file mode 100644 (file)
index 0000000..ad4ced2
--- /dev/null
@@ -0,0 +1,11 @@
+source 'https://rubygems.org'
+
+gemspec
+
+gem 'rake'
+gem 'rack', '1.3'
+
+group :test do
+  gem 'rspec', '~>2'
+  gem 'rack-test'
+end
diff --git a/ruby-warden/History.rdoc b/ruby-warden/History.rdoc
new file mode 100644 (file)
index 0000000..494eae9
--- /dev/null
@@ -0,0 +1,150 @@
+== Version 1.2.3 / 2013-07-14
+* Fix an issue with lazy loaded sessions
+
+== Version 1.2.2 / 2013-07-12
+* Support nil session stores on logout
+* Fix strategies blowing up with undefined method base
+
+== Version 1.2.1 / 2012-06-16
+* Minor caching and speed improvements
+* Add support to #lock in the proxy
+* Add support to after_failed_fetch callback
+
+== Version 1.2.0 / 2012-05-08
+* Deprecate warden_cookies since it was never functional
+* Add support to serialize_from_session and serialize_into_session per scope
+
+== Version 1.1.1 / 2012-02-16
+* Allow run_callbacks as an option to set_user and user
+
+== Version 1.1.0 / 2011-11-02
+* Use the default scopes action when using a bare throw(:warden)
+
+== Version 1.0.6
+* Remove gem files from the packaged gem
+
+== Version 1.0.3
+* Do not renew session on user fetch
+
+== Version 1.0.2
+* Added :intercept_401 to Warden::Config
+
+== Version 1.0.1
+* Bug fix on strategies errors handler
+
+== Version 1.0.0
+* Bump!
+* Allow strategies to configure if user should be stored or not
+* Force session id renewal when user is set
+
+== Version 0.10.7
+* Performance boost. config object to use raw accessors
+* Add per strategy storage option
+
+== Version 0.10.6 / 0.10.7 / 2010-05-22
+* Bugfix set_user was not respecting logouts in hooks
+
+== Version 0.10.4 / 0.10.5 / 2010-05-20
+* Add action specifying in scope_defaults
+
+== Version 0.10.3 / 2010-03-01
+* Bugfix prevent halted winning strategy from being skipped in subsequent runs
+
+== Version 0.10.2 / 2010-03-26
+* Halt on fail!.  Add fail to allow cascading
+* cache the winning strategy
+* Make the config object Dupable
+
+== Version 0.10.1 / 2010-03-23
+* Merge previous from master
+* tag
+
+== Version 0.10.0 / 2010-03-22
+* Allow default strategies to be set on the proxy
+* Provide each scope with it's own default strategies
+* Provide each scope with default set_user opts
+* depricate the Proxy#default_strategies= method
+
+== Version 0.9.5 / 2010-02-28
+
+* Add Warden.test_mode!
+* Add Warden.on_next_request
+* Add test helpers in Warden::Test::Helpers
+** login_as
+** logout
+
+== Version 0.9.4 / 2010-02-23
+
+* Fix an issue where winning_strategy was not cleaned, allowing multiple scopes to sign in, even when the second one should not
+
+== Version 0.9.3 / 2010-02-17
+
+* Add prepend_ to all hooks (josevalim)
+
+== Version 0.9.2 / 2010-02-10
+
+* Ruby 1.9 compatibility changes (grimen)
+
+== Version 0.9.1 / 2010-02-09
+
+* Support for passing a custom message with Warden::Strategy::Base#success! as second optional (grimen)
+
+== Version 0.9.0 / 2010-01-21
+
+* Remove serializers and make strategies more powerful, including cache behavior (josevalim)
+
+== Version 0.8.1 / 2010-01-06
+
+* Fix a bug when silence missing serializers is set (josevalim)
+
+== Version 0.8.0 / 2010-01-06
+
+* enhancements
+  * Add conditionals to callbacks (josevalim)
+  * Extract Warden::Config from Warden::Manager (josevalim)
+
+== Version 0.7.0 / 2010-01-04
+
+* enhancements
+  * Expose config in warden proxy (hassox)
+
+== Version 0.6.0 / 2009-11-16
+
+* enhancements
+  * added serializers, including session serializer (set by default) and a cookie serializer (josevalim)
+
+* deprecation
+  * serializer_into_session and serializer_from_session are deprecated, overwrite serialize and deserializer in Warden::Serializers::Session instead (josevalim)
+
+== Version 0.5.3 / 2009-11-10
+* bug fixes
+  * authenticated? and unauthenticated? should return true or false, not the user or false. (hassox)
+
+== Version 0.5.2 / 2009-11-09
+* enhancements
+  * authenticated? always try to serialize the user from session (josevalim)
+  * stored_in_session? checks if user information is stored in session, without serializing (josevalim)
+  * 401 behaves exactly like throw :warden (staugaard)
+
+=== Version 0.5.1 / 2009-10-25
+* enhancements
+  * Adds yeilding to authenticated? and unauthenticated? methods (hassox)
+  * Adds an option to silence missing strategies (josevalim)
+  * Add an option to authenticate(!) to prevent storage of a user into the session (hassox)
+  * allow custom :action to be thrown (josevalim)
+
+=== Version 0.4.0 / 2009-10-12
+
+* enhancements
+  * add Content-Type header to redirects (staugaard)
+  * Make scope available to strategies (josevalim)
+
+* bug fixes
+  * Do not consume opts twice, otherwise just the first will parse the scope (josevalim)
+
+=== Version 0.3.2 / 2009-09-15
+
+* enhancements
+  * add a hook for plugins to specify how they can clear the whole section
+
+
diff --git a/ruby-warden/LICENSE b/ruby-warden/LICENSE
new file mode 100644 (file)
index 0000000..ea4b483
--- /dev/null
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Daniel Neighman
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/ruby-warden/README.textile b/ruby-warden/README.textile
new file mode 100644 (file)
index 0000000..a45c0fb
--- /dev/null
@@ -0,0 +1,9 @@
+Please see the "Warden Wiki":http://wiki.github.com/hassox/warden for overview documentation.
+
+h2. Maintainers
+
+* Daniel Neighman (hassox)
+* José Valim (josevalim)
+* Justin Smestad (jsmestad)
+
+"A list of all contributors is available on Github.":https://github.com/hassox/warden/contributors
diff --git a/ruby-warden/Rakefile b/ruby-warden/Rakefile
new file mode 100644 (file)
index 0000000..938a8b7
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- encoding: utf-8 -*-
+require 'rubygems'
+require 'rake'
+$:.unshift  File.join(File.dirname(__FILE__), "lib")
+
+require 'rspec/core'
+require 'rspec/core/rake_task'
+
+task :default => :spec
+
+desc "Run all specs in spec directory"
+RSpec::Core::RakeTask.new(:spec)
diff --git a/ruby-warden/debian/changelog b/ruby-warden/debian/changelog
new file mode 100644 (file)
index 0000000..8914155
--- /dev/null
@@ -0,0 +1,5 @@
+ruby-warden (1.2.3-1) UNRELEASED; urgency=medium
+
+  * Initial release (Closes: #nnnn)
+
+ -- František Dvořák <valtri@civ.zcu.cz>  Thu, 13 Mar 2014 14:30:29 +0100
diff --git a/ruby-warden/debian/compat b/ruby-warden/debian/compat
new file mode 100644 (file)
index 0000000..7f8f011
--- /dev/null
@@ -0,0 +1 @@
+7
diff --git a/ruby-warden/debian/control b/ruby-warden/debian/control
new file mode 100644 (file)
index 0000000..aa2375e
--- /dev/null
@@ -0,0 +1,19 @@
+Source: ruby-warden
+Section: ruby
+Priority: optional
+Maintainer: Debian Ruby Extras Maintainers <pkg-ruby-extras-maintainers@lists.alioth.debian.org>
+Uploaders: František Dvořák <valtri@civ.zcu.cz>
+Build-Depends: debhelper (>= 7.0.50~), gem2deb (>= 0.6.1~)
+Standards-Version: 3.9.4
+#Vcs-Git: git://anonscm.debian.org/pkg-ruby-extras/ruby-warden.git
+#Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-ruby-extras/ruby-warden.git;a=summary
+Homepage: http://github.com/hassox/warden
+XS-Ruby-Versions: all
+
+Package: ruby-warden
+Architecture: all
+XB-Ruby-Versions: ${ruby:Versions}
+Depends: ${shlibs:Depends}, ${misc:Depends}, ruby | ruby-interpreter
+# rack (>= 1.0)
+Description: Rack middleware that provides authentication for rack applications
+ <insert long description, indented with spaces>
diff --git a/ruby-warden/debian/copyright b/ruby-warden/debian/copyright
new file mode 100644 (file)
index 0000000..084ff98
--- /dev/null
@@ -0,0 +1,35 @@
+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: warden
+Source: FIXME <http://example.com/>
+
+Files: *
+Copyright: <years> <put author's name and email here>
+           <years> <likewise for another author>
+License: GPL-2+ (FIXME)
+
+Files: debian/*
+Copyright: 2014 František Dvořák <valtri@civ.zcu.cz>
+License: GPL-2+ (FIXME)
+Comment: the Debian packaging is licensed under the same terms as the original package.
+
+License: GPL-2+ (FIXME)
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later
+ version.
+ .
+ This program is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied
+ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ PURPOSE.  See the GNU General Public License for more
+ details.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA  02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 2 can be found in the file
+ `/usr/share/common-licenses/GPL-2'.
diff --git a/ruby-warden/debian/ruby-tests.rb b/ruby-warden/debian/ruby-tests.rb
new file mode 100644 (file)
index 0000000..eac5ce6
--- /dev/null
@@ -0,0 +1,13 @@
+# FIXME
+# there's a spec/ or a test/ directory in the upstream source, but
+# no test suite was defined in the Gem specification. It would be
+# a good idea to define it here so the package gets tested at build time.
+# Examples:
+# $: << 'lib' << '.'
+# Dir['{spec,test}/**/*.rb'].each { |f| require f }
+#
+# require 'test/ts_foo.rb'
+#
+# require 'rbconfig'
+# ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
+# exec("#{ruby} -I. test/runtests.rb")
diff --git a/ruby-warden/debian/ruby-warden.docs b/ruby-warden/debian/ruby-warden.docs
new file mode 100644 (file)
index 0000000..2c81977
--- /dev/null
@@ -0,0 +1,2 @@
+# FIXME: READMEs found
+# README.textile
diff --git a/ruby-warden/debian/rules b/ruby-warden/debian/rules
new file mode 100755 (executable)
index 0000000..82ddc0c
--- /dev/null
@@ -0,0 +1,15 @@
+#!/usr/bin/make -f
+#export DH_VERBOSE=1
+#
+# Uncomment to ignore all test failures (but the tests will run anyway)
+#export DH_RUBY_IGNORE_TESTS=all
+#
+# Uncomment to ignore some test failures (but the tests will run anyway).
+# Valid values:
+#export DH_RUBY_IGNORE_TESTS=ruby1.9.1 ruby2.0 require-rubygems
+#
+# If you need to specify the .gemspec (eg there is more than one)
+#export DH_RUBY_GEMSPEC=gem.gemspec
+
+%:
+       dh $@ --buildsystem=ruby --with ruby
diff --git a/ruby-warden/debian/source/format b/ruby-warden/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/ruby-warden/debian/watch b/ruby-warden/debian/watch
new file mode 100644 (file)
index 0000000..eec6d2a
--- /dev/null
@@ -0,0 +1,2 @@
+version=3
+http://pkg-ruby-extras.alioth.debian.org/cgi-bin/gemwatch/warden .*/warden-(.*).tar.gz
diff --git a/ruby-warden/lib/warden.rb b/ruby-warden/lib/warden.rb
new file mode 100644 (file)
index 0000000..df556d7
--- /dev/null
@@ -0,0 +1,45 @@
+# encoding: utf-8
+require 'forwardable'
+
+require 'warden/mixins/common'
+require 'warden/proxy'
+require 'warden/manager'
+require 'warden/errors'
+require 'warden/session_serializer'
+require 'warden/strategies'
+require 'warden/strategies/base'
+
+module Warden
+  class NotAuthenticated < StandardError; end
+
+  module Test
+    autoload :WardenHelpers,  'warden/test/warden_helpers'
+    autoload :Helpers,        'warden/test/helpers'
+  end
+
+  # Provides helper methods to warden for testing.
+  #
+  # To setup warden in test mode call the +test_mode!+ method on warden
+  #
+  # @example
+  #   Warden.test_mode!
+  #
+  # This will provide a number of methods.
+  # Warden.on_next_request(&blk) - captures a block which is yielded the warden proxy on the next request
+  # Warden.test_reset! - removes any captured blocks that would have been executed on the next request
+  #
+  # Warden.test_reset! should be called in after blocks for rspec, or teardown methods for Test::Unit
+  def self.test_mode!
+    unless Warden::Test::WardenHelpers === Warden
+      Warden.extend Warden::Test::WardenHelpers
+      Warden::Manager.on_request do |proxy|
+        unless proxy.asset_request?
+          while blk = Warden._on_next_request.shift
+            blk.call(proxy)
+          end
+        end
+      end
+    end
+    true
+  end
+end
diff --git a/ruby-warden/lib/warden/config.rb b/ruby-warden/lib/warden/config.rb
new file mode 100644 (file)
index 0000000..9f6904e
--- /dev/null
@@ -0,0 +1,112 @@
+# encoding: utf-8
+
+module Warden
+  # This is a class which is yielded on use Warden::Manager. If you have a plugin
+  # and wants to add more configuration to warden, you just need to extend this
+  # class.
+  class Config < Hash
+    # Creates an accessor that simply sets and reads a key in the hash:
+    #
+    #   class Config < Hash
+    #     hash_accessor :failure_app
+    #   end
+    #
+    #   config = Config.new
+    #   config.failure_app = Foo
+    #   config[:failure_app] #=> Foo
+    #
+    #   config[:failure_app] = Bar
+    #   config.failure_app #=> Bar
+    #
+    def self.hash_accessor(*names) #:nodoc:
+      names.each do |name|
+        class_eval <<-METHOD, __FILE__, __LINE__ + 1
+          def #{name}
+            self[:#{name}]
+          end
+
+          def #{name}=(value)
+            self[:#{name}] = value
+          end
+        METHOD
+      end
+    end
+
+    hash_accessor :failure_app, :default_scope, :intercept_401
+
+    def initialize(other={})
+      merge!(other)
+      self[:default_scope]      ||= :default
+      self[:scope_defaults]     ||= {}
+      self[:default_strategies] ||= {}
+      self[:intercept_401] = true unless key?(:intercept_401)
+    end
+
+    def initialize_copy(other)
+      super
+      deep_dup(:scope_defaults, other)
+      deep_dup(:default_strategies, other)
+    end
+
+    # Do not raise an error if a missing strategy is given.
+    # :api: plugin
+    def silence_missing_strategies!
+      self[:silence_missing_strategies] = true
+    end
+
+    def silence_missing_strategies? #:nodoc:
+      !!self[:silence_missing_strategies]
+    end
+
+    # Set the default strategies to use.
+    # :api: public
+    def default_strategies(*strategies)
+      opts  = Hash === strategies.last ? strategies.pop : {}
+      hash  = self[:default_strategies]
+      scope = opts[:scope] || :_all
+
+      hash[scope] = strategies.flatten unless strategies.empty?
+      hash[scope] || hash[:_all] || []
+    end
+
+    # A short hand way to set up a particular scope
+    # :api: public
+    def scope_defaults(scope, opts = {})
+      if strategies = opts.delete(:strategies)
+        default_strategies(strategies, :scope => scope)
+      end
+
+      if opts.empty?
+        self[:scope_defaults][scope] || {}
+      else
+        self[:scope_defaults][scope] ||= {}
+        self[:scope_defaults][scope].merge!(opts)
+      end
+    end
+
+    # Quick accessor to strategies from manager
+    # :api: public
+    def strategies
+      Warden::Strategies
+    end
+
+    # Hook from configuration to serialize_into_session.
+    # :api: public
+    def serialize_into_session(*args, &block)
+      Warden::Manager.serialize_into_session(*args, &block)
+    end
+
+    # Hook from configuration to serialize_from_session.
+    # :api: public
+    def serialize_from_session(*args, &block)
+      Warden::Manager.serialize_from_session(*args, &block)
+    end
+
+  protected
+
+    def deep_dup(key, other)
+      self[key] = hash = other[key].dup
+      hash.each { |k, v| hash[k] = v.dup }
+    end
+  end
+end
diff --git a/ruby-warden/lib/warden/errors.rb b/ruby-warden/lib/warden/errors.rb
new file mode 100644 (file)
index 0000000..1351dce
--- /dev/null
@@ -0,0 +1,66 @@
+# encoding: utf-8
+module Warden
+  class Proxy
+    # Lifted from DataMapper's dm-validations plugin :)
+    # @author Guy van den Berg
+    # @since  DM 0.9
+    class Errors
+
+      include Enumerable
+
+      # Clear existing authentication errors.
+      def clear!
+        errors.clear
+      end
+
+      # Add a authentication error. Use the field_name :general if the errors does
+      # not apply to a specific field of the Resource.
+      #
+      # @param <Symbol> field_name the name of the field that caused the error
+      # @param <String> message    the message to add
+      def add(field_name, message)
+        (errors[field_name] ||= []) << message
+      end
+
+      # Collect all errors into a single list.
+      def full_messages
+        errors.inject([]) do |list,pair|
+          list += pair.last
+        end
+      end
+
+      # Return authentication errors for a particular field_name.
+      #
+      # @param <Symbol> field_name the name of the field you want an error for
+      def on(field_name)
+        errors_for_field = errors[field_name]
+        blank?(errors_for_field) ? nil : errors_for_field
+      end
+
+      def each
+        errors.map.each do |k,v|
+          next if blank?(v)
+          yield(v)
+        end
+      end
+
+      def empty?
+        entries.empty?
+      end
+
+      def method_missing(meth, *args, &block)
+        errors.send(meth, *args, &block)
+      end
+
+      private
+      def errors
+        @errors ||= {}
+      end
+
+      def blank?(thing)
+        thing.nil? || thing == "" || (thing.respond_to?(:empty?) && thing.empty?)
+      end
+
+    end # class Errors
+  end # Proxy
+end # Warden
diff --git a/ruby-warden/lib/warden/hooks.rb b/ruby-warden/lib/warden/hooks.rb
new file mode 100644 (file)
index 0000000..1bb4ea0
--- /dev/null
@@ -0,0 +1,211 @@
+# encoding: utf-8
+module Warden
+  module Hooks
+
+    # Hook to _run_callbacks asserting for conditions.
+    def _run_callbacks(kind, *args) #:nodoc:
+      options = args.last # Last callback arg MUST be a Hash
+
+      send("_#{kind}").each do |callback, conditions|
+        invalid = conditions.find do |key, value|
+          value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key])
+        end
+
+        callback.call(*args) unless invalid
+      end
+    end
+
+    # A callback hook set to run every time after a user is set.
+    # This callback is triggered the first time one of those three events happens
+    # during a request: :authentication, :fetch (from session) and :set_user (when manually set).
+    # You can supply as many hooks as you like, and they will be run in order of decleration.
+    #
+    # If you want to run the callbacks for a given scope and/or event, you can specify them as options.
+    # See parameters and example below.
+    #
+    # Parameters:
+    # <options> Some options which specify when the callback should be executed
+    #   scope  - Executes the callback only if it maches the scope(s) given
+    #   only   - Executes the callback only if it matches the event(s) given
+    #   except - Executes the callback except if it matches the event(s) given
+    # <block> A block where you can set arbitrary logic to run every time a user is set
+    #   Block Parameters: |user, auth, opts|
+    #     user - The user object that is being set
+    #     auth - The raw authentication proxy object.
+    #     opts - any options passed into the set_user call includeing :scope
+    #
+    # Example:
+    #   Warden::Manager.after_set_user do |user,auth,opts|
+    #     scope = opts[:scope]
+    #     if auth.session["#{scope}.last_access"].to_i > (Time.now - 5.minutes)
+    #       auth.logout(scope)
+    #       throw(:warden, :scope => scope, :reason => "Times Up")
+    #     end
+    #     auth.session["#{scope}.last_access"] = Time.now
+    #   end
+    #
+    #   Warden::Manager.after_set_user :except => :fetch do |user,auth,opts|
+    #     user.login_count += 1
+    #   end
+    #
+    # :api: public
+    def after_set_user(options = {}, method = :push, &block)
+      raise BlockNotGiven unless block_given?
+
+      if options.key?(:only)
+        options[:event] = options.delete(:only)
+      elsif options.key?(:except)
+        options[:event] = [:set_user, :authentication, :fetch] - Array(options.delete(:except))
+      end
+
+      _after_set_user.send(method, [block, options])
+    end
+
+    # Provides access to the array of after_set_user blocks to run
+    # :api: private
+    def _after_set_user # :nodoc:
+      @_after_set_user ||= []
+    end
+
+    # after_authentication is just a wrapper to after_set_user, which is only invoked
+    # when the user is set through the authentication path. The options and yielded arguments
+    # are the same as in after_set_user.
+    #
+    # :api: public
+    def after_authentication(options = {}, method = :push, &block)
+      after_set_user(options.merge(:event => :authentication), method, &block)
+    end
+
+    # after_fetch is just a wrapper to after_set_user, which is only invoked
+    # when the user is fetched from sesion. The options and yielded arguments
+    # are the same as in after_set_user.
+    #
+    # :api: public
+    def after_fetch(options = {}, method = :push, &block)
+      after_set_user(options.merge(:event => :fetch), method, &block)
+    end
+
+    # A callback that runs just prior to the failur application being called.
+    # This callback occurs after PATH_INFO has been modified for the failure (default /unauthenticated)
+    # In this callback you can mutate the environment as required by the failure application
+    # If a Rails controller were used for the failure_app for example, you would need to set request[:params][:action] = :unauthenticated
+    #
+    # Parameters:
+    # <options> Some options which specify when the callback should be executed
+    #   scope  - Executes the callback only if it maches the scope(s) given
+    # <block> A block to contain logic for the callback
+    #   Block Parameters: |env, opts|
+    #     env - The rack env hash
+    #     opts - any options passed into the authenticate call includeing :scope
+    #
+    # Example:
+    #   Warden::Manager.before_failure do |env, opts|
+    #     params = Rack::Request.new(env).params
+    #     params[:action] = :unauthenticated
+    #     params[:warden_failure] = opts
+    #   end
+    #
+    # :api: public
+    def before_failure(options = {}, method = :push, &block)
+      raise BlockNotGiven unless block_given?
+      _before_failure.send(method, [block, options])
+    end
+
+    # Provides access to the callback array for before_failure
+    # :api: private
+    def _before_failure
+      @_before_failure ||= []
+    end
+
+    # A callback that runs if no user could be fetched, meaning there is now no user logged in.
+    #
+    # Parameters:
+    # <options> Some options which specify when the callback should be executed
+    #   scope  - Executes the callback only if it maches the scope(s) given
+    # <block> A block to contain logic for the callback
+    #   Block Parameters: |user, auth, scope|
+    #     user - The authenticated user for the current scope
+    #     auth - The warden proxy object
+    #     opts - any options passed into the authenticate call including :scope
+    #
+    # Example:
+    #   Warden::Manager.after_failed_fetch do |user, auth, opts|
+    #     I18n.locale = :en
+    #   end
+    #
+    # :api: public
+    def after_failed_fetch(options = {}, method = :push, &block)
+      raise BlockNotGiven unless block_given?
+      _after_failed_fetch.send(method, [block, options])
+    end
+
+    # Provides access to the callback array for after_failed_fetch
+    # :api: private
+    def _after_failed_fetch
+      @_after_failed_fetch ||= []
+    end
+
+    # A callback that runs just prior to the logout of each scope.
+    #
+    # Parameters:
+    # <options> Some options which specify when the callback should be executed
+    #   scope  - Executes the callback only if it maches the scope(s) given
+    # <block> A block to contain logic for the callback
+    #   Block Parameters: |user, auth, scope|
+    #     user - The authenticated user for the current scope
+    #     auth - The warden proxy object
+    #     opts - any options passed into the authenticate call including :scope
+    #
+    # Example:
+    #   Warden::Manager.before_logout do |user, auth, opts|
+    #     user.forget_me!
+    #   end
+    #
+    # :api: public
+    def before_logout(options = {}, method = :push, &block)
+      raise BlockNotGiven unless block_given?
+      _before_logout.send(method, [block, options])
+    end
+
+    # Provides access to the callback array for before_logout
+    # :api: private
+    def _before_logout
+      @_before_logout ||= []
+    end
+
+    # A callback that runs on each request, just after the proxy is initialized
+    #
+    # Parameters:
+    # <block> A block to contain logic for the callback
+    #   Block Parameters: |proxy|
+    #     proxy - The warden proxy object for the request
+    #
+    # Example:
+    #   user = "A User"
+    #   Warden::Manager.on_request do |proxy|
+    #     proxy.set_user = user
+    #   end
+    #
+    # :api: public
+    def on_request(options = {}, method = :push, &block)
+      raise BlockNotGiven unless block_given?
+      _on_request.send(method, [block, options])
+    end
+
+    # Provides access to the callback array for before_logout
+    # :api: private
+    def _on_request
+      @_on_request ||= []
+    end
+
+    # Add prepend filters version
+    %w(after_set_user after_authentication after_fetch on_request
+       before_failure before_logout).each do |filter|
+      class_eval <<-METHOD, __FILE__, __LINE__ + 1
+        def prepend_#{filter}(options={}, &block)
+          #{filter}(options, :unshift, &block)
+        end
+      METHOD
+    end
+  end # Hooks
+end # Warden
diff --git a/ruby-warden/lib/warden/manager.rb b/ruby-warden/lib/warden/manager.rb
new file mode 100644 (file)
index 0000000..05fd340
--- /dev/null
@@ -0,0 +1,136 @@
+# encoding: utf-8
+require 'warden/hooks'
+require 'warden/config'
+
+module Warden
+  # The middleware for Rack Authentication
+  # The middlware requires that there is a session upstream
+  # The middleware injects an authentication object into
+  # the rack environment hash
+  class Manager
+    extend Warden::Hooks
+
+    attr_accessor :config
+
+    # Initialize the middleware. If a block is given, a Warden::Config is yielded so you can properly
+    # configure the Warden::Manager.
+    # :api: public
+    def initialize(app, options={})
+      default_strategies = options.delete(:default_strategies)
+
+      @app, @config = app, Warden::Config.new(options)
+      @config.default_strategies *default_strategies if default_strategies
+      yield @config if block_given?
+      self
+    end
+
+    # Invoke the application guarding for throw :warden.
+    # If this is downstream from another warden instance, don't do anything.
+    # :api: private
+    def call(env) # :nodoc:
+      return @app.call(env) if env['warden'] && env['warden'].manager != self
+
+      env['warden'] = Proxy.new(env, self)
+      result = catch(:warden) do
+        @app.call(env)
+      end
+
+      result ||= {}
+      case result
+      when Array
+        if result.first == 401 && intercept_401?(env)
+          process_unauthenticated(env)
+        else
+          result
+        end
+      when Hash
+        process_unauthenticated(env, result)
+      end
+    end
+
+    # :api: private
+    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.
+      #
+      # You can supply different methods of serialization for different scopes by passing a scope symbol
+      #
+      # Example:
+      #   Warden::Manager.serialize_into_session{ |user| user.id }
+      #   # With Scope:
+      #   Warden::Manager.serialize_into_session(:admin) { |user| user.id }
+      #
+      # :api: public
+      def serialize_into_session(scope = nil, &block)
+        method_name = scope.nil? ? :serialize : "#{scope}_serialize"
+        Warden::SessionSerializer.send :define_method, method_name, &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
+      # You can supply different methods of de-serialization for different scopes by passing a scope symbol
+      #
+      # Example:
+      #   Warden::Manager.serialize_from_session{ |id| User.get(id) }
+      #   # With Scope:
+      #   Warden::Manager.serialize_from_session(:admin) { |id| AdminUser.get(id) }
+      #
+      # :api: public
+      def serialize_from_session(scope = nil, &block)
+        method_name = scope.nil? ? :deserialize : "#{scope}_deserialize"
+        Warden::SessionSerializer.send :define_method, method_name, &block
+      end
+    end
+
+  private
+
+    def intercept_401?(env)
+      config[:intercept_401] && !env['warden'].custom_failure?
+    end
+
+    # When a request is unauthenticated, here's where the processing occurs.
+    # It looks at the result of the proxy to see if it's been executed and what action to take.
+    # :api: private
+    def process_unauthenticated(env, options={})
+      options[:action] ||= begin
+        opts = config[:scope_defaults][config.default_scope] || {}
+        opts[:action] || 'unauthenticated'
+      end
+
+      proxy  = env['warden']
+      result = options[:result] || proxy.result
+
+      case result
+      when :redirect
+        body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
+        [proxy.status, proxy.headers, [body]]
+      when :custom
+        proxy.custom_response
+      else
+        call_failure_app(env, options)
+      end
+    end
+
+    # Calls the failure app.
+    # The before_failure hooks are run on each failure
+    # :api: private
+    def call_failure_app(env, options = {})
+      if config.failure_app
+        options.merge!(:attempted_path => ::Rack::Request.new(env).fullpath)
+        env["PATH_INFO"] = "/#{options[:action]}"
+        env["warden.options"] = options
+
+        _run_callbacks(:before_failure, env, options)
+        config.failure_app.call(env).to_a
+      else
+        raise "No Failure App provided"
+      end
+    end # call_failure_app
+  end
+end # Warden
diff --git a/ruby-warden/lib/warden/mixins/common.rb b/ruby-warden/lib/warden/mixins/common.rb
new file mode 100644 (file)
index 0000000..606a568
--- /dev/null
@@ -0,0 +1,44 @@
+# encoding: utf-8
+module Warden
+  module Mixins
+    module Common
+
+      # Convinience method to access the session
+      # :api: public
+      def session
+        env['rack.session']
+      end # session
+
+      # Alias :session to :raw_session since the former will be user API for storing scoped data.
+      alias :raw_session :session
+
+      # Convenience method to access the rack request.
+      # :api: public
+      def request
+        @request ||= Rack::Request.new(@env)
+      end # request
+
+      # Provides a warden repository for cookies. Those are sent to the client
+      # when the response is streamed back from the app.
+      # :api: public
+      def warden_cookies
+        warn "warden_cookies was never functional and is going to be removed in next versions"
+        env['warden.cookies'] ||= {}
+      end # warden_cookies
+
+      # Convenience method to access the rack request params
+      # :api: public
+      def params
+        request.params
+      end # params
+
+      # Resets the session.  By using this non-hash like sessions can
+      # be cleared by overwriting this method in a plugin
+      # @api overwritable
+      def reset_session!
+        raw_session.clear
+      end # reset_session!
+
+    end # Common
+  end # Mixins
+end # Warden
diff --git a/ruby-warden/lib/warden/proxy.rb b/ruby-warden/lib/warden/proxy.rb
new file mode 100644 (file)
index 0000000..1b1a6a2
--- /dev/null
@@ -0,0 +1,371 @@
+# encoding: utf-8
+
+module Warden
+  class UserNotSet < RuntimeError; end
+
+  class Proxy
+    # An accessor to the winning strategy
+    # :api: private
+    attr_accessor :winning_strategy
+
+    # An accessor to the rack env hash, the proxy owner and its config
+    # :api: public
+    attr_reader :env, :manager, :config, :winning_strategies
+
+    extend ::Forwardable
+    include ::Warden::Mixins::Common
+
+    ENV_WARDEN_ERRORS = 'warden.errors'.freeze
+    ENV_SESSION_OPTIONS = 'rack.session.options'.freeze
+
+    # :api: private
+    def_delegators :winning_strategy, :headers, :status, :custom_response
+
+    # :api: public
+    def_delegators :config, :default_strategies
+
+    def initialize(env, manager) #:nodoc:
+      @env, @users, @winning_strategies, @locked = env, {}, {}, false
+      @manager, @config = manager, manager.config.dup
+      @strategies = Hash.new { |h,k| h[k] = {} }
+      manager._run_callbacks(:on_request, self)
+    end
+
+    # Lazily initiate errors object in session.
+    # :api: public
+    def errors
+      @env[ENV_WARDEN_ERRORS] ||= Errors.new
+    end
+
+    # Points to a SessionSerializer instance responsible for handling
+    # everything related with storing, fetching and removing the user
+    # session.
+    # :api: public
+    def session_serializer
+      @session_serializer ||= Warden::SessionSerializer.new(@env)
+    end
+
+    # Clear the cache of performed strategies so far. Warden runs each
+    # strategy just once during the request lifecycle. You can clear the
+    # strategies cache if you want to allow a strategy to be run more than
+    # once.
+    #
+    # This method has the same API as authenticate, allowing you to clear
+    # specific strategies for given scope:
+    #
+    # Parameters:
+    #   args - a list of symbols (labels) that name the strategies to attempt
+    #   opts - an options hash that contains the :scope of the user to check
+    #
+    # Example:
+    #   # Clear all strategies for the configured default_scope
+    #   env['warden'].clear_strategies_cache!
+    #
+    #   # Clear all strategies for the :admin scope
+    #   env['warden'].clear_strategies_cache!(:scope => :admin)
+    #
+    #   # Clear password strategy for the :admin scope
+    #   env['warden'].clear_strategies_cache!(:password, :scope => :admin)
+    #
+    # :api: public
+    def clear_strategies_cache!(*args)
+      scope, opts = _retrieve_scope_and_opts(args)
+
+      @winning_strategies.delete(scope)
+      @strategies[scope].each do |k, v|
+        v.clear! if args.empty? || args.include?(k)
+      end
+    end
+
+    # Locks the proxy so new users cannot authenticate during the
+    # request lifecycle. This is useful when the request cannot
+    # be verified (for example, using a CSRF verification token).
+    # Notice that already authenticated users are kept as so.
+    #
+    # :api: public
+    def lock!
+      @locked = true
+    end
+
+    # Run the authentiation strategies for the given strategies.
+    # If there is already a user logged in for a given scope, the strategies are not run
+    # This does not halt the flow of control and is a passive attempt to authenticate only
+    # When scope is not specified, the default_scope is assumed.
+    #
+    # Parameters:
+    #   args - a list of symbols (labels) that name the strategies to attempt
+    #   opts - an options hash that contains the :scope of the user to check
+    #
+    # Example:
+    #   env['warden'].authenticate(:password, :basic, :scope => :sudo)
+    #
+    # :api: public
+    def authenticate(*args)
+      user, opts = _perform_authentication(*args)
+      user
+    end
+
+    # Same API as authenticated, but returns a boolean instead of a user.
+    # The difference between this method (authenticate?) and authenticated?
+    # is that the former will run strategies if the user has not yet been
+    # authenticated, and the second relies on already performed ones.
+    # :api: public
+    def authenticate?(*args)
+      result = !!authenticate(*args)
+      yield if result && block_given?
+      result
+    end
+
+    # The same as +authenticate+ except on failure it will throw an :warden symbol causing the request to be halted
+    # and rendered through the +failure_app+
+    #
+    # Example
+    #   env['warden'].authenticate!(:password, :scope => :publisher) # throws if it cannot authenticate
+    #
+    # :api: public
+    def authenticate!(*args)
+      user, opts = _perform_authentication(*args)
+      throw(:warden, opts) unless user
+      user
+    end
+
+    # Check to see if there is an authenticated user for the given scope.
+    # This brings the user from the session, but does not run strategies before doing so.
+    # If you want strategies to be run, please check authenticate?.
+    #
+    # Parameters:
+    #   scope - the scope to check for authentication. Defaults to default_scope
+    #
+    # Example:
+    #   env['warden'].authenticated?(:admin)
+    #
+    # :api: public
+    def authenticated?(scope = @config.default_scope)
+      result = !!user(scope)
+      yield if block_given? && result
+      result
+    end
+
+    # Same API as authenticated?, but returns false when authenticated.
+    # :api: public
+    def unauthenticated?(scope = @config.default_scope)
+      result = !authenticated?(scope)
+      yield if block_given? && result
+      result
+    end
+
+    # Manually set the user into the session and auth proxy
+    #
+    # Parameters:
+    #   user - An object that has been setup to serialize into and out of the session.
+    #   opts - An options hash.  Use the :scope option to set the scope of the user, set the :store option to false to skip serializing into the session, set the :run_callbacks to false to skip running the callbacks (the default is true).
+    #
+    # :api: public
+    def set_user(user, opts = {})
+      scope = (opts[:scope] ||= @config.default_scope)
+
+      # Get the default options from the master configuration for the given scope
+      opts = (@config[:scope_defaults][scope] || {}).merge(opts)
+      opts[:event] ||= :set_user
+      @users[scope] = user
+
+      if opts[:store] != false && opts[:event] != :fetch
+        options = env[ENV_SESSION_OPTIONS]
+        options[:renew] = true if options
+        session_serializer.store(user, scope)
+      end
+
+      run_callbacks = opts.fetch(:run_callbacks, true)
+      manager._run_callbacks(:after_set_user, user, self, opts) if run_callbacks
+
+      @users[scope]
+    end
+
+    # Provides acccess to the user object in a given scope for a request.
+    # Will be nil if not logged in. Please notice that this method does not
+    # perform strategies.
+    #
+    # Example:
+    #   # without scope (default user)
+    #   env['warden'].user
+    #
+    #   # with scope
+    #   env['warden'].user(:admin)
+    #
+    #   # as a Hash
+    #   env['warden'].user(:scope => :admin)
+    #
+    #   # with default scope and run_callbacks option
+    #   env['warden'].user(:run_callbacks => false)
+    #
+    #  # with a scope and run_callbacks option
+    #  env['warden'].user(:scope => :admin, :run_callbacks => true)
+    #
+    # :api: public
+    def user(argument = {})
+      opts  = argument.is_a?(Hash) ? argument : { :scope => argument }
+      scope = (opts[:scope] ||= @config.default_scope)
+
+      if @users.has_key?(scope)
+        @users[scope]
+      else
+        unless user = session_serializer.fetch(scope)
+          run_callbacks = opts.fetch(:run_callbacks, true)
+          manager._run_callbacks(:after_failed_fetch, user, self, :scope => scope) if run_callbacks
+        end
+
+        @users[scope] = user ? set_user(user, opts.merge(:event => :fetch)) : nil
+      end
+    end
+
+    # Provides a scoped session data for authenticated users.
+    # Warden manages clearing out this data when a user logs out
+    #
+    # Example
+    #  # default scope
+    #  env['warden'].session[:foo] = "bar"
+    #
+    #  # :sudo scope
+    #  env['warden'].session(:sudo)[:foo] = "bar"
+    #
+    # :api: public
+    def session(scope = @config.default_scope)
+      raise NotAuthenticated, "#{scope.inspect} user is not logged in" unless authenticated?(scope)
+      raw_session["warden.user.#{scope}.session"] ||= {}
+    end
+
+    # Provides logout functionality.
+    # The logout also manages any authenticated data storage and clears it when a user logs out.
+    #
+    # Parameters:
+    #   scopes - a list of scopes to logout
+    #
+    # Example:
+    #  # Logout everyone and clear the session
+    #  env['warden'].logout
+    #
+    #  # Logout the default user but leave the rest of the session alone
+    #  env['warden'].logout(:default)
+    #
+    #  # Logout the :publisher and :admin user
+    #  env['warden'].logout(:publisher, :admin)
+    #
+    # :api: public
+    def logout(*scopes)
+      if scopes.empty?
+        scopes = @users.keys
+        reset_session = true
+      end
+
+      scopes.each do |scope|
+        user = @users.delete(scope)
+        manager._run_callbacks(:before_logout, user, self, :scope => scope)
+
+        raw_session.delete("warden.user.#{scope}.session") unless raw_session.nil?
+        session_serializer.delete(scope, user)
+      end
+
+      reset_session! if reset_session
+    end
+
+    # proxy methods through to the winning strategy
+    # :api: private
+    def result # :nodoc:
+      winning_strategy && winning_strategy.result
+    end
+
+    # Proxy through to the authentication strategy to find out the message that was generated.
+    # :api: public
+    def message
+      winning_strategy && winning_strategy.message
+    end
+
+    # Provides a way to return a 401 without warden defering to the failure app
+    # The result is a direct passthrough of your own response
+    # :api: public
+    def custom_failure!
+      @custom_failure = true
+    end
+
+    # Check to see if the custom failure flag has been set
+    # :api: public
+    def custom_failure?
+      !!@custom_failure
+    end
+
+    # Check to see if this is an asset request
+    # :api: public
+    def asset_request?
+      ::Warden::asset_paths.any? { |r| env['PATH_INFO'].to_s.match(r) }
+    end
+
+    def inspect(*args)
+      "Warden::Proxy:#{object_id} @config=#{@config.inspect}"
+    end
+
+    def to_s(*args)
+      inspect(*args)
+    end
+
+    private
+
+    def _perform_authentication(*args)
+      scope, opts = _retrieve_scope_and_opts(args)
+      user = nil
+
+      # 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.
+      return user, opts if user = user(opts.merge(:scope => scope))
+      _run_strategies_for(scope, args)
+
+      if winning_strategy && winning_strategy.user
+        opts[:store] = opts.fetch(:store, winning_strategy.store?)
+        set_user(winning_strategy.user, opts.merge!(:event => :authentication))
+      end
+
+      [@users[scope], opts]
+    end
+
+    def _retrieve_scope_and_opts(args) #:nodoc:
+      opts  = args.last.is_a?(Hash) ? args.pop : {}
+      scope = opts[:scope] || @config.default_scope
+      opts  = (@config[:scope_defaults][scope] || {}).merge(opts)
+      [scope, opts]
+    end
+
+    # Run the strategies for a given scope
+    def _run_strategies_for(scope, args) #:nodoc:
+      self.winning_strategy = @winning_strategies[scope]
+      return if winning_strategy && winning_strategy.halted?
+
+      # Do not run any strategy if locked
+      return if @locked
+
+      if args.empty?
+        defaults   = @config[:default_strategies]
+        strategies = defaults[scope] || defaults[:_all]
+      end
+
+      (strategies || args).each do |name|
+        strategy = _fetch_strategy(name, scope)
+        next unless strategy && !strategy.performed? && strategy.valid?
+
+        self.winning_strategy = @winning_strategies[scope] = strategy
+        strategy._run!
+        break if strategy.halted?
+      end
+    end
+
+    # Fetchs strategies and keep them in a hash cache.
+    def _fetch_strategy(name, scope)
+      @strategies[scope][name] ||= if klass = Warden::Strategies[name]
+        klass.new(@env, scope)
+      elsif @config.silence_missing_strategies?
+        nil
+      else
+        raise "Invalid strategy #{name}"
+      end
+    end
+  end # Proxy
+
+end # Warden
diff --git a/ruby-warden/lib/warden/session_serializer.rb b/ruby-warden/lib/warden/session_serializer.rb
new file mode 100644 (file)
index 0000000..f0b65bd
--- /dev/null
@@ -0,0 +1,52 @@
+# encoding: utf-8
+module Warden
+  class SessionSerializer
+    attr_reader :env
+
+    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
+      method_name = "#{scope}_serialize"
+      specialized = respond_to?(method_name)
+      session[key_for(scope)] = specialized ? send(method_name, user) : serialize(user)
+    end
+
+    def fetch(scope)
+      key = session[key_for(scope)]
+      return nil unless key
+
+      method_name = "#{scope}_deserialize"
+      user = respond_to?(method_name) ? send(method_name, key) : 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
+
+    # We can't cache this result because the session can be lazy loaded
+    def session
+      env["rack.session"] || {}
+    end
+  end # SessionSerializer
+end # Warden
diff --git a/ruby-warden/lib/warden/strategies.rb b/ruby-warden/lib/warden/strategies.rb
new file mode 100644 (file)
index 0000000..3509337
--- /dev/null
@@ -0,0 +1,47 @@
+# encoding: utf-8
+module Warden
+  module Strategies
+    class << self
+      # Add a strategy and store it in a hash.
+      def add(label, strategy = nil, &block)
+        strategy ||= Class.new(Warden::Strategies::Base)
+        strategy.class_eval(&block) if block_given?
+
+        unless strategy.method_defined?(:authenticate!)
+          raise NoMethodError, "authenticate! is not declared in the #{label.inspect} strategy"
+        end
+
+        base = Warden::Strategies::Base
+        unless strategy.ancestors.include?(base)
+          raise "#{label.inspect} is not a #{base}"
+        end
+
+        _strategies[label] = strategy
+      end
+
+      # Update a previously given strategy.
+      def update(label, &block)
+        strategy = _strategies[label]
+        raise "Unknown strategy #{label.inspect}" unless strategy
+        add(label, strategy, &block)
+      end
+
+      # Provides access to strategies by label
+      # :api: public
+      def [](label)
+        _strategies[label]
+      end
+
+      # Clears all declared.
+      # :api: public
+      def clear!
+        _strategies.clear
+      end
+
+      # :api: private
+      def _strategies
+        @strategies ||= {}
+      end
+    end # << self
+  end # Strategies
+end # Warden
diff --git a/ruby-warden/lib/warden/strategies/base.rb b/ruby-warden/lib/warden/strategies/base.rb
new file mode 100644 (file)
index 0000000..dd8e89c
--- /dev/null
@@ -0,0 +1,175 @@
+# encoding: utf-8
+module Warden
+  module Strategies
+    # A strategy is a place where you can put logic related to authentication. Any strategy inherits
+    # from Warden::Strategies::Base.
+    #
+    # The Warden::Strategies.add method is a simple way to provide custom strategies.
+    # You _must_ declare an @authenticate!@ method.
+    # You _may_ provide a @valid?@ method.
+    # The valid method should return true or false depending on if the strategy is a valid one for the request.
+    #
+    # The parameters for Warden::Strategies.add method is:
+    #   <label: Symbol> The label is the name given to a strategy.  Use the label to refer to the strategy when authenticating
+    #   <strategy: Class|nil> The optional stragtegy argument if set _must_ be a class that inherits from Warden::Strategies::Base and _must_
+    #                         implement an @authenticate!@ method
+    #   <block> The block acts as a convinient way to declare your strategy.  Inside is the class definition of a strategy.
+    #
+    # Examples:
+    #
+    #   Block Declared Strategy:
+    #    Warden::Strategies.add(:foo) do
+    #      def authenticate!
+    #        # authentication logic
+    #      end
+    #    end
+    #
+    #    Class Declared Strategy:
+    #      Warden::Strategies.add(:foo, MyStrategy)
+    #
+    class Base
+      # :api: public
+      attr_accessor :user, :message
+
+      # :api: private
+      attr_accessor :result, :custom_response
+
+      # :api: public
+      attr_reader :env, :scope, :status
+
+      include ::Warden::Mixins::Common
+
+      # :api: private
+      def initialize(env, scope=nil) # :nodoc:
+        @env, @scope = env, scope
+        @status, @headers = nil, {}
+        @halted, @performed = false, false
+      end
+
+      # The method that is called from above. This method calls the underlying authenticate! method
+      # :api: private
+      def _run! # :nodoc:
+        @performed = true
+        authenticate!
+        self
+      end
+
+      # Returns if this strategy was already performed.
+      # :api: private
+      def performed? #:nodoc:
+        @performed
+      end
+
+      # Marks this strategy as not performed.
+      # :api: private
+      def clear!
+        @performed = false
+      end
+
+      # Acts as a guarding method for the strategy.
+      # If #valid? responds false, the strategy will not be executed
+      # Overwrite with your own logic
+      # :api: overwritable
+      def valid?; true; end
+
+      # Provides access to the headers hash for setting custom headers
+      # :api: public
+      def headers(header = {})
+        @headers ||= {}
+        @headers.merge! header
+        @headers
+      end
+
+      # Access to the errors object.
+      # :api: public
+      def errors
+        @env['warden'].errors
+      end
+
+      # Cause the processing of the strategies to stop and cascade no further
+      # :api: public
+      def halt!
+        @halted = true
+      end
+
+      # Checks to see if a strategy was halted
+      # :api: public
+      def halted?
+        !!@halted
+      end
+
+      # Checks to see if a strategy should result in a permanent login
+      # :api: public
+      def store?
+        true
+      end
+
+      # A simple method to return from authenticate! if you want to ignore this strategy
+      # :api: public
+      def pass; end
+
+      # Whenever you want to provide a user object as "authenticated" use the +success!+ method.
+      # This will halt the strategy, and set the user in the approprieate scope.
+      # It is the "login" method
+      #
+      # Parameters:
+      #   user - The user object to login.  This object can be anything you have setup to serialize in and out of the session
+      #
+      # :api: public
+      def success!(user, message = nil)
+        halt!
+        @user = user
+        @message = message
+        @result = :success
+      end
+
+      # This causes the strategy to fail.  It does not throw an :warden symbol to drop the request out to the failure application
+      # You must throw an :warden symbol somewhere in the application to enforce this
+      # Halts the strategies so that this is the last strategy checked
+      # :api: public
+      def fail!(message = "Failed to Login")
+        halt!
+        @message = message
+        @result = :failure
+      end
+
+      # Casuses the strategy to fail, but not halt.  The strategies will cascade after this failure and warden will check the next strategy.  The last strategy to fail will have it's message displayed.
+      # :api: public
+      def fail(message = "Failed to Login")
+        @message = message
+        @result = :failure
+      end
+
+      # Causes the authentication to redirect.  An :warden symbol must be thrown to actually execute this redirect
+      #
+      # Parameters:
+      #  url <String> - The string representing the URL to be redirected to
+      #  pararms <Hash> - Any parameters to encode into the URL
+      #  opts <Hash> - Any options to recirect with.
+      #    available options: permanent => (true || false)
+      #
+      # :api: public
+      def redirect!(url, params = {}, opts = {})
+        halt!
+        @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'
+
+        @message = opts[:message] || "You are being redirected to #{headers["Location"]}"
+        @result = :redirect
+
+        headers["Location"]
+      end
+
+      # Return a custom rack array.  You must throw an :warden symbol to activate this
+      # :api: public
+      def custom!(response)
+        halt!
+        @custom_response = response
+        @result = :custom
+      end
+
+    end # Base
+  end # Strategies
+end # Warden
diff --git a/ruby-warden/lib/warden/test/helpers.rb b/ruby-warden/lib/warden/test/helpers.rb
new file mode 100644 (file)
index 0000000..db262bb
--- /dev/null
@@ -0,0 +1,36 @@
+# encoding: utf-8
+
+module Warden
+  module Test
+    # A collection of test helpers for testing full stack rack applications using Warden
+    # These provide the ability to login and logout on any given request
+    # Note: During the teardown phase of your specs you should include: Warden.test_reset!
+    module Helpers
+      def self.included(base)
+        ::Warden.test_mode!
+      end
+
+      # A helper method that will peform a login of a user in warden for the next request
+      # Provide it the same options as you would to Warden::Proxy#set_user
+      # @see Warden::Proxy#set_user
+      # @api public
+      def login_as(user, opts = {})
+        Warden.on_next_request do |proxy|
+          opts[:event] ||= :authentication
+          proxy.set_user(user, opts)
+        end
+      end
+
+      # Logs out a user from the session.
+      # Without arguments, all users will be logged out
+      # Provide a list of scopes to only log out users with that scope.
+      # @see Warden::Proxy#logout
+      # @api public
+      def logout(*scopes)
+        Warden.on_next_request do |proxy|
+          proxy.logout(*scopes)
+        end
+      end
+    end
+  end
+end
diff --git a/ruby-warden/lib/warden/test/warden_helpers.rb b/ruby-warden/lib/warden/test/warden_helpers.rb
new file mode 100644 (file)
index 0000000..a5fa4a0
--- /dev/null
@@ -0,0 +1,43 @@
+# encoding: utf-8
+
+module Warden
+
+  module Test
+    module WardenHelpers
+      # Returns list of regex objects that match paths expected to be an asset
+      # @see Warden::Proxy#asset_request?
+      # @api public
+      def asset_paths
+        @asset_paths ||= [/^\/assets\//]
+      end
+
+      # Sets list of regex objects that match paths expected to be an asset
+      # @see Warden::Proxy#asset_request?
+      # @api public
+      def asset_paths=(*vals)
+        @asset_paths = vals
+      end
+
+      # Adds a block to be executed on the next request when the stack reaches warden.
+      # The warden proxy is yielded to the block
+      # @api public
+      def on_next_request(&blk)
+        _on_next_request << blk
+      end
+
+      # resets wardens tests
+      # any blocks queued to execute will be removed
+      # @api public
+      def test_reset!
+        _on_next_request.clear
+      end
+
+      # A containter for the on_next_request items.
+      # @api private
+      def _on_next_request
+        @_on_next_request ||= []
+        @_on_next_request
+      end
+    end
+  end
+end
diff --git a/ruby-warden/lib/warden/version.rb b/ruby-warden/lib/warden/version.rb
new file mode 100644 (file)
index 0000000..770981f
--- /dev/null
@@ -0,0 +1,4 @@
+# encoding: utf-8
+module Warden
+  VERSION = "1.2.3".freeze
+end
diff --git a/ruby-warden/metadata.yml b/ruby-warden/metadata.yml
new file mode 100644 (file)
index 0000000..e5c7f01
--- /dev/null
@@ -0,0 +1,104 @@
+--- !ruby/object:Gem::Specification
+name: warden
+version: !ruby/object:Gem::Version
+  prerelease: 
+  version: 1.2.3
+platform: ruby
+authors:
+- Daniel Neighman
+autorequire: 
+bindir: bin
+cert_chain: []
+date: 2013-07-14 00:00:00.000000000 Z
+dependencies:
+- !ruby/object:Gem::Dependency
+  version_requirements: !ruby/object:Gem::Requirement
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '1.0'
+    none: false
+  name: rack
+  type: :runtime
+  prerelease: false
+  requirement: !ruby/object:Gem::Requirement
+    requirements:
+    - - ! '>='
+      - !ruby/object:Gem::Version
+        version: '1.0'
+    none: false
+description: 
+email: has.sox@gmail.com
+executables: []
+extensions: []
+extra_rdoc_files:
+- LICENSE
+- README.textile
+files:
+- Gemfile
+- History.rdoc
+- lib/warden/config.rb
+- lib/warden/errors.rb
+- lib/warden/hooks.rb
+- lib/warden/manager.rb
+- lib/warden/mixins/common.rb
+- lib/warden/proxy.rb
+- lib/warden/session_serializer.rb
+- lib/warden/strategies/base.rb
+- lib/warden/strategies.rb
+- lib/warden/test/helpers.rb
+- lib/warden/test/warden_helpers.rb
+- lib/warden/version.rb
+- lib/warden.rb
+- LICENSE
+- Rakefile
+- README.textile
+- spec/helpers/request_helper.rb
+- spec/helpers/strategies/failz.rb
+- spec/helpers/strategies/invalid.rb
+- spec/helpers/strategies/pass.rb
+- spec/helpers/strategies/pass_with_message.rb
+- spec/helpers/strategies/password.rb
+- spec/helpers/strategies/single.rb
+- spec/spec_helper.rb
+- spec/warden/authenticated_data_store_spec.rb
+- spec/warden/config_spec.rb
+- spec/warden/errors_spec.rb
+- spec/warden/hooks_spec.rb
+- spec/warden/manager_spec.rb
+- spec/warden/proxy_spec.rb
+- spec/warden/scoped_session_serializer.rb
+- spec/warden/session_serializer_spec.rb
+- spec/warden/strategies/base_spec.rb
+- spec/warden/strategies_spec.rb
+- spec/warden/test/helpers_spec.rb
+- spec/warden/test/test_mode_spec.rb
+- warden.gemspec
+homepage: http://github.com/hassox/warden
+licenses:
+- MIT
+post_install_message: 
+rdoc_options:
+- --charset=UTF-8
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+  none: false
+required_rubygems_version: !ruby/object:Gem::Requirement
+  requirements:
+  - - ! '>='
+    - !ruby/object:Gem::Version
+      version: '0'
+  none: false
+requirements: []
+rubyforge_project: warden
+rubygems_version: 1.8.23
+signing_key: 
+specification_version: 3
+summary: Rack middleware that provides authentication for rack applications
+test_files: []
+has_rdoc: 
diff --git a/ruby-warden/spec/helpers/request_helper.rb b/ruby-warden/spec/helpers/request_helper.rb
new file mode 100644 (file)
index 0000000..29e6d9d
--- /dev/null
@@ -0,0 +1,51 @@
+# encoding: utf-8
+module Warden::Spec
+  module Helpers
+    FAILURE_APP = lambda{|e|[401, {"Content-Type" => "text/plain"}, ["You Fail!"]] }
+
+    def env_with_params(path = "/", params = {}, env = {})
+      method = params.delete(:method) || "GET"
+      env = { 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => "#{method}" }.merge(env)
+      Rack::MockRequest.env_for("#{path}?#{Rack::Utils.build_query(params)}", env)
+    end
+
+    def setup_rack(app = nil, opts = {}, &block)
+      app ||= block if block_given?
+
+      opts[:failure_app]         ||= failure_app
+      opts[:default_strategies]  ||= [:password]
+      opts[:default_serializers] ||= [:session]
+      blk = opts[:configurator] || proc{}
+
+      Rack::Builder.new do
+        use opts[:session] || Warden::Spec::Helpers::Session unless opts[:nil_session]
+        use Warden::Manager, opts, &blk
+        run app
+      end
+    end
+
+    def valid_response
+      Rack::Response.new("OK").finish
+    end
+
+    def failure_app
+      Warden::Spec::Helpers::FAILURE_APP
+    end
+
+    def success_app
+      lambda{|e| [200, {"Content-Type" => "text/plain"}, ["You Win"]]}
+    end
+
+    class Session
+      attr_accessor :app
+      def initialize(app,configs = {})
+        @app = app
+      end
+
+      def call(e)
+        e['rack.session'] ||= {}
+        @app.call(e)
+      end
+    end # session
+  end
+end
diff --git a/ruby-warden/spec/helpers/strategies/failz.rb b/ruby-warden/spec/helpers/strategies/failz.rb
new file mode 100644 (file)
index 0000000..0d9f1e1
--- /dev/null
@@ -0,0 +1,8 @@
+# encoding: utf-8
+Warden::Strategies.add(:failz) do
+  def authenticate!
+    request.env['warden.spec.strategies'] ||= []
+    request.env['warden.spec.strategies'] << :failz
+    fail!("The Fails Strategy Has Failed You")
+  end
+end
diff --git a/ruby-warden/spec/helpers/strategies/invalid.rb b/ruby-warden/spec/helpers/strategies/invalid.rb
new file mode 100644 (file)
index 0000000..1729229
--- /dev/null
@@ -0,0 +1,8 @@
+# encoding: utf-8
+Warden::Strategies.add(:invalid) do
+  def valid?
+    false
+  end
+
+  def authenticate!; end
+end
\ No newline at end of file
diff --git a/ruby-warden/spec/helpers/strategies/pass.rb b/ruby-warden/spec/helpers/strategies/pass.rb
new file mode 100644 (file)
index 0000000..37fffbe
--- /dev/null
@@ -0,0 +1,8 @@
+# encoding: utf-8
+Warden::Strategies.add(:pass) do
+  def authenticate!
+    request.env['warden.spec.strategies'] ||= []
+    request.env['warden.spec.strategies'] << :pass
+    success!("Valid User") unless scope == :failz
+  end
+end
diff --git a/ruby-warden/spec/helpers/strategies/pass_with_message.rb b/ruby-warden/spec/helpers/strategies/pass_with_message.rb
new file mode 100644 (file)
index 0000000..071c8b8
--- /dev/null
@@ -0,0 +1,8 @@
+# encoding: utf-8
+Warden::Strategies.add(:pass_with_message) do
+  def authenticate!
+    request.env['warden.spec.strategies'] ||= []
+    request.env['warden.spec.strategies'] << :pass_with_message
+    success!("Valid User", "The Success Strategy Has Accepted You") unless scope == :failz
+  end
+end
diff --git a/ruby-warden/spec/helpers/strategies/password.rb b/ruby-warden/spec/helpers/strategies/password.rb
new file mode 100644 (file)
index 0000000..34ff502
--- /dev/null
@@ -0,0 +1,13 @@
+# encoding: utf-8
+Warden::Strategies.add(:password) do
+  def authenticate!
+    request.env['warden.spec.strategies'] ||= []
+    request.env['warden.spec.strategies'] << :password
+    if params["password"] || params["username"]
+      params["password"] == "sekrit" && params["username"] == "fred" ?
+        success!("Authenticated User") : fail!("Username or password is incorrect")
+    else
+      pass
+    end
+  end
+end
diff --git a/ruby-warden/spec/helpers/strategies/single.rb b/ruby-warden/spec/helpers/strategies/single.rb
new file mode 100644 (file)
index 0000000..bf6d574
--- /dev/null
@@ -0,0 +1,12 @@
+# encoding: utf-8
+Warden::Strategies.add(:single) do
+  def authenticate!
+    request.env['warden.spec.strategies'] ||= []
+    request.env['warden.spec.strategies'] << :single
+    success!("Valid User")
+  end
+
+  def store?
+    false
+  end
+end
diff --git a/ruby-warden/spec/spec_helper.rb b/ruby-warden/spec/spec_helper.rb
new file mode 100644 (file)
index 0000000..2c27af6
--- /dev/null
@@ -0,0 +1,24 @@
+# encoding: utf-8
+$TESTING=true
+
+$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
+$:.unshift File.expand_path(File.join(File.dirname(__FILE__)))
+require 'warden'
+
+require 'rubygems'
+require 'rack'
+
+Dir[File.join(File.dirname(__FILE__), "helpers", "**/*.rb")].each do |f|
+  require f
+end
+
+RSpec.configure do |config|
+  config.include(Warden::Spec::Helpers)
+  config.include(Warden::Test::Helpers)
+
+  def load_strategies
+    Dir[File.join(File.dirname(__FILE__), "helpers", "strategies", "**/*.rb")].each do |f|
+      load f
+    end
+  end
+end
diff --git a/ruby-warden/spec/warden/authenticated_data_store_spec.rb b/ruby-warden/spec/warden/authenticated_data_store_spec.rb
new file mode 100644 (file)
index 0000000..9a7826a
--- /dev/null
@@ -0,0 +1,114 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe "authenticated data store" do
+
+  before(:each) do
+    @env = env_with_params
+    @env['rack.session'] = {
+      "warden.user.foo.key"     => "foo user",
+      "warden.user.default.key" => "default user",
+      :foo => "bar"
+    }
+  end
+
+  it "should store data for the default scope" do
+    app = lambda do |e|
+      e['warden'].authenticate(:pass)
+      e['warden'].authenticate(:pass, :scope => :foo)
+      e['warden'].should be_authenticated
+      e['warden'].should be_authenticated(:foo)
+
+      # Store the data for :default
+      e['warden'].session[:key] = "value"
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should == {:key => "value"}
+    @env['rack.session']['warden.user.foo.session'].should be_nil
+  end
+
+  it "should store data for the foo user" do
+    app = lambda do |e|
+      e['warden'].session(:foo)[:key] = "value"
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.foo.session'].should == {:key => "value"}
+  end
+
+  it "should store the data seperately" do
+    app = lambda do |e|
+      e['warden'].session[:key] = "value"
+      e['warden'].session(:foo)[:key] = "another value"
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should == {:key => "value"}
+    @env['rack.session']['warden.user.foo.session'    ].should == {:key => "another value"}
+  end
+
+  it "should clear the foo scoped data when foo logs out" do
+    app = lambda do |e|
+      e['warden'].session[:key] = "value"
+      e['warden'].session(:foo)[:key] = "another value"
+      e['warden'].logout(:foo)
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should == {:key => "value"}
+    @env['rack.session']['warden.user.foo.session'    ].should be_nil
+  end
+
+  it "should clear out the default data when :default logs out" do
+    app = lambda do |e|
+      e['warden'].session[:key] = "value"
+      e['warden'].session(:foo)[:key] = "another value"
+      e['warden'].logout(:default)
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should be_nil
+    @env['rack.session']['warden.user.foo.session'    ].should == {:key => "another value"}
+  end
+
+  it "should clear out all data when a general logout is performed" do
+    app = lambda do |e|
+      e['warden'].session[:key] = "value"
+      e['warden'].session(:foo)[:key] = "another value"
+      e['warden'].logout
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should be_nil
+    @env['rack.session']['warden.user.foo.session'    ].should be_nil
+  end
+
+  it "should logout multuiple personas at once" do
+    @env['rack.session']['warden.user.bar.key'] = "bar user"
+
+    app = lambda do |e|
+      e['warden'].session[:key] = "value"
+      e['warden'].session(:foo)[:key] = "another value"
+      e['warden'].session(:bar)[:key] = "yet another"
+      e['warden'].logout(:bar, :default)
+      valid_response
+    end
+    setup_rack(app).call(@env)
+    @env['rack.session']['warden.user.default.session'].should be_nil
+    @env['rack.session']['warden.user.foo.session'    ].should == {:key => "another value"}
+    @env['rack.session']['warden.user.bar.session'    ].should be_nil
+  end
+
+  it "should not store data for a user who is not logged in" do
+    @env['rack.session']
+    app = lambda do |e|
+      e['warden'].session(:not_here)[:key] = "value"
+      valid_response
+    end
+
+    lambda do
+      setup_rack(app).call(@env)
+    end.should raise_error(Warden::NotAuthenticated)
+  end
+end
diff --git a/ruby-warden/spec/warden/config_spec.rb b/ruby-warden/spec/warden/config_spec.rb
new file mode 100644 (file)
index 0000000..03cfd57
--- /dev/null
@@ -0,0 +1,48 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Config do
+
+  before(:each) do
+    @config = Warden::Config.new
+  end
+
+  it "should behave like a hash" do
+    @config[:foo] = :bar
+    @config[:foo].should == :bar
+  end
+
+  it "should provide hash accessors" do
+    @config.failure_app = :foo
+    @config[:failure_app].should == :foo
+    @config[:failure_app] = :bar
+    @config.failure_app.should == :bar
+  end
+
+  it "should allow to read and set default strategies" do
+    @config.default_strategies :foo, :bar
+    @config.default_strategies.should == [:foo, :bar]
+  end
+
+  it "should allow to silence missing strategies" do
+    @config.silence_missing_strategies!
+    @config.silence_missing_strategies?.should be_true
+  end
+
+  it "should set the default_scope" do
+    @config.default_scope.should == :default
+    @config.default_scope = :foo
+    @config.default_scope.should == :foo
+  end
+
+  it "should merge given options on initialization" do
+    Warden::Config.new(:foo => :bar)[:foo].should == :bar
+  end
+
+  it "should setup defaults with the scope_defaults method" do
+    c = Warden::Config.new
+    c.scope_defaults :foo, :strategies => [:foo, :bar], :store => false
+    c.default_strategies(:scope => :foo).should == [:foo, :bar]
+    c.scope_defaults(:foo).should == {:store => false}
+  end
+end
diff --git a/ruby-warden/spec/warden/errors_spec.rb b/ruby-warden/spec/warden/errors_spec.rb
new file mode 100644 (file)
index 0000000..6dc1874
--- /dev/null
@@ -0,0 +1,47 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Proxy::Errors do
+
+  before(:each) do
+    @errors = Warden::Proxy::Errors.new
+  end
+
+  it "should report that it is empty on first creation" do
+    @errors.empty?.should == true
+  end
+
+  it "should continue to report that it is empty even after being checked" do
+    @errors.on(:foo)
+    @errors.empty?.should == true
+  end
+
+  it "should add an error" do
+    @errors.add(:login, "Login or password incorrect")
+    @errors[:login].should == ["Login or password incorrect"]
+  end
+
+  it "should allow many errors to be added to the same field" do
+    @errors.add(:login, "bad 1")
+    @errors.add(:login, "bad 2")
+    @errors.on(:login).should == ["bad 1", "bad 2"]
+  end
+
+  it "should give the full messages for an error" do
+    @errors.add(:login, "login wrong")
+    @errors.add(:password, "password wrong")
+    ["password wrong", "login wrong"].each do |msg|
+      @errors.full_messages.should include(msg)
+    end
+  end
+
+  it "should return the error for a specific field / label" do
+    @errors.add(:login, "wrong")
+    @errors.on(:login).should == ["wrong"]
+  end
+
+  it "should return nil for a specific field if it's not been set" do
+    @errors.on(:not_there).should be_nil
+  end
+
+end
diff --git a/ruby-warden/spec/warden/hooks_spec.rb b/ruby-warden/spec/warden/hooks_spec.rb
new file mode 100644 (file)
index 0000000..8e959a7
--- /dev/null
@@ -0,0 +1,373 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe "standard authentication hooks" do
+
+  before(:all) do
+    load_strategies
+  end
+
+  describe "after_set_user" do
+    before(:each) do
+      RAM = Warden::Manager unless defined?(RAM)
+      RAM._after_set_user.clear
+    end
+
+    after(:each) do
+      RAM._after_set_user.clear
+    end
+
+    it "should allow me to add an after_set_user hook" do
+      RAM.after_set_user do |user, auth, opts|
+        "boo"
+      end
+      RAM._after_set_user.should have(1).item
+    end
+
+    it "should allow me to add multiple after_set_user hooks" do
+      RAM.after_set_user{|user, auth, opts| "foo"}
+      RAM.after_set_user{|u,a| "bar"}
+      RAM._after_set_user.should have(2).items
+    end
+
+    it "should run each after_set_user hook after the user is set" do
+      RAM.after_set_user{|u,a,o| a.env['warden.spec.hook.foo'] = "run foo"}
+      RAM.after_set_user{|u,a,o| a.env['warden.spec.hook.bar'] = "run bar"}
+      RAM.after_set_user{|u,a,o| a.logout}
+      app = lambda do |e|
+        e['warden'].set_user("foo")
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden'].user.should be_nil
+      env['warden.spec.hook.foo'].should == "run foo"
+      env['warden.spec.hook.bar'].should == "run bar"
+    end
+
+    it "should not run the event specified with except" do
+      RAM.after_set_user(:except => :set_user){|u,a,o| fail}
+      app = lambda do |e|
+        e['warden'].set_user("foo")
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+    end
+
+    it "should only run the event specified with only" do
+      RAM.after_set_user(:only => :set_user){|u,a,o| fail}
+      app = lambda do |e|
+        e['warden'].authenticate(:pass)
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+    end
+
+    it "should run filters in the given order" do
+      RAM.after_set_user{|u,a,o| a.env['warden.spec.order'] << 2}
+      RAM.after_set_user{|u,a,o| a.env['warden.spec.order'] << 3}
+      RAM.prepend_after_set_user{|u,a,o| a.env['warden.spec.order'] << 1}
+      app = lambda do |e|
+        e['warden.spec.order'] = []
+        e['warden'].set_user("foo")
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden.spec.order'].should == [1,2,3]
+    end
+
+    context "after_authentication" do
+      it "should be a wrapper to after_set_user behavior" do
+        RAM.after_authentication{|u,a,o| a.env['warden.spec.hook.baz'] = "run baz"}
+        RAM.after_authentication{|u,a,o| a.env['warden.spec.hook.paz'] = "run paz"}
+        RAM.after_authentication{|u,a,o| o[:event].should == :authentication }
+        app = lambda do |e|
+          e['warden'].authenticate(:pass)
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+        env['warden.spec.hook.baz'].should == 'run baz'
+        env['warden.spec.hook.paz'].should == 'run paz'
+      end
+
+      it "should not be invoked on default after_set_user scenario" do
+        RAM.after_authentication{|u,a,o| fail}
+        app = lambda do |e|
+          e['warden'].set_user("foo")
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+      end
+
+      it "should run filters in the given order" do
+        RAM.after_authentication{|u,a,o| a.env['warden.spec.order'] << 2}
+        RAM.after_authentication{|u,a,o| a.env['warden.spec.order'] << 3}
+        RAM.prepend_after_authentication{|u,a,o| a.env['warden.spec.order'] << 1}
+        app = lambda do |e|
+          e['warden.spec.order'] = []
+          e['warden'].authenticate(:pass)
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+        env['warden.spec.order'].should == [1,2,3]
+      end
+
+      it "should allow me to log out a user in an after_set_user block" do
+        RAM.after_set_user{|u,a,o| a.logout}
+
+        app = lambda do |e|
+          e['warden'].authenticate(:pass)
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+        env['warden'].authenticated?.should be_false
+      end
+    end
+
+    context "after_fetch" do
+      it "should be a wrapper to after_set_user behavior" do
+        RAM.after_fetch{|u,a,o| a.env['warden.spec.hook.baz'] = "run baz"}
+        RAM.after_fetch{|u,a,o| a.env['warden.spec.hook.paz'] = "run paz"}
+        RAM.after_fetch{|u,a,o| o[:event].should == :fetch }
+        env = env_with_params
+        setup_rack(lambda { |e| valid_response }).call(env)
+        env['rack.session']['warden.user.default.key'] = "Foo"
+        env['warden'].user.should == "Foo"
+        env['warden.spec.hook.baz'].should == 'run baz'
+        env['warden.spec.hook.paz'].should == 'run paz'
+      end
+
+      it "should not be invoked on default after_set_user scenario" do
+        RAM.after_fetch{|u,a,o| fail}
+        app = lambda do |e|
+          e['warden'].set_user("foo")
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+      end
+
+      it "should not be invoked if fetched user is nil" do
+        RAM.after_fetch{|u,a,o| fail}
+        env = env_with_params
+        setup_rack(lambda { |e| valid_response }).call(env)
+        env['rack.session']['warden.user.default.key'] = nil
+        env['warden'].user.should be_nil
+      end
+
+      it "should run filters in the given order" do
+        RAM.after_fetch{|u,a,o| a.env['warden.spec.order'] << 2}
+        RAM.after_fetch{|u,a,o| a.env['warden.spec.order'] << 3}
+        RAM.prepend_after_fetch{|u,a,o| a.env['warden.spec.order'] << 1}
+        app = lambda do |e|
+          e['warden.spec.order'] = []
+          e['rack.session']['warden.user.default.key'] = "Foo"
+          e['warden'].user
+          valid_response
+        end
+        env = env_with_params
+        setup_rack(app).call(env)
+        env['warden.spec.order'].should == [1,2,3]
+      end
+    end
+  end
+
+
+  describe "after_failed_fetch" do
+    before(:each) do
+      RAM = Warden::Manager unless defined?(RAM)
+      RAM._after_failed_fetch.clear
+    end
+
+    after(:each) do
+      RAM._after_failed_fetch.clear
+    end
+
+    it "should not be called when user is fetched" do
+      RAM.after_failed_fetch{|u,a,o| fail }
+      env = env_with_params
+      setup_rack(lambda { |e| valid_response }).call(env)
+      env['rack.session']['warden.user.default.key'] = "Foo"
+      env['warden'].user.should == "Foo"
+    end
+
+    it "should be called if fetched user is nil" do
+      calls = 0
+      RAM.after_failed_fetch{|u,a,o| calls += 1 }
+      env = env_with_params
+      setup_rack(lambda { |e| valid_response }).call(env)
+      env['warden'].user.should be_nil
+      calls.should == 1
+    end
+  end
+
+  describe "before_failure" do
+    before(:each) do
+      RAM = Warden::Manager unless defined?(RAM)
+      RAM._before_failure.clear
+    end
+
+    after(:each) do
+      RAM._before_failure.clear
+    end
+
+    it "should allow me to add a before_failure hook" do
+      RAM.before_failure{|env, opts| "foo"}
+      RAM._before_failure.should have(1).item
+    end
+
+    it "should allow me to add multiple before_failure hooks" do
+      RAM.before_failure{|env, opts| "foo"}
+      RAM.before_failure{|env, opts| "bar"}
+      RAM._before_failure.should have(2).items
+    end
+
+    it "should run each before_failure hooks before failing" do
+      RAM.before_failure{|e,o| e['warden.spec.before_failure.foo'] = "foo"}
+      RAM.before_failure{|e,o| e['warden.spec.before_failure.bar'] = "bar"}
+      app = lambda{|e| e['warden'].authenticate!(:failz); valid_response}
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden.spec.before_failure.foo'].should == "foo"
+      env['warden.spec.before_failure.bar'].should  == "bar"
+    end
+
+    it "should run filters in the given order" do
+      RAM.before_failure{|e,o| e['warden.spec.order'] << 2}
+      RAM.before_failure{|e,o| e['warden.spec.order'] << 3}
+      RAM.prepend_before_failure{|e,o| e['warden.spec.order'] << 1}
+      app = lambda do |e|
+        e['warden.spec.order'] = []
+        e['warden'].authenticate!(:failz)
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden.spec.order'].should == [1,2,3]
+    end
+  end
+
+  describe "before_logout" do
+    before(:each) do
+      RAM = Warden::Manager unless defined?(RAM)
+      RAM._before_logout.clear
+    end
+
+    after(:each) do
+      RAM._before_logout.clear
+    end
+
+    it "should allow me to add an before_logout hook" do
+      RAM.before_logout{|user, auth, scopes| "foo"}
+      RAM._before_logout.should have(1).item
+    end
+
+    it "should allow me to add multiple after_authentication hooks" do
+      RAM.before_logout{|u,a,o| "bar"}
+      RAM.before_logout{|u,a,o| "baz"}
+      RAM._before_logout.should have(2).items
+    end
+
+    it "should run each before_logout hook before logout is run" do
+      RAM.before_logout{|u,a,o| a.env['warden.spec.hook.lorem'] = "run lorem"}
+      RAM.before_logout{|u,a,o| a.env['warden.spec.hook.ipsum'] = "run ipsum"}
+      app = lambda{|e| e['warden'].authenticate(:pass); valid_response}
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden'].logout
+      env['warden.spec.hook.lorem'].should == 'run lorem'
+      env['warden.spec.hook.ipsum'].should == 'run ipsum'
+    end
+
+    it "should run before_logout hook for a specified scope" do
+      RAM.before_logout(:scope => :scope1){|u,a,o| a.env["warden.spec.hook.a"] << :scope1 }
+      RAM.before_logout(:scope => [:scope2]){|u,a,o| a.env["warden.spec.hook.b"] << :scope2 }
+
+      app = lambda do |e|
+        e['warden'].authenticate(:pass, :scope => :scope1)
+        e['warden'].authenticate(:pass, :scope => :scope2)
+        valid_response
+      end
+      env = env_with_params
+      env["warden.spec.hook.a"] ||= []
+      env["warden.spec.hook.b"] ||= []
+      setup_rack(app).call(env)
+
+      env['warden'].logout(:scope1)
+      env['warden.spec.hook.a'].should == [:scope1]
+      env['warden.spec.hook.b'].should == []
+
+      env['warden'].logout(:scope2)
+      env['warden.spec.hook.a'].should == [:scope1]
+      env['warden.spec.hook.b'].should == [:scope2]
+    end
+
+    it "should run filters in the given order" do
+      RAM.before_logout{|u,a,o| a.env['warden.spec.order'] << 2}
+      RAM.before_logout{|u,a,o| a.env['warden.spec.order'] << 3}
+      RAM.prepend_before_logout{|u,a,o| a.env['warden.spec.order'] << 1}
+      app = lambda do |e|
+        e['warden.spec.order'] = []
+        e['warden'].authenticate(:pass)
+        e['warden'].logout
+        valid_response
+      end
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden.spec.order'].should == [1,2,3]
+    end
+  end
+
+  describe "on_request" do
+    before(:each) do
+      @old_on_request = RAM._on_request.dup
+      RAM = Warden::Manager unless defined?(RAM)
+      RAM._on_request.clear
+    end
+
+    after(:each) do
+      RAM._on_request.clear
+      RAM._on_request.replace(@old_on_request)
+    end
+
+    it "should allow me to add an on_request hook" do
+      RAM.on_request{|proxy| "foo"}
+      RAM._on_request.should have(1).item
+    end
+
+    it "should allow me to add multiple on_request hooks" do
+      RAM.on_request{|proxy| "foo"}
+      RAM.on_request{|proxy| "bar"}
+      RAM._on_request.should have(2).items
+    end
+
+    it "should run each on_request hooks when initializing" do
+      RAM.on_request{|proxy| proxy.env['warden.spec.on_request.foo'] = "foo"}
+      RAM.on_request{|proxy| proxy.env['warden.spec.on_request.bar'] = "bar"}
+      app = lambda{|e| valid_response}
+      env = env_with_params
+      setup_rack(app).call(env)
+      env['warden.spec.on_request.foo'].should == "foo"
+      env['warden.spec.on_request.bar'].should  == "bar"
+    end
+
+    it "should run filters in the given order" do
+      RAM.on_request{|proxy| proxy.env['warden.spec.order'] << 2}
+      RAM.on_request{|proxy| proxy.env['warden.spec.order'] << 3}
+      RAM.prepend_on_request{|proxy| proxy.env['warden.spec.order'] << 1}
+      app = lambda do |e|
+        valid_response
+      end
+      env = Rack::MockRequest.env_for("/", "warden.spec.order" => [])
+      setup_rack(app).call(env)
+      env['warden.spec.order'].should == [1,2,3]
+    end
+  end
+end
diff --git a/ruby-warden/spec/warden/manager_spec.rb b/ruby-warden/spec/warden/manager_spec.rb
new file mode 100644 (file)
index 0000000..c94867b
--- /dev/null
@@ -0,0 +1,316 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Manager do
+
+  before(:all) do
+    load_strategies
+  end
+
+  it "should insert a Proxy object into the rack env" do
+    env = env_with_params
+    setup_rack(success_app).call(env)
+    env["warden"].should be_an_instance_of(Warden::Proxy)
+  end
+
+  describe "thrown auth" do
+    before(:each) do
+      @basic_app = lambda{|env| [200,{'Content-Type' => 'text/plain'},'OK']}
+      @authd_app = lambda do |e|
+        if e['warden'].authenticated?
+          [200,{'Content-Type' => 'text/plain'},"OK"]
+        else
+          [401,{'Content-Type' => 'text/plain'},"Fail From The App"]
+        end
+      end
+      @env = Rack::MockRequest.
+        env_for('/', 'HTTP_VERSION' => '1.1', 'REQUEST_METHOD' => 'GET')
+    end # before(:each)
+
+    describe "Failure" do
+      it "should respond with a 401 response if the strategy fails authentication" do
+         env = env_with_params("/", :foo => "bar")
+         app = lambda do |env|
+           env['warden'].authenticate(:failz)
+           throw(:warden, :action => :unauthenticated)
+         end
+         result = setup_rack(app, :failure_app => @fail_app).call(env)
+         result.first.should == 401
+      end
+
+      it "should use the failure message given to the failure method" do
+        env = env_with_params("/", {})
+        app = lambda do |env|
+          env['warden'].authenticate(:failz)
+          throw(:warden)
+        end
+        result = setup_rack(app, :failure_app => @fail_app).call(env)
+        result.last.should == ["You Fail!"]
+      end
+
+      it "should render the failure app when there's a failure" do
+        app = lambda do |e|
+          throw(:warden, :action => :unauthenticated) unless e['warden'].authenticated?(:failz)
+        end
+        fail_app = lambda do |e|
+          [401, {"Content-Type" => "text/plain"}, ["Failure App"]]
+        end
+        result = setup_rack(app, :failure_app => fail_app).call(env_with_params)
+        result.last.should == ["Failure App"]
+      end
+
+      it "should call failure app if warden is thrown even after successful authentication" do
+        env = env_with_params("/", {})
+        app = lambda do |env|
+          env['warden'].authenticate(:pass)
+          throw(:warden)
+        end
+        result = setup_rack(app, :failure_app => @fail_app).call(env)
+        result.first.should == 401
+        result.last.should == ["You Fail!"]
+      end
+
+      it "should set the attempted url in warden.options hash" do
+        env = env_with_params("/access/path", {})
+        app = lambda do |env|
+          env['warden'].authenticate(:pass)
+          throw(:warden)
+        end
+        result = setup_rack(app, :failure_app => @fail_app).call(env)
+        result.first.should == 401
+        env["warden.options"][:attempted_path].should == "/access/path"
+      end
+
+      it "should catch a resubmitted request" do
+        # this is a bit convoluted. but it's occurred in the field with Rack::OpenID
+        $count = 0
+        $throw_count = 0
+        env = env_with_params("/foo")
+        class ::ResubmittingMiddleware
+          @@app = nil
+          def initialize(app)
+            @@app = app
+          end
+
+          def self.call(env)
+            if $count > 1
+              Rack::Response.new("Bad", 401)
+            else
+              $count += 1
+              @@app.call(env)
+            end
+          end
+
+          def call(env)
+            $count += 1
+            @@app.call(env)
+          end
+
+        end
+
+        app = lambda do |e|
+          $throw_count += 1
+          throw(:warden)
+        end
+
+        builder = Rack::Builder.new do
+          use ResubmittingMiddleware
+          use Warden::Manager do |config|
+            config.failure_app = ResubmittingMiddleware
+          end
+          run app
+        end
+
+        result = builder.to_app.call(env)
+        result[0].should == 401
+        result[2].body.should == ["Bad"]
+        $throw_count.should == 2
+      end
+
+      it "should use the default scopes action when a bare throw is used" do
+         env = env_with_params("/", :foo => "bar")
+         action = nil
+
+         failure = lambda do |env|
+           action = env['PATH_INFO'] 
+           [401, {}, ['fail']]
+         end
+
+         app = lambda do |env|
+           throw(:warden)
+         end
+         result = setup_rack(app,
+                             :failure_app => failure,
+                             :configurator => lambda{ |c| c.scope_defaults(:default, :action => 'my_action', :strategies => [:password]) }
+                            ).call(env)
+
+         action.should == "/my_action"
+         result.first.should == 401
+      end
+    end # failure
+  end
+
+  describe "integrated strategies" do
+    before(:each) do
+      RAS = Warden::Strategies unless defined?(RAS)
+      Warden::Strategies.clear!
+      @app = setup_rack do |env|
+        env['warden'].authenticate!(:foobar)
+        [200, {"Content-Type" => "text/plain"}, ["Foo Is A Winna"]]
+      end
+    end
+
+    describe "redirecting" do
+
+      it "should redirect with a message" do
+        RAS.add(:foobar) do
+          def authenticate!
+            redirect!("/foo/bar", {:foo => "bar"}, :message => "custom redirection message")
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 302
+        result[1]["Location"].should == "/foo/bar?foo=bar"
+        result[2].should == ["custom redirection message"]
+      end
+
+      it "should redirect with a default message" do
+        RAS.add(:foobar) do
+          def authenticate!
+            redirect!("/foo/bar", {:foo => "bar"})
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 302
+        result[1]['Location'].should == "/foo/bar?foo=bar"
+        result[2].should == ["You are being redirected to /foo/bar?foo=bar"]
+      end
+
+      it "should redirect with a permanent redirect" do
+        RAS.add(:foobar) do
+          def authenticate!
+            redirect!("/foo/bar", {}, :permanent => true)
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 301
+      end
+
+      it "should redirect with a content type" do
+        RAS.add(:foobar) do
+          def authenticate!
+            redirect!("/foo/bar", {:foo => "bar"}, :content_type => "text/xml")
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 302
+        result[1]["Location"].should == "/foo/bar?foo=bar"
+        result[1]["Content-Type"].should == "text/xml"
+      end
+
+      it "should redirect with a default content type" do
+        RAS.add(:foobar) do
+          def authenticate!
+            redirect!("/foo/bar", {:foo => "bar"})
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 302
+        result[1]["Location"].should == "/foo/bar?foo=bar"
+        result[1]["Content-Type"].should == "text/plain"
+      end
+    end
+
+    describe "failing" do
+      it "should fail according to the failure app" do
+        RAS.add(:foobar) do
+          def authenticate!
+            fail!
+          end
+        end
+        env = env_with_params
+        result = @app.call(env)
+        result[0].should == 401
+        result[2].should == ["You Fail!"]
+        env['PATH_INFO'].should == "/unauthenticated"
+      end
+
+      it "should allow you to customize the response" do
+        app = lambda do |e|
+          e['warden'].custom_failure!
+          [401,{'Content-Type' => 'text/plain'},["Fail From The App"]]
+        end
+        env = env_with_params
+        result = setup_rack(app).call(env)
+        result[0].should == 401
+        result[2].should == ["Fail From The App"]
+      end
+
+      it "should allow you to customize the response without the explicit call to custom_failure! if not intercepting 401" do
+        app = lambda do |e|
+          [401,{'Content-Type' => 'text/plain'},["Fail From The App"]]
+        end
+        env = env_with_params
+        result = setup_rack(app, :intercept_401 => false).call(env)
+        result[0].should == 401
+        result[2].should == ["Fail From The App"]
+      end
+
+      it "should render the failure application for a 401 if no custom_failure flag is set" do
+        app = lambda do |e|
+          [401,{'Content-Type' => 'text/plain'},["Fail From The App"]]
+        end
+        result = setup_rack(app).call(env_with_params)
+        result[0].should == 401
+        result[2].should == ["You Fail!"]
+      end
+
+    end # failing
+
+    describe "custom rack response" do
+      it "should return a custom rack response" do
+        RAS.add(:foobar) do
+          def authenticate!
+            custom!([523, {"Content-Type" => "text/plain", "Custom-Header" => "foo"}, ["Custom Stuff"]])
+          end
+        end
+        result = @app.call(env_with_params)
+        result[0].should == 523
+        result[1]["Custom-Header"].should == "foo"
+        result[2].should == ["Custom Stuff"]
+      end
+    end
+
+    describe "success" do
+      it "should pass through to the application when there is success" do
+        RAS.add(:foobar) do
+          def authenticate!
+            success!("A User")
+          end
+        end
+        env = env_with_params
+        result = @app.call(env)
+        result[0].should == 200
+        result[2].should == ["Foo Is A Winna"]
+      end
+    end
+  end # integrated strategies
+
+  it "should allow me to set a different default scope for warden" do
+    Rack::Builder.new do
+      use Warden::Manager, :default_scope => :default do |manager|
+        manager.default_scope.should == :default
+        manager.default_scope = :other
+        manager.default_scope.should == :other
+      end
+    end
+  end
+
+  it "should allow me to access strategies through manager" do
+    Rack::Builder.new do
+      use Warden::Manager do |manager|
+        manager.strategies.should == Warden::Strategies
+      end
+    end
+  end
+end
diff --git a/ruby-warden/spec/warden/proxy_spec.rb b/ruby-warden/spec/warden/proxy_spec.rb
new file mode 100644 (file)
index 0000000..ece377b
--- /dev/null
@@ -0,0 +1,1041 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Proxy do
+  before(:all) do
+    load_strategies
+  end
+
+  before(:each) do
+    @basic_app = lambda{|env| [200,{'Content-Type' => 'text/plain'},'OK']}
+    @authd_app = lambda do |e|
+      e['warden'].authenticate
+      if e['warden'].authenticated?
+        [200,{'Content-Type' => 'text/plain'},"OK"]
+      else
+        [401,{'Content-Type' => 'text/plain'},"You Fail"]
+      end
+    end
+    @env = env_with_params("/")
+  end # before(:each)
+
+  describe "authentication" do
+
+    it "should not check the authentication if it is not checked" do
+      app = setup_rack(@basic_app)
+      app.call(@env).first.should == 200
+    end
+
+    it "should check the authentication if it is explicity checked" do
+      app = setup_rack(@authd_app)
+      app.call(@env).first.should == 401
+    end
+
+    it "should not allow the request if incorrect conditions are supplied" do
+      env = env_with_params("/", :foo => "bar")
+      app = setup_rack(@authd_app)
+      response = app.call(env)
+      response.first.should == 401
+    end
+
+    it "should allow the request if the correct conditions are supplied" do
+      env = env_with_params("/", :username => "fred", :password => "sekrit")
+      app = setup_rack(@authd_app)
+      resp = app.call(env)
+      resp.first.should == 200
+    end
+
+    it "should allow authentication in my application" do
+      env = env_with_params('/', :username => "fred", :password => "sekrit")
+      app = lambda do |env|
+        env['warden'].authenticate
+        env['warden'].should be_authenticated
+        env['warden.spec.strategies'].should == [:password]
+        valid_response
+      end
+      setup_rack(app).call(env)
+    end
+
+    it "should allow me to select which strategies I use in my appliction" do
+      env = env_with_params("/", :foo => "bar")
+      app = lambda do |env|
+        env['warden'].authenticate(:failz)
+        env['warden'].should_not be_authenticated
+        env['warden.spec.strategies'].should == [:failz]
+        valid_response
+      end
+      setup_rack(app).call(env)
+    end
+
+    it "should raise error on missing strategies" do
+      app = lambda do |env|
+        env['warden'].authenticate(:unknown)
+      end
+      lambda {
+        setup_rack(app).call(@env)
+      }.should raise_error(RuntimeError, "Invalid strategy unknown")
+    end
+
+    it "should not raise error on missing strategies if silencing" do
+      app = lambda do |env|
+        env['warden'].authenticate
+        valid_response
+      end
+      lambda {
+        setup_rack(app, :silence_missing_strategies => true, :default_strategies => [:unknown]).call(@env)
+      }.should_not raise_error
+    end
+
+    it "should allow me to get access to the user at warden.user." do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass)
+        env['warden'].should be_authenticated
+        env['warden.spec.strategies'].should == [:pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should run strategies when authenticate? is asked" do
+      app = lambda do |env|
+        env['warden'].should_not be_authenticated
+        env['warden'].authenticate?(:pass)
+        env['warden'].should be_authenticated
+        env['warden.spec.strategies'].should == [:pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should properly send the scope to the strategy" do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass, :scope => :failz)
+        env['warden'].should_not be_authenticated
+        env['warden.spec.strategies'].should == [:pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should try multiple authentication strategies" do
+      app = lambda do |env|
+        env['warden'].authenticate(:password,:pass)
+        env['warden'].should be_authenticated
+        env['warden.spec.strategies'].should == [:password, :pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should look for an active user in the session with authenticate" do
+      app = lambda do |env|
+        env['rack.session']["warden.user.default.key"] = "foo as a user"
+        env['warden'].authenticate(:pass)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['warden'].user.should == "foo as a user"
+    end
+
+    it "should look for an active user in the session with authenticate?" do
+      app = lambda do |env|
+        env['rack.session']['warden.user.foo_scope.key'] = "a foo user"
+        env['warden'].authenticate?(:pass, :scope => :foo_scope)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['warden'].user(:foo_scope).should == "a foo user"
+    end
+
+    it "should look for an active user in the session with authenticate!" do
+      app = lambda do |env|
+        env['rack.session']['warden.user.foo_scope.key'] = "a foo user"
+        env['warden'].authenticate!(:pass, :scope => :foo_scope)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['warden'].user(:foo_scope).should == "a foo user"
+    end
+
+    it "should throw an error when authenticate!" do
+      app = lambda do |env|
+        env['warden'].authenticate!(:pass, :scope => :failz)
+        raise "OMG"
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should login 2 different users from the session" do
+      app = lambda do |env|
+        env['rack.session']['warden.user.foo.key'] = 'foo user'
+        env['rack.session']['warden.user.bar.key'] = 'bar user'
+        env['warden'].should be_authenticated(:foo)
+        env['warden'].should be_authenticated(:bar)
+        env['warden'].should_not be_authenticated # default scope
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['warden'].user(:foo).should == 'foo user'
+      @env['warden'].user(:bar).should == 'bar user'
+      @env['warden'].user.should be_nil
+    end
+
+    it "should not authenticate other scopes just because the first is authenticated" do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass, :scope => :foo)
+        env['warden'].authenticate(:invalid, :scope => :bar)
+        env['warden'].should be_authenticated(:foo)
+        env['warden'].should_not be_authenticated(:bar)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    SID_REGEXP = /rack\.session=([^;]*);/
+
+    it "should renew session when user is set" do
+      app = lambda do |env|
+        env["rack.session"]["counter"] ||= 0
+        env["rack.session"]["counter"] += 1
+        if env["warden.on"]
+          env["warden"].authenticate!(:pass)
+          env["warden"].should be_authenticated
+        end
+        valid_response
+      end
+
+      # Setup a rack app with Pool session.
+      app = setup_rack(app, :session => Rack::Session::Pool).to_app
+      response = app.call(@env)
+      @env["rack.session"]["counter"].should == 1
+
+      # Ensure a cookie was given back
+      cookie = response[1]["Set-Cookie"]
+      cookie.should_not be_nil
+
+      # Ensure a session id was given
+      sid = cookie.match(SID_REGEXP)[1]
+      sid.should_not be_nil
+
+      # Do another request, giving a cookie but turning on warden authentication
+      env = env_with_params("/", {}, 'rack.session' => @env['rack.session'], "HTTP_COOKIE" => cookie, "warden.on" => true)
+      response = app.call(env)
+      env["rack.session"]["counter"].should == 2
+
+      # Regardless of rack version, a cookie should be sent back
+      new_cookie = response[1]["Set-Cookie"]
+      new_cookie.should_not be_nil
+
+      # And the session id in this cookie should not be the same as the previous one
+      new_sid = new_cookie.match(SID_REGEXP)[1]
+      new_sid.should_not be_nil
+      new_sid.should_not == sid
+    end
+
+    it "should not renew session when user is fetch" do
+      app = lambda do |env|
+        env["rack.session"]["counter"] ||= 0
+        env["rack.session"]["counter"] += 1
+        env["warden"].authenticate!(:pass)
+        env["warden"].should be_authenticated
+        valid_response
+      end
+
+      # Setup a rack app with Pool session.
+      app = setup_rack(app, :session => Rack::Session::Pool).to_app
+      response = app.call(@env)
+      @env["rack.session"]["counter"].should == 1
+
+      # Ensure a cookie was given back
+      cookie = response[1]["Set-Cookie"]
+      cookie.should_not be_nil
+
+      # Ensure a session id was given
+      sid = cookie.match(SID_REGEXP)[1]
+      sid.should_not be_nil
+
+      # Do another request, passing the cookie. The user should be fetched from cookie.
+      env = env_with_params("/", {}, "HTTP_COOKIE" => cookie)
+      response = app.call(env)
+      env["rack.session"]["counter"].should == 2
+
+      # Depending on rack version, a cookie will be returned with the
+      # same session id or no cookie is given back (becase it did not change).
+      # If we don't get any of these two behaviors, raise an error.
+      # Regardless of rack version, a cookie should be sent back
+      new_cookie = response[1]["Set-Cookie"]
+      if new_cookie && new_cookie.match(SID_REGEXP)[1] != sid
+        raise "Expected a cookie to not be sent or session id to match"
+      end
+    end
+  end
+
+  describe "authentication cache" do
+    it "should run strategies just once for a given scope" do
+      app = lambda do |env|
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden'].should_not be_authenticated(:failz)
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden'].should_not be_authenticated(:failz)
+        env['warden.spec.strategies'].should == [:password, :pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should run strategies for a given scope several times if cache is cleaned" do
+      app = lambda do |env|
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden'].clear_strategies_cache!(:scope => :failz)
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden.spec.strategies'].should == [:password, :pass, :password, :pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should clear the cache for a specified strategy" do
+      app = lambda do |env|
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden'].clear_strategies_cache!(:password, :scope => :failz)
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden.spec.strategies'].should == [:password, :pass, :password]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should run the strategies several times for different scopes" do
+      app = lambda do |env|
+        env['warden'].authenticate(:password, :pass, :scope => :failz)
+        env['warden'].should_not be_authenticated(:failz)
+        env['warden'].authenticate(:password, :pass)
+        env['warden'].should be_authenticated
+        env['warden.spec.strategies'].should == [:password, :pass, :password, :pass]
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not run strategies until cache is cleaned if latest winning strategy halted" do
+      app = lambda do |env|
+        env['warden'].authenticate(:failz)
+        env['warden'].should_not be_authenticated
+        env['warden'].authenticate(:pass)
+        env['warden'].winning_strategy.message.should == "The Fails Strategy Has Failed You"
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not store user if strategy isn't meant for permanent login" do
+      session = Warden::SessionSerializer.new(@env)
+      app = lambda do |env|
+        env['warden'].authenticate(:single)
+        env['warden'].should be_authenticated
+        env['warden'].user.should == "Valid User"
+        session.should_not be_stored(:default)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+  end
+
+  describe "set user" do
+    it "should store the user into the session" do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass)
+        env['warden'].should be_authenticated
+        env['warden'].user.should == "Valid User"
+        env['rack.session']["warden.user.default.key"].should == "Valid User"
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not store the user if the :store option is set to false" do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass, :store => false)
+        env['warden'].should be_authenticated
+        env['warden'].user.should == "Valid User"
+        env['rack.session']['warden.user.default.key'].should be_nil
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not throw error when no session is configured and store is false" do
+      app = lambda do |env|
+        env['rack.session'] = nil
+        env['warden'].authenticate(:pass, :store => false)
+        env['warden'].should be_authenticated
+        env['warden'].user.should == "Valid User"
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not run the callbacks when :run_callbacks is false" do
+      app = lambda do |env|
+        env['warden'].manager.should_not_receive(:_run_callbacks)
+        env['warden'].authenticate(:run_callbacks => false, :scope => :pass)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should run the callbacks when :run_callbacks is true" do
+      app = lambda do |env|
+        env['warden'].manager.should_receive(:_run_callbacks).at_least(:once)
+        env['warden'].authenticate(:pass)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should run the callbacks by default" do
+      app = lambda do |env|
+        env['warden'].manager.should_receive(:_run_callbacks).at_least(:once)
+        env['warden'].authenticate(:pass)
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end    
+  end
+
+  describe "lock" do
+    it "should not run any strategy" do
+      app = lambda do |env|
+        env['warden'].lock!
+        env['warden'].authenticate(:pass)
+        env['warden'].user.should be_nil
+        valid_response
+      end
+    end
+
+    it "should keep already authenticated users" do
+      app = lambda do |env|
+        env['warden'].authenticate(:pass)
+        env['warden'].lock!
+        env['warden'].user.should be
+        valid_response
+      end
+    end
+  end
+
+  describe "get user" do
+    before(:each) do
+      @env['rack.session'] ||= {}
+      @env['rack.session'].delete("warden.user.default.key")
+    end
+
+    it "should return nil when not logged in" do
+      app = lambda do |env|
+        env['warden'].user.should be_nil
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should not run strategies when not logged in" do
+      app = lambda do |env|
+        env['warden'].user.should be_nil
+        env['warden.spec.strategies'].should be_nil
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should cache unfound user" do
+      Warden::SessionSerializer.any_instance.should_receive(:fetch).once
+      app = lambda do |env|
+        env['warden'].user.should be_nil
+        env['warden'].user.should be_nil
+        valid_response
+      end
+      setup_rack(app).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'] = []
+      end
+
+      it "should take the user from the session when logged in" do
+        app = lambda do |env|
+          env['warden'].user.should == "A Previous User"
+          valid_response
+        end
+        setup_rack(app).call(@env)
+      end
+
+      it "should cache found user" do
+        Warden::SessionSerializer.any_instance.should_receive(:fetch).once.and_return "A Previous User"
+        app = lambda do |env|
+          env['warden'].user.should == "A Previous User"
+          env['warden'].user.should == "A Previous User"
+          valid_response
+        end
+        setup_rack(app).call(@env)
+      end
+
+      it "should not run strategies when the user exists in the session" do
+        app = lambda do |env|
+          env['warden'].authenticate!(:pass)
+          valid_response
+        end
+        setup_rack(app).call(@env)
+        @env['warden.spec.strategies'].should_not include(:pass)
+      end
+
+      describe "run callback option" do
+        it "should not call run_callbacks when we pass a :run_callback => false" do
+          app = lambda do |env|
+            env['warden'].manager.should_not_receive(:_run_callbacks)
+            env['warden'].user(:run_callbacks => false)
+            valid_response
+          end
+          setup_rack(app).call(@env)
+        end
+
+        it "should call run_callbacks when we pass a :run_callback => true" do
+          app = lambda do |env|
+            env['warden'].manager.should_receive(:_run_callbacks).at_least(:once)
+            env['warden'].user(:run_callbacks => true)
+            valid_response
+          end
+          setup_rack(app).call(@env)
+        end
+
+        it "should call run_callbacks by default" do
+          app = lambda do |env|
+            env['warden'].manager.should_receive(:_run_callbacks).at_least(:once)
+            env['warden'].user
+            valid_response
+          end
+          setup_rack(app).call(@env)
+        end
+      end
+    end
+  end
+
+  describe "logout" do
+    before(:each) do
+      @env['rack.session'] = {"warden.user.default.key" => "default key", "warden.user.foo.key" => "foo key", :foo => "bar"}
+      @app = lambda do |e|
+        e['warden'].logout(e['warden.spec.which_logout'])
+        valid_response
+      end
+    end
+
+    it "should logout only the scoped foo user" do
+      @app = setup_rack(@app)
+      @env['warden.spec.which_logout'] = :foo
+      @app.call(@env)
+      @env['rack.session']['warden.user.default.key'].should == "default key"
+      @env['rack.session']['warden.user.foo.key'].should be_nil
+      @env['rack.session'][:foo].should == "bar"
+    end
+
+    it "should logout only the scoped default user" do
+      @app = setup_rack(@app)
+      @env['warden.spec.which_logout'] = :default
+      @app.call(@env)
+      @env['rack.session']['warden.user.default.key'].should be_nil
+      @env['rack.session']['warden.user.foo.key'].should == "foo key"
+      @env['rack.session'][:foo].should == "bar"
+    end
+
+    it "should clear the session when no argument is given to logout" do
+      @env['rack.session'].should_not be_nil
+      app = lambda do |e|
+        e['warden'].logout
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['rack.session'].should be_empty
+    end
+
+    it "should not raise exception if raw_session is nil" do
+      @app = setup_rack(@app, { nil_session: true })
+      @env['rack.session'] = nil
+      @env['warden.spec.which_logout'] = :foo
+      expect { @app.call(@env) }.to_not raise_error(NoMethodError)
+    end
+
+    it "should clear the user when logging out" do
+      @env['rack.session'].should_not be_nil
+      app = lambda do |e|
+        e['warden'].user.should_not be_nil
+        e['warden'].logout
+        e['warden'].should_not be_authenticated
+        e['warden'].user.should be_nil
+        valid_response
+      end
+      setup_rack(app).call(@env)
+      @env['warden'].user.should be_nil
+    end
+
+    it "should clear the session data when logging out" do
+      @env['rack.session'].should_not be_nil
+      app = lambda do |e|
+        e['warden'].user.should_not be_nil
+        e['warden'].session[:foo] = :bar
+        e['warden'].logout
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    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|
+        e['warden'].user.should_not be_nil
+        e['warden'].should_receive(:reset_session!)
+        e['warden'].logout
+        valid_response
+      end
+      setup_rack(app).call(@env)
+    end
+  end
+
+  describe "messages" do
+    it "should allow access to the failure message" do
+      failure = lambda do |e|
+        [401, {"Content-Type" => "text/plain"}, [e['warden'].message]]
+      end
+      app = lambda do |e|
+        e['warden'].authenticate! :failz
+      end
+      result = setup_rack(app, :failure_app => failure).call(@env)
+      result.last.should == ["The Fails Strategy Has Failed You"]
+    end
+
+    it "should allow access to the success message" do
+      success = lambda do |e|
+        [200, {"Content-Type" => "text/plain"}, [e['warden'].message]]
+      end
+      app = lambda do |e|
+        e['warden'].authenticate! :pass_with_message
+        success.call(e)
+      end
+      result = setup_rack(app).call(@env)
+      result.last.should == ["The Success Strategy Has Accepted You"]
+    end
+
+    it "should not die when accessing a message from a source where no authentication has occured" do
+      app = lambda do |e|
+        [200, {"Content-Type" => "text/plain"}, [e['warden'].message]]
+      end
+      result = setup_rack(app).call(@env)
+      result[2].should == [nil]
+    end
+  end
+
+  describe "when all strategies are not valid?" do
+    it "should return false for authenticated? when there are no valid? strategies" do
+     @env['rack.session'] = {}
+     app = lambda do |e|
+       e['warden'].authenticate(:invalid).should be_nil
+       e['warden'].should_not be_authenticated
+     end
+     setup_rack(app).call(@env)
+    end
+
+    it "should return nil for authenticate when there are no valid strategies" do
+      @env['rack.session'] = {}
+      app = lambda do |e|
+        e['warden'].authenticate(:invalid).should be_nil
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should return false for authenticate? when there are no valid strategies" do
+      @env['rack.session'] = {}
+      app = lambda do |e|
+        e['warden'].authenticate?(:invalid).should be_false
+      end
+      setup_rack(app).call(@env)
+    end
+
+    it "should respond with a 401 when authenticate! cannot find any valid strategies" do
+      @env['rack.session'] = {}
+      app = lambda do |e|
+        e['warden'].authenticate!(:invalid)
+      end
+      result = setup_rack(app).call(@env)
+      result.first.should == 401
+    end
+  end
+
+  describe "authenticated?" do
+    describe "positive authentication" do
+      before do
+        @env['rack.session'] = {'warden.user.default.key' => 'defult_key'}
+        $captures = []
+      end
+
+      it "should return true when authenticated in the session" do
+        app = lambda do |e|
+          e['warden'].should be_authenticated
+        end
+        setup_rack(app).call(@env)
+      end
+
+      it "should yield to a block when the block is passed and authenticated" do
+        app = lambda do |e|
+          e['warden'].authenticated? do
+            $captures << :in_the_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == [:in_the_block]
+      end
+
+      it "should authenticate for a user in a different scope" do
+        @env['rack.session'] = {'warden.user.foo.key' => 'foo_key'}
+        app = lambda do |e|
+          e['warden'].authenticated?(:foo) do
+            $captures << :in_the_foo_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == [:in_the_foo_block]
+      end
+    end
+
+    describe "negative authentication" do
+      before do
+        @env['rack.session'] = {'warden.foo.default.key' => 'foo_key'}
+        $captures = []
+      end
+
+      it "should return false when authenticated in the session" do
+        app = lambda do |e|
+          e['warden'].should_not be_authenticated
+        end
+        setup_rack(app).call(@env)
+      end
+
+      it "should return false if scope cannot be retrieved from session" do
+        begin
+          Warden::Manager.serialize_from_session { |k| nil }
+          app = lambda do |env|
+            env['rack.session']['warden.user.foo_scope.key'] = "a foo user"
+            env['warden'].authenticated?(:foo_scope)
+            valid_response
+          end
+          setup_rack(app).call(@env)
+          @env['warden'].user(:foo_scope).should be_nil
+        ensure
+          Warden::Manager.serialize_from_session { |k| k }
+        end
+      end
+
+      it "should not yield to a block when the block is passed and authenticated" do
+        app = lambda do |e|
+          e['warden'].authenticated? do
+            $captures << :in_the_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == []
+      end
+
+      it "should not yield for a user in a different scope" do
+        app = lambda do |e|
+          e['warden'].authenticated?(:bar) do
+            $captures << :in_the_bar_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == []
+      end
+    end
+  end
+
+  describe "unauthenticated?" do
+    describe "negative unauthentication" do
+      before do
+        @env['rack.session'] = {'warden.user.default.key' => 'defult_key'}
+        $captures = []
+      end
+
+      it "should return false when authenticated in the session" do
+        app = lambda do |e|
+          e['warden'].should_not be_unauthenticated
+        end
+        result = setup_rack(app).call(@env)
+      end
+
+      it "should not yield to a block when the block is passed and authenticated" do
+        app = lambda do |e|
+          e['warden'].unauthenticated? do
+            $captures << :in_the_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == []
+      end
+
+      it "should not yield to the block for a user in a different scope" do
+        @env['rack.session'] = {'warden.user.foo.key' => 'foo_key'}
+        app = lambda do |e|
+          e['warden'].unauthenticated?(:foo) do
+            $captures << :in_the_foo_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == []
+      end
+    end
+
+    describe "positive unauthentication" do
+      before do
+        @env['rack.session'] = {'warden.foo.default.key' => 'foo_key'}
+        $captures = []
+      end
+
+      it "should return false when unauthenticated in the session" do
+        app = lambda do |e|
+          e['warden'].should be_unauthenticated
+        end
+        setup_rack(app).call(@env)
+      end
+
+      it "should yield to a block when the block is passed and authenticated" do
+        app = lambda do |e|
+          e['warden'].unauthenticated? do
+            $captures << :in_the_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == [:in_the_block]
+      end
+
+      it "should yield for a user in a different scope" do
+        app = lambda do |e|
+          e['warden'].unauthenticated?(:bar) do
+            $captures << :in_the_bar_block
+          end
+        end
+        setup_rack(app).call(@env)
+        $captures.should == [:in_the_bar_block]
+      end
+    end
+  end
+
+  describe "attributes" do
+    def def_app(&blk)
+      @app = setup_rack(blk)
+    end
+
+    it "should have a config attribute" do
+      app = def_app do |e|
+        e['warden'].config.should be_a_kind_of(Hash)
+        valid_response
+      end
+      app.call(@env)
+    end
+  end
+end
+
+describe "dynamic default_strategies" do
+  before(:all) do
+    load_strategies
+
+    class ::DynamicDefaultStrategies
+      def initialize(app, &blk)
+        @app, @blk = app, blk
+      end
+
+      def call(env)
+        @blk.call(env)
+        @app.call(env)
+      end
+    end
+
+    Warden::Strategies.add(:one) do
+      def authenticate!; $captures << :one; success!("User") end
+    end
+
+    Warden::Strategies.add(:two) do
+      def authenticate!; $captures << :two; fail("User not found") end
+    end
+  end
+
+  before(:each) do
+    @app = lambda{|e| e['warden'].authenticate! }
+    @env = env_with_params("/")
+    $captures = []
+  end
+
+  def wrap_app(app, &blk)
+    builder = Rack::Builder.new do
+      use DynamicDefaultStrategies, &blk
+      run app
+    end
+    builder.to_app
+  end
+
+  it "should allow me to change the default strategies on the fly" do
+    app = wrap_app(@app) do |e|
+      e['warden'].default_strategies.should == [:password]
+      e['warden'].config.default_strategies.should == [:password]
+      e['warden'].default_strategies :one
+      e['warden'].authenticate!
+      Rack::Response.new("OK").finish
+    end
+    setup_rack(app).call(@env)
+
+    $captures.should == [:one]
+  end
+
+  it "should allow me to append to the default strategies on the fly" do
+    app = wrap_app(@app) do |e|
+      e['warden'].default_strategies << :one
+      e['warden'].default_strategies.should == [:password, :one]
+      e['warden'].authenticate!
+      Rack::Response.new("OK").finish
+    end
+    setup_rack(app).call(@env)
+
+    $captures.should == [:one]
+  end
+
+  it "should allow me to set the default strategies on a per scope basis" do
+    app = wrap_app(@app) do |e|
+      w = e['warden']
+      w.default_strategies(:two, :one, :scope => :foo)
+      w.default_strategies(:two, :scope => :default)
+      w.default_strategies(:scope => :foo).should == [:two, :one]
+      w.authenticate(:scope => :foo)
+      $captures.should == [:two, :one]
+      $captures.clear
+      w.authenticate
+      $captures.should == [:two]
+    end
+    setup_rack(app).call(@env)
+    $captures.should == [:two]
+  end
+
+  it "should allow me to setup default strategies for each scope on the manager" do
+    builder = Rack::Builder.new do
+      use Warden::Spec::Helpers::Session
+      use Warden::Manager do |config|
+        config.default_strategies :one
+        config.default_strategies :two, :one, :scope => :foo
+        config.failure_app = Warden::Spec::Helpers::FAILURE_APP
+      end
+      run(lambda do |e|
+        w = e['warden']
+        w.authenticate
+        $captures.should == [:one]
+        $captures.clear
+        w.authenticate(:scope => :foo)
+        $captures.should == [:two, :one]
+        $captures << :complete
+      end)
+    end
+    builder.to_app.call(@env)
+    $captures.should include(:complete)
+  end
+
+  it "should not change the master configurations strategies when I change them" do
+    app = wrap_app(@app) do |e|
+      e['warden'].default_strategies << :one
+      e['warden'].default_strategies.should == [:password, :one]
+      e['warden'].manager.config.default_strategies.should == [:password]
+      e['warden'].authenticate!
+      Rack::Response.new("OK").finish
+    end
+    setup_rack(app).call(@env)
+
+    $captures.should == [:one]
+  end
+
+  describe "default scope options" do
+
+    it "should allow me to set a default action for a given scope" do
+      $captures = []
+      builder = Rack::Builder.new do
+        use Warden::Manager do |config|
+          config.scope_defaults :foo, :strategies => [:two], :action => "some_bad_action"
+          config.failure_app = Warden::Spec::Helpers::FAILURE_APP
+        end
+
+        run(lambda do |e|
+          e['warden'].authenticate!(:scope => :foo)
+        end)
+      end
+
+      env = env_with_params("/foo")
+      env["rack.session"] = {}
+      builder.to_app.call(env)
+      request = Rack::Request.new(env)
+      request.path.should == "/some_bad_action"
+    end
+
+    it "should allow me to set store, false on a given scope" do
+      $captures = []
+      builder = Rack::Builder.new do
+        use Warden::Manager do |config|
+          config.default_strategies :one
+          config.default_strategies :two, :one, :scope => :foo
+          config.default_strategies :two, :one, :scope => :bar
+
+          config.scope_defaults :bar, :store => false
+          config.scope_defaults :baz, :store => false
+          config.failure_app = Warden::Spec::Helpers::FAILURE_APP
+        end
+        run(lambda do |e|
+          w = e['warden']
+          w.authenticate
+          w.authenticate(:scope => :foo)
+          w.authenticate(:one, :scope => :bar)
+          w.authenticate(:one, :scope => :baz, :store => true)
+          w.user.should == "User"
+          w.user(:foo).should == "User"
+          w.user(:bar).should == "User"
+          w.user(:baz).should == "User"
+          $captures << :complete
+          Rack::Response.new("OK").finish
+        end)
+      end
+      session = @env["rack.session"] = {}
+      builder.to_app.call(@env)
+      $captures.should include(:complete)
+      session['warden.user.default.key'].should == "User"
+      session['warden.user.foo.key'].should == "User"
+      session.key?('warden.user.bar.key').should be_false
+      session['warden.user.bar.key'].should be_nil
+      session['warden.user.baz.key'].should == "User"
+    end
+  end
+
+  describe "#asset_request?" do
+    before(:each) do
+      @asset_regex = /^\/assets\//
+      ::Warden.asset_paths = @asset_regex
+    end
+
+    it "should return true if PATH_INFO is in asset list" do
+      env = env_with_params('/assets/fun.gif')
+      setup_rack(success_app).call(env)
+      proxy = env["warden"]
+
+      proxy.env['PATH_INFO'].should match(@asset_regex)
+      proxy.should be_asset_request
+    end
+
+    it "should return false if PATH_INFO is not in asset list" do
+      env = env_with_params('/home')
+      setup_rack(success_app).call(env)
+      proxy = env["warden"]
+
+      proxy.env['PATH_INFO'].should_not match(@asset_regex)
+      proxy.should_not be_asset_request
+    end
+  end
+end
diff --git a/ruby-warden/spec/warden/scoped_session_serializer.rb b/ruby-warden/spec/warden/scoped_session_serializer.rb
new file mode 100644 (file)
index 0000000..ed4101e
--- /dev/null
@@ -0,0 +1,123 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Manager do
+  before(:each) do
+    @env = env_with_params
+    @env['rack.session'] ||= {}
+    Warden::Manager.serialize_from_session { |k| k }
+    Warden::Manager.serialize_into_session { |u| u }
+    begin
+      Warden::SessionSerializer.send :remove_method, :admin_serialize
+    rescue
+    end
+    begin
+      Warden::SessionSerializer.send :remove_method, :admin_deserialize
+    rescue
+    end
+  end
+  after(:each) do
+    Warden::Manager.serialize_from_session { |k| k }
+    Warden::Manager.serialize_into_session { |u| u }
+    begin
+      Warden::SessionSerializer.send :remove_method, :admin_deserialize
+      Warden::SessionSerializer.send :remove_method, :admin_serialize
+    rescue
+    end
+  end
+
+  def serializer_respond_to?(name)
+    Warden::SessionSerializer.new(@env).respond_to? name
+  end
+
+  it "should respond to :serialize" do
+    serializer_respond_to?(:serialize).should == true
+  end
+
+  it "should respond to :deserialize" do
+    serializer_respond_to?(:deserialize).should == true
+  end
+
+  it "should respond to {scope}_deserialize if Manager.serialize_from_session is called with scope" do
+    Rack::Builder.new do 
+      Warden::Manager.serialize_from_session ( :admin ) { |n| n }
+    end
+    serializer_respond_to?(:admin_deserialize).should == true
+  end
+
+  it "should respond to {scope}_serialize if Manager.serialize_into_session is called with scope" do
+    Rack::Builder.new do 
+      Warden::Manager.serialize_into_session(:admin) { |n| n }
+    end
+    serializer_respond_to?(:admin_serialize).should == true
+  end
+
+  def initialize_with_scope(scope, &block)
+    Rack::Builder.new do
+      Warden::Manager.serialize_into_session(scope, &block)
+    end
+  end
+
+  it "should execute serialize if no {scope}_serialize is present" do
+    serialized_object = nil
+    initialize_with_scope(nil) do |user|
+      serialized_object = user
+      user
+    end
+    serializer = Warden::SessionSerializer.new(@env)
+    serializer.store("user", :admin)
+    serialized_object.should == "user"
+  end
+
+  it "should not have a {scope}_serialize by default" do
+    serializer_respond_to?(:admin_serialize).should == false
+  end
+
+  it "should execute {scope}_serialize when calling store with a scope" do
+    serialized_object = nil
+    initialize_with_scope(:admin) do |user|
+      serialized_object = user
+      user
+    end
+
+    serializer = Warden::SessionSerializer.new(@env)
+    serializer.store("user", :admin)
+    serialized_object.should == "user"
+  end
+
+
+  it "should execute {scope}_deserialize when calling store with a scope" do
+    serialized_object = nil
+
+    Rack::Builder.new do
+      Warden::Manager.serialize_from_session(:admin) do |key|
+        serialized_object = key
+        key
+      end
+    end
+
+    serializer = Warden::SessionSerializer.new(@env)
+    @env['rack.session'][serializer.key_for(:admin)] = "test"
+    serializer.fetch(:admin)
+
+    serialized_object.should == "test"
+  end
+
+  it "should execute deserialize if {scope}_deserialize is not present" do
+    serialized_object = nil
+
+    Rack::Builder.new do
+      Warden::Manager.serialize_from_session do |key|
+        serialized_object = key
+        key
+      end
+    end
+
+    serializer = Warden::SessionSerializer.new(@env)
+    @env['rack.session'][serializer.key_for(:admin)] = "test"
+    serializer.fetch(:admin)
+
+    serialized_object.should == "test"
+  end
+
+end
diff --git a/ruby-warden/spec/warden/session_serializer_spec.rb b/ruby-warden/spec/warden/session_serializer_spec.rb
new file mode 100644 (file)
index 0000000..1696551
--- /dev/null
@@ -0,0 +1,53 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::SessionSerializer do
+  before(:each) do
+    @env = env_with_params
+    @env['rack.session'] ||= {}
+    @session = Warden::SessionSerializer.new(@env)
+  end
+
+  it "should store data for the default scope" do
+    @session.store("user", :default)
+    @env['rack.session'].should == { "warden.user.default.key"=>"user" }
+  end
+
+  it "should check if a data is stored or not" do
+    @session.should_not be_stored(:default)
+    @session.store("user", :default)
+    @session.should be_stored(:default)
+  end
+
+  it "should load an user from store" do
+    @session.fetch(:default).should be_nil
+    @session.store("user", :default)
+    @session.fetch(:default).should == "user"
+  end
+
+  it "should store data based on the scope" do
+    @session.store("user", :default)
+    @session.fetch(:default).should == "user"
+    @session.fetch(:another).should be_nil
+  end
+
+  it "should delete data from store" do
+    @session.store("user", :default)
+    @session.fetch(:default).should == "user"
+    @session.delete(:default)
+    @session.fetch(:default).should be_nil
+  end
+
+  it "should delete information from store if user cannot be retrieved" do
+    @session.store("user", :default)
+    @env['rack.session'].should have_key("warden.user.default.key")
+    @session.instance_eval "def deserialize(key); nil; end"
+    @session.fetch(:default)
+    @env['rack.session'].should_not have_key("warden.user.default.key")
+  end
+
+  it "should support a nil session store" do
+    @env['rack.session'] = nil
+    @session.fetch(:default).should be_nil
+  end
+end
diff --git a/ruby-warden/spec/warden/strategies/base_spec.rb b/ruby-warden/spec/warden/strategies/base_spec.rb
new file mode 100644 (file)
index 0000000..b7dc27d
--- /dev/null
@@ -0,0 +1,313 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Strategies::Base do
+
+  before(:each) do
+    RAS = Warden::Strategies unless defined?(RAS)
+    Warden::Strategies.clear!
+  end
+
+  describe "headers" do
+    it "should have headers" do
+      Warden::Strategies.add(:foo) do
+        def authenticate!
+          headers("foo" => "bar")
+        end
+      end
+      strategy = Warden::Strategies[:foo].new(env_with_params)
+      strategy._run!
+      strategy.headers["foo"].should == "bar"
+    end
+
+    it "should allow us to clear the headers" do
+      Warden::Strategies.add(:foo) do
+        def authenticate!
+          headers("foo" => "bar")
+        end
+      end
+      strategy = Warden::Strategies[:foo].new(env_with_params)
+      strategy._run!
+      strategy.headers["foo"].should == "bar"
+      strategy.headers.clear
+      strategy.headers.should be_empty
+    end
+  end
+
+  it "should have a user object" do
+    RAS.add(:foobar) do
+      def authenticate!
+        success!("foo")
+      end
+    end
+    strategy = RAS[:foobar].new(env_with_params)
+    strategy._run!
+    strategy.user.should == "foo"
+  end
+
+  it "should be performed after run" do
+    RAS.add(:foobar) do
+      def authenticate!; end
+    end
+    strategy = RAS[:foobar].new(env_with_params)
+    strategy.should_not be_performed
+    strategy._run!
+    strategy.should be_performed
+    strategy.clear!
+    strategy.should_not be_performed
+  end
+
+  it "should set the scope" do
+    RAS.add(:foobar) do
+      def authenticate!
+        self.scope.should == :user
+      end
+    end
+    strategy = RAS[:foobar].new(env_with_params, :user)
+  end
+
+  it "should allow you to set a message" do
+    RAS.add(:foobar) do
+      def authenticate!
+        self.message = "foo message"
+      end
+    end
+    strategy = RAS[:foobar].new(env_with_params)
+    strategy._run!
+    strategy.message.should == "foo message"
+  end
+
+  it "should provide access to the errors" do
+    RAS.add(:foobar) do
+      def authenticate!
+        errors.add(:foo, "foo has an error")
+      end
+    end
+    env = env_with_params
+    env['warden'] = Warden::Proxy.new(env, Warden::Manager.new({}))
+    strategy = RAS[:foobar].new(env)
+    strategy._run!
+    strategy.errors.on(:foo).should == ["foo has an error"]
+  end
+
+  describe "halting" do
+    it "should allow you to halt a strategy" do
+      RAS.add(:foobar) do
+        def authenticate!
+          halt!
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.should be_halted
+    end
+
+    it "should not be halted if halt was not called" do
+      RAS.add(:foobar) do
+        def authenticate!
+          "foo"
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.should_not be_halted
+    end
+
+  end
+
+  describe "pass" do
+    it "should allow you to pass" do
+      RAS.add(:foobar) do
+        def authenticate!
+          pass
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.should_not be_halted
+      str.user.should be_nil
+    end
+  end
+
+  describe "redirect" do
+    it "should allow you to set a redirection" do
+      RAS.add(:foobar) do
+        def authenticate!
+          redirect!("/foo/bar")
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.user.should be_nil
+    end
+
+    it "should mark the strategy as halted when redirecting" do
+      RAS.add(:foobar) do
+        def authenticate!
+          redirect!("/foo/bar")
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.should be_halted
+    end
+
+    it "should escape redirected url parameters" do
+      RAS.add(:foobar) do
+        def authenticate!
+          redirect!("/foo/bar", :foo => "bar")
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.headers["Location"].should == "/foo/bar?foo=bar"
+    end
+
+    it "should allow you to set a message" do
+      RAS.add(:foobar) do
+        def authenticate!
+          redirect!("/foo/bar", {:foo => "bar"}, :message => "You are being redirected foo")
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.headers["Location"].should == "/foo/bar?foo=bar"
+      str.message.should == "You are being redirected foo"
+    end
+
+    it "should set the action as :redirect" do
+      RAS.add(:foobar) do
+        def authenticate!
+          redirect!("/foo/bar", {:foo => "bar"}, :message => "foo")
+        end
+      end
+      str = RAS[:foobar].new(env_with_params)
+      str._run!
+      str.result.should == :redirect
+    end
+  end
+
+  describe "failure" do
+
+    before(:each) do
+      RAS.add(:hard_fail) do
+        def authenticate!
+          fail!("You are not cool enough")
+        end
+      end
+
+      RAS.add(:soft_fail) do
+        def authenticate!
+          fail("You are too soft")
+        end
+      end
+      @hard = RAS[:hard_fail].new(env_with_params)
+      @soft = RAS[:soft_fail].new(env_with_params)
+    end
+
+    it "should allow you to fail hard" do
+      @hard._run!
+      @hard.user.should be_nil
+    end
+
+    it "should halt the strategies when failing hard" do
+      @hard._run!
+      @hard.should be_halted
+    end
+
+    it "should allow you to set a message when failing hard" do
+      @hard._run!
+      @hard.message.should == "You are not cool enough"
+    end
+
+    it "should set the action as :failure when failing hard" do
+      @hard._run!
+      @hard.result.should == :failure
+    end
+
+    it "should allow you to fail soft" do
+      @soft._run!
+      @soft.user.should be_nil
+    end
+
+    it "should not halt the strategies when failing soft" do
+      @soft._run!
+      @soft.should_not be_halted
+    end
+
+    it "should allow you to set a message when failing soft" do
+      @soft._run!
+      @soft.message.should == "You are too soft"
+    end
+
+    it "should set the action as :failure when failing soft" do
+      @soft._run!
+      @soft.result.should == :failure
+    end
+  end
+
+  describe "success" do
+    before(:each) do
+      RAS.add(:foobar) do
+        def authenticate!
+          success!("Foo User", "Welcome to the club!")
+        end
+      end
+      @str = RAS[:foobar].new(env_with_params)
+    end
+
+    it "should allow you to succeed" do
+      @str._run!
+    end
+
+    it "should be authenticated after success" do
+      @str._run!
+      @str.user.should_not be_nil
+    end
+
+    it "should allow you to set a message when succeeding" do
+      @str._run!
+      @str.message.should == "Welcome to the club!"
+    end
+
+    it "should store the user" do
+      @str._run!
+      @str.user.should == "Foo User"
+    end
+
+    it "should set the action as :success" do
+      @str._run!
+      @str.result.should == :success
+    end
+  end
+
+  describe "custom response" do
+    before(:each) do
+      RAS.add(:foobar) do
+        def authenticate!
+          custom!([521, {"foo" => "bar"}, ["BAD"]])
+        end
+      end
+      @str = RAS[:foobar].new(env_with_params)
+      @str._run!
+    end
+
+    it "should allow me to set a custom rack response" do
+      @str.user.should be_nil
+    end
+
+    it "should halt the strategy" do
+      @str.should be_halted
+    end
+
+    it "should provide access to the custom rack response" do
+      @str.custom_response.should == [521, {"foo" => "bar"}, ["BAD"]]
+    end
+
+    it "should set the action as :custom" do
+      @str._run!
+      @str.result.should == :custom
+    end
+  end
+
+end
diff --git a/ruby-warden/spec/warden/strategies_spec.rb b/ruby-warden/spec/warden/strategies_spec.rb
new file mode 100644 (file)
index 0000000..e1b9066
--- /dev/null
@@ -0,0 +1,93 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Strategies do
+  it "should let me add a strategy via a block" do
+    Warden::Strategies.add(:strategy1) do
+      def authenticate!
+        success("foo")
+      end
+    end
+    Warden::Strategies[:strategy1].ancestors.should include(Warden::Strategies::Base)
+  end
+
+  it "should raise an error if I add a strategy via a block, that does not have an authenticate! method" do
+    lambda do
+      Warden::Strategies.add(:strategy2) do
+      end
+    end.should raise_error
+  end
+
+  it "should raise an error if I add a strategy that does not extend Warden::Strategies::Base" do
+    non_base = Class.new do
+      def authenticate!
+      end
+    end
+    expect do
+      Warden::Strategies.add(:strategy_non_base, non_base)
+    end.to raise_error(/is not a Warden::Strategies::Base/)
+  end
+
+  it "should allow me to get access to a particular strategy" do
+    Warden::Strategies.add(:strategy3) do
+      def authenticate!; end
+    end
+    strategy = Warden::Strategies[:strategy3]
+    strategy.should_not be_nil
+    strategy.ancestors.should include(Warden::Strategies::Base)
+  end
+
+  it "should allow me to add a strategy with the required methods" do
+    class MyStrategy < Warden::Strategies::Base
+      def authenticate!; end
+    end
+    lambda do
+      Warden::Strategies.add(:strategy4, MyStrategy)
+    end.should_not raise_error
+  end
+
+  it "should not allow a strategy that does not have an authenticate! method" do
+    class MyOtherStrategy
+    end
+    lambda do
+      Warden::Strategies.add(:strategy5, MyOtherStrategy)
+    end.should raise_error
+  end
+
+  it "should allow me to change a class when providing a block and class" do
+    class MyStrategy < Warden::Strategies::Base
+    end
+
+    Warden::Strategies.add(:foo, MyStrategy) do
+      def authenticate!; end
+    end
+
+    Warden::Strategies[:foo].ancestors.should include(MyStrategy)
+  end
+
+  it "should allow me to update a previously given strategy" do
+    class MyStrategy < Warden::Strategies::Base
+      def authenticate!; end
+    end
+
+    Warden::Strategies.add(:strategy6, MyStrategy)
+
+    new_module = Module.new
+    Warden::Strategies.update(:strategy6) do
+      include new_module
+    end
+
+    Warden::Strategies[:strategy6].ancestors.should include(new_module)
+  end
+
+  it "should allow me to clear the strategies" do
+    Warden::Strategies.add(:foobar) do
+      def authenticate!
+        :foo
+      end
+    end
+    Warden::Strategies[:foobar].should_not be_nil
+    Warden::Strategies.clear!
+    Warden::Strategies[:foobar].should be_nil
+  end
+end
diff --git a/ruby-warden/spec/warden/test/helpers_spec.rb b/ruby-warden/spec/warden/test/helpers_spec.rb
new file mode 100644 (file)
index 0000000..95f1532
--- /dev/null
@@ -0,0 +1,93 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Test::Helpers do
+  before{ $captures = [] }
+  after{ Warden.test_reset! }
+
+  it "should log me in as a user" do
+    user = "A User"
+    login_as user
+    app = lambda{|e|
+      $captures << :run
+      e['warden'].should be_authenticated
+      e['warden'].user.should == "A User"
+      valid_response
+    }
+    setup_rack(app).call(env_with_params)
+    $captures.should == [:run]
+  end
+
+  it "should log me in as a user of a given scope" do
+    user = {:some => "user"}
+    login_as user, :scope => :foo_scope
+    app = lambda{|e|
+      $captures << :run
+      w = e['warden']
+      w.should be_authenticated(:foo_scope)
+      w.user(:foo_scope).should == {:some => "user"}
+    }
+    setup_rack(app).call(env_with_params)
+    $captures.should == [:run]
+  end
+
+  it "should login multiple users with different scopes" do
+    user      = "A user"
+    foo_user  = "A foo user"
+    login_as user
+    login_as foo_user, :scope => :foo
+    app = lambda{|e|
+      $captures << :run
+      w = e['warden']
+      w.user.should == "A user"
+      w.user(:foo).should == "A foo user"
+      w.should be_authenticated
+      w.should be_authenticated(:foo)
+    }
+    setup_rack(app).call(env_with_params)
+    $captures.should == [:run]
+  end
+
+  it "should log out all users" do
+    user = "A user"
+    foo  = "Foo"
+    login_as user
+    login_as foo, :scope => :foo
+    app = lambda{|e|
+      $captures << :run
+      w = e['warden']
+      w.user.should == "A user"
+      w.user(:foo).should == "Foo"
+      w.logout
+      w.user.should be_nil
+      w.user(:foo).should be_nil
+      w.should_not be_authenticated
+      w.should_not be_authenticated(:foo)
+    }
+    setup_rack(app).call(env_with_params)
+    $captures.should == [:run]
+  end
+
+  it "should logout a specific user" do
+    user = "A User"
+    foo  = "Foo"
+    login_as user
+    login_as foo, :scope => :foo
+    app = lambda{|e|
+      $captures << :run
+      w = e['warden']
+      w.logout :foo
+      w.user.should == "A User"
+      w.user(:foo).should be_nil
+      w.should_not be_authenticated(:foo)
+    }
+    setup_rack(app).call(env_with_params)
+    $captures.should == [:run]
+  end
+
+  describe "#asset_paths" do
+    it "should default asset_paths to anything asset path regex" do
+      Warden.asset_paths.should == [/^\/assets\//]      
+    end
+  end
+end
diff --git a/ruby-warden/spec/warden/test/test_mode_spec.rb b/ruby-warden/spec/warden/test/test_mode_spec.rb
new file mode 100644 (file)
index 0000000..309ab71
--- /dev/null
@@ -0,0 +1,76 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe Warden::Test::WardenHelpers do
+  before :all do
+    Warden.test_mode!
+  end
+
+  before do
+    $captures = []
+    @app = lambda{|e| valid_response }
+  end
+
+  after do
+    Warden.test_reset!
+  end
+
+  it{ Warden.should respond_to(:test_mode!)       }
+  it{ Warden.should respond_to(:on_next_request)  }
+  it{ Warden.should respond_to(:test_reset!)      }
+
+  it "should execute the on_next_request block on the next request" do
+    Warden.on_next_request do |warden|
+      $captures << warden
+    end
+
+    setup_rack(@app).call(env_with_params)
+    $captures.should have(1).item
+    $captures.first.should be_an_instance_of(Warden::Proxy)
+  end
+
+  it "should execute many on_next_request blocks on the next request" do
+    Warden.on_next_request{|w| $captures << :first    }
+    Warden.on_next_request{|w| $captures << :second   }
+    setup_rack(@app).call(env_with_params)
+    $captures.should have(2).items
+    $captures.should == [:first, :second]
+  end
+
+  it "should not execute on_next_request blocks on subsequent requests" do
+    app = setup_rack(@app)
+    Warden.on_next_request{|w| $captures << :first }
+    app.call(env_with_params)
+    $captures.should == [:first]
+    $captures.clear
+    app.call(env_with_params)
+    $captures.should be_empty
+  end
+
+  it "should allow me to set new_on_next_request items to execute in the same test" do
+    app = setup_rack(@app)
+    Warden.on_next_request{|w| $captures << :first }
+    app.call(env_with_params)
+    $captures.should == [:first]
+    Warden.on_next_request{|w| $captures << :second }
+    app.call(env_with_params)
+    $captures.should == [:first, :second]
+  end
+
+  it "should remove the on_next_request items when test is reset" do
+    app = setup_rack(@app)
+    Warden.on_next_request{|w| $captures << :first }
+    Warden.test_reset!
+    app.call(env_with_params)
+    $captures.should == []
+  end
+
+  context "asset requests" do
+    it "should not execute on_next_request blocks if this is an asset request" do
+      app = setup_rack(@app)
+      Warden.on_next_request{|w| $captures << :first }
+      app.call(env_with_params("/assets/fun.gif"))
+      $captures.should == []
+    end
+  end
+end
diff --git a/ruby-warden/warden.gemspec b/ruby-warden/warden.gemspec
new file mode 100644 (file)
index 0000000..71e0a6d
--- /dev/null
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+
+require './lib/warden/version'
+
+Gem::Specification.new do |s|
+  s.name = %q{warden}
+  s.version = Warden::VERSION.dup
+  s.authors = ["Daniel Neighman"]
+  s.email = %q{has.sox@gmail.com}
+  s.license = "MIT"
+  s.extra_rdoc_files = [
+    "LICENSE",
+     "README.textile"
+  ]
+  s.files = Dir["**/*"] - Dir["*.gem"] - ["Gemfile.lock"]
+  s.homepage = %q{http://github.com/hassox/warden}
+  s.rdoc_options = ["--charset=UTF-8"]
+  s.require_paths = ["lib"]
+  s.rubyforge_project = %q{warden}
+  s.rubygems_version = %q{1.3.7}
+  s.summary = %q{Rack middleware that provides authentication for rack applications}
+  s.add_dependency "rack", ">= 1.0"
+end
+