|
| 1 | +module ActionController #:nodoc: |
| 2 | + module Verification #:nodoc: |
| 3 | + extend ActiveSupport::Concern |
| 4 | + |
| 5 | + include AbstractController::Callbacks, Flash, Rendering |
| 6 | + |
| 7 | + # This module provides a class-level method for specifying that certain |
| 8 | + # actions are guarded against being called without certain prerequisites |
| 9 | + # being met. This is essentially a special kind of before_filter. |
| 10 | + # |
| 11 | + # An action may be guarded against being invoked without certain request |
| 12 | + # parameters being set, or without certain session values existing. |
| 13 | + # |
| 14 | + # When a verification is violated, values may be inserted into the flash, and |
| 15 | + # a specified redirection is triggered. If no specific action is configured, |
| 16 | + # verification failures will by default result in a 400 Bad Request response. |
| 17 | + # |
| 18 | + # Usage: |
| 19 | + # |
| 20 | + # class GlobalController < ActionController::Base |
| 21 | + # # Prevent the #update_settings action from being invoked unless |
| 22 | + # # the 'admin_privileges' request parameter exists. The |
| 23 | + # # settings action will be redirected to in current controller |
| 24 | + # # if verification fails. |
| 25 | + # verify :params => "admin_privileges", :only => :update_post, |
| 26 | + # :redirect_to => { :action => "settings" } |
| 27 | + # |
| 28 | + # # Disallow a post from being updated if there was no information |
| 29 | + # # submitted with the post, and if there is no active post in the |
| 30 | + # # session, and if there is no "note" key in the flash. The route |
| 31 | + # # named category_url will be redirected to if verification fails. |
| 32 | + # |
| 33 | + # verify :params => "post", :session => "post", "flash" => "note", |
| 34 | + # :only => :update_post, |
| 35 | + # :add_flash => { "alert" => "Failed to create your message" }, |
| 36 | + # :redirect_to => :category_url |
| 37 | + # |
| 38 | + # Note that these prerequisites are not business rules. They do not examine |
| 39 | + # the content of the session or the parameters. That level of validation should |
| 40 | + # be encapsulated by your domain model or helper methods in the controller. |
| 41 | + module ClassMethods |
| 42 | + # Verify the given actions so that if certain prerequisites are not met, |
| 43 | + # the user is redirected to a different action. The +options+ parameter |
| 44 | + # is a hash consisting of the following key/value pairs: |
| 45 | + # |
| 46 | + # <tt>:params</tt>:: |
| 47 | + # a single key or an array of keys that must be in the <tt>params</tt> |
| 48 | + # hash in order for the action(s) to be safely called. |
| 49 | + # <tt>:session</tt>:: |
| 50 | + # a single key or an array of keys that must be in the <tt>session</tt> |
| 51 | + # in order for the action(s) to be safely called. |
| 52 | + # <tt>:flash</tt>:: |
| 53 | + # a single key or an array of keys that must be in the flash in order |
| 54 | + # for the action(s) to be safely called. |
| 55 | + # <tt>:method</tt>:: |
| 56 | + # a single key or an array of keys--any one of which must match the |
| 57 | + # current request method in order for the action(s) to be safely called. |
| 58 | + # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for |
| 59 | + # example.) |
| 60 | + # <tt>:xhr</tt>:: |
| 61 | + # true/false option to ensure that the request is coming from an Ajax |
| 62 | + # call or not. |
| 63 | + # <tt>:add_flash</tt>:: |
| 64 | + # a hash of name/value pairs that should be merged into the session's |
| 65 | + # flash if the prerequisites cannot be satisfied. |
| 66 | + # <tt>:add_headers</tt>:: |
| 67 | + # a hash of name/value pairs that should be merged into the response's |
| 68 | + # headers hash if the prerequisites cannot be satisfied. |
| 69 | + # <tt>:redirect_to</tt>:: |
| 70 | + # the redirection parameters to be used when redirecting if the |
| 71 | + # prerequisites cannot be satisfied. You can redirect either to named |
| 72 | + # route or to the action in some controller. |
| 73 | + # <tt>:render</tt>:: |
| 74 | + # the render parameters to be used when the prerequisites cannot be satisfied. |
| 75 | + # <tt>:only</tt>:: |
| 76 | + # only apply this verification to the actions specified in the associated |
| 77 | + # array (may also be a single value). |
| 78 | + # <tt>:except</tt>:: |
| 79 | + # do not apply this verification to the actions specified in the associated |
| 80 | + # array (may also be a single value). |
| 81 | + def verify(options={}) |
| 82 | + before_filter :only => options[:only], :except => options[:except] do |
| 83 | + verify_action options |
| 84 | + end |
| 85 | + end |
| 86 | + end |
| 87 | + |
| 88 | + private |
| 89 | + |
| 90 | + def verify_action(options) #:nodoc: |
| 91 | + if prereqs_invalid?(options) |
| 92 | + flash.update(options[:add_flash]) if options[:add_flash] |
| 93 | + response.headers.merge!(options[:add_headers]) if options[:add_headers] |
| 94 | + apply_remaining_actions(options) unless performed? |
| 95 | + end |
| 96 | + end |
| 97 | + |
| 98 | + def prereqs_invalid?(options) # :nodoc: |
| 99 | + verify_presence_of_keys_in_hash_flash_or_params(options) || |
| 100 | + verify_method(options) || |
| 101 | + verify_request_xhr_status(options) |
| 102 | + end |
| 103 | + |
| 104 | + def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc: |
| 105 | + [*options[:params] ].find { |v| v && params[v.to_sym].nil? } || |
| 106 | + [*options[:session]].find { |v| session[v].nil? } || |
| 107 | + [*options[:flash] ].find { |v| flash[v].nil? } |
| 108 | + end |
| 109 | + |
| 110 | + def verify_method(options) # :nodoc: |
| 111 | + [*options[:method]].all? { |v| request.method != v.to_sym } if options[:method] |
| 112 | + end |
| 113 | + |
| 114 | + def verify_request_xhr_status(options) # :nodoc: |
| 115 | + request.xhr? != options[:xhr] unless options[:xhr].nil? |
| 116 | + end |
| 117 | + |
| 118 | + def apply_redirect_to(redirect_to_option) # :nodoc: |
| 119 | + (redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option |
| 120 | + end |
| 121 | + |
| 122 | + def apply_remaining_actions(options) # :nodoc: |
| 123 | + case |
| 124 | + when options[:render] ; render(options[:render]) |
| 125 | + when options[:redirect_to] ; redirect_to(apply_redirect_to(options[:redirect_to])) |
| 126 | + else head(:bad_request) |
| 127 | + end |
| 128 | + end |
| 129 | + end |
| 130 | +end |
| 131 | + |
| 132 | +ActionController::Base.send :include, ActionController::Verification |
0 commit comments