How Can I Tell Controller Specs to Use the Signed OAuth Request
Asked Answered
V

3

7

I am building a 2-Legged OAuth provider for my api. Everything is hooked up properly and I can make signed calls from the rails console. The problem I have is that I am having trouble integrating OAuth into the controller_spec.

Here is an example of a working call on my server:

coneybeare $ rails c test
Loading test environment (Rails 3.2.0)
rails test: main 
>> consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
# => #<OAuth::Consumer:0x007f9d01252268 @key="one_key", @secret="MyString", @options={:signature_method=>"HMAC-SHA1", :request_token_path=>"/oauth/request_token", :authorize_path=>"/oauth/authorize", :access_token_path=>"/oauth/access_token", :proxy=>nil, :scheme=>:header, :http_method=>:post, :oauth_version=>"1.0", :site=>[REDACTED]}>  

ruby: main 
>> req = consumer.create_signed_request(:get, "/api/v1/client_applications.json", nil)
# => #<Net::HTTP::Get GET>  

ruby: main 
>> res = Net::HTTP.start([REDACTED]) {|http| http.request(req) }
# => #<Net::HTTPOK 200 OK readbody=true>  

ruby: main 
>> puts res.body
{"client_applications":[{"id":119059960,"name":"FooBar1","url":"http://test1.com"},{"id":504489040,"name":"FooBar2","url":"http://test2.com"}]}
# => nil  

And here is what I am doing in my controller tests:

require 'oauth/client/action_controller_request'
describe Api::ClientApplicationsController do
  include OAuthControllerSpecHelper
  …
  … 
    it "assigns all client_applications as @client_applications" do
      consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
      ActionController::TestRequest.use_oauth=true
      @request.configure_oauth(consumer)
      @request.apply_oauth!
      puts "request.env['Authorization'] = #{@request.env['Authorization']}"
      get :index, {:api_version => 'v1', :format => :json}
      response.should be_success # Just this for now until I can get authorization, then proper controller testing
    end
end

The output of that test:

request.env['Authorization'] = OAuth oauth_consumer_key="one_key", oauth_nonce="gzAbvBSWyFtIYKfuokMAdu6VnH39EHeXvebbH2qUtE", oauth_signature="juBkJo5K0WLu9mYqHVC3Ar%2FATUs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1328474800", oauth_version="1.0"
1) Api::ClientApplicationsController GET index assigns all client_applications as @client_applications
   Failure/Error: response.should be_success
     expected success? to return true, got false

And the corresponding server call from the rails log:

Processing by Api::ClientApplicationsController#index as JSON
  Parameters: {"api_version"=>1}
  Rendered text template (0.0ms)
Filter chain halted as #<OAuth::Controllers::ApplicationControllerMethods::Filter:0x007f85a51a8858 @options={:interactive=>false, :strategies=>:two_legged}, @strategies=[:two_legged]> rendered or redirected
Completed 401 Unauthorized in 15ms (Views: 14.1ms | ActiveRecord: 0.0ms)
   (0.2ms)  ROLLBACK

I just can't figure out why it's not working :/ Am I making an obvious mistake?

Vagrom answered 5/2, 2012 at 21:0 Comment(9)
sounds like it's not really a controller spec. Did you try it as a request spec?Huck
It is a controller spec. The example was just stripped down to its bare essentials. If you are correct, and this type of test should go into the request spec, how else am I supposed to test my controller when I have it protected by OAuth?Vagrom
I am testing an API these days and in controller specs, I simply pass proper values in header. I'm sorry, I dont really know for OAuth. What I fear in your case is that create_signed_request doesn't get the expected result. Did you try to debug it? Just thinking: you can bypass some before_filters while testingHuck
The only before filter I have is the oauthenticate method from the oauth-plugin gem. create_signed_request is the one that is working, it is the controller test which is not. I think what might be happening is that apply_oauth! is done before the get :index call is made, causing the path to not be in the signature. I might have to make my own wrapper around the get call to figure out what the path is, set it, sign it and then call the super implementation of get. Seems like a lot of unnecessary work though...Vagrom
Well, I try to explain, I doubt create_signed_request is getting what it expects because no server is runned in controller specs. If you simply want to test the rest of your controller, bypass the before_filter. If you want to test the auth, try in a request spec.Huck
I see what you mean. So something like :skip_before_filter :authenticate in the before(:all) of the controller tests. Let me try it outVagrom
let us continue this discussion in chatVagrom
just seen you tied to contact me via chat. Still no adequate solution found now?Huck
Not yet... there is a bounty now.Vagrom
V
0

Turns out that the best way to test my controller was the simplest as well. Instead of trying to sign each test so the controller gets the right information (something that indeed does belong in a request spec not a controller spec), I figured out that I could just give the controller the information it needed manually.

To do this, I simply had to stub 2 methods:

fixtures :client_applications
before(:each) do
  @client_application1 = client_applications(:client_application1)
  Api::ClientApplicationsController::Authenticator.any_instance.stub(:allow?).and_return(true)
  controller.stub(:client_application).and_return(@client_application1)
end

Stubbing the allow? method caused the rack auth to be fooled into thinking it was authenticated. allow? also set the client_application based on the credentials though, so I had to stub that as well. Now that the auth is out of the way, I can test my controller properly.

Vagrom answered 15/2, 2012 at 14:31 Comment(0)
A
1

If you'd like to test it in a request spec and actually need to test without stubbing, you can build an OAuth consumer and sign a request like this:

    @access_token = FactoryGirl.create :access_token
    @consumer = OAuth::Consumer.new(@access_token.app.key, @access_token.app.secret, :site => "http://www.example.com/")
    @path = "/path/to/request"
    @request = @consumer.create_signed_request(:get, @path, OAuth::AccessToken.new(@consumer, @access_token.token, @access_token.secret))
    get @path, nil, { 'HTTP_AUTHORIZATION' => @request.get_fields('authorization').first }
Amphictyony answered 10/10, 2012 at 4:8 Comment(0)
G
0

I would take a look as to how the Omniauth test helpers work, specifically these files: https://github.com/intridea/omniauth/tree/master/lib/omniauth/test. See their wiki page on integration testing for ideas of how this is set up. I realize that you're building a provider, not a client, but this may be a good starting point. Also, as some of the commenters have already said, I don't know if you can do this with a controller test; you may need a request or integration test to fully simulate the rack environment.

Gibbs answered 14/2, 2012 at 2:6 Comment(0)
V
0

Turns out that the best way to test my controller was the simplest as well. Instead of trying to sign each test so the controller gets the right information (something that indeed does belong in a request spec not a controller spec), I figured out that I could just give the controller the information it needed manually.

To do this, I simply had to stub 2 methods:

fixtures :client_applications
before(:each) do
  @client_application1 = client_applications(:client_application1)
  Api::ClientApplicationsController::Authenticator.any_instance.stub(:allow?).and_return(true)
  controller.stub(:client_application).and_return(@client_application1)
end

Stubbing the allow? method caused the rack auth to be fooled into thinking it was authenticated. allow? also set the client_application based on the credentials though, so I had to stub that as well. Now that the auth is out of the way, I can test my controller properly.

Vagrom answered 15/2, 2012 at 14:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.