5

I'm working on a MVP (minimum viable product). In order to offer a simpler way to secure the admin pages, I just did add http_basic_authenticate_with to my AdminController.

Problem is that when I want to test my AdminController, I get "unauthorized" (401) for not being logged in.

In this scenario, it's irrelevant to test the authentication - it's just temporary, and as soon I go to the next sprint, it's going to removed -, so I'm trying to skip it within RSpec.

Problem is I tried many ways, and none seems to be working.

For example, I tried to modify the http_basic_authenticate_with in order to avoid the authentication. Like this:

require 'spec_helper'

module ActionController
  module HttpAuthentication
    module Basic
      def http_basic_authenticate_with(*args)
      end
    end
  end
end


describe Admin::SubscribersController do  
  describe "GET 'index'" do
    it "should be OK" do 
      get 'index'
      response.should be_successful
    end
  end
end

But when I run it, it still returns "false" for that simple test.

Btw, in order to simplify this test, I just have an empty index action on my AdminController and an empty view (index.html.erb).

Nicholas Pufal
  • 2,175
  • 20
  • 23

4 Answers4

5

Finally I got it working.

Something as stated in the docs didn't work for me:

get 'index', nil, 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials("admin", "password")

So I tried an "old" approach to do it, which is to set request.env['HTTP_AUTHORIZATION'] :

request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials("admin","password")

None of the other solutions worked, so I will just keep in the meanwhile with this one.

Thanks.

Nicholas Pufal
  • 2,175
  • 20
  • 23
4

If it is ok to skip authentication for all tests for a controller, here's the technique I'm using on a current project.

unless Rails.env.test?
  http_basic_authenticate_with name: "slothbear", password: "kuniklo"
end
Soviut
  • 88,194
  • 49
  • 192
  • 260
slothbear
  • 2,016
  • 3
  • 21
  • 38
  • Hm, now that I've typed that out loud, I think I'd change the condition to `if Rails.env.production?` I really don't need authentication in the development environment either. Thanks for leading me to greater productivity. – slothbear Nov 12 '14 at 21:09
  • And here's a good recommendation for not using `production?` as the test: http://stackoverflow.com/q/8741398/2464 – slothbear Nov 12 '14 at 21:15
2

In Rails 5.x, this works:

allow(subject).to receive(:authenticate_or_request_with_http_basic)
  .with(anything).and_return true

In Rails 6.x, this works:

allow(subject).to receive(:http_basic_authenticate_or_request_with)
  .with(anything).and_return true

This is because http_basic_authenticate_with is a class-level method that adds a before_action that actually calls one of these two methods under the hood.

You can see which one to use by checking out http_authentication.rb here for Rails 6 or here for Rails 5

Unixmonkey
  • 18,485
  • 7
  • 55
  • 78
0

You can or even should test authentication. Write test for unauthenticated (it is now) and authenticated. See Testing HTTP Basic Auth in Rails 2.2+ it should help.

Community
  • 1
  • 1
Sławosz
  • 11,187
  • 15
  • 73
  • 106
  • Thanks, but in my case (as I said it's just temporary, and probably next week it's going to be removed already) I want to avoid it. Is it possible? Any suggestions on how to skip it? – Nicholas Pufal Oct 13 '11 at 19:48
  • So maybe you dont need tests at all? But you can do some metaprogramming to avoid it or, which is fastest option - use method in link I found... – Sławosz Oct 13 '11 at 19:52
  • I'm sorry, I guess I did express myself a little wrong. The example I gave above was just an example. My real AdminController isn't going to be just that - however, HTTP authentication is temporary. I didn't want to authenticate before my tests because that means when I change the password, I will have to change test too. Thought skip it would be easier and a better approach. If you have an example on how I could skip (the example I gave was the only idea I had), please let me know. – Nicholas Pufal Oct 13 '11 at 22:56
  • As I said, you need to investigate here: https://github.com/rails/rails/blob/9959233e24eedec4b39e53b6ea0261420ac41f63/actionpack/lib/action_controller/metal/http_authentication.rb#L111, maybe skip_before_filter can help? Or store password in top level constant to avoid changing it in specs. – Sławosz Oct 14 '11 at 06:16
  • And on your example above it should be: self << Admin::SubscribersController; def http_basic_authenticate_with(*); end; end – Sławosz Oct 14 '11 at 06:19
  • I tried a before(:each) hook, containing: controller.class.skip_before_filter :http_basic_authenticate_with. No luck. So I did try to add: 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials("root", "root") as my 3rd parameter on the HTTP request. Still no luck. Both cases give me a response.status of 401, which means both are not bypassing/authenticating. I did not understand your last example. Shouldn't it be something as "class << self"? Thanks for all the support. – Nicholas Pufal Oct 14 '11 at 12:54
  • ups, I mean class << Admin::SubscribersController; def http_basic_authenticate_with(*); end; end – Sławosz Oct 14 '11 at 13:28