1
0
Fork 0
mirror of https://github.com/heartcombo/devise.git synced 2022-11-09 12:18:31 -05:00

Added navigational formats to specify when it should return a 302 and when a 401, closes #234 and #249.

This commit is contained in:
José Valim 2010-05-16 19:13:20 +02:00
parent a65fd873dd
commit bff64a6291
9 changed files with 63 additions and 30 deletions

View file

@ -18,6 +18,7 @@
* No need to append ?unauthenticated=true in URLs anymore since Flash was moved to a middleware in Rails 3. * No need to append ?unauthenticated=true in URLs anymore since Flash was moved to a middleware in Rails 3.
* :activatable is included by default in your models. * :activatable is included by default in your models.
* Allow to set cookie domain for the remember token. (by github.com/mantas) * Allow to set cookie domain for the remember token. (by github.com/mantas)
* Added navigational formats to specify when it should return a 302 and when a 401.
* bug fix * bug fix
* Fix a bug with STI. * Fix a bug with STI.

View file

@ -147,6 +147,9 @@ module Devise
mattr_accessor :token_authentication_key mattr_accessor :token_authentication_key
@@token_authentication_key = :auth_token @@token_authentication_key = :auth_token
mattr_accessor :navigational_formats
@@navigational_formats = [:html]
# Private methods to interface with Warden. # Private methods to interface with Warden.
mattr_accessor :warden_config mattr_accessor :warden_config
@@warden_config = nil @@warden_config = nil

View file

@ -64,7 +64,7 @@ module Devise
end end
def http_auth? def http_auth?
request.authorization !Devise.navigational_formats.include?(request.format.to_sym) || request.xhr?
end end
def http_auth_body def http_auth_body
@ -97,7 +97,7 @@ module Devise
# yet, but we still need to store the uri based on scope, so different scopes # yet, but we still need to store the uri based on scope, so different scopes
# would never use the same uri to redirect. # would never use the same uri to redirect.
def store_location! def store_location!
session[:"#{scope}_return_to"] = attempted_path if request && request.get? session[:"#{scope}_return_to"] = attempted_path if request.get? && !http_auth?
end end
end end
end end

View file

@ -4,6 +4,11 @@ Devise.setup do |config|
# Configure the e-mail address which will be shown in DeviseMailer. # Configure the e-mail address which will be shown in DeviseMailer.
config.mailer_sender = "please-change-me@config-initializers-devise.com" config.mailer_sender = "please-change-me@config-initializers-devise.com"
# ==> ORM configuration
# Load and configure the ORM. Supports :active_record (default), :mongoid
# (bson_ext recommended) and :data_mapper (experimental).
require 'devise/orm/<%= options[:orm] %>'
# ==> Configuration for any authentication mechanism # ==> Configuration for any authentication mechanism
# Configure which keys are used when authenticating an user. By default is # Configure which keys are used when authenticating an user. By default is
# just :email. You can configure it to use [:username, :subdomain], so for # just :email. You can configure it to use [:username, :subdomain], so for
@ -85,11 +90,7 @@ Devise.setup do |config|
# Defines name of the authentication token params key # Defines name of the authentication token params key
# config.token_authentication_key = :auth_token # config.token_authentication_key = :auth_token
# ==> General configuration # ==> Scopes configuration
# Load and configure the ORM. Supports :active_record (default), :mongoid
# (bson_ext recommended) and :data_mapper (experimental).
require 'devise/orm/<%= options[:orm] %>'
# Turn scoped views on. Before rendering "sessions/new", it will first check for # Turn scoped views on. Before rendering "sessions/new", it will first check for
# "sessions/users/new". It's turned off by default because it's slower if you # "sessions/users/new". It's turned off by default because it's slower if you
# are using only default views. # are using only default views.
@ -105,6 +106,15 @@ Devise.setup do |config|
# role declared in your routes. # role declared in your routes.
# config.default_scope = :user # config.default_scope = :user
# ==> Navigation configuration
# Lists the formats that should be treated as navigational. Formats like
# :html, should redirect to the sign in page when the user does not have
# access, but formats like :xml or :json, should return 401.
# If you have any extra navigational formats, like :iphone or :mobile, you
# should add them to the navigational formats lists. Default is [:html]
# config.navigational_formats = [:html, :iphone]
# ==> Warden configuration
# If you want to use other strategies, that are not (yet) supported by Devise, # If you want to use other strategies, that are not (yet) supported by Devise,
# you can configure them inside the config.warden block. The example below # you can configure them inside the config.warden block. The example below
# allows you to setup OAuth, using http://github.com/roman/warden_oauth # allows you to setup OAuth, using http://github.com/roman/warden_oauth

