Currently, my ApplicationController looks like this:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
include CurrentUser
protect_from_forgery with: :exception
before_action :authorize
before_action :get_current_user
protected
def authorize
unless User.find_by(id: session[:user_id])
redirect_to login_url, notice: "Please log in"
end
end
end
As you can see, authorize checks to make sure a user is login, and if not - redirects them to the login page. This works perfectly.
On the "front facing" pages, where users don't have to be logged in, I do this:
skip_before_action :authorize, only: [:names, :of, :ok, :pages]
Now, I have added some pages where elevated privileges are required, so that method would (I think) look something like this:
def check_for_elevated_role
u = User.find(id: session[:user_id])
unless u.role >= 10
redirect_to some_url, notice: "Insufficient Role"
end
end
My question is, what is the best way to implement this? Should I simply add this as a before action to the pages I want to lock down, or (Like I did with authorize), add this to the ApplicationController and skip this on pages where users don't need privileges (that's the safe way, right?). Whatever the case, can you show me what the before_actions should look like?
I'm adding this as an answer as it's a little long as a comment.
You most probably want to create a different controller with that before_action and inherit from that controller
class AuthorizeController < ApplicationController
before_action :authorize_admin
protected
def authorize_admin
# your logic here
end
end
class ControllerThatNeedsAuthorizationController < AuthorizeController
skip_before_action :action_that_doesnt_need_authorization
end
Related
I'm using Devise + ominauth2 to allow users to sign in via different services. If they sign in they get a slightly better experience but if not life still goes on. However I don't see a way with Devise to have a page that allows both authenticated and unauthenticated access.
For example:
class SomeController < ApplicationController
before_action :authenticate_user!
def some_action
end
end
In the above controller, some_action is only accessible if the user authenticates. But what I actually want is something more like:
class SomeController < ApplicationController
def some_action
loadSessionFromCookieIfExists
end
end
Then, I can use user_signed_in? in my views. But this doesn't work because authenticate_user! will redirect them elsewhere. Unless there is an authenticate_if_possible method?
So what I have now, which is really gross is:
class SomeController < ApplicationController
before_action :authenticate_user!
skip_before_action :authenticate_user!, :only=>[:no_auth]
def action
do_stuff
return
end
def no_auth
do_stuff
return
end
private
def do_stuff
render :template => "action"
end
end
I should add, that omniauth2 doesn't support rememberable out of the gate so it's possible this is all supposed to work just fine and the problem is with omniauth2.
I want one pages of my ruby on rails web application inaccessible to one of my STI model types. I have two models typeA and typeB inheriting from User. I have used the column type in the User table to implement STI. I am using Devise gem for User sessions. I want one webpage 'http://localhost:3000/rate' inaccessible to my typeA User. Whenever an User logs in who is of the type 'typeA', he does not have the option of seeing the link 'Rate'. But I also do not want him to be able to access that page by the link 'http://localhost:3000/rate'. If he tries to access it through that link, I want to sign him out and make him log in again.
I managed this by using a piece of code in my Controller with the specific method for 'rate'.
def rate
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
else
#Code for User of typeB
end
end
This is working but I wanted to know if this can be done in a better way using before_filter :authenticate_user! or something else
Right now my before_filter part looks like this
before_filter :authenticate_user!, except: [:index, :show]
Is there any way I can make a change to the upper code to achieve that functionality.
P.S: Maybe this can be done better if I had used roles or other gems like CanCan/Pundit but I do not have much time left to submit my project, so I do not want to get into all that right now.
you can add another before_filter on the controller you want to restrict the access just to confirm your STI user type without overiding devise's authenticate_user! filter.
application_controller.rb
class ApplicationController < ActionController::Base
def confirm_user_type(user_type)
redirect_to new_user_session_path unless current_user.is_a?(user_type)
end
end
pages_controller.rb
class PagesController < ApplicationController
# must be authenticated to access
before_filter :authenticate_user!
# must be user of TypeA to access
before_filter { |c| c.confirm_user_type(TypeA) }
def rate
...
end
end
Then, you can use the same filter before_filter { |c| c.confirm_user_type(TypeB) } for STI user type: 'TypeB'
Try this:
class ApplicationController
before_action :authenticate_user!
def authorize_user!
if current_user.type == "typeA"
sign_out(current_user)
redirect_to new_user_session_path
end
end
end
with your controller:
class SomeController < ApplicationController
before_action :authorize_user!, except: [:index, :show]
def top_secret
...
end
end
I believe if a before_action (the new name for before_filter) renders or redirects, the action won't be processed.
I have a Ruby on Rails web application, where a user needs to provide his nickname and password to register. After successful registration, the user is redirected to an edit page, where he should enter his email.
The use case requires to always redirect the user to the edit page until he has submitted a valid email address. However, in the current state, the user can click on any menu item and is then taken to the corresponding page. How do I prevent this behavior and redirect the user back to the edit page when clicking on a menu link?
You can create a before_action in your ApplicationController which checks if the user is logged in and has submitted his email, something like this:
class ApplicationController < ActionController::Base
before_action :validate_email_confirmed
def validate_email_confirmed
return unless current_user
redirect_to user_edit_path unless current_user.email?
end
end
Keep in mind that you have to skip this before_action for both the user edit and update actions, or you'll end up having a redirect loop. This is how to skip an existing before_action for specific actions:
class UsersController < ApplicationController
skip_before_action :validate_email_confirmed, only: [:edit, :update]
end
Did some more digging, and found this on the official docs, seems to fit your needs:
class ApplicationController < ActionController::Base
before_action LoginFilter
end
class LoginFilter
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to access this section"
controller.redirect_to controller.new_login_url
end
end
end
You'd of course have to rework this some, to get awaiting_email or such, but the principle is the same.
I'm getting a redirect loop. I have a clear idea why, user is logged out, redirected to login page (welcome#index) and user is still logged out and we have an endless loop.
How do I get out of loop?
I read about several options.
before_action :require_login placing it inside controllers where login is required. EASY, but a lot of copy paste, we love dry don't' we?
except, before_action :require_login, :except => root? I couldn't find details about except. I'm getting a lot of hits on before_filter which seems to be deprecated.
skip_before_action same here, I can only find bits and pieces :(
There should be a better way to handle these, is it rails way to do check routes level in config/routes.rb?
Application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
helper_method :current_user
before_action :require_login
private
def current_user
#current_user ||= Dedit::User.find(session[:user_id]) if session[:user_id]
end
private
def require_login
redirect_to root_path unless current_user.present?
end
end
login page controller:
class WelcomeController < ApplicationController
layout 'basic'
def index
if current_user.present? then redirect_to dedit_path end
end
end
before_action :require_login, except: [:index]
I am setting up a very simple rails app that involves a simple authentication check before you can enter the site. But, when the before_filter runs, and the user is redirected to the login path, a redirect loop occurs.
ApplicationController:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :check_session
private
def check_session
redirect_to login_path and return unless current_user?
end
def current_user?
!session[:username].blank?
end
end
SessionsController
class SessionsController < ApplicationController
def new
end
end
The issue is that, since your SessionsController inherits from ApplicationController, it actually inherits the before_filter as well. This means you are not allowing someone to see the login page unless they are logged in, which is usually undesirable behavior. You want to skip this before_filter on the login page:
class SessionsController < ApplicationController
skip_before_filter :check_session, only: :new
def new
end
end
I think you have problem in your routes. One way of solution is defining a root path for your app as:
root 'app_entry#index'
Then create a controller for it as given below:
class AppEntryController < ApplicationController
def index
if current_user
redirect_to 'controller_name/action_name'
else
redirect_to '/users/sign_in'
end
end
Hope this helps you.
You should use the before filter like this
before_filter :check_session, :except => [:new]