EasyCo
EasyCo

Reputation: 2036

Rails Thread.current[] & thread safety

So there I was, right. Just looking through some code, studying bits and pieces when all of a sudden, my ocular receptors were assaulted by the unfamiliar. I was like:

What!!! What's that all about!

Anyway, what I saw was (source):

def authenticate_user!
  if doorkeeper_token
    Thread.current[:current_user] = User.find(doorkeeper_token.resource_owner_id)
  end

  # ...
end

So after looking at it for a while, thinking:

Wtf is this Thread.current[] insanity? Is this even necessary? What's it even trying to do?

It seemed to me it was kind of like wearing a baseball cap backwards: You may look pretty f'ing cool but that sun glare is winning. I then decided to Google around, read some articles and some SO.

None seemed to concisely answer my question: Given the context of the code, would it not be the same as:

def authenticate_user!
  if doorkeeper_token
    @current_user = User.find(doorkeeper_token.resource_owner_id)
  end

  # ...
end

If not, what situation/scenario is it useful/protecting against?

I hope you enjoyed my story and want to contribute an awesome ending.

Upvotes: 2

Views: 1786

Answers (2)

EasyCo
EasyCo

Reputation: 2036

From the author:

I use Thread to store current user to be able transparently authenticate user and extend this later. Currently this code gives priority to token-based authentication via Doorkeepr, and you can extend it to any other algorithm. I am not a big fan of Devise and did not want to use the token-based authentication strategy it provides.

Once you write user info inside Thread.currrent[:current_user], any other service can read it independently and use for whatever purposes it is needed. For example, as i mentioned above, checking authorization, logging who did the events happening in the system, etc

as for writing it to @current_user, i am not sure it will be accessible in all contexts. But i have not really checked this

Reference: https://github.com/rilian/devise-doorkeeper-cancan-api-example/issues/1#issuecomment-143479288

Upvotes: 0

7stud
7stud

Reputation: 48599

There are different bulletin boards on which you can pin information. The bulletin board where local variables are posted is hidden behind hedges and cannot be seen by the views who live in the next yard:

current_user = User.find(doorkeeper_token.resource_owner_id)

The bulletin board where @variables are posted is nailed to the top of a ladder, so the views have an unobstructed view of the @variables over the tops of the hedges:

@current_user = User.find(doorkeeper_token.resource_owner_id)

But other methods, which live in classes across the street, cannot see the @variables posted on the bulletin board on top of the ladder because a row of trees is in the way.

Thread variables, such as:

Thread.current[:current_user] = User.find(doorkeeper_token.resource_owner_id)

launch a kite, which flies higher than the trees, and the variables posted on the kite can be seen by the methods that live across the street.

Why not create a ruby global variable, e.g. $current_user, instead? Because then requests initiated simultaneously by different users will write to the same global variable, potentially screwing things up.

Given the context of the code

There's not enough context to tell why a (thread) global variable is needed.

Upvotes: 5

Related Questions