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!
Related
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).
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.
I am trying to require login on all pages on my Rails 4 web site.
In the ApplicationController I have added before_action :authenticate_user!, but it simply doesn't do anything. I have tried to add the same before_action :authenticate_user! to another controller, and it works fine.
Do I need to do something else to the ApplicationController, to make the login be required on all actions (Except signup/signin)?
Here's the actual code we use:
#app/controllers/application_controller.rb
Class ApplicationController < ActionController::Base
#Actions
before_action :authenticate_user! #-> routes to the login / signup if not authenticated
end
The problem you probably have is two-fold:
--
Make sure your other controllers are inheriting from application_controller:
#app/controllers/other_controller.rb
Class OtherController < ApplicationController
...
end
--
You're somehow "skipping" the before_action callback
If you're using skip_before_action anywhere, you need to remove it. It will likely cause a problem with your authenticate_user! method. To fix this, I would firstly test without any skip_before_action callbacks, and then see what gets it working correctly
A further note - signin / signup won't matter. They all inherit from the Devise controllers; they'll just run as required.
UPDATED 2019
In your routes.rb file you may have mentioned only authenticated_user path like below
authenticated :user do
root to: 'home#index', as: :root_app
end
You should mention unauthenticated_user path too to make it work or just root path without unauthenticated_user or authenticated_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.
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