I have an authorization module that I built to authorize user actions. Everything is working great, except now I want to skip the action if the user requesting the page is the current user. The resource is nested below user so passes a :user_id as part of the params.
From what I've been able to find out, the simplest way to do this is to use a lambda, but it doesn't appear that I have access to the passed params from the before filter.
This is my controller
class Certifications::FitnessController < ApplicationController
prepend_before_action :authenticate_user!
before_action :authorize, unless: -> { params[:user_id] == current_user.id }
end
The problem is that the :authorize before_action is never called so all actions are allowed (I assume because the unless statement is always evaluating to true), but I can't examine what is going on because if I stop execution there, no params seem to be there (which I would think should make it always evaluate to false, not true).
If anyone can tell me either what I'm doing wrong or a better way to implement, I would really appreciate it.
EDIT: The code above actually works if you convert the params to an integer to match the current_user.id
before_action :authorize, unless: -> { params[:user_id].to_i == current_user.id }
before_action do |controller|
unless params[:user_id].to_i == current_user.id
controller.authorize
end
end
Alternatively you can do so like:-
before_action :authorize
def authorize
unless params[:user_id].to_i == current_user.id
#do your stuff..
end
end
2nd Alternative
before_action :authorize, unless: -> { params[:user_id].to_i == current_user.id }
Related
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 want to restrict the access to some pages to only logged users.
Those pages have an attribute `area = restricted``
So in my controller i am restricting the access as following:
before_action find_page # Where we instantiate #page
before_action authenticate_user! only: [:show], :if => :restricted? , :unless => :logged_in?
and farther in the controller i have
def restricted?
#page.area == "restricted" ? true : false
end
def logged_in?
current_user ? true : false
end
But for some reason, it didn't work...
Do you see something obvious in the code ?
Thanks !
UPDATE:
I simplified the code above by mixing the 2 conditions into one method as following:
before_action find_page # Where we instantiate #page
before_action authenticate_user!, only: [:show], :if => :must_be_authenticated
And my method us defined as following
def must_be_authenticated
puts "must_be_athenticated call"
return false if #page.area != "restricted" # If the area is not restricted no need for athentication
return false if user_signed_in? # If user is signed_in no need for athentication
return true
end
The problem persists, and even the string "must_be_athenticated call" is not written in the log. So it seems that the before_action :authenticate_user!, :if => :must_be_authenticatedis just ignored since no print out in the log of the debug string, neither the page is restricted...
UPDATE:
The show method is defined as following:
def show
authorize! :show, #page
render template: "pages/show"
end
I normally have everything scoped with current_user
eg:
#integrations = current_user.integrations.all
For trouble shooting, I want the admin to see everything for all users. I currently have a boolean on the user model that is admin: true
To get around the admin seeing everything, I keep doing this:
def index
if current_user.admin?
#integrations = Integration.all.includes(:user)
#reports = Report.all
else
#integrations = current_user.integrations
#reports = current_user.reports
end
end
I feel like there is an easier way... suggestions?
Thanks!
You could maybe abstract the admin check to a protected/private method in your User controller:
def is_admin
current_user.admin?
end
And then at the top of the controller, place a before action to catch whatever methods you want:
class UsersController < ApplicationController
before_action :is_admin, only: [:index, :show]
# rest of your code
end
OR
class UsersController < ApplicationController
before_action :is_admin, except: [:destroy]
# rest of your code
end
You could make a protected method in the application_controller.rb file. As stated above it will check to see if the current_user is and admin. Then you can use conditional statements to set privileges.
assuming that admin is a boolean value
def is_admin?
current_user.admin == true
end
In a view you can then say
if current_user.is_admin?
........
end
is it possible to have:
before_filter :authenticate_user! || :authenticate_admin!
before_filter :do_authentication
def do_authentication
authenticate_user! || authenticate_admin!
end
before_filter {authenticate_user! || authenticate_admin!}
Passing in a proc to the before_filter method, would be the closest to what you have provided in you question.
Since :authenticate_user! is a symbol, :foo == true is valid. Therefore, your example will always just equate to before_filter :authenticate_user!
Try something like (not sure of your setup here..):
if method_defined?(:authenticate_user!)
before_filter :authenticate_user!
else
before_filter :authenticate_admin!
end
If you have both methods defined and want to run authenticate_user! then if that returns false, run authenticate_admin!, just make one authenticate! method that checks for uses/admin and run that before_filter.
Using Ruby on Rails, I want to before filter an action but only when users are logged in.
How is this possible?
before_filter :only_when_user_is_logged_in, :only => :the_action
Or for multiple
before_filter :only_when_user_is_logged_in, :only => [:the_action, :another_action]
On the flip side, you can also provide an :except => :this_action option
I think you're asking how to run a before filter only if a user is logged in. There is no built-in semantic for this, but it's easy enough to inline:
class SomeController < ApplicationController
before_filter :do_something
def do_something
if logged_in?
# the stuff you want to do
end
end
end
Before filters take an optional block which is passed the current controller instance
so you could do something like this:
before_filter :do_stuff, lambda { |controller| controller.logged_in? }
If you really don't want the before_filter executing for anyone other than logged in users consider using #skip_before_filter in your authentication filter. For instance if when you're checking if users are logged in in your authentication filter, if authentication fails, merely call skip_before_filter :filter_for_logged_in_users_only.
Other than that you can simply test if the user is logged in before executing the member only filter. For example:
def filter_for_logged_in_users_only
return true unless current_user && logged_in?
#rest of the logic
end
If you're using restful authentication, it's just before_filter :login_required. If you are using your own authentication framework, you can create a method in application.rb that returns true if the user is logged in or redirects to the login page otherwise.
class LoginsController < ApplicationController
skip_before_filter :require_login, :only => [:new, :create]
end