views:

3503

answers:

3

I have a RESTful Rails application with a resource called "Foo". I'm trying to use REST Client to do a put:

resource = RestClient::Resource.new 'http://localhost:3000/foos/1', :user => 'me', :password => 'secret'
resource.put :name => 'somethingwitty', :content_type => 'application/xml'

But my app raises:

ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'

It seems like my app isn't getting the message that this is an XML request and that the AuthenticityToken should be ignored. Maybe I'm not using REST Client correctly. Any ideas on why I'm getting the exception?

+3  A: 

Try putting an :only => [:update, :delete, :create] on the protect_from_forgery line in your application controller.

More info: http://ryandaigle.com/articles/2007/9/24/what-s-new-in-edge-rails-better-cross-site-request-forging-prevention

Jarrod
Thanks for the idea - unfortunately it didn't work.
Rich Apodaca
+1  A: 

Hi,

I think you need to make two changes;

(a) Use the rails routing to tag this as an XML request (b) Use HTTP Basic Authentication to authenticate the request.

This means changing your URL above to include the username, password like this

me:secret@localhost:3000/foos/1.xml

also note .xml bit

I guess that somewhere on your server-side you have code that authenticates in-bound requests via a before filter. This needs to work something like this ...

#
#  If you haven't authenticated already then you are either
#  reqirected to the logon screen (for HTML formats) or 
#  the browser prompts you. You are always allowed to pass 
#  the username/password in the URL
#
def login_required
    @current_user = valid_session?

    unless @current_user
        if params["format"]
            #
            #   If you specify a format you must authenticate now
            # 
            do_basic_authentication
        else
            display_logon_screen
        end
    end
end


#
#   Ask Rails for the login and password then authenticate as if this
#   were a new login.
#
def do_basic_authentication
    user = authenticate_with_http_basic do |login, password|
        User.authenticate(login, password)
    end

    if user
        current_user(@current_user = user)
    else
        request_http_basic_authentication
    end
end

That's from our own app and is triggered by a before_filter in ApplicationController.

Also, I don't think you need the :content_type => 'application/xml'. What I normally do is just call post or put directly like this ..

response = RestClient.post URI.encode(url), :record => args

where the url contains the basic authentication and the ".xml"

Happy coding

Chris

Chris McCauley
+1  A: 

Use something like:

resource.put '<foo><name>somethingwitty</name></foo>', :content_type => 'application/xml'
Rich Apodaca