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
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 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
I have some controller and in each method of each controller I have next code:
#user = session[:user]
Is there a way to avoid putting this code on every method of each controller?
You can add your code in ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :current_user
def current_user
#user = session[:user]
end
end
The anwer of #nash is fine, and here is an alternative providing helper methods you can use in every method/view. This is the way gems like Devise go:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user
helper_method :user_signed_in?
private
def current_user
#current_user ||= User.find_by_id(session[:user_id]) if session[:user_id]
end
def user_signed_in?
return 1 if current_user
end
def authenticate_user!
if !current_user
flash[:error] = 'You need to sign in before accessing this page!'
redirect_to signin_services_path
end
end
end
I am troubleshooting why my ApplicationController's methods don't seem to work in my namespaced admin area and it seems like when I'm in a namespace I cannot access my ApplicationController's private methods, is this right?
If it is like that, what's the best practise to reuse something like Authlogic's example ApplicationController methods in my namespaced controller? I could easily copy and paste the methods to an AdminController or something, and I could also un-private these methods - but this doesn't seem like such a good way to do it.
Here's what the example ApplicationController from Authlogic (and mine) looks like:
class ApplicationController < ActionController::Base
protect_from_forgery
helper_method :current_user_session, :current_user
private
def current_user_session
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def require_user
unless current_user
store_location
flash[:notice] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
# and some more methods here...
end
And that's how I inherit from it in my namespace:
class Admin::DashboardController < ApplicationController
layout 'administration'
require_user # fails
def index
end
end
Thanks for your help,
Arne
You should use before_filter in Admin::DashboardController:
class Admin::DashboardController < ApplicationController
layout 'administration'
before_filter :require_user
def index
end
end
I haven't used authlogic, but maybe you need to change
require_user
to
before_filter :require_user