request_forgery_protection_test.rb 7.6 KB
Newer Older
1
require 'abstract_unit'
M
Michael Koziarski 已提交
2
require 'digest/sha1'
3
require 'active_support/core_ext/string/strip'
4

5 6 7 8 9
# common controller actions
module RequestForgeryProtectionActions
  def index
    render :inline => "<%= form_tag('/') {} %>"
  end
10

11 12 13
  def show_button
    render :inline => "<%= button_to('New', '/') {} %>"
  end
14

15 16 17
  def unsafe
    render :text => 'pwn'
  end
18

J
Jeremy Kemper 已提交
19
  def meta
20
    render :inline => "<%= csrf_meta_tags %>"
J
Jeremy Kemper 已提交
21 22
  end

23 24 25 26 27 28
  def rescue_action(e) raise e end
end

# sample controllers
class RequestForgeryProtectionController < ActionController::Base
  include RequestForgeryProtectionActions
J
Jeremy Kemper 已提交
29
  protect_from_forgery :only => %w(index meta)
30 31
end

32
class FreeCookieController < RequestForgeryProtectionController
33
  self.allow_forgery_protection = false
34

35 36 37
  def index
    render :inline => "<%= form_tag('/') {} %>"
  end
38

39 40 41 42 43
  def show_button
    render :inline => "<%= button_to('New', '/') {} %>"
  end
end

44 45 46 47 48 49 50
class CustomAuthenticityParamController < RequestForgeryProtectionController
  def form_authenticity_param
    'foobar'
  end
end


51 52
# common test methods

53
module RequestForgeryProtectionTests
54 55 56
  def teardown
    ActionController::Base.request_forgery_protection_token = nil
  end
57

58

59
  def test_should_render_form_with_token_tag
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
     get :index
     assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
   end

   def test_should_render_button_to_with_token_tag
     get :show_button
     assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token
   end

   def test_should_allow_get
     get :index
     assert_response :success
   end

   def test_should_allow_post_without_token_on_unsafe_action
     post :unsafe
     assert_response :success
   end

  def test_should_not_allow_html_post_without_token
    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
81
    assert_raise(ActionController::InvalidAuthenticityToken) { post :index, :format => :html }
82
  end
83

84 85
  def test_should_not_allow_html_put_without_token
    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
86
    assert_raise(ActionController::InvalidAuthenticityToken) { put :index, :format => :html }
87
  end
88

89 90
  def test_should_not_allow_html_delete_without_token
    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
91
    assert_raise(ActionController::InvalidAuthenticityToken) { delete :index, :format => :html }
92
  end
93

94 95
  def test_should_allow_api_formatted_post_without_token
    assert_nothing_raised do
96 97 98 99
      post :index, :format => 'xml'
    end
  end

100
  def test_should_not_allow_api_formatted_put_without_token
101
    assert_nothing_raised do
102 103 104 105
      put :index, :format => 'xml'
    end
  end

106 107
  def test_should_allow_api_formatted_delete_without_token
    assert_nothing_raised do
108 109 110 111 112
      delete :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_post_sent_as_url_encoded_form_without_token
113
    assert_raise(ActionController::InvalidAuthenticityToken) do
114 115 116 117 118 119
      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
      post :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_put_sent_as_url_encoded_form_without_token
120
    assert_raise(ActionController::InvalidAuthenticityToken) do
121 122 123 124 125 126
      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
      put :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_delete_sent_as_url_encoded_form_without_token
127
    assert_raise(ActionController::InvalidAuthenticityToken) do
128 129 130 131 132 133
      @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
      delete :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_post_sent_as_multipart_form_without_token
134
    assert_raise(ActionController::InvalidAuthenticityToken) do
135 136 137 138 139 140
      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
      post :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_put_sent_as_multipart_form_without_token
141
    assert_raise(ActionController::InvalidAuthenticityToken) do
142 143 144 145 146 147
      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
      put :index, :format => 'xml'
    end
  end

  def test_should_not_allow_api_formatted_delete_sent_as_multipart_form_without_token
148
    assert_raise(ActionController::InvalidAuthenticityToken) do
149
      @request.env['CONTENT_TYPE'] = Mime::MULTIPART_FORM.to_s
