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
Related
I'm new to rails and building app from data on an api, I have two tables I want to use for users - students and educators.
I can authenticate in the controllers with
private
def fetch_user_data(username, password)
require 'URI'
uri = URI('the url for the api')
res = Net::HTTP.post_form(uri, 'username' => username, 'password' => password)
xml = res.body
doc = Nokogiri::Slop(xml)
#status = doc.auth.status.content.to_s
#username = doc.auth.username.content.to_s
#token = doc.auth.token.content.to_s
#person_id = doc.auth.person_pk.content.to_i
#security_roles = doc.auth.security_roles.content.to_s.downcase
end
def assign_user
if /faculty/ =~ #security_roles
#user = Educator.find_by(person_id: #person_id)
elsif /student/ =~ #security_roles
#user = Student.find_by(person_id: #person_id)
end
end
def authenticate_user(username, password)
fetch_user_data(username, password)
assign_user
session[:user_id] = #user.id
redirect_to #user
end
Now I know it's probably not pretty, but I'm learning as I go. I use the authenticate_user() in the sessions controller, and based on the redirect, the authentication seems fine. I do have one question about the :user_id key in the session -- is that a key only created for the session or is it trying to pull a value from a user table? Knowing that would help. My guess is it's just created for the session, but I have no idea.
Ok so now for my real problem. I'm trying to use cancan and I'm getting stuck at defining current user.
I figured I could have the #current_user instance point to the #user I assigned in assign_user. This doesn't seem to be working though. I tried a couple things, but I'm stuck. Maybe I can't even do that? Larger question? Do I have to have a user model to make cancan work? Can I use the two models Educators and Students and mask the user references in cancan on those?
I tried this, but it's not working -- any help?
def current_user
#current_user ||= #user
end
edit: Figured out a bit.
1. methods were in ApplicationHelper. Moved current_user() to ApplicationController . Changed syntax to conventional, but added conditionals.
def current_user
if Student.where(id:session[:user_id]).count == 0
#current_user ||= Educator.find(session[:user_id])
else
#current_user ||= Student.find(session[:user_id])
end
end
This seems to fix things, and allows me to use both tables as the user models.
There are several ways to define current_user (if you aren't using Devise), but this one here is pretty standard:
class ApplicationController < ActionController::Base
def current_user
#current_user ||= User.find(session[:user_id])
end
end
As for your question about the session, the way you have it set up, you're setting session[:user_id] equal to #user.id (which is always the same for each user).
minor aside consider using the gem CanCanCan which is as it sounds. CanCan not maintained and some one set up replacement CanCanCan. You won'thave to change any code.
I'm trying to set the current user into a variable to display "Logged in as Joe" on every page. Not really sure where to begin...
Any quick tips? Specifically, what file should something like this go in...
My current user can be defined as (I think): User.find_by_id(session[:user_id])
TY :)
You might want to use something like Authlogic or Devise to handle this rather than rolling your own auth system, especially when you aren't very familiar with the design patterns common in Rails applications.
That said, if you want to do what you're asking in the question, you should probably define a method in your ApplicationController like so:
def current_user
#current_user ||= User.limit(1).where('id = ?', session[:user_id])
end
You inherit from your ApplicationController on all of your regular controllers, so they all have access to the current_user method. Also, you might want access to the method as a helper in your views. Rails takes care of you with that too (also in your ApplicationController):
helper_method :current_user
def current_user ...
Note: If you use the find_by_x methods they will raise an ActiveRecord::RecordNotFound error if nothing is returned. You probably don't want that, but you might want something to prevent non-users from accessing user only resources, and again, Rails has you covered:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
before_filter :require_user
private
def current_user
#current_user ||= User.limit(1).where('id = ?', session[:user_id])
end
def require_user
unless current_user
flash[:notice] = "You must be logged in to access this page"
redirect_to new_session_url
return false
end
end
end
Cheers!
It belongs in your controllers.
All your controllers inheirit from Application Controller for exactly this reason. Create a method in your Application Controller that returns whatever you need and then you can access it in any of your other controllers.
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.
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 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