mirror of
https://github.com/heartcombo/devise.git
synced 2022-11-09 12:18:31 -05:00
Do not reproduce warden behavior, simply rely on the controller calling it
This commit is contained in:
parent
a9b7a4a1de
commit
5e845ee265
3 changed files with 57 additions and 92 deletions
|
@ -74,50 +74,48 @@ module Devise
|
|||
|
||||
result ||= {}
|
||||
|
||||
# set the response. In production, the rack result is returned from Warden::Manager#call, which
|
||||
# the following is modelled on.
|
||||
# Set the response. In production, the rack result is returned
|
||||
# from Warden::Manager#call, which the following is modelled on.
|
||||
case result
|
||||
when Array
|
||||
if result.first == 401 && intercept_401?(env) # does this happen during testing?
|
||||
process_unauthenticated(env)
|
||||
else
|
||||
result # result is already the array rack response
|
||||
end
|
||||
when Hash
|
||||
process_unauthenticated(env, result)
|
||||
when Array
|
||||
if result.first == 401 && intercept_401?(env) # does this happen during testing?
|
||||
_process_unauthenticated(env)
|
||||
else
|
||||
# any other type, eg: ActionController::TestResponse pass through unchanged.
|
||||
result
|
||||
end
|
||||
when Hash
|
||||
_process_unauthenticated(env, result)
|
||||
else
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def process_unauthenticated(env, options = {})
|
||||
|
||||
def _process_unauthenticated(env, options = {})
|
||||
options[:action] ||= :unauthenticated
|
||||
proxy = env['warden']
|
||||
result = options[:result] || proxy.result
|
||||
# set the controller response as well as return our rack response array
|
||||
|
||||
ret = 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
|
||||
env["PATH_INFO"] = "/#{options[:action]}"
|
||||
env["warden.options"] = options
|
||||
Warden::Manager._run_callbacks(:before_failure, env, options)
|
||||
when :redirect
|
||||
body = proxy.message || "You are being redirected to #{proxy.headers['Location']}"
|
||||
[proxy.status, proxy.headers, [body]]
|
||||
when :custom
|
||||
proxy.custom_response
|
||||
else
|
||||
env["PATH_INFO"] = "/#{options[:action]}"
|
||||
env["warden.options"] = options
|
||||
Warden::Manager._run_callbacks(:before_failure, env, options)
|
||||
|
||||
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
|
||||
@controller.send :render, :status => status, :text => body,
|
||||
:content_type => headers["Content-Type"], :location => headers["Location"]
|
||||
nil # causes process return @response
|
||||
end
|
||||
status, headers, body = Devise.warden_config[:failure_app].call(env).to_a
|
||||
@controller.send :render, :status => status, :text => body,
|
||||
:content_type => headers["Content-Type"], :location => headers["Location"]
|
||||
nil # causes process return @response
|
||||
end
|
||||
|
||||
# ensure that the controller response is set up. In production, this is not necessary since warden returns
|
||||
# the results to rack. However, at testing time, we want the response to be available to the testing framework
|
||||
# to verify what would be returned to rack.
|
||||
# ensure that the controller response is set up. In production, this is
|
||||
# not necessary since warden returns the results to rack. However, at
|
||||
# testing time, we want the response to be available to the testing
|
||||
# framework to verify what would be returned to rack.
|
||||
if ret.is_a?(Array)
|
||||
# ensure the controller response is set to our response.
|
||||
@controller.response ||= @response
|
||||
|
@ -127,7 +125,6 @@ module Devise
|
|||
end
|
||||
|
||||
ret
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,74 +3,42 @@ require 'ostruct'
|
|||
require 'warden/strategies/base'
|
||||
require 'devise/test_helpers'
|
||||
|
||||
class MyController < DeviseController
|
||||
class CustomStrategyController < ActionController::Base
|
||||
def new
|
||||
warden.authenticate!(:custom_strategy)
|
||||
end
|
||||
end
|
||||
|
||||
# These tests are to prove that a warden strategy can successfully
|
||||
# return a custom response, including a specific status code and
|
||||
# custom http response headers. This does work in production,
|
||||
# however, at the time of writing this, the Devise test helpers do
|
||||
# not recognise the custom response and proceed to calling the
|
||||
# Failure App. This makes it impossible to write tests for a
|
||||
# strategy that return a custom response with Devise.
|
||||
class CustomStrategy < Warden::Strategies::Base
|
||||
def authenticate!
|
||||
custom_headers = { "X-FOO" => "BAR" }
|
||||
response = Rack::Response.new("BAD REQUEST", 400, custom_headers)
|
||||
custom! response.finish
|
||||
end
|
||||
end
|
||||
|
||||
class CustomStrategyTest < ActionController::TestCase
|
||||
tests MyController
|
||||
tests CustomStrategyController
|
||||
|
||||
include Devise::TestHelpers
|
||||
|
||||
# These tests are to prove that a warden strategy can successfully return a custom response, including a specific
|
||||
# status code and custom http response headers. This does work in production, however, at the time of writing this,
|
||||
# the Devise test helpers do not recognise the custom response and proceed to calling the Failure App. This makes
|
||||
# it impossible to write tests for a strategy that return a custom response with Devise.
|
||||
#
|
||||
# The code this test needs to verify is in Devise::TestHelpers#_catch_warden (which appears to have no other test
|
||||
# coverage at this point.) The functionality of this function should mirror Warden::Manager#call(env) and
|
||||
# Warden::Manager#process_unauthenticated which correctly detects the custom response when set by a strategy.
|
||||
#
|
||||
class CustomStrategy < Warden::Strategies::Base
|
||||
def authenticate!
|
||||
custom_headers = { "X-FOO" => "BAR" }
|
||||
response = Rack::Response.new("BAD REQUEST", 400, custom_headers)
|
||||
custom! response.finish
|
||||
end
|
||||
setup do
|
||||
Warden::Strategies.add(:custom_strategy, CustomStrategy)
|
||||
end
|
||||
|
||||
# call the custom strategy, returning the rack result array
|
||||
def call_custom(env_params={})
|
||||
request.env ||= {}
|
||||
request.env.merge! ({
|
||||
'REQUEST_URI' => 'http://test.host/',
|
||||
'HTTP_HOST' => 'test.host',
|
||||
'REQUEST_METHOD' => 'GET',
|
||||
'warden.options' => { :scope => :user },
|
||||
'rack.session' => {},
|
||||
'action_dispatch.request.formats' => Array(env_params.delete('formats') || Mime::HTML),
|
||||
'rack.input' => "",
|
||||
})
|
||||
request.env.merge!(env_params)
|
||||
env = request.env
|
||||
|
||||
# create a strategy instance
|
||||
strategy = CustomStrategy.new env, :user
|
||||
|
||||
# processing a test request eventually calls _catch_warden:
|
||||
ret = _catch_warden do
|
||||
|
||||
# when a controller action is triggered, its before filter would require authentication. Devise uses
|
||||
# warden to execute the strategies.
|
||||
|
||||
# simulate its selection as the winning strategy (the custom response is read from .winning_strategy)
|
||||
warden.winning_strategy = strategy
|
||||
|
||||
# And then the strategy is executed:
|
||||
strategy.authenticate!
|
||||
|
||||
# after the strategy executes, halt! has been called (from in custom!) above which eventually results in
|
||||
# the :warden symbol being thrown, which is caught in Devise::TestHelpers#_catch_warden
|
||||
throw :warden
|
||||
end
|
||||
|
||||
# after this point, @response should be set to the custom response when triggered by a custom strategy, or
|
||||
# the response of the FailureApp as normal.
|
||||
ret
|
||||
teardown do
|
||||
Warden::Strategies._strategies.delete(:custom_strategy)
|
||||
end
|
||||
|
||||
|
||||
test "custom strategy can return its own status code" do
|
||||
ret = call_custom
|
||||
ret = get :new
|
||||
|
||||
# check the returned rack array
|
||||
assert ret.is_a?(Array)
|
||||
|
@ -82,7 +50,7 @@ class CustomStrategyTest < ActionController::TestCase
|
|||
end
|
||||
|
||||
test "custom strategy can return custom headers" do
|
||||
ret = call_custom
|
||||
ret = get :new
|
||||
|
||||
# check the returned rack array
|
||||
assert ret.is_a?(Array)
|
||||
|
@ -91,5 +59,4 @@ class CustomStrategyTest < ActionController::TestCase
|
|||
# check the saved response headers as well.
|
||||
assert_equal response.headers['X-FOO'], 'BAR'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -84,6 +84,7 @@ Rails.application.routes.draw do
|
|||
|
||||
match "/set", :to => "home#set"
|
||||
match "/unauthenticated", :to => "home#unauthenticated"
|
||||
match "/custom_strategy/new"
|
||||
|
||||
root :to => "home#index"
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue