Understand "Current_User" Concept When Creating a Login Session in Ruby

Understand current_user concept when creating a login session in ruby

A few things.

First, you're reading the method names wrong (which is not surprising given how cryptic ruby method naming can be). def current_user=(user) is actually read as defining the method current_user= that takes an argument user, whereas def current_user defines a method current_user that takes no arguments. These are referred to respectively as setters and getters.

Here's a reference: Ruby (programming language): What are setters and getters in Ruby?

So that explains the duplication. On to your next question.

I don't understand what does self.current_user = user means

self is a topic unto itself, worthy of its own discussion, so I won't even try to explain it (here's one reference out of many). For the purposes of this question it's just important to remember that in order to set instance variables, you need to prefix your assignment with self, even within the class (where for other purposes it would be implicit). The rest of the line is a call to the current_user= setter method I mentioned above, with the argument user.

why is it even necessary to keep this line of code - I have the cookie with the token - why do I need to know the current user?

The reason it's necessary is that you don't want to be looking up the user from the token every time you need to get the current user. Take a look at the getter method:

def current_user
@current_user ||= User.find_by_remember_token(cookies[:remember_token])
end

What this says is: if I haven't looked up and set the instance variable @current_user yet, then look it up; if I have already set it, then just return it. That saves a lot of looking up.

I think that answers your questions. There are a lot of deeper issues (self, etc.) which you can find more information about elsewhere. Here's one discussion of why you need to include self in setters on SO: Why do Ruby setters need "self." qualification within the class?

UPDATE: Small clarification, that last link about using self for setters within the class is actually a bit off-topic, since you're calling it within a module and not directly from a class. In the context of a module, the self in self.current_user = user will become the class that the module is included inside of, e.g. User.current_user if it was called within the class User, etc. Again, another topic of discussion unto itself...

Rails - Why use self.current_user = user in sign_in method

You are right that the @current_user is not set after the redirection.

@current_user ||= User.find_by_remember_token(cookies[:remember_token])

This statement helps avoid repeated calls to the database, but is only useful if current_user variable is used more than once for a single user request. Consequently, setting the current user is only helpful during a single call.

Similarly, setting the current user to nil and removing the token from cookies during sign_out ensures that subsequent processing will take the signing out into account. Otherwise, there is a risk of other methods referring current user variable and thinking that the user is still logged in.

Ruby on rails, @current_user is set null when reloding webseite or changing page

You are missing this part:

application_controller.rb

class ApplicationController < ActionController::Base
include CurrentUserConcern # This is missing

skip_before_action :verify_authenticity_token
before_action :require_login, except: [:welcome, :register, :create]

private

def require_login
unless @current_user
#reset_session
flash[:error] = "You must be logged in"
render json: {status: "You are not logged in", user: @current_user, session: session}
#render sessionhandling_welcome_path
end
end
end

Also, I would change the CurrentUserConcern to handle the #require_login as well. There is no guarantee of which before_action will run first, require_login or set_current_user and even if it is guaranteed then it's confusing.

Good idea to access session in observer or not?

I find this to be a very interesting question. I'm going to think out loud here a moment...

Ultimately, what we are faced with is a decision to violate a design-pattern acceptable practice in order to achieve a specific set of functionality. So, we must ask ourselves

1) What are the possible solutions that would not violate MVC pattern

2) What are the possible solutions that would violate the MVC pattern

3) Which option is best? I consider design patterns and standard practices very important, but at the same time if holding to them makes your code more complex, then the right solution may very well be to violate the practice. Some people might disagree with me on that.

Lets consider #1 first.

Off the top of my head, I would think of the following possible solutions

A) If you are really interested in who is performing these actions, should this data be stored in the model any way? It would make this information available to your Observer. And it also means that any other front-end caller of your ActiveRecord class gets the same functionality.

B) If you are not really interested in understanding who created a entry, but more interested in logging the web actions themselves, then you might consider "observing" the controller actions. It's been some time since I've poked around Rails source, so I'm not sure who their ActiveRecord::Observer "observes" the model, but you might be able to adapt it to a controller observer. In this sense, you aren't observing the model anymore, and it makes sense to make session and other controller type data information to that observer.
C) The simplest solution, with the least "structure", is to simply drop your logging code at the end of your action methods that you're watching.

Consider option #2 now, breaking MVC practices.

A) As you propose, you could find the means to getting your model Observer to have access to the Session data. You've coupled your model to your business logic.

B) Can't think of any others here :)

My personal inclination, without knowing anymore details about your project, is either 1A, if I want to attach people to records, or 1C if there are only a few places where I'm interested in doing this. If you are really wanting a robust logging solution for all your controllers and actions, you might consider 1B.

Having your model observer find session data is a bit "stinky", and would likely break if you tried to use your model in any other project/situation/context.

How session works in Rails

session is not a global hash. It's a method that returns a new hash in the context of each request. How that hash is created depends on the underlying session store.

Let's take a look at 2 typical session stores.

Encrypted cookie store

This is the default session store of Rails applications. Rails serializes then encrypts the whole session hashes into cookies, and stores those cookies on the clients (e.g. browsers). Each time a request hits Rails app, Rails decrypts then deserializes that session cookie to a hash. That hash is what the method session returns.

Redis session store

This session store is not shipped with Rails. It's a separate gem.

With this session store, Rails serializes the session, gives it an ID (called session ID), and stores the ID-hash pair into Redis. Rails then set the session ID to cookie and send that cookie to the client. Each time a request hits Rails app, Rails retrieves the session ID from the cookie, gets the serialized session associated with that session ID from Redis, and deserializes that into a hash. That hash is what the method session returns.

Rails: Access to current_user from within a model in Ruby on Rails

I'd say your instincts to keep current_user out of the model are correct.

Like Daniel I'm all for skinny controllers and fat models, but there is also a clear division of responsibilities. The purpose of the controller is to manage the incoming request and session. The model should be able to answer the question "Can user x do y to this object?", but it's nonsensical for it to reference the current_user. What if you are in the console? What if it's a cron job running?

In many cases with the right permissions API in the model, this can be handled with one-line before_filters that apply to several actions. However if things are getting more complex you may want to implement a separate layer (possibly in lib/) that encapsulates the more complex authorization logic to prevent your controller from becoming bloated, and prevent your model from becoming too tightly coupled to the web request/response cycle.



Related Topics



Leave a reply



Submit