# 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
