Test an HTTPS (SSL) request in RSpec Rails
Asked Answered
W

4

20

I'd like to take advantage of the force_ssl feature in rails 3.1rc4.

class ApplicationController < ActionController::Base
  force_ssl
end

Problem is this breaks most/all of my existing RSpec controller specs. For example, this fails:

describe "GET 'index'" do
  it "renders the Start page" do
    get :index
    response.should render_template 'index'
  end
end

Instead of rendering the page the response is a 301 redirect to https://test.host/.

How can I change my specs to simulate an HTTPS GET/POST?

And do I have to change each test manually or is there an easier way? (I realise I could invoke force_ssl only in production, but that's not ideal. Really, I should be testing that force_ssl does indeed redirect to https:// when expected.)

Washstand answered 22/7, 2011 at 3:12 Comment(0)
L
44

To instruct RSpec to make SSL requests in a controller spec use:

request.env['HTTPS'] = 'on'

In your example this looks like:

describe "GET 'index'" do
  it "renders the Start page" do
    request.env['HTTPS'] = 'on'
    get :index
    response.should render_template 'index'
  end
end
Lawanda answered 29/11, 2011 at 2:18 Comment(5)
This does not work with RSpec 2.10. See here: github.com/rspec/rspec-rails/issues/534Amyloid
I am using rspe 2.11.0 and I am getting always this error message: "undefined method `env' for nil:NilClass".Amyloid
Noting this is fixed with Rails-rspec 2.14 and Devise 3.0.0 github.com/rspec/rspec-rails/issues/534#issuecomment-21560832Aweless
Rails does not provide request in an integration test until after the action. github.com/rspec/rspec-rails/issues/596#issuecomment-7611241Pukka
For actual request specs (as opposed to the now deprecated controller specs), it works like this: get "/path", headers: { "HTTPS" => "on" }. The HTTPS header will be picked up by Rack::Request, telling everything down the line to consider the request to be an SSL/TLS one.Messner
H
2

For rspec 3 syntax, https://mcmap.net/q/662358/-using-rails-39-quot-post-quot-in-controller-tests-in-rspec-with-scoping-and-protocol-on-routes

get :index, protocol: 'https://'

There may be a slight different version that above code doesn't work. So use code below:

get :index, protocol: :https

Harkness answered 6/2, 2015 at 8:28 Comment(1)
I'm not sure then. Mine work well. But I will update the answer.Harkness
S
1

For reference I'm currently running Rails 5.2.4 and RSpec 4.

The best solution as given in this answer is to change from using the _path helper to using the _url helper.

Then you can pass the protocol param to generate the HTTPS URL:

get login_url(protocol: :https)

Note that you cannot pass protocol to the _path helper:

# NOTE: THIS IS WRONG. Doesn't work with the `_path` helper:
# get login_path(protocol: :https)

An alternative option given in carp's comment above is to pass the "HTTPS" header with the fake request.

So if you're using strings/symbols or the _path helper for the request path:

get login_path, headers: {"HTTPS" => "on"}

get '/login', headers: {"HTTPS" => "on"}

Here is carp's full comment:

For actual request specs (as opposed to the now deprecated controller specs), it works like this: get "/path", headers: { "HTTPS" => "on" }. The HTTPS header will be picked up by Rack::Request, telling everything down the line to consider the request to be an SSL/TLS one.

Syneresis answered 24/6, 2020 at 10:31 Comment(0)
P
0

If you need SSL at all, anywhere in your application, you should use it everywhere in your application. Then all you need is to use the rack-ssl gem and add the Rack::SSL middleware to your config/environments/production.rb. No additional testing needed; no breakage; problem solved.

Parkland answered 22/7, 2011 at 3:16 Comment(4)
Thanks @Justice, but the end result of the rack-ssl solution is no different to force_ssl if Rails.env.production? which is probably easier. In any case, isn't it better practice for test to mirror production, allowing you test for SSL redirects? This is what I'd prefer to do, but only if rspec is capable of simulating HTTPS requests.Washstand
Correct. But that's not the point. The point is that if your entire site is, in production, entirely SSL, then you don't need to write any test cases for that. Just require that gem and use that middleware in your production environment, and you're good to go. Remember, this only works if your entire site is over SSL, not just some pieces of your site. But, as a general security principle, if any part of your site needs to be over SSL, then all of your site needs to be over SSL.Parkland
This unfortunately is not a universally true principle. It's more on the order of 50%. Quite often you want to have public resources that are cacheable for performance and scalability reasons. If the majority of your traffic is public unauthenticated traffic (which is quite a common traffic profile) then universal SSL may be a higher cost than you want to pay.Reticulation
There are various attacks against SSL sessions where the vulnerability is the fact that the page with the link to the SSL-only page is not itself SSL-protected. There are various other attacks against SSL sessions which target content that is included into the page, such as JavaScript, which included content is itself not SSL-protected. I am not aware of any legitimate exception to the principle, including for caching. High-performance and cached delivery of public resources can be done with SSL.Parkland

© 2022 - 2024 — McMap. All rights reserved.