I'm using devise for Authentication in a Rails 4 app. Almost all of the app requires a user to be authenticated, however, there are a few 'public' pages (Terms and conditions - that kind of stuff).
Devise itself makes the sign up and sign in pages public, using prepend_before_filter :require_no_authentication. However, that method is intended only for devise controllers. And devise doesn't seem to provide a 'proper' way to make single actions public.
So, what's the best way to make just a couple of actions public?
The only ways I can think of so far is:
To make a new public namespace, and have separate controllers for public actions, which inherit from a PublicController that does not execute before_filter :authenticate_user!
Same as above, but ditch the inheritance and the namespace. So the PublicController could act as a bucket for anything that needs to be public - which at this stage isn't much.
Is there a better way to make individual actions public with devise?
You can optionally use skip_before_action for the public controllers. Example from guides:
class LoginsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
In your controller, you want to have:
before_action :authenticate_user!, :except => [:index]
This will perform the :authenticate_user! method on all actions except index action.
You can find more explanation in this answer.
Related
I have before_action :authenticate_user! in the application controller, which is very efficient because it applies to all controllers but there's one page (home page) that should be available without login.
Is there a way to keep before_action :authenticate_user! in application_controller.rb (for the sake of DRY) and make an exception to allow access to just home#home to be available without authentication?
Notes:
we could obviously remove before_action :authenticate_user! from the application controller and place it in all controllers but for 'home#home' (the downside to this is it's not very DRY)
just a side note, but I'm not exactly sure why the /users/sign_in url is even accessible (something in devise must make it so, but I'm not sure where that mechanic lives or what it is exactly - perhaps if I found it I could add more routes to it?)
You want to skip the before_action using skip_before_action
In the controller for that page put
skip_before_action :authenticate_user!, only: [:home]
This specifies it just for that one action, if all the pages in that controller need to be skipped you can just use
skip_before_action :authenticate_user!
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 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.
I have a Rails 3 App.
When the user is not signed in... I want devise to show non-signed in pages: SignIn, register, about us, blog etc...
When a user is signed in I want it to go to the web app
where do I make this switch and how do I set it up? thanks
This is easy! I just finished a Rails 3 app with devise, so my pain can be your gain. Just include the before_filter at the beginning of the controllers you want to protect. Let's use the example of a Videos controller:
class VideosController < ApplicationController
before_filter :authenticate_user!
# all your actions go here: index, new, create, etc #
end
You can also pick and choose what actions in the controller are filtered:
class VideosController < ApplicationController
before_filter :authenticate_user!, :only => [:edit, :update, :destroy]
# all your actions go here: index, new, create, etc #
end
Devise gives you the authenticate_user! method, and redirects for you.
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