diff --git a/lib/warden/manager.rb b/lib/warden/manager.rb index f238a0a..572c958 100644 --- a/lib/warden/manager.rb +++ b/lib/warden/manager.rb @@ -134,16 +134,32 @@ def process_unauthenticated(env, options={}) # 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 + raise "No Failure App provided" unless config.failure_app - _run_callbacks(:before_failure, env, options) - config.failure_app.call(env).to_a + options.merge!(:attempted_path => ::Rack::Request.new(env).fullpath) + env["PATH_INFO"] = "/#{options[:action]}" + env["warden.options"] = options + + _run_callbacks(:before_failure, env, options) + + failure_app_config = config.failure_app + case failure_app_config + when Array + call_multiple_failure_apps(failure_app_config, env, options) + when Hash + call_multiple_failure_apps([failure_app_config], env, options) else - raise "No Failure App provided" + call_multiple_failure_apps([{ app: failure_app_config, scope: options[:scope] }], env, options) end end # call_failure_app + + # :api: private + def call_multiple_failure_apps(failure_app_config, env, options) + failure_app_config.each do |c| + return c[:app].call(env).to_a if options[:scope] == c[:scope] + end + + raise "No Failure App provided for scope" + end # call_multiple_failure_apps end end # Warden diff --git a/spec/warden/manager_spec.rb b/spec/warden/manager_spec.rb index f76b844..56cc4a2 100644 --- a/spec/warden/manager_spec.rb +++ b/spec/warden/manager_spec.rb @@ -53,6 +53,46 @@ expect(result.last).to eq(["Failure App"]) end + it "should raise an error when no matching failure app by scope is found" do + app = lambda { |e| throw(:warden, :scope => :default) } + fail_app = lambda { |e| [401, {"Content-Type" => "text/plain"}, ["Failure App"]] } + + expect { + setup_rack( + app, + :failure_app => { app: fail_app, scope: :foo } + ).call(env_with_params) + }.to raise_error(RuntimeError, 'No Failure App provided for scope') + end + + it "should render the matching failure app by scope" do + app = lambda { |e| throw(:warden, :scope => :default) } + fail_app = lambda { |e| [401, {"Content-Type" => "text/plain"}, ["Failure App"]] } + + result = setup_rack( + app, + :failure_app => { app: fail_app, scope: :default } + ).call(env_with_params) + + expect(result.last).to eq(["Failure App"]) + end + + it "should render the right configured matching failure app by scope" do + app = lambda { |e| throw(:warden, :scope => :default) } + fail_app1 = lambda { |e| [401, {"Content-Type" => "text/plain"}, ["Failure App #1"]] } + fail_app2 = lambda { |e| [401, {"Content-Type" => "text/plain"}, ["Failure App #2"]] } + + result = setup_rack( + app, + :failure_app => [ + { app: fail_app1, scope: :foo }, + { app: fail_app2, scope: :default } + ] + ).call(env_with_params) + + expect(result.last).to eq(["Failure App #2"]) + end + it "should call failure app if warden is thrown even after successful authentication" do env = env_with_params("/", {}) app = lambda do |_env|