150 151 152
      delete :index, :format => 'xml'
    end
  end
153

154 155 156
  def test_should_allow_xhr_post_without_token
    assert_nothing_raised { xhr :post, :index }
  end
157

158 159
  def test_should_allow_xhr_put_without_token
    assert_nothing_raised { xhr :put, :index }
160
  end
161

162 163
  def test_should_allow_xhr_delete_without_token
    assert_nothing_raised { xhr :delete, :index }
164
  end
165

166 167 168 169
  def test_should_allow_xhr_post_with_encoded_form_content_type_without_token
    @request.env['CONTENT_TYPE'] = Mime::URL_ENCODED_FORM.to_s
    assert_nothing_raised { xhr :post, :index }
  end
170

171
  def test_should_allow_post_with_token
172
    post :index, :authenticity_token => @token
173 174
    assert_response :success
  end
175

176
  def test_should_allow_put_with_token
177
    put :index, :authenticity_token => @token
178 179
    assert_response :success
  end
180

181
  def test_should_allow_delete_with_token
182
    delete :index, :authenticity_token => @token
183 184
    assert_response :success
  end
185

186
  def test_should_allow_post_with_xml
187
    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
188 189 190
    post :index, :format => 'xml'
    assert_response :success
  end
191

192
  def test_should_allow_put_with_xml
193
    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
194 195 196
    put :index, :format => 'xml'
    assert_response :success
  end
197

198
  def test_should_allow_delete_with_xml
199
    @request.env['CONTENT_TYPE'] = Mime::XML.to_s
200 201 202 203 204
    delete :index, :format => 'xml'
    assert_response :success
  end
end

205
# OK let's get our test on
206

207
class RequestForgeryProtectionControllerTest < ActionController::TestCase
208 209 210 211
  include RequestForgeryProtectionTests
  def setup
    @controller = RequestForgeryProtectionController.new
    @request    = ActionController::TestRequest.new
212
    @request.format = :html
213
    @response   = ActionController::TestResponse.new
J
Jeremy Kemper 已提交
214
    @token      = "cf50faa3fe97702ca1ae"
215

216
    ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
217
    ActionController::Base.request_forgery_protection_token = :authenticity_token
218
  end
J
Jeremy Kemper 已提交
219 220

  test 'should emit a csrf-token meta tag' do
221
    ActiveSupport::SecureRandom.stubs(:base64).returns(@token + '<=?')
J
Jeremy Kemper 已提交
222
    get :meta
223
    assert_equal <<-METAS.strip_heredoc.chomp, @response.body
224 225 226
      <meta name="csrf-param" content="authenticity_token"/>
      <meta name="csrf-token" content="cf50faa3fe97702ca1ae&lt;=?"/>
    METAS
J
Jeremy Kemper 已提交
227
  end
228 229
end

230
class FreeCookieControllerTest < ActionController::TestCase
231 232 233 234
  def setup
    @controller = FreeCookieController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
J
Jeremy Kemper 已提交
235
    @token      = "cf50faa3fe97702ca1ae"
236 237

    ActiveSupport::SecureRandom.stubs(:base64).returns(@token)
238
  end
239

240 241 242 243
  def test_should_not_render_form_with_token_tag
    get :index
    assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
  end
244

245 246 247 248
  def test_should_not_render_button_to_with_token_tag
    get :show_button
    assert_select 'form>div>input[name=?][value=?]', 'authenticity_token', @token, false
  end
249

250 251 252 253 254
  def test_should_allow_all_methods_without_token
    [:post, :put, :delete].each do |method|
      assert_nothing_raised { send(method, :index)}
    end
  end
J
Jeremy Kemper 已提交
255 256 257

  test 'should not emit a csrf-token meta tag' do
    get :meta
258
    assert_blank @response.body
J
Jeremy Kemper 已提交
259
  end
260
end
261 262 263

class CustomAuthenticityParamControllerTest < ActionController::TestCase
  def setup
J
Jeremy Kemper 已提交
264
    ActionController::Base.request_forgery_protection_token = :authenticity_token
265 266 267 268 269 270 271
  end

  def test_should_allow_custom_token
    post :index, :authenticity_token => 'foobar'
    assert_response :ok
  end
end