views:

812

answers:

4

Hello,

I'd like to do the following:

define a before_filter in application.rb that extracts the user's IP address and stores it anywhere, preferably in the session.

define two before filters in all my models as before_create and before_update that add the current user's IP to the object to be stored.

Problem: I cannot access session[] neither env[] in a model. Can anyone help with a standard solution that I don't know yet?

Regards Jason

A: 

If you want to save the IP in the session, you can create a before filter in the applicationController. Like this, for each action, the filter is called and the ip is stored.

Kiva
Hi Kiva, this is exactly what I tried to to as mentioned above. Problem: I can store the IP in the session, but I cannot access the session in my models!
Jason Nerer
I don't understand why you need to save the ip for each create/update object. You can't access to the session object in the model because this object is only accessible in controllers.
Kiva
We provide a system for a customers who'se coworkers can change content. If a misconfiguration occures, we need to know, who made the mistake. And due to several reasons we cannot rely on the login data aof a user but on the IP. I know, that I cannot access the env or session in a model. Thats what I wrote in my initial question. So the question was: Is there a common receipe to store the users IP with every create/update of any model?
Jason Nerer
A: 

authlogic is a plugin to manage users login/sessions etc, it has a built in option to track the users IP

amikazmi
Hi amikazmi, authorization itself is not the problem at all. I will have a look at the plugin either way. What I want to do, is, to store the users IP with every requests that creates or updates an object in my DB. Problem: I cannot access the session or env in my model in a before filter.
Jason Nerer
if you need to save the IP in your user table, that plugin will do that for you, if you need to save the IP in each of your models that got created updated, kiva answer is the right way.
amikazmi
Hi Again, I'm quite unsure what you mean. What kiva wrote, is exactly what I planned. I can store the IP in the session using a before_filter in my application controller. Ok. But when I want to store the IP with every create/update of an object, I need to create before_create and before_update filters in my model. So far. My problem is, that I cannot access the session in my model. And kiva's answer does not address this at all.
Jason Nerer
+1  A: 

Try this. In your user model add a class attribute accessor

cattr_accessor :current_ip

In your application controller add:

before_filter :set_current_ip

protected
def set_current_ip
    User.current_ip = request.env['REMOTE_ADDR']
end

Then in your model you should be able to just call User.current_ip

We do something similar to get the current_user object passed through.

Lee Irving
One note to this is that the REMOTE_ADDR is sometimes a list of IP addresses depending on your setup.
Lee Irving
This solution is a hack that forces instance-specific data into the class. if you ever wanted to use this concurrenly, you're going to have problems. You're also coupling the models to the controller layer. This makes it harder to do background jobs with your models, as you'll be requiring session data to be present, and when you run with console or Rake, it won't be. The correct place to audit current user and ip info is at the controller layer, because that's where the requests are coming through. You can use around filters or cachesweepers to do this at the controller layer.
Brian Hogan
Fair point, I stand corrected.
Lee Irving
A: 

You're having trouble doing what you want because Rails is designed not to allow you to have access to session information in your models. It's the classic separation of concerns with MVC. Models are meant to work independently of your other layers, and you'll be thankful they do when you start doing things with Rake or other system tasks where you won't have a session.

The

cattr_accessor :current_ip

is a horrible approach. It's a hack and it should be apparent why. Yes, it may work, but it's the wrong approach to this problem.

Since you're tracking "who" did "what" by their IP, the logical place for this to happen is in the controller layer. There are several approaches you can take, including using CacheSweepers as auditors, as outlined in the Rails Recipes book. CacheSweepers can observe models but also have access to all controller information. Using the ditry attributes in a rails model, you can see exactly what changed.

@user  = User.find_by_login "bphogan"
@user.login = "Brian"
@user.save
@user.changed
=> ["login"]
@user.changes
=> {"login"=>["bphogan", "brian"]}
@user.login_was
=> "bphogan"

Combine this with the session info you have and you have a pretty awesome auditor.

Does that help?

Brian Hogan