Skip to content

Commit

Permalink
Added Verifications that allows you to specify preconditions to actio…
Browse files Browse the repository at this point in the history
…ns 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. rails#897 [Jamis Buck]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1008 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information
dhh committed Mar 26, 2005
1 parent 48b4d28 commit f569a14
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 0 deletions.
2 changes: 2 additions & 0 deletions actionpack/CHANGELOG
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
2 changes: 2 additions & 0 deletions actionpack/lib/action_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
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
Expand All @@ -64,4 +65,5 @@
include ActionController::Session
include ActionController::Caching
include ActionController::Components
include ActionController::Verification
end
79 changes: 79 additions & 0 deletions actionpack/lib/action_controller/verification.rb
Original file line number Diff line number Diff line change
@@ -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
137 changes: 137 additions & 0 deletions actionpack/test/controller/verification_test.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f569a14

Please sign in to comment.