ruby-on-railsthread-safetyactiveresourceclass-variables

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


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:password@site.com/"

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


Solution

  • 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