undefined local variable or method `current_user' cancancan - ruby-on-rails

I'm working on a login/logout system. Instead of using devise, I created an active records User model and use sessions to remember if a user is logged in. Everything was working fine until I added these lines in the application_controller.rb to have a layout before login and one after.
layout :set_layout
def set_layout
if session[:current_user_id]
'afterlogin'
else
'application'
end
end
Now, after I log in and cancancan is being used somewhere in a html page I get undefined local variable or method 'current_user'. I think that I have to add a current_user method but I'm not exactly where and how to define it.
Edit: I already had something similar in another class that is being used by login:
class Admin::ApplicationController < ApplicationController
before_action :authorize
def authorize
begin
#current_user ||= User.find(session[:current_user_id]) if session[:current_user_id]
rescue ActiveRecord::RecordNotFound
session.destroy
redirect_to '/login',alert: 'Please login'
end
end
end
Should I modify this after I add that method ?

CanCanCan expects a current_user method to exist in the controller.
First, set up some authentication (such as Authlogic or Devise).
See Changing Defaults if you need different behavior.
I would suggest you to install Devise so that it comes with a complimentary current_user method.
FYI: https://github.com/plataformatec/devise
UPDATE
when a user logins successfully, you can store the user's id in session.
session[:current_user_id]=user.id
so that, in your applicationcontroller, you can do
def current_user
#current_user ||= session[:current_user_id] && User.find_by_id(session[:current_user_id])
end
helper_method :current_user

Related

Require a user to be logged in (Rails)

How do I guarantee that users only access the routes on my web app if they are logged in? I already have Users and Session models and users are able to create accounts. But how do I make sure that if they are not logged in they are always redirected to the login/sign up page, but if they are they have access to all the routes?
EDIT: So this is what my Application Controller looks like right now:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
So if there isn't a current user, I want to allow access only to the my Pages controller and its actions (which are basically home, signup, login, etc.). If there is a user, on the other hand, I want that user to be able to access all the routes in my route file.
class SomeController < ApplicationController
def show
if current_user.nil?
redirect_to '/path/to/login'
end
end
end
could probably give a more detailed answer if you paste in some code otherwise we all are just guess what your methods are called.
If you are using devise it comes with the built in helper method authenticate_user! which should be placed in your application controller.
If you are not using devise you can define you own method (for this example I will copy devise) authenticate_user! in application controller and call the before action
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
hide_action :current_user
private
def authenticate_user!
redirect_to :root if current_user.nil?
end
end

How to create `authenticate_user' method without devise in ror

I am new in Ruby on Rails and i am using Ruby version 1.9.3 and Rails version 4.0.2.
My Query is:-
How to create `authenticate_user' method without devise in Ruby on Rails.
Below my routes
get "admin/users/sign_in" => "admin/users#sign_in"
Below My Application Controller:-
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
rescue_from CanCan::AccessDenied do |exception|
flash[:alert] = "Access denied. You are not authorized to access the requested page."
redirect_to root_path and return
end
helper_method :current_user
before_filter :authenticate_user, :current_user
def current_user
# Note: we want to use "find_by_id" because it's OK to return a nil.
# If we were to use User.find, it would throw an exception if the user can't be found.
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
#current_user ||= User.find_by_authentication_token(cookies[:auth_token]) if cookies[:auth_token] && #current_user.nil?
#current_user
end
def authenticate_user
if #current_user.nil?
flash[:error] = 'You must be signed in to view that page.'
redirect_to :admin_users_sign_in
end
end
protected
#derive the model name from the controller. egs UsersController will return User
def self.permission
return name = self.name.gsub('Controller','').singularize.split('::').last.constantize.name rescue nil
end
def current_ability
#current_ability ||= Ability.new(current_user)
end
#load the permissions for the current user so that UI can be manipulated
def load_permissions
#current_permissions = current_user.role.permissions.collect{|i| [i.subject_class, i.action]}
end
end
Below code using my controller
before_filter :authenticate_user!
My authenticate_user method not redirect properly
redirect_to :admin_users_sign_in
admin_users_sign_in path define in routes see on top
Above the code every time say on browser "The page isn't redirecting properly"
Please help
I suspect the problem is due to this line:
redirect_to :admin_users_sign_in
You need to pass either an action & controller or a friendly name of the path to redirect_to.
Change your routes to be something like
get "admin/users/sign_in" => "admin/users#sign_in", :as => :admin_user_signin
Then you can do something like
redirect_to admin_user_signin_path
This looks an infinite loop.
You defined authenticate_user at ApplicationController level. So, when a visitor visited page 'foo', he is denied by this method because current_user is nil. Then he got redirected to admin sign in page, but that page has this before_filter as well, so he got redirected again, to the same page and never end.
To fix, move such filter to specific controllers which need protection. And do not set it in sign in/sign up page.
Side notes:
You've already used CanCan which has authorization on "read" as well. There is no point to use authenticate_user again for same functionality.

Current_user method for authenticate_or_request_with_http_digest - Rails 4

I am using authenticate_or_request_with_http_digest for simple administration within my app. It would be nice if the admin could see all of the delete, edit links for an object when logged in but have these hidden for regular users.
The app has no scope for users signing up or multiple users so devise or a similar authentication platform seems overkill in this instance.
I have tried to use the authenticate method in the view, as you would with a current_user method. However, it infinitely prompts you to login, which isn't ideal.
Is there a way to replicate the popular current_user method to check whether a session has been created and use this as a helper method?
application_controller.rb
helper_method :authenticate
USERS = { "username" => "password",
"APP" => Digest::MD5.hexdigest(["APP", "realm", "password"].join(":"))}
private
def authenticate
authenticate_or_request_with_http_digest(CONFIG[:realm]) do |username|
USERS[username]
end
end
usage in controller
before_action :authenticate
Update
Thanks to Peter Goldstein's answer, I was able to save the username inside the authenticate block into a session[:admin] variable and use this inside the current_user helper method.
Something like this:
def authenticate
current_user_name = nil
is_authenticated = authenticate_or_request_with_http_digest(CONFIG[:realm]) do |username|
current_user_name = username
USERS[username]
end
#current_user = current_user_name if is_authenticated
is_authenticated
end
def current_user
#current_user
end
helper_method :current_user
should capture the username from the HTTP digest request and make it visible in the current_user method

How to let the Application controller know that a user has signed in (users are handled with devise)

I'm using Devise to handle users in a shopping application. What I want to do is create a new cart each time a user signs in (and ideally, destroy the same cart each time a user signs out, but I'll just stick to that first part for this question).
So far, I've looked at this question: Devise call backs
And I came up with this:
class ApplicationController < ActionController::Base
helper :all
protect_from_forgery
before_filter :fetch_categories
.
.
.
Warden::Manager.after_authentication do
session[:cart_id] ||= Cart.create!.id
end
end
...But clearly this isn't correct, because I'm getting this error:
NameError in Devise::SessionsController#create
undefined local variable or method `session' for ApplicationController:Class
Is there some other way I can tell the application controller to respond to a user sign in, or should I be putting this code elsewhere (other than the application controller)? Thanks for any help.
Glancing at the docs, it looks like you can do something like this:
Warden::Manager.after_authentication do |user, auth, opts|
auth.session[:cart_id] ||= Cart.create!.id
end
Simpally write a before filter
In application controller
before_filter :set_current_user
def set_current_user
Authorization.current_user = current_user
end
and
you can check throughout application
using
` if !current_user.nil?
end`

Ruby on rails global variable?

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.

Resources