I have my regular ApplicationController class & I have a Admin::ApplicationController class. The problem is that Admin::ApplicationController doesn't seem to be getting loaded or executed or anything. Am I not allowed to have a namespaced application controller? The reasoning for wanting to have it is so that I can check if a user is an admin w/ CanCan & redirect them out if they're not.
Call this controller Admin::BaseController, as it is more to act as the base of the namespace than to do anything for the appilcation. For it to do what you want to do, you will need to make all admin namespaced controllers inherit from this controller.
The only times I've seen namespacing like that is when the controller is nested in a subfolder. So Admin::ApplicationController would expect to be in controllers/admin/application_controller.rb
One possible solution:
If you want everything except your home page to kick them out, simply set a before_filter on your application controller with an exception for home/index like this:
ApplicationController.rb
before_filter :authorize_admin
def authorize_admin
//dostuff
end
HomeController.rb
skip_before_filter :authorize_admin, :only => ['index']
Where index is your action that you want to skip. Leave off the only to skip the filter for the whole controller.
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]
In application_controller.rb I have the following:
before_filter :get_events
before_filter :get_sitemap
def login_required
return true if session[:user]
# If we get to this point, I want to avoid running get_events and get_sitemap
flash[:warning] = 'Please login to continue'
redirect_to login_users_url
return false
end
And in other controllers I have for example:
before_filter :login_required, :except => [:show]
So essentially I have methods running that access the database for all pages except I don't want it to happen when the user is required to log in (i.e., being redirected from a page where normally get_events and get_sitemap are required).
I realize one way of doing this is to run login_required as a before_filter on ALL controllers before any other before_filters are set and then exclude certain controller models but I'd like to know if there's a way of doing it without having to change all my controllers.
For something like this, I usually create an AuthenticatedController (in app/controllers/authenticated_controller.rb):
class AuthenticatedController < ApplicationController
before_filter :require_login
end
Then I derive all controllers requiring authentication from that, specifying a skip_before_filter :require_login for actions I want to exclude on a controller-by-controller basis. For example:
class PlanetsController < AuthenticatedController
skip_before_filter :require_login, only: [:index]
end
This will require you to change all controllers, but only for the purpose of deriving from AuthenticatedController. As far as I know this is a perfectly acceptable way of handling it, and I don't think there's a way outside of monkey patching ActionController::Base to make this apply to all controllers, which is a pretty bad idea for a variety of reasons.
Well after considering Joshua's answer I still couldn't get what I wanted to go for...so what I did was do a little hybrid solution. All controllers still reference ApplicationController, but within ApplicationController I always run the before_filter :login_required FIRST before other before_filters. Redirects in rails seem to stop other before_filters from running, which is what I wanted. In the other controllers I use the skip_before_filter :login_required, :only => [:this_time] wherever necessary.
I'm using CanCan for permissions in my Rails application in which I have built my own engine for some generic form functionality. I would like to lock down permissions in my system so that users cannot freely access my engine controllers' actions. These controllers, for the most part, just make use of the 7 REST actions, so I would like to use CanCan's load_and_authorize_resource at the top of each controller.
However, when I write my code like this:
module MyEngine
class FormController < ApplicationController
load_and_authorize_resource
...
end
end
I get this error:
uninitialized constant Form
My guess is that the automatic loader in load_and_authorize_resource is tied to my MainApp namespace, and does not recognize that I am calling it in a different namespace, and so does a call like Form.find(params[:id]) rather than MyEngine::Form.find(params[:id]).
If this is the case, how can I fix this? It's not a huge deal because authorize! still works properly, so I can define the authorization in each action individually, but it would feel much cleaner if I were able to use the load_and_authorize_resource method.
CanCan can not find namespaced models. Try to specify class:
load_and_authorize_resource class: MyEngine::Form
It seems to be a bug in CanCan::ControllerResource#namespace:
def namespace
#params[:controller].split("::")[0..-2]
end
As you see, it tries to split controller path by :: but it comes in the form of my_engine/my_controller.
So the fix is dumb simple:
def namespace
#params[:controller].split("/")[0..-2]
end
Wonder how they could miss such a stupid bug for so long. Shall send them a pull request.
P.S. Have just signed up to answer 8)
If the model class is namespaced differently than the controller, you will need to specify the :class option.
module MyEngine
class FormController < ApplicationController
load_and_authorize_resource :class => MyEngine::Form
...
end
end
I am attempting to use active admin with a project. This project also uses another gem to separate out different tenants, as well as has_secure_password for normal authentication.
I am having an issue skipping both of these filters when the user goes to active admin (just a different namespace - admin).
class ApplicationController < ActionController::Base
force_ssl
helper :all
protect_from_forgery
set_current_tenant_by_subdomain(:account, :subdomain) # need to skip this call when in the admin namespace
before_filter :require_user # need to skip this call when in the admin namespace
end
Thanks for your help!
You could create a BaseController that includes set_current_tenant_by_subdomain and before_filter :require_user and have your non-admin controllers inherit from that, while your admin controller inherits directly from the ApplicationController. That's worked for me in the past.
in config/initializers/active_admin.rb you can add the line:
config.skip_before_filter :offending_filter
sadly this does everything except the dashboard controller... for that you need
controller do
skip_before_filter :offending_filter
end
in app/admin/dashboard.rb
Per the Agile Development book, I have an Admin MVC that controls how users log in. In ApplicationController, I have a before_filter that checks for authorization. So, this will check that the user has logged in for every page.
The problem is that I want everyone to be able to access the new method, for example, in Users (that is, anyone should be able to create a new user -- naturally! Only admin users should have access to the other methods in UsersController such as edit, etc.). What's the best way to do that?
You can either of this
before_filter :except=>[:method_name] #methods you want to skip filter
OR
before_filter :only=>[:method_name] #methods you want to be filtered before called.
EDITED
before_filter :filter_method, :except=>[:method_name] #methods you want to skip filter
OR
before_filter :filter_method, :only=>[:method_name] #methods you want to be filtered before called.
You can use the skip_before_filter method in child controller classes to skip the default filter processing. For example:
class UsersController < ApplicationController
skip_before_filter :authorize, :only => [:new, :create]
end
—Will skip the before filter named :authorize only for the new and create actions within the users controller i.e. the filter will still get applied for all other actions.
I would also suggest using CanCan gem for authorization as it has a really simple and clean way to define authorization rules.
http://github.com/ryanb/cancan