I'm getting a redirect loop. I have a clear idea why, user is logged out, redirected to login page (welcome#index) and user is still logged out and we have an endless loop.
How do I get out of loop?
I read about several options.
before_action :require_login placing it inside controllers where login is required. EASY, but a lot of copy paste, we love dry don't' we?
except, before_action :require_login, :except => root? I couldn't find details about except. I'm getting a lot of hits on before_filter which seems to be deprecated.
skip_before_action same here, I can only find bits and pieces :(
There should be a better way to handle these, is it rails way to do check routes level in config/routes.rb?
Application 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
helper_method :current_user
before_action :require_login
private
def current_user
#current_user ||= Dedit::User.find(session[:user_id]) if session[:user_id]
end
private
def require_login
redirect_to root_path unless current_user.present?
end
end
login page controller:
class WelcomeController < ApplicationController
layout 'basic'
def index
if current_user.present? then redirect_to dedit_path end
end
end
before_action :require_login, except: [:index]
Related
I'm using Devise + ominauth2 to allow users to sign in via different services. If they sign in they get a slightly better experience but if not life still goes on. However I don't see a way with Devise to have a page that allows both authenticated and unauthenticated access.
For example:
class SomeController < ApplicationController
before_action :authenticate_user!
def some_action
end
end
In the above controller, some_action is only accessible if the user authenticates. But what I actually want is something more like:
class SomeController < ApplicationController
def some_action
loadSessionFromCookieIfExists
end
end
Then, I can use user_signed_in? in my views. But this doesn't work because authenticate_user! will redirect them elsewhere. Unless there is an authenticate_if_possible method?
So what I have now, which is really gross is:
class SomeController < ApplicationController
before_action :authenticate_user!
skip_before_action :authenticate_user!, :only=>[:no_auth]
def action
do_stuff
return
end
def no_auth
do_stuff
return
end
private
def do_stuff
render :template => "action"
end
end
I should add, that omniauth2 doesn't support rememberable out of the gate so it's possible this is all supposed to work just fine and the problem is with omniauth2.
I want one pages of my ruby on rails web application inaccessible to one of my STI model types. I have two models typeA and typeB inheriting from User. I have used the column type in the User table to implement STI. I am using Devise gem for User sessions. I want one webpage 'http://localhost:3000/rate' inaccessible to my typeA User. Whenever an User logs in who is of the type 'typeA', he does not have the option of seeing the link 'Rate'. But I also do not want him to be able to access that page by the link 'http://localhost:3000/rate'. If he tries to access it through that link, I want to sign him out and make him log in again.
I managed this by using a piece of code in my Controller with the specific method for 'rate'.
def rate
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
else
#Code for User of typeB
end
end
This is working but I wanted to know if this can be done in a better way using before_filter :authenticate_user! or something else
Right now my before_filter part looks like this
before_filter :authenticate_user!, except: [:index, :show]
Is there any way I can make a change to the upper code to achieve that functionality.
P.S: Maybe this can be done better if I had used roles or other gems like CanCan/Pundit but I do not have much time left to submit my project, so I do not want to get into all that right now.
you can add another before_filter on the controller you want to restrict the access just to confirm your STI user type without overiding devise's authenticate_user! filter.
application_controller.rb
class ApplicationController < ActionController::Base
def confirm_user_type(user_type)
redirect_to new_user_session_path unless current_user.is_a?(user_type)
end
end
pages_controller.rb
class PagesController < ApplicationController
# must be authenticated to access
before_filter :authenticate_user!
# must be user of TypeA to access
before_filter { |c| c.confirm_user_type(TypeA) }
def rate
...
end
end
Then, you can use the same filter before_filter { |c| c.confirm_user_type(TypeB) } for STI user type: 'TypeB'
Try this:
class ApplicationController
before_action :authenticate_user!
def authorize_user!
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
end
end
end
with your controller:
class SomeController < ApplicationController
before_action :authorize_user!, except: [:index, :show]
def top_secret
...
end
end
I believe if a before_action (the new name for before_filter) renders or redirects, the action won't be processed.
I am setting up a very simple rails app that involves a simple authentication check before you can enter the site. But, when the before_filter runs, and the user is redirected to the login path, a redirect loop occurs.
ApplicationController:
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_filter :check_session
private
def check_session
redirect_to login_path and return unless current_user?
end
def current_user?
!session[:username].blank?
end
end
SessionsController
class SessionsController < ApplicationController
def new
end
end
The issue is that, since your SessionsController inherits from ApplicationController, it actually inherits the before_filter as well. This means you are not allowing someone to see the login page unless they are logged in, which is usually undesirable behavior. You want to skip this before_filter on the login page:
class SessionsController < ApplicationController
skip_before_filter :check_session, only: :new
def new
end
end
I think you have problem in your routes. One way of solution is defining a root path for your app as:
root 'app_entry#index'
Then create a controller for it as given below:
class AppEntryController < ApplicationController
def index
if current_user
redirect_to 'controller_name/action_name'
else
redirect_to '/users/sign_in'
end
end
Hope this helps you.
You should use the before filter like this
before_filter :check_session, :except => [:new]
Currently, my ApplicationController looks like this:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
include CurrentUser
protect_from_forgery with: :exception
before_action :authorize
before_action :get_current_user
protected
def authorize
unless User.find_by(id: session[:user_id])
redirect_to login_url, notice: "Please log in"
end
end
end
As you can see, authorize checks to make sure a user is login, and if not - redirects them to the login page. This works perfectly.
On the "front facing" pages, where users don't have to be logged in, I do this:
skip_before_action :authorize, only: [:names, :of, :ok, :pages]
Now, I have added some pages where elevated privileges are required, so that method would (I think) look something like this:
def check_for_elevated_role
u = User.find(id: session[:user_id])
unless u.role >= 10
redirect_to some_url, notice: "Insufficient Role"
end
end
My question is, what is the best way to implement this? Should I simply add this as a before action to the pages I want to lock down, or (Like I did with authorize), add this to the ApplicationController and skip this on pages where users don't need privileges (that's the safe way, right?). Whatever the case, can you show me what the before_actions should look like?
I'm adding this as an answer as it's a little long as a comment.
You most probably want to create a different controller with that before_action and inherit from that controller
class AuthorizeController < ApplicationController
before_action :authorize_admin
protected
def authorize_admin
# your logic here
end
end
class ControllerThatNeedsAuthorizationController < AuthorizeController
skip_before_action :action_that_doesnt_need_authorization
end
I've set a fake user to demo my app. The idea is that visitors, who won't be signing in, should still be able to see all the functionality that a real user would have. When a non-logged-in visitor hits the site, the app will sign in the demo user and visitors will see fake data belonging to this demo user.
With this goal in mind, I setup my application controller like this.
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :set_current_user
def demo_user
#demo_user ||= User.find_by_email("demo#example.com")
end
protected
def set_current_user
if current_user.nil?
sign_in(demo_user)
end
end
end
My problem is that I accidentally made it impossible for real users to sign in. Anytime a real user presses the "sign in" link, they're told that they're already signed in (as the demo user).
So clearly, what I've done is nowhere close to a "best practice." What would a smart programmer do in this situation? How do I keep my nifty automatically signed in demo user but still leave the door open to real users to sign in?
You need to add an :except to your :before_filter so that it doesn't run on the sign_in action, whatever it may be. Assuming you're using devise, that would be SessionsController#new, so it would look like:
before_filter :set_current_user, :except => :new
Note that this will skip the filter for all 'new' actions, so a more targetted way of doing it would be (again, assuming you are using Devise) to create a custom SessionsController which inherits from Devise::SessionsController and leave it blank except for:
skip_before_filter :set_current_user, :only => [:new, create]
Your set_current_user function is in a before_filter which means it runs once for every request. The current_user will be nil the first time anyone visits and so they will be signed in as the demo user. You can skip the before_filter for your sessions_controller#create action (or whatever it's called in your app. For example, if you are using Devise:
class SessionsController < Devise::SessionsController
skip_before_filter :set_current_user, :only => :create
end
Here's what I ended up with. I started off with Omnikron's solution, but I needed a little more.
This post helped: https://groups.google.com/forum/#!msg/plataformatec-devise/0WylcwjSAJY/ITDF6kFjJvwJ.
class SessionsController < Devise::SessionsController
skip_before_filter :set_current_user, only: [:new, :create]
skip_before_filter :require_no_authentication, :only => [:new, :create]
def new
if user_signed_in?
sign_out current_user
redirect_to new_user_session_path
else
super
end
end
end