I have a login page saving a session to allow users to navigate subsequent pages. If you're not logged, I want to redirect you to the log in page. I have a SessionsHelper method for checking if the user is logged in and then if not, redirecting them back to the login page, But I don't want to have to call this in every controller action. Is there a way to easily run this method globally?
Traditionally this is done via a before_action filter. Something along these lines:
class ApplicationController
before_action :require_current_user
def require_current_user
redirect_to login_path unless current_user
end
end
class SessionsController < ApplicationController
# do not cause endless redirect loop
skip_before_action :require_current_user, only: [:new, :create]
end
Also, helpers are for simplifying views (currency formatting, styling, etc.). They are not to be used for this kind of functionality (session management, in this case).
Related
I'm using Devise for authentication in a Rails 6 app. Once a user logs in, I'd like to conditionally check that they have completed onboarding before allowing them to visit any and all authenticated routes. If they haven't, then they should be redirected back through the onboarding flow. Something like this:
unless current_user.has_completed_onboarding
redirect_to '/onboarding'
end
I have about a dozen routes where I want to implement this logic. What's the best way to add this check before each request to an authenticated route without duplicating it within each controller? Thanks!
what you can do in this type of case is write a method in the ApplicationController. Suppose check_user_onboarding
def check_user_onboarding
return redirect_to '/onboarding' unless current_user.has_completed_onboarding
end
Then in the controllers you can check by adding a before_action callback. The below one will check all the methods of that controller.
before_action :check_user_onboarding
You can also specify the methods that needs to be checked like -
before_action :check_user_onboarding, only: [:method1, :method2]
or there is also except
before_action :check_user_onboarding, except: [:method1, :method2]
I have a Ruby on Rails web application, where a user needs to provide his nickname and password to register. After successful registration, the user is redirected to an edit page, where he should enter his email.
The use case requires to always redirect the user to the edit page until he has submitted a valid email address. However, in the current state, the user can click on any menu item and is then taken to the corresponding page. How do I prevent this behavior and redirect the user back to the edit page when clicking on a menu link?
You can create a before_action in your ApplicationController which checks if the user is logged in and has submitted his email, something like this:
class ApplicationController < ActionController::Base
before_action :validate_email_confirmed
def validate_email_confirmed
return unless current_user
redirect_to user_edit_path unless current_user.email?
end
end
Keep in mind that you have to skip this before_action for both the user edit and update actions, or you'll end up having a redirect loop. This is how to skip an existing before_action for specific actions:
class UsersController < ApplicationController
skip_before_action :validate_email_confirmed, only: [:edit, :update]
end
Did some more digging, and found this on the official docs, seems to fit your needs:
class ApplicationController < ActionController::Base
before_action LoginFilter
end
class LoginFilter
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to access this section"
controller.redirect_to controller.new_login_url
end
end
end
You'd of course have to rework this some, to get awaiting_email or such, but the principle is the same.
I'm trying to set up something that allows users to go to certain urls only under certain circumstances. Right now I have a setEvent/:id url that sets a property on users to an event_id, then redirects the user to the event url. The user can access a url like .../whatever/event/1 where 1 needs to equal the event_id, and if it doesn't it redirects the user.
However, this doesn't stop someone from just typing .../whatever/setEvent/:id into their address bar to get access to the page.
The proper way to do this is with a before action in your controllers. Here is an example from one of my apps where a user who is not logged in will always be redirected to the new_session URL.
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?, :herd_user
def herd_user
redirect_to new_session_url unless logged_in?
end
... other medthods...
end
and
class StaticPagesController < ApplicationController
before_action :herd_user
def index
end
end
without bringing in more gems you can just do a before_action
before_action :enforce_tenancy, except: [:index]
before_action :allow_only_admin, only: [:index]
private
def enforce_tenancy
render unauthorized unless event.user_id == current_user.id
end
def allow_only_admin
render no_way_sucka_path unless current_user.admin?
end
I had a similar problem and this might not be the best way to handle it but in the action for that page you can check the current url and check the property then redirect to the one they can access if they go to an incorrect url.
Something kind of like:
url_id = request.fullpath.sub('/whatever/event/', '')
redirect_to user_page_path(user.id) unless (current_user.event_id.to_s == url_id)
Sorry if the code isn't great I tried to write it based off of the info you gave.
Edit* Make sure to do this before getting any info for the page from your database or it will be less efficient.
So i've built my first app using Devise! I'm pretty stoked, but I would like to know how one goes about having the app re-direct to a specific page after logging in?
In other words,
Instead of logging in, and remaining at the home page, how do I get rails to redirect to a microposts page for example?
In my case specifically it only redirects to the posts page sometimes, and other times it just stays at the initial home page.
Here is my posts controller:
class PostsController < ApplicationController
before_filter :authenticate_user!, :except => [:show, :index]
def posts
#title = "Posts"
end
end
By default, devise redirects you to the root, you can customize after_sign_in_path_for method anyway you like. There's also after_sign_out_path_for method at your disposal to customize.
ApplicationController < ActionController::Base
# extra stuff
def after_sign_in_path_for(user)
if something
posts_path
else
root_path
end
end
def after_sign_out_path_for(user)
new_some_other_path
end
end
I'd like /something to only be accessible for logged in users, I have a current_user helper which returns a user id or nil if the current visitor is not logged in.
Where would be the best place to limit access to /something in the controller or can it be added as part of the routes?
You must add in controller :before_filter and create action for that.
:before_filter :authenticate
def authenticate
redirect_to(registration_path) unless current_user.nil?
end
Also you can use :only or :except filter options.
Or i did not understant question?
You should handle that in your controller. Routes decide where things go and then it is up to the controller to decide if you're allowed to go there.
You should have a general purpose authenticate method in your ApplicationController that checks if someone is logged in and redirects them to a login page if they're not. Then in your specific controller:
class SomethingController < ApplicationController
before_filter :authenticate
def handler
#...
end
end
You can skip authentication for a specific handling with the :except option:
before_filter :authenticate, :except => [ :this_one, :and_this_one ]
There are other options as well, see the filters section of the Action Controller Overview for details.