I use authlogic to authenticate users. In my controllers I use current_user, defined (as documented) as follows:
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
I also use declarative_authorization to manage the current user's permissions. All works fine in the normal runtime scenario, but when I create functional tests that use request statements like " get_with", current_user in the controller is nil. I looked through the declarative_authorization test helper code and found that in this scenario, the declarative_authorization gem actually stores the current user in Authorization.current_user (which in turn comes from Thread.current["current_user"]). So there seems to be quite a mixup of how a current user is handled in different scenario's.
My question: what is the appropriate way of finding the current_user in both the normal runtime and the test scenario?
You can define a before_filter like this in application_controller.
before_filter { |c| Authorization.current_user = c.current_user }
Angelus, you were right. I shouldn't be using get_with, post_with, etc. These just set the Authorization.current_user, session[:user] and session[:user_id] and these seem to be obsolete with authlogic. And for some reason, these even set UserSession to nil, which was causing the problem. So UserSession.create(users(:admin)), followed by a regular get, post, etc. is the way to go.
Related
I've been looking around recently into Rails and notice that there are a lot of references to current_user. Does this only come from Devise? and do I have to manually define it myself even if I use Devise? Are there prerequisites to using current_user (like the existence of sessions, users, etc)?
It is defined by several gems, e.g. Devise
You'll need to store the user_id somewhere, usually in the session after logging in. It also assumes your app has and needs users, authentication, etc.
Typically, it's something like:
class ApplicationController < ActionController::Base
def current_user
return unless session[:user_id]
#current_user ||= User.find(session[:user_id])
end
end
This assumes that the User class exists, e.g. #{Rails.root}/app/models/user.rb.
Updated: avoid additional database queries when there is no current user.
Yes, current_user uses session. You can do something similar in your application controller if you want to roll your own authentication:
def current_user
return unless session[:user_id]
#current_user ||= User.find(session[:user_id])
end
please help me to understand something. In Authlogic example in UsersController it's always used #current_user, so for instance:
def show
#user = #current_user
end
(taken from http://github.com/binarylogic/authlogic_example/blob/master/app/controllers/users_controller.rb)
Why is that? In my controllers I use just current_user instead of #current_user.
And besides - Authlogic works perfectly for me, but I don't see magic columns being populated (like last_login_at or last_login_ip). Should I initialize them somehow specifically besides just adding into migration?
UPD
After some investigation, I found that if there're only fields last_login_at and last_login_ip from "Magic fields", then they will not be populated. If I add a full set of magic fields, it is working perfectly.
UPD2
My concern regarding current_user is only about UsersController: why does it have #current_user and not current_user?
current_user is typically a method defined in app/controllers/application_controller.rb which sets the #current_user instance variable if it is not already defined -- here is an example:
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
Re the "magic columns", these should be set by Authlogic automatically. For example, if your user sessions controller logs in a user:
#user_session = UserSession.new(params[:user_session])
#user_session.save
Authlogic should write the last_login_at and last_login_ip attributes for you. More info in the Authlogic docs under Module: Authlogic::Session::MagicColumns
As for last_login_at and last_login_ip, do you have current_login_at and current_login_ip fields in your table ? last_login_at and last_login_ip are set with the values of current_login_at and current_login_ip before they are reset.
I think the code from the example isn't a really good example.
You shouldn't use #current_user to set the #user variable. Because it won't work if the ApplicationController#current_user method isn't called before show action of the UserController. Basically they are both exactly the same after current_user is called once.
the User Controller should look like this
class UserController < ApplicationController
def show
#user = current_user
end
end
As for the Magic Columns I have no Idea why they don't work for you.
I'm using restful_authentication plugin for Ruby on Rails. All seems fine except that it seems the user session is not getting created at all. I have the create method below. It appears that the self.current_user is being set but that the actual session is never created. When and how is the current_user_session supposed to be defined. I have the method in my application controller but this is where it always fails.
def create
logout_keeping_session!
user = User.authenticate(params[:login], params[:password])
if user
# Protects against session fixation attacks, causes request forgery
# protection if user resubmits an earlier form using back
# button. Uncomment if you understand the tradeoffs.
# reset_session
self.current_user = user
new_cookie_flag = (params[:remember_me] == "1")
handle_remember_cookie! new_cookie_flag
redirect_back_or_default('/')
flash[:notice] = "Logged in successfully"
else
note_failed_signin
#login = params[:login]
#remember_me = params[:remember_me]
render :action => 'new'
end
end
Application_Controller
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
UserSession model is empty
Do not use restful_authentication if you can avoid it. There are a number of better alternatives out there that are actually RESTful and better maintained:
Clearance
Authlogic
Devise
When you say session, do you actually mean a session or is this some restful_authentication magic?
I used to use restful_authentication, and some older apps still do. However, they used cookie-based session management and not a user session model.
Are you using rails 2.3.5?
I am seeing issues with this using redirect_to, basically removing any variables added to the session before the redirecting.
Reverting to 2.3.4 seems to of solved my problem, but there is a bug on lighthouse in regards to some weirdness to session in rails 2.3.X
This may not be same issue for you, but has taken me hours to realise a revert fixed my issue, so might be worth a quick test.
I'm trying to get the permit method to work using the rails-authorization-plugin and authlogic, and I keep running into this error:
When I try:
class ApplicationController < ActionController::Base
...
before_filter permit 'admin'
...
I get this:
Authorization::CannotObtainUserObject in HomeController#index
Couldn't find #current_user or #user, and nothing appropriate found in hash
Now I do have my current_user method setup, and it works, because I used it just about everywhere else in my app:
class ApplicationController < ActionController::Base
...
helper_method :current_user
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
...
I also know that I have users with the appropriate roles in my database, because this method works:
def require_admin
unless current_user.is_admin? || current_user.is_root?
flash[:warning] = 'You are not an administrator and cannot access this page.'
redirect_to root_path
end
end
I can make everything work if I just check on the user level using this:
before_filter :require_admin, :only => 'index'
... but shouldn't I be able to the same thing effectively with permit and permit??
Any help would be much appreciated. Let me know if you need to see more code and I'll be happy to post it. There really is nothing on Google that I can make heads-or-tails of regarding getting these two systems to work with each other.
Okay, I think I figured it out.
As Jared correctly pointed out, the proper usage is
permit 'admin'
(Not as part of a before_filter).
HOWEVER...
... the default :get_user_method is set to #current_user, which is what the acts_as_authenticated plugin uses. I, as noted earlier, am using AuthLogic, in where I have the method defined as current_user (without the pound sign).
So, I had tried the following:
permit 'admin', :get_user_method => current_user
Only to be greeted by a nice error message explaining that I had no such variable or method. What I was missing, however, is that the hash option takes a string, not a direct call to the method!! (stupid mistake, I know!)
So
permit 'admin', :get_user_method => 'current_user'
... seems to work for me.
I love Ruby and Rails, but sometimes its simplicity can be a curse of its own; I always get owned by the simple things. :)
You are using the plugin incorrectly. It should not be placed in a before filter.
On the global level, you simply declare:
permit 'admin'
That's it.
All of your actions will look for a current_user or #user object and redirect to the login page if not.
On a per-action level, you use it as a block:
def index
permit 'admin' do
#some_models = SomeModel.all
end
end
I know this sounds like a really, really simple use case and I'm hoping that it is, but I swear I've looked all over the place and haven't found any mention of any way - not even the best way - of doing this.
I'm brand-spanking new to Ruby, Rails and everything surrounding either (which may explain a lot). The dummy app that I'm using as my learning tool requires authentication in order to do almost anything meaningful, so I chose to start by solving that problem. I've installed the AuthLogic gem and have it working nicely to the extent that is covered by the intro documentation and Railscast, but now that I can register, login and logout...I need to do something with it.
As an example, I need to create a page where users can upload images. I'm planning to have an ImagesController with an upload action method, but I want that only accessible to logged in users. I suppose that in every restricted action I could add code to redirect if there's no current_user, but that seems really verbose.
Is there a better way of doing this that allows me to define or identify restricted areas and handle the authentication check in one place?
Make sure you have these methods in your application_controller.rb
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.record
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
Then in your controllers you can use a before filter to limit access to pages
class ExamplesController < ActionController::Base
before_filter :require_user, :only => :private
def public
// some public stuff
end
def private
// some protected stuff
end
end
before_filter is your friend here. You define a require_authentication function that returns false if there is no valid session and then set it up as a before_filter in the controllers and actions to your liking.
Take a look at the Authlogic Sample application, which defines some filters in the application_controller.rb and then uses it where needed (for example here, where you need to be logged to destroy your account, and not logged to create a new one.
You will need to use a before_filter on your page so that only logged in users can see it. If you want a running example of how Authlogic should be used (including the before_filter stuff), you can check out the Authlogic Exmaple from Github.
You have the entire code Gist available here at Github. Its roughly 360 lines of code. Inclusive of steps.
http://gist.github.com/96556.txt