I have a controller before_filter that redirects non-admin users to the root path, if they attempt to update someone else's profile:
before_filter :correct_user, only: [:edit, :update]
....
private
def correct_user
#user=User.find(params[:id])
redirect_to(root_path) unless current_user?(#user) || current_user.admin?
end
I'm using this filter in two different controllers, and I want to DRY up the code.
My specs still pass when I move the method into application_helper.rb, but I'm weary of assigning the #user variable in a helper file. Can any harm come from this? Should I at least be delegating the assignment of the variable to a private method in the helper?
Is it better to have code duplication in this instance, or is my solution safe enough?
move your correct_user method into your application controller and then you can use in any of the controller with before_filter.
Related
I need to define the same instance variables:
#note = Note.new
#notes = current_user.notes.all
In multiple controllers:
UsersController#home
NotesController#create
NotesController#update
Where would be an appropriate place to house a class/function that
def createNoteInstancesVars
#note = Note.new
#notes = current_user.notes.all
end
?
Is this the intended use of a controller concern or is there a different/better way I am not thinking of? If I do put it in a concern, doesn't that mean these queries are getting run for every single controller#action? I would like to avoid that.
You could add the method in your ApplicationController.
Then add in NotesController:
before_action :create_note_instances_vars, only: [:create, :update]
Same logic in UsersController
I think concern is not good idea for just initialise instance variable.
You can write this def in ApplicationController and call via callbacks when you need it in any controller.
I have the following code
- if current_user && !current_user.is_owner?(#product)
p do this
I added the current_user && logic because an error would occur if user is not logged in. but I am wondering if it is the correct way of doing it.
You will probably end up having to work around other problems in your views if you need the views to process parameters from the logged on user.
I would recommend you add a before_action that redirect to the login page if the current_user variable is not populated.
something like:
class YourController < ActionController::Base
before_action :redirectIfNoCurrentUser
** your functions/actions **
def redirectIfNoCurrentUser
redirect_to(new_user_session_path) unless current_user
end
end
before_action and skip_before_action can be configured in a per action basis with the :only option if required
You are right, this is already the correct way of doing this.
After it checks and sees that current_user returns false it knows the conditional will be false and does not execute the second part of the conditional.
It is a better practice of checking any value as current_user.present? instead of current_user because the previous one returns a boolean value.
You can try using authenticate_user! method of devise, If you do not want the user who is not logged in to access the page.
class YourController < ActionController::Base
before_action :authenticate_user!, only: [:your_actions]
def your_actions
end
end
Then in your views you can handle your conditions.
Your code is fine, but if you want to avoid littering your code with nil checks you may wish to consider using the null object pattern and make a NullUser with defined default behavior.
- if user_signed_in? && !current_user.is_owner?(#product)
p do this
Maybe you can try to use try method.
if current_user.try(:is_owner?, #product)
p do this
end
I am implementing a User modeling for my app. That only current user can update and destroy his account I put in the user controller:
before_action :correct_user, only: [:edit, :update, :destroy]
But now I also want that the admin can do those actions (well he can actually do everything)
I proposed to create another function in the helper admin_user to allow him the access to all actions then call it in the user controller like:
before_action :admin_user
but it seems like he is ignoring it. Any mathematic function to solve this issue?
Thanks!
You could just define that correct_user always returns immediately if the current_user is an admin:
def correct_user
return if current_user.admin?
# put existing logic here
end
I have a ChatController and an #user variable in it. On the main page I display #user.name. I also have destroy and create methods that work with ajax, so when I delete a message from my chat, #user becomes nil. To prevent problems from calling name on a nil object, I can add #user=User.find_by_id(:user_id) to every method. But this becomes tedious if I have many methods. Can I declare #user=User.find_by_id(:user_id) once and DRY up my code?
Yes, this is done in a before_filter (Documentation).
Something like:
class ChatController < ActionController::Base
before_filter :find_user
private
def find_user
#user ||= User.find_by_id(params[:user_id])
end
end
You may also consider using Inherited Resources which automates this for you.
I have several controllers that require a correct user for their edit/update/delete actions. What is the Rails-way to accomplish the following:
Currently, in each controller I have the following code:
class FooController < ApplicationController
before_filter :correct_user, :only => [:edit, :update, :destroy]
# normal controller code
private
def correct_user
#foo = Foo.find params[:id]
redirect_to some_path unless current_user == #foo.user
end
end
I have similar code in 3 controllers. I started to bring it out to a helper like this:
module ApplicationHelper
def correct_user( object, path )
if object.respond_to? :user
redirect_to path unless object.user == current_user
end
end
But I'm wondering if this is a good way to do it. What's the accepted way to solve this?
Thank you
EDIT
The correct user check here is because I want to make sure it's only the author who can make edits/deltes to each of the objects.
To clarify, the objects would be things like Questions and Posts. I don't want to use something like CanCan as it's overkill for something simple like this.
I really like using RyanB's CanCan, which allows you to both restrict access to actions based on the user, and centralize such authorization into basically a single file.
CanCan on GitHub: https://github.com/ryanb/cancan
Screencast explaining how to setup/use it: http://railscasts.com/episodes/192-authorization-with-cancan
EDIT
No problem. I hear you on CanCan - it takes a little while to get up and running on it, but it's designed to do exactly what you're asking - per object authorization.
Alternative:
Another way to do this is move your authoriship/current_user check to the ApplicationController class, from which all of your other Controllers inherit (so they will get that code through inheritance - and you don't need to write the same code in multiple Controllers), and it would look something like...
class ApplicationController < ActionController::Base
...
helper_method :correct_user
private
def correct_user( object, path )
redirect_to path unless object.user == current_user
end
end
You should do the following :
def edit
#foo = current_user.foos.find(params[:id])
end
This way, only if the current user is the owner of the Foo he will be able to see it.