Is it thread safe to set Active Resource HTTP authentication on a per-user basis?
Asked Answered
S

2

9

Active Resource can make use of HTTP authentication set at the class level. For instance:

class Resource < ActiveResource::Base
end

Resource.user = 'user'
Resource.password = 'password'

or

Resource.site = "http://user:[email protected]/"

But what if I use different HTTP authentication based on which user is logged in? If I change Resource.user and Resource.password, is that going to cause a race condition where requests from one thread suddenly start using the authentication of a user whose requests are running simultaneously in a different thread? Or is this a non-issue (as long as I reset the authentication between requests) because rails servers are not multithreaded?

Even if there's no thread safety problem, it still seems risky that if I fail to reset them, the previous user's credentials will be used automatically by future requests.

Update: After being frustrated with ActiveResource, I wrote my own REST library: https://github.com/DeepWebTechnologies/well_rested

Sybaris answered 11/11, 2011 at 0:8 Comment(0)
B
7

Monkey patch the host, user and password methods of ActiveResource::Base class:

class ActiveResource::Base
  # store the attribute value in a thread local variable
  class << self
    %w(host user password).each do |attr|               

      define_method(attr) do
        Thread.current["active_resource.#{attr}"]
      end

      define_method("#{attr}=") do |val|
        Thread.current["active_resource.#{attr}"] = val
      end
    end
  end
end

Now set the credentials in every request

class ApplicationController < ActionController::Base

  around_filter :set_api_credentials

private 

  # set the credentials in every request
  def set_api_credentials
    ActiveResource::Base.host, 
      ActiveResource::Base.user, 
        ActiveResource::Base.password = current_user_credentials
    yield
  ensure
    ActiveResource::Base.host = 
      ActiveResource::Base.user = 
        ActiveResource::Base.password = nil
  end

  DEFAULT_HOST, DEFAULT_USER, DEFAULT_PASSWORD= [
    "http://www.foo.com", "user1", "user78102" ]

  def current_user_credentials
    current_user.present? ? 
      [ current_user.host, current_user.login, current_user.password] : 
      [ DEFAULT_HOST, DEFAULT_USER, DEFAULT_PASSWORD]
  end

end
Bibbs answered 11/11, 2011 at 3:39 Comment(4)
Does this mean that yes, it does cause a race condition? Or is this just a precaution?Sybaris
Yes it does, in production mode you can run in to race condition if you want to change the connection parameter per request. Using Thread local variable addresses this issue.Bibbs
I have similar solution for setting self.site without race condition problem #8623704Playground
I have updated the answer to work in webservers such as thin and puma.Bibbs
P
2

As of Active Resource 4.1.0, those settings are thread local, so this example would not cause a race condition anymore.

This is the relevant commit: https://github.com/rails/activeresource/commit/538588ddba9ffc9bf356790e9186dc7e6adad12f

Partizan answered 10/10, 2016 at 13:23 Comment(1)
Woah! Thanks for the tip!Diathermic

© 2022 - 2024 — McMap. All rights reserved.