I want to execute some function before any controller action to check if the user is signed in. I am using devise so that I can use is_signed_in?, but I have to put if else condition to every method in the controller.
What I want is to have something like this:
#some_controller.rb
before_action :is_signed_in?
def is_signed_in?
if !user_signed_in?
redirect_to new_user_session_path
else
..proceed to the action intended to call
end
end
So I want this method to execute before any action (or some set of actions) and redirect to sign in if false or let that action to be executed if true.
Devise is shipped with some useful built-in helpers.
In your case, one that interested you is authenticate_user!. Take a look at controller filters and helpers in Devise documentation.
You can filter your actions in your controller with this method to ensure only logged-in users can process a given action or all actions in a controller, else if user isn't logged-in then he is redirect to login page.
before_action :authenticate_user!
before_action :authenticate_user!, only: [:show]
You can also create your own helper method.
In your users_controller.rb, create a before_action filter
class UsersController < ApplicationController
before_action :logged_in_user
...
end
and in your session_helper.rb
module SessionHelper
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
If you want to check whether user is signed for every action in the application, you have to put the filter in the application controller. You can do this for a specific controller also.
You can use the devise method:
class SomeController < ApplicationController
before_action :authenticate_user!
...
end
You can create your own filter also:
class SomeController < ApplicationController
before_action :my_authentication
...
def my_authentication
if user_signed_in?
# do something ...
else
# do something else ...
end
end
end
Are you using devise? You can use existing filter:
class SomeController < ApplicationController
before_filter :authenticate_user!
...
end
if not, create your filter in application controller and add it to your needed controllers:
class SomeController < ApplicationController
before_filter :my_auth_filter
...
end
You can add this method to your ApplicationController
def user_is_logged_in
if !session[:current_user]
redirect_to login_path
end
end
Use it before invoking any actions. Like so,
class AdminsController < ApplicationController
before_action :user_is_logged_in
...
end
Related
I am using the idiom described in https://guides.rubyonrails.org/v2.3/action_controller_overview.html#other-ways-to-use-filters
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter do |controller|
redirect_to new_login_url unless controller.send(:logged_in?)
end
end
Now if the signing in process is successful, how
can I examine if it was, and b)
how can I re-redirect the user to the requested controller action?
How do I do this login-process via AJAX and JSON ?
EDIT: Also I get the following Error Message
uninitialized constant ApplicationController::LoginFilter
When I use the more elaborate solution suggested in 6.2 Other Ways to Use Filters instead of the one above such that my Controller looks like this
# /app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_action :set_return_path, LoginFilter
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
class LoginFilter
def self.filter(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in"
controller.redirect_to controller.new_login_url
end
end
end
end
Thanks
von Spotz
You can add a before_action in the application_controller.rb where you save the requested page url:
class ApplicationController < ActionController::Base
before_action :set_return_path
def set_return_path
return if devise_controller?
session['user_return_to'] = request.url unless current_user
end
end
And then redirect the user to this url after the successful sign-in:
class SessionsController < Devise::SessionsController
def after_sign_in_path_for(resource)
return root_url if session['user_return_to'].blank?
session['user_return_to']
end
end
I've got a situation where User after successfully Sign up process (I'm using Devise gem) will have access only for two controllers until he passes all validation process. To check if validation process is in progress I'm using status column. If status == '' means validation is not finished.
I'm wondering how to implement such an access to only two pages? my first thought was Pundit gem but if I have 50 controllers I would need to implement 50 policies, inside of which I'll have numerous methods correspondent to controller actions. Is there any better way to do so?
[EDIT]
This is what I have so far:
class BaseController < ApplicationController
before_action :authorized_user
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def authorized_user
policy_scope(current_user)
end
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to(request.referrer || root_path)
end
end
User policy:
class UserPolicy < ApplicationPolicy
class Scope < Scope
def resolve
return raise(Pundit::NotAuthorizedError) unless user.status == 'active'
scope.all
end
end
end
This Answer only works if your controllers inherit from ApplicationController.
Add a before_action to your ApplicationController which does something when the user is not validated. This triggers now on all controller actions.
class ApplicationController < ActionController::Base
before_action :require_validated
private
def require_validated
redirect_to somewhere_url unless current_user.status == 'validated'
end
end
To except specific controller you can skip_before_action
class SomeController < ApplicationController
skip_before_action :require_validated
# skip only for specific actions
skip_before_action :require_validated, only: [:new, :create]
end
Thanks to the suggestions in the comments from #tadam I ended up with below code:
#intermediate controller class
class BaseController < ApplicationController
before_action :authorized_user
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def authorized_user
authorize :global, :only_active?
end
private
def user_not_authorized
flash[:alert] = 'You are not authorized to perform this action.'
redirect_to root_path
end
end
Global policy used in BaseController
class GlobalPolicy < ApplicationPolicy
def only_active?
active?
end
end
I'm fairly new to Rails and am building a webapp to learn. I currently have the webapp working to accept new users and log them in. However, I want to block access to pages other than the login page when users aren't logged in. I've searched for hours and nothing I have found has worked. I don't want to use to Devise. Here is my code:
Application controller:
class ApplicationController < ActionController::Base
protect_from_forgery
include SessionsHelper
before_action :logged_in_user
end
Session Helper:
module SessionsHelper
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def logged_in?
!current_user.nil?
end
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to 'name of login page'
end
end
end
Currently, the page just reloads over and over (because the user isn't logged in) and the page times out. What am I doing wrong?
when you use before_action in the ApplicationController the specified action will run before any action in all controller that inherits ApplicationController unless you skip it by skip_before_action.
In your case you need either to apply before_action in ApplicationController and use skip_before_action before login action in for example in the SessionsController or you can only use before_action in all other controllers instead of ApplicationController to specify when you need them to run.
class SessionsController
skip_before_action :logged_in_user, only: :login
def login
end
end
Also remember to handle when a user is already logged in and he tries
to visit the login page.
In Rails 4, I use before_action :require_login for my UserController. This is supposed to redirect if the user is not logged in. However the filter doesn't seem to be used. Why?
require_login is defined in app/helpers/sessions_helper.rb. So it is not a helper for my controller UsersController.
How does UserController know to use the function from sessions_helper.rb? It seems like I should be more specific: before_action "session/require_login".
The controller in users_controller.rb:
class UsersController < ApplicationController
before_action :signed_in_user
def index
end
end
The helper function in sessions_helper.rb:
def require_login
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
end
You're following Michael Hartl's tutorial? Helpers in /app/helpers designed for view logic, not for controller code.
However, to get the helper included into your controllers you can use include SessionsHelper.
Reference: http://ruby.railstutorial.org/chapters/sign-in-sign-out#code-sessions_helper_include
before_filter can only call controller method, not helper.
You need to move the helper require_login from helper to UsesController or its parent say ApplicationController.
If you still want to use require_login as a helper in view, you can expose it from controller by helper_method
class UsersController < ApplicationController
before_fitler :require_login
helper_method :require_login
def require_login
# code
end
end
I have a controller and every method of it starts with the following code:
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
return
end
I'm just wondering if it is possible to avoid code duplication in this case ?
Yes, use a before_filter
class YourController < ApplicationController
before_filter :check_user
def check_user
..
end
end
Absolutely.
class MyController < ApplicationController
before_filter :ensure_logged_in
# actions here.
def ensure_logged_in
#user = UserData.find_by_login(session[:cuser])
if #user == nil
redirect_to(:controller=> 'user_data', :action=> 'login')
end
end
end
You shouldn't need to worry about the 'return', as rails will bail out of the filter pipeline once the redirect happens.
To avoid duplication you just need to add before_filter in every controller where you want to check user authentication.
class SomeController < ApplicationController
before_filter :authenticate_user
end
then add your user authentication logic in application controller something like this,
class ApplicationController < ActionController::Base
private
def current_user
#current_user ||= UserData.find_by_login(session[:cuser]) if session[:cuser]
end
helper_method :current_user
def authenticate_user
redirect_to({:controller=> 'user_data', :action=> 'login'}, :alert => "Not authorized") if current_user.nil?
end
end
You can use current_user helper method in every controller to get current user.
Try to use before filter. This should be fine