View file

@ -8,11 +8,12 @@ class FailureTest < ActiveSupport::TestCase
def call_failure(env_params={}) def call_failure(env_params={})
env = { env = {
'warden.options' => { :scope => :user },
'REQUEST_URI' => 'http://test.host/', 'REQUEST_URI' => 'http://test.host/',
'HTTP_HOST' => 'test.host', 'HTTP_HOST' => 'test.host',
'REQUEST_METHOD' => 'GET', 'REQUEST_METHOD' => 'GET',
'warden.options' => { :scope => :user },
'rack.session' => {}, 'rack.session' => {},
'action_dispatch.request.formats' => Array(env_params.delete('formats') || :html),
'rack.input' => "", 'rack.input' => "",
'warden' => OpenStruct.new(:message => nil) 'warden' => OpenStruct.new(:message => nil)
}.merge!(env_params) }.merge!(env_params)
@ -21,11 +22,6 @@ class FailureTest < ActiveSupport::TestCase
@request = ActionDispatch::Request.new(env) @request = ActionDispatch::Request.new(env)
end end
def call_failure_with_http(env_params={})
env = { "HTTP_AUTHORIZATION" => "Basic #{ActiveSupport::Base64.encode64("foo:bar")}" }
call_failure(env_params.merge!(env))
end
context 'When redirecting' do context 'When redirecting' do
test 'return 302 status' do test 'return 302 status' do
call_failure call_failure
@ -61,22 +57,41 @@ class FailureTest < ActiveSupport::TestCase
assert_match /redirected/, @response.last.body assert_match /redirected/, @response.last.body
assert_match /users\/sign_in/, @response.last.body assert_match /users\/sign_in/, @response.last.body
end end
test 'works for any navigational format' do
swap Devise, :navigational_formats => [:xml] do
call_failure('formats' => :xml)
assert_equal 302, @response.first
end
end
end end
context 'For HTTP request' do context 'For HTTP request' do
test 'return 401 status' do test 'return 401 status' do
call_failure_with_http call_failure('formats' => :xml)
assert_equal 401, @response.first assert_equal 401, @response.first
end end
test 'return WWW-authenticate headers' do test 'return WWW-authenticate headers' do
call_failure_with_http call_failure('formats' => :xml)
assert_equal 'Basic realm="Application"', @response.second["WWW-Authenticate"] assert_equal 'Basic realm="Application"', @response.second["WWW-Authenticate"]
end end
test 'uses the proxy failure message as response body' do test 'uses the proxy failure message as response body' do
call_failure_with_http('warden' => OpenStruct.new(:message => :invalid)) call_failure('formats' => :xml, 'warden' => OpenStruct.new(:message => :invalid))
assert_equal 'Invalid email or password.', @response.third.body assert_match '<error>Invalid email or password.</error>', @response.third.body
end
test 'works for any non navigational format' do
swap Devise, :navigational_formats => [] do
call_failure('formats' => :html)
assert_equal 401, @response.first
end
end
test 'works for xml http requests' do
call_failure('formats' => :html, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest')
assert_equal 401, @response.first
end end
end end

View file

@ -155,7 +155,6 @@ class AuthenticationTest < ActionController::IntegrationTest
test 'redirect to default url if no other was configured' do test 'redirect to default url if no other was configured' do
sign_in_as_user sign_in_as_user
assert_template 'home/index' assert_template 'home/index'
assert_nil session[:"user_return_to"] assert_nil session[:"user_return_to"]
end end
@ -188,6 +187,12 @@ class AuthenticationTest < ActionController::IntegrationTest
assert_nil session[:"user_return_to"] assert_nil session[:"user_return_to"]
end end
test 'xml http requests does not store urls for redirect' do
get users_path, {}, 'HTTP_X_REQUESTED_WITH' => 'XMLHttpRequest'
assert_equal 401, response.status
assert_nil session[:"user_return_to"]
end
test 'redirect to configured home path for a given scope after sign in' do test 'redirect to configured home path for a given scope after sign in' do
sign_in_as_admin sign_in_as_admin
assert_equal "/admin_area/home", @request.path assert_equal "/admin_area/home", @request.path

View file

@ -5,8 +5,7 @@ class HttpAuthenticationTest < ActionController::IntegrationTest
test 'sign in should authenticate with http' do test 'sign in should authenticate with http' do
sign_in_as_new_user_with_http sign_in_as_new_user_with_http
assert_response :success assert_response :success
assert_template 'users/index' assert_match '<email>user@test.com</email>', response.body
assert_contain 'Welcome'
assert warden.authenticated?(:user) assert warden.authenticated?(:user)
end end
@ -17,10 +16,10 @@ class HttpAuthenticationTest < ActionController::IntegrationTest
end end
test 'uses the request format as response content type' do test 'uses the request format as response content type' do
sign_in_as_new_user_with_http("unknown", "123456", :xml) sign_in_as_new_user_with_http("unknown")
assert_equal 401, status assert_equal 401, status
assert_equal "application/xml; charset=utf-8", headers["Content-Type"] assert_equal "application/xml; charset=utf-8", headers["Content-Type"]
assert response.body.include?("<error>Invalid email or password.</error>") assert_match "<error>Invalid email or password.</error>", response.body
end end
test 'returns a custom response with www-authenticate and chosen realm' do test 'returns a custom response with www-authenticate and chosen realm' do
@ -33,19 +32,18 @@ class HttpAuthenticationTest < ActionController::IntegrationTest
test 'sign in should authenticate with http even with specific authentication keys' do test 'sign in should authenticate with http even with specific authentication keys' do
swap Devise, :authentication_keys => [:username] do swap Devise, :authentication_keys => [:username] do
sign_in_as_new_user_with_http "usertest" sign_in_as_new_user_with_http("usertest")
assert_response :success assert_response :success
assert_template 'users/index' assert_match '<email>user@test.com</email>', response.body
assert_contain 'Welcome'
assert warden.authenticated?(:user) assert warden.authenticated?(:user)
end end
end end
private private
def sign_in_as_new_user_with_http(username="user@test.com", password="123456", format=:html) def sign_in_as_new_user_with_http(username="user@test.com", password="123456")
user = create_user user = create_user
get users_path(:format => format), {}, "HTTP_AUTHORIZATION" => "Basic #{ActiveSupport::Base64.encode64("#{username}:#{password}")}" get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => "Basic #{ActiveSupport::Base64.encode64("#{username}:#{password}")}"
user user
end end
end end

View file

@ -18,8 +18,7 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
sign_in_as_new_user_with_token(:http_auth => true) sign_in_as_new_user_with_token(:http_auth => true)
assert_response :success assert_response :success
assert_template 'users/index' assert_match '<email>user@test.com</email>', response.body
assert_contain 'Welcome'
assert warden.authenticated?(:user) assert warden.authenticated?(:user)
end end
end end
@ -78,7 +77,7 @@ class TokenAuthenticationTest < ActionController::IntegrationTest
if options[:http_auth] if options[:http_auth]
header = "Basic #{ActiveSupport::Base64.encode64("#{VALID_AUTHENTICATION_TOKEN}:X")}" header = "Basic #{ActiveSupport::Base64.encode64("#{VALID_AUTHENTICATION_TOKEN}:X")}"
get users_path, {}, "HTTP_AUTHORIZATION" => header get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => header
else else
visit users_path(options[:auth_token_key].to_sym => options[:auth_token]) visit users_path(options[:auth_token_key].to_sym => options[:auth_token])
end end

View file

@ -1,8 +1,10 @@
class UsersController < ApplicationController class UsersController < ApplicationController
before_filter :authenticate_user! before_filter :authenticate_user!
respond_to :html, :xml
def index def index
user_session[:cart] = "Cart" user_session[:cart] = "Cart"
respond_with(current_user)
end end
def expire def expire