Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 [Jamis Buck]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1008 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson
2005-03-26 21:41:10 +00:00
parent 48b4d28d81
commit f569a14318
4 changed files with 220 additions and 0 deletions

View File

@@ -1,5 +1,7 @@
*SVN*
* Added Verifications that allows you to specify preconditions to actions in form of statements like <tt>verify :only => :update_post, :params => "admin_privileges", :redirect_to => { :action => "settings" }</tt>, which ensure that the update_post action is only called if admin_privileges is available as a parameter -- otherwise the user is redirected to settings. #897 [Jamis Buck]
* Fixed Form.Serialize for the JavascriptHelper to also seriliaze password fields #934 [dweitzman@gmail.com]
* Added JavascriptHelper#escape_javascript as a public method (was private) and made it escape both single and double quotes and new lines #940 [mortonda@dgrmm.net]

View File

@@ -46,6 +46,7 @@ require 'action_controller/cookies'
require 'action_controller/cgi_process'
require 'action_controller/caching'
require 'action_controller/components'
require 'action_controller/verification'
require 'action_view'
ActionController::Base.template_class = ActionView::Base
@@ -64,4 +65,5 @@ ActionController::Base.class_eval do
include ActionController::Session
include ActionController::Caching
include ActionController::Components
include ActionController::Verification
end

View File

@@ -0,0 +1,79 @@
module ActionController #:nodoc:
# This module provides a class-level method for specifying that certain
# actions are guarded against being called without certain prerequisites
# being met. This is essentially a special kind of before_filter.
#
# An action may be guarded against being invoked without certain request
# parameters being set, or without certain session values existing.
#
# When a verification is violated, values may be inserted into the flash, and
# a specified redirection is triggered.
#
# Usage:
#
# class GlobalController < ActionController::Base
# # prevent the #update_settings action from being invoked unless
# # the 'admin_privileges' request parameter exists.
# verify :params => "admin_privileges", :only => :update_post
# :redirect_to => { :action => "settings" }
#
# # disallow a post from being updated if there was no information
# # submitted with the post, and if there is no active post in the
# # session, and if there is no "note" key in the flash.
# verify :params => "post", :session => "post", "flash" => "note",
# :only => :update_post,
# :add_flash => { "alert" => "Failed to create your message" },
# :redirect_to => :category_url
#
module Verification
def self.append_features(base) #:nodoc:
super
base.extend(ClassMethods)
end
module ClassMethods
# Verify the given actions so that if certain prerequisites are not met,
# the user is redirected to a different action. The +options+ parameter
# is a hash consisting of the following key/value pairs:
#
# * <tt>:params</tt>: a single key or an array of keys that must
# be in the @params hash in order for the action(s) to be safely
# called.
# * <tt>:session</tt>: a single key or an array of keys that must
# be in the @session in order for the action(s) to be safely called.
# * <tt>:flash</tt>: a single key or an array of keys that must
# be in the flash in order for the action(s) to be safely called.
# * <tt>:add_flash</tt>: a hash of name/value pairs that should be merged
# into the session's flash if the prerequisites cannot be satisfied.
# * <tt>:redirect_to</tt>: the redirection parameters to be used when
# redirecting if the prerequisites cannot be satisfied.
# * <tt>:only</tt>: only apply this verification to the actions specified in
# the associated array (may also be a single value).
# * <tt>:except</tt>: do not apply this verification to the actions specified in
# the associated array (may also be a single value).
def verify(options={})
filter_opts = { :only => options[:only], :except => options[:except] }
before_filter(filter_opts) do |c|
c.send :verify_action, options
end
end
end
def verify_action(options) #:nodoc:
prereqs_invalid =
[*options[:params] ].find { |v| @params[v].nil? } ||
[*options[:session]].find { |v| @session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
if prereqs_invalid
flash.update(options[:add_flash]) if options[:add_flash]
redirect_to(options[:redirect_to])
return false
end
true
end
private :verify_action
end
end

View File

@@ -0,0 +1,137 @@
require File.dirname(__FILE__) + '/../abstract_unit'
class VerificationTest < Test::Unit::TestCase
class TestController < ActionController::Base
verify :only => :guarded_one, :params => "one",
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_two, :params => %w( one two ),
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_with_flash, :params => "one",
:add_flash => { "notice" => "prereqs failed" },
:redirect_to => { :action => "unguarded" }
verify :only => :guarded_in_session, :session => "one",
:redirect_to => { :action => "unguarded" }
verify :only => [:multi_one, :multi_two], :session => %w( one two ),
:redirect_to => { :action => "unguarded" }
def guarded_one
render_text "#{@params["one"]}"
end
def guarded_with_flash
render_text "#{@params["one"]}"
end
def guarded_two
render_text "#{@params["one"]}:#{@params["two"]}"
end
def guarded_in_session
render_text "#{@session["one"]}"
end
def multi_one
render_text "#{@session["one"]}:#{@session["two"]}"
end
def multi_two
render_text "#{@session["two"]}:#{@session["one"]}"
end
def unguarded
render_text "#{@params["one"]}"
end
end
def setup
@controller = TestController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
end
def test_guarded_one_with_prereqs
process "guarded_one", "one" => "here"
assert_equal "here", @response.body
end
def test_guarded_one_without_prereqs
process "guarded_one"
assert_redirected_to :action => "unguarded"
end
def test_guarded_with_flash_with_prereqs
process "guarded_with_flash", "one" => "here"
assert_equal "here", @response.body
assert_flash_empty
end
def test_guarded_with_flash_without_prereqs
process "guarded_with_flash"
assert_redirected_to :action => "unguarded"
assert_flash_equal "prereqs failed", "notice"
end
def test_guarded_two_with_prereqs
process "guarded_two", "one" => "here", "two" => "there"
assert_equal "here:there", @response.body
end
def test_guarded_two_without_prereqs_one
process "guarded_two", "two" => "there"
assert_redirected_to :action => "unguarded"
end
def test_guarded_two_without_prereqs_two
process "guarded_two", "one" => "here"
assert_redirected_to :action => "unguarded"
end
def test_guarded_two_without_prereqs_both
process "guarded_two"
assert_redirected_to :action => "unguarded"
end
def test_unguarded_with_params
process "unguarded", "one" => "here"
assert_equal "here", @response.body
end
def test_unguarded_without_params
process "unguarded"
assert_equal "", @response.body
end
def test_guarded_in_session_with_prereqs
process "guarded_in_session", {}, "one" => "here"
assert_equal "here", @response.body
end
def test_guarded_in_session_without_prereqs
process "guarded_in_session"
assert_redirected_to :action => "unguarded"
end
def test_multi_one_with_prereqs
process "multi_one", {}, "one" => "here", "two" => "there"
assert_equal "here:there", @response.body
end
def test_multi_one_without_prereqs
process "multi_one"
assert_redirected_to :action => "unguarded"
end
def test_multi_two_with_prereqs
process "multi_two", {}, "one" => "here", "two" => "there"
assert_equal "there:here", @response.body
end
def test_multi_two_without_prereqs
process "multi_two"
assert_redirected_to :action => "unguarded"
end
end