making Session mandatory in a Rails app - ruby-on-rails

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.

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 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 ;)

detecting if user is logged in at different domain

I'm developing a rails app that creates a widget that you can put on your website. When the widget is displayed (on a website with a different host) to a user who is logged in my app, I would like to display him some additional admin options.
What would be the best and easiest way to figure out if the user is logged in the app?
I was thinking of storing the IP when user logs in, and then compare the IP from the request that is sent to the widget controller.
I followed the Omniauth Railscast Episode and have been using session variables and a SessionsController to create and destroy sessions when the user logs in and out.
class SessionsController < ApplicationController
def create
# create user if new user, or find user account if returning user.
session[:user_id] = user.id
redirect_to root_url # or wherever
end
def destroy
session[:user_id] = nil
redirect_to root_url # or wherever
end
end
Then in the Application Controller,
class ApplicationController < ActionController::Base
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
You can then easily determine if a user is logged in or not via if current_user, or equivalently, if session[:user_id] is nil.
IP could be deceptive. Try cookies.
Edit: not only in an actively deceptive manner (i.e. spoofing/Tor) but rather if two people are on separate sites from the same public IP, then you have a false correlation.

Access user properties through a helper method in 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.

Rails: where does the infamous "current_user" come from?

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

Resources