mirror of https://github.com/rails/rails
Added authentication framework to protect actions behind a condition and redirect on failure. See ActionController::Authentication for more.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@351 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
298cbbd3a0
commit
75fca04590
|
@ -1,5 +1,7 @@
|
|||
*SVN*
|
||||
|
||||
* Added authentication framework to protect actions behind a condition and redirect on failure. See ActionController::Authentication for more.
|
||||
|
||||
* Added the possibility of passing nil to UrlHelper#link_to to use the link itself as the name
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
$:.unshift(File.dirname(__FILE__))
|
||||
|
||||
require 'action_controller/support/core_ext'
|
||||
require 'action_controller/support/clean_logger'
|
||||
require 'action_controller/support/misc'
|
||||
require 'action_controller/support/dependencies'
|
||||
|
@ -40,6 +41,7 @@ require 'action_controller/helpers'
|
|||
require 'action_controller/cookies'
|
||||
require 'action_controller/cgi_process'
|
||||
require 'action_controller/caching'
|
||||
require 'action_controller/authentication'
|
||||
|
||||
ActionController::Base.class_eval do
|
||||
include ActionController::Filters
|
||||
|
@ -53,6 +55,7 @@ ActionController::Base.class_eval do
|
|||
include ActionController::Cookies
|
||||
include ActionController::Session
|
||||
include ActionController::Caching
|
||||
include ActionController::Authentication
|
||||
end
|
||||
|
||||
require 'action_view'
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
module ActionController #:nodoc:
|
||||
module Authentication #:nodoc:
|
||||
def self.append_features(base)
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
end
|
||||
|
||||
# Authentication standardizes the need to protect certain actions unless a given condition is fulfilled. It doesn't address
|
||||
# _how_ someone becomes authorized, but only that if the condition isn't fulfilled a redirect to a given place will happen.
|
||||
#
|
||||
# The authentication model is setup up in two stages. One to configure the authentication, which is often done in the super-most
|
||||
# class (such as ApplicationController in Rails), and then the protection of actions in the individual controller subclasses:
|
||||
#
|
||||
# class ApplicationController < ActionController::Base
|
||||
# authentication :by => '@session[:authenticated]', :failure => { :controller => "login" }
|
||||
# end
|
||||
#
|
||||
# class WeblogController < ApplicationController
|
||||
# authenticates :edit, :update
|
||||
#
|
||||
# def show() render_text "I showed something" end
|
||||
# def index() render_text "I indexed something" end
|
||||
# def edit() render_text "I edited something" end
|
||||
# def update() render_text "I updated something" end
|
||||
# def login() @session[:authenticated] = true; render_nothing end
|
||||
# end
|
||||
#
|
||||
# In the example above, the edit and update methods are protected by an authentication condition that requires
|
||||
# <tt>@session[:authenticated]</tt> to be true. If that is not the case, the request is redirected to LoginController#index.
|
||||
# Note that the :by condition is enclosed in single quotes. This is because we want to defer evaluation of the condition until
|
||||
# we're at run time. Also note, that the :failure option uses the same format as Base#url_for and friends do to perform the redirect.
|
||||
module ClassMethods
|
||||
# Enables authentication for this class and all its subclasses.
|
||||
#
|
||||
# Options are:
|
||||
# * <tt>:by</tt> - the code fragment that will be evaluated on each request to determine whether the request is authenticated.
|
||||
# * <tt>:failure</tt> - redirection options following the format of Base#url_for.
|
||||
def authentication(options)
|
||||
options.assert_valid_keys([:by, :failure])
|
||||
class_eval <<-EOV
|
||||
protected
|
||||
def actions_excepted_from_authentication
|
||||
self.class.read_inheritable_attribute("actions_excepted_from_authentication") || []
|
||||
end
|
||||
|
||||
def actions_included_in_authentication
|
||||
actions = self.class.read_inheritable_attribute("actions_included_in_authentication")
|
||||
|
||||
if actions == :all
|
||||
action_methods.collect { |action| action.intern }
|
||||
elsif actions.is_a?(Array)
|
||||
actions
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def action_needs_authentication?
|
||||
if actions_excepted_from_authentication.include?(action_name.intern)
|
||||
false
|
||||
elsif actions_included_in_authentication.include?(action_name.intern)
|
||||
true
|
||||
elsif actions_excepted_from_authentication.length > 0
|
||||
true
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate
|
||||
if !action_needs_authentication? || #{options[:by]}
|
||||
return true
|
||||
else
|
||||
redirect_to(#{options[:failure].inspect})
|
||||
return false
|
||||
end
|
||||
end
|
||||
EOV
|
||||
|
||||
before_filter :authenticate
|
||||
end
|
||||
|
||||
# Protects the actions specified behind the authentication condition.
|
||||
def authenticates(*actions)
|
||||
write_inheritable_array("actions_included_in_authentication", actions)
|
||||
end
|
||||
|
||||
# Protects all the actions of this controller behind the authentication condition.
|
||||
def authenticates_all
|
||||
write_inheritable_attribute("actions_included_in_authentication", :all)
|
||||
end
|
||||
|
||||
# Protects all the actions of this controller _except_ the listed behind the authentication condition.
|
||||
def authenticates_all_except(*actions)
|
||||
write_inheritable_array("actions_excepted_from_authentication", actions)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -602,7 +602,7 @@ module ActionController #:nodoc:
|
|||
|
||||
def action_methods
|
||||
action_controller_classes = self.class.ancestors.reject{ |a| [Object, Kernel].include?(a) }
|
||||
action_controller_classes.inject([]) { |action_methods, klass| action_methods + klass.instance_methods(false) }
|
||||
action_controller_classes.inject([]) { |action_methods, klass| action_methods + klass.public_instance_methods(false) }
|
||||
end
|
||||
|
||||
def add_variables_to_assigns
|
||||
|
|
|
@ -3,7 +3,7 @@ module ActionController #:nodoc:
|
|||
def self.append_features(base)
|
||||
super
|
||||
base.extend(ClassMethods)
|
||||
base.class_eval { include ActionController::Filters::InstanceMethods }
|
||||
base.send(:include, ActionController::Filters::InstanceMethods)
|
||||
end
|
||||
|
||||
# Filters enable controllers to run shared pre and post processing code for its actions. These filters can be used to do
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
require File.dirname(__FILE__) + '/../abstract_unit'
|
||||
|
||||
class AuthenticationTest < Test::Unit::TestCase
|
||||
class ApplicationController < ActionController::Base
|
||||
authentication :by => '@session[:authenticated]', :failure => { :controller => "login" }
|
||||
end
|
||||
|
||||
class WeblogController < ApplicationController
|
||||
def show() render_text "I showed something" end
|
||||
def index() render_text "I indexed something" end
|
||||
def edit() render_text "I edited something" end
|
||||
def update() render_text "I updated something" end
|
||||
def login() @session[:authenticated] = true; render_nothing end
|
||||
end
|
||||
|
||||
class AuthenticatesWeblogController < WeblogController
|
||||
authenticates :edit, :update
|
||||
end
|
||||
|
||||
class AuthenticatesAllWeblogController < WeblogController
|
||||
authenticates_all
|
||||
end
|
||||
|
||||
class AuthenticatesAllExceptWeblogController < WeblogController
|
||||
authenticates_all_except :show, :index, :login
|
||||
end
|
||||
|
||||
class AuthenticatesSomeController < AuthenticatesAllWeblogController
|
||||
authenticates_all_except :show
|
||||
end
|
||||
|
||||
def setup
|
||||
@request = ActionController::TestRequest.new
|
||||
@response = ActionController::TestResponse.new
|
||||
end
|
||||
|
||||
def test_access_on_authenticates
|
||||
@controller = AuthenticatesWeblogController.new
|
||||
|
||||
get :show
|
||||
assert_success
|
||||
|
||||
get :edit
|
||||
assert_redirected_to :controller => "login"
|
||||
end
|
||||
|
||||
def test_access_on_authenticates_all
|
||||
@controller = AuthenticatesAllWeblogController.new
|
||||
|
||||
get :show
|
||||
assert_redirected_to :controller => "login"
|
||||
|
||||
get :edit
|
||||
assert_redirected_to :controller => "login"
|
||||
end
|
||||
|
||||
def test_access_on_authenticates_all_except
|
||||
@controller = AuthenticatesAllExceptWeblogController.new
|
||||
|
||||
get :show
|
||||
assert_success
|
||||
|
||||
get :edit
|
||||
assert_redirected_to :controller => "login"
|
||||
end
|
||||
|
||||
def test_access_on_authenticates_some
|
||||
@controller = AuthenticatesSomeController.new
|
||||
|
||||
get :show
|
||||
assert_success
|
||||
|
||||
get :edit
|
||||
assert_redirected_to :controller => "login"
|
||||
end
|
||||
|
||||
def test_authenticated_access_on_authenticates
|
||||
@controller = AuthenticatesWeblogController.new
|
||||
|
||||
get :login
|
||||
assert_success
|
||||
|
||||
get :show
|
||||
assert_success
|
||||
|
||||
get :edit
|
||||
assert_success
|
||||
end
|
||||
end
|
|
@ -126,6 +126,11 @@ class RenderTest < Test::Unit::TestCase
|
|||
assert_raises(ActionController::UnknownAction, "No action responded to [clone]") { process_request }
|
||||
end
|
||||
|
||||
def test_private_methods
|
||||
@request.action = "determine_layout"
|
||||
assert_raises(ActionController::UnknownAction, "No action responded to [determine_layout]") { process_request }
|
||||
end
|
||||
|
||||
def test_access_to_request_in_view
|
||||
ActionController::Base.view_controller_internals = false
|
||||
|
||||
|
|
Loading…
Reference in New Issue