How do I set current_user in middleware?

3.3k views Asked by At

To give more background to my question, please refer to this Github issue - https://github.com/getsentry/raven-ruby/issues/144

I am using raven which is an error logger. I want to add the id for the current_user if user is logged in. The answer I received was

This should be done via your middleware or somewhere similar.

where this means setting the current_user in Raven.

I have read about middlewares, but still have not been able to figure out how can I get current_user in one.

2

There are 2 answers

1
Rohit On

I do not have much idea about Raven, but below is a way, using which, we access the current user in a request, all over our application.

We have created a class, which acts as a cache, and inserts/retrieves data from the current thread

class CustomCache
    def self.namespace
      "my_application"
    end

    def self.get(res)
      Thread.current[self.namespace] ||= {}
        val = Thread.current[self.namespace][res] 
        if val.nil? and block_given?
            val = yield
            self.set(res, val) unless val.nil?
        end
        return val
    end

    def self.set(key, value)
        Thread.current[self.namespace][key] = value
    end

    def self.reset
      Thread.current[self.namespace] = {}
    end
  end

And then, when the request is received, a check for the current session is performed, and then the user's model is inserted in the cache as below

def current_user
  if defined?(@current_user)
    return @current_user
  end
  @current_user = current_user_session && current_user_session.record
  CustomCache.set(:current_user, @current_user)
  return @current_user
end

Now, you can retrieve the current user from anywhere in your application, using the code below,

CustomCache.get(:current_user)

We also make sure to reset the cache before and after the request has been served, so we do this,

CustomCache.reset

Hope this helps.

2
Arthur N On

For Rails apps, I've had success just setting the Raven (Sentry) context in a before_action inside ApplicationController:

# application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_raven_context

  def set_raven_context
    # I use subdomains in my app, but you could leave this next line out if it's not relevant
    context = { account: request.subdomain }
    context.merge!({ user_id: current_user.id, email: current_user.email }) unless current_user.blank?
    Raven.user_context(context)
  end
end

This works because the raven Rack middleware clears the context after every request. See here. It may not be the most efficient, however, since you are setting the context even in the majority of cases that don't result in an Exception. But in any case, it's not that expensive of an operation, and it'll get you pretty far w/o really needing to mess with injecting new Rack middleware or anything.