Access user properties through a helper method in rails - ruby-on-rails

I wrote this helper method:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
helper_method :current_user
end
To which i should be able, in my mind, to do current_user.role == 'some role' but when I do that it spazzes out saying "undefined method role for nil:NilClass" now does that mean the role column is empty and has nothing it in or that the user object is empty? because I assure you I am logged in, I exist in the database and .... the role field in the database is empty how ever.
Update I should probably state that doing User.role == 'admin' works, as their is a role attribute in the database, or well column. Why can't I do .role on current_user?

Based on this error, you can be certain that current_user is returning the value nil. So the issue isn't the method role. You should note that User.role is a class method on the model User, so it is not calling a method on one particular user. current_user.role on the other hand is an instance method for one particular user, the user that is signed in.
I would put the following right above the method that is throwing the error:
raise session[:user_id].inspect
After confirming the appropriate user_id is in the session cookie using the above method, you could also put the following at the end of your current_user helper method to confirm that a user is actually being returned:
raise #current_user.inspect
What is the logic you are using to create the session[:user_id]? Also, you may want to clear your browser cache or open an Incognito Window (in chrome it is cmd + shift + n) and go back through the sign in process of your app.

Related

How can I use Devise methods in my ConnectionAdapter callback?

I have a rails 5.1 app that's using Devise to handle authentication with my User model. This app has an Oracle database backend that requires setting a system context variable with the logged-in user prior to executing any queries, so I was hoping to do that in the :checkout callback for the ConnectionAdapter.
class ApplicationController < ActionController::Base
before_action :log_user
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.set_callback :checkout, :after do
# Would like to get the logged-in user's username here so I can apply
# it to the oracle sys_context.
# The below throws "undefined method 'user_signed_in?'"
username = current_user.username if user_signed_in?
end
def log_user
# When in this method, user_signed_in? and current_user work fine.
puts "User is #{current_user.username}" if user_signed_in?
end
end
The user_signed_in? method isn't found when run in the :checkout callback block, though it's generally available in the controller. Why?
Also, current_user within the block seems to evaluate to the current_user method defined within the ConnectionAdapter rather than the one defined by Devise. How can I get access to Devise's current_user?
How can I use these Devise-provided methods from within this callback?
You can't use the checkout callback, at the point that it's executed, it has no connection to the controller context. The fact that you've defined it here in your ApplicationController is irrelevant to the context it's actually executed in.
You will need to set the connection option in the before_action so you're running in the controller context. Something like:
before_action :set_user_context
def set_user_context
if current_user
ApplicationRecord.connection.execute "DBMS_SESSION.SET_CONTEXT('whatever', 'goes', 'here', '#{current_user.username}')"
end
end
...or something like that. Note that you might want to add a checkin callback to clear the value when the connection is finished with.
Btw, I answered a nearly identical question a few days ago: https://stackoverflow.com/a/54837596/152786 Different commands though, but might help.

making Session mandatory in a Rails app

I want to make it so that a session has to be present in order to use a site. If not, then redirect to the root path, so that a user can choose whether to browse the site as a guest, log in or register. I'm using a basic authentication made from scratch, based on a Railscast.
in the app controller
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_action :set_artists
before_filter :check_session
helper_method :current_user
private
def set_artists
#artists = Artist.all
end
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def check_session
unless current_user
redirect_to root
end
end
end
I had a guest user logged in, then wiped out all of the guest users through the Rails console: User.where(guest: true).destroy_all. I have a rake task that wipes out guest sessions that are 1 day old, so this would a pretty typical situation. Trying to reload after that, the error comes up: Couldn't find User with 'id'=8
The problem is that your users will keep their cookies, so when you run:
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
the user still has the session cookie with a :user_id and the call to User.find is failing to find the now-deleted user.
A solution is to replace this with a call that can fail, such as:
def current_user
#current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
Where find will raise an exception if the User cannot be found, find_by will just return nil leaving your current_user empty.
That being said, I agree with the earlier commenter that this is not really the "Rails way." Deleting all the guest users each day is probably not the best way to get the behavior you want.
Instead, one idea is that you could add a timestamp to the User model indicating when the user was last asked to validate as a guest, and then if more than 24 hours have elapsed, you could bounce guest users back to the page that prompts them to register. You don't have to delete their User entry; you can just reuse it when they re-register.

Defining a method on current_user without it throwing a no method error on non logged in users

I made a moderator method thats in the user model
def mod_of_game?(guide_id)
game_mods_relationships.exists?(game_category_id: guide_id)
end
Problem is that whenever the user isn't logged in it just throws a no method error on the page.
I'll be making more user methods in the future and i can only assume i'll come across this problem every time.
I haven't tried it but i guess i could put an if else statement in the method
def mod_of_game?(guide_id)
if current_user.nil?
#empty method
else
game_mods_relationships.exists?(game_category_id: guide_id)
end
But I feel there is a more efficient way that i'm not aware of. I'm Building an app to learn rails better so i guess this is one of the things I just dont know.
The problem is that if no user is logged in, current_user will be nil, not an instance of the User class. So, there is no way to fix this inside the User model, as current_user is not a User if it is nil. Also, current_user is generally not available in the model, just in the controller and view.
What I would recommend is to add a filter in the controller, to make sure that if no user is logged in, the visitor will be redirected to the log in page. This can be done with a before_action filter in the controller, something like:
class YourController < ApplicationController
before_filter :authenticate_user!
...
end
Otherwise you can always check if current_user is nil before calling .mod_of_game?, like so:
current_user.mod_of_game?(#guide) unless current_user.nil?
Try following:
# It will return `nil` if user is not logged in
def mod_of_game?(guide_id)
game_mods_relationships.exists?(game_category_id: guide_id) if current_user
end
Your pattern is wrong.
Calling mod_of_game? is an instance method, which means it's got to be called on an instance of User.
By the nature of current_user, you wouldn't be able to call this method unless the user was logged in, or at least invoked.
You'll have to use all the conditions on the front-end to determine firstly whether current_user exists, and then to call mod_of_game? on it...
<% if user_signed_in? && current_user.mod_of_game?(#guide) %>
--
A much better way would be to either create your own helper method, or to use the .try method:
#app/helpers/application_helper.rb
class ApplicationHelper
def mod? guide
return false unless current_user
current_user.mod_of_game? guide
end
end
This would allow you to call:
<% if mod? #guide %>
... which will return false if the user is not signed in, or the user is not a mod.
The reason the pattern is bad is because you're having to base logic on two conditions: user signed in? AND are they a mod?
What you want is a single point of logic, which will return true or false:
<% if current_user.try(:mod_of_game?, #guide) %>

undefined method `is_admin?' for nil:NilClass :(

I want to restrict the access to my pannel admin (gem active_admin) for admin only.
That's my code
class ApplicationController < ActionController::Base
def authenticate_admin!
unless current_user.is_admin?
flash[:error] = "Access denied"
redirect_to root_path
end
end
and the problem is : undefined method `is_admin?' for nil:NilClass
there is a boolean admin (0 false, true 1) in my DB
I've to define my is_admin?, but i try and he is never found. So where do i have to do that ?
Thx for your help
The issue is in your error message. nil doesn't have the method 'is_admin?'. This means that your current user variable isn't being set. You need to redirect users who are not logged-in to a screen where they can do so. Then direct them either through this authenticate_admin! function either first to redirect all user who are admin to the /admin path or simply push all users to your home page allowing them to click an admin link.
It could be that current_user is being set correctly, but this method is being called when there is no logged in user.
You should use this method in conjunction with another filter which requires the user to be logged in, ie which requires current_user to be defined. This is typically called require_user
Eg, in your application controller (so it gets inherited by all controllers)
before_filter :require_user
protected
def require_user
unless current_user
redirect_to "/" and return
end
end
You then make exceptions for the non-logged-in actions, with skip_before_filter.
Now, you can add authenticate_admin! as a before filter in your admin controller: it will only ever by called when require_user has already been passed, so it should be safe.
Add try: <% if current_user.try(:is_admin?) %>
Try simply add before_action :authenticate_user! (if your user called 'user') before :authentificate_admin! method. After this change your app will redirect non-logged users to login form first and only after that will ask your user 'is he admin?'.

How do I check if my user is an admin in rails even after new http requests?

My user model has the attributes password, password_confirmation, username, email, and admin.
I'm wondering how exactly do I check whether the currently logged in user is an admin. How would I go about the methods? I've tried if user.admin? on my views, but it seems that doesn't work.
I'm new to rails so any suggestions would be helpful!
There is a "session" hash which persists through the whole session.
Once a user has logged in, you would store the current user's id in the session hash, like so
session[:user_id] = user.id
Now, if you want the current user to be accessible from your controllers and in your views, you can go to apps/controllers/application_controller and make some useful methods...
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user, :signed_in?, :is_admin?
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def signed_in?
!!current_user
end
def is_admin?
signed_in? ? current_user.admin : false
end
end
Now in your views you can use "is_admin?" to check if the current user is an admin, use
"signed_in?" to check if a user is signed in, and "current_user" to access the user object if it exists (if no user is logged in, "current_user" will be nil)
Cheers
I suggest you to consult with this Devise guide. It shows how to create a basic user model with help of Devise and how to perform admin checks. And yes, by giving you this link, I strongly encourage you to use Devise gem for users and all that stuff ;)

Resources