I'm trying to restrict access to the user edit page so that logged in user can only edit his/her own profile. FYI, I'm using Devise for user authentication, login, register, etc. This is supposed to be pretty easy to do with
class UsersController < ApplicationController
before_action :find_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
end
def update
if #user.update_attributes(user_params)
flash[:success] = 'Profile Updated!'
redirect_to edit_user_path(#user)
else
render 'edit'
end
end
private
def user_params
# code left out... but pretty self explanatory right?
end
def find_user
#user = User.find(params[:id])
end
def correct_user
redirect_to(root_url) unless current_user == find_user
end
end
The weird thing is, when I have the before_action :correct_user, when the user updates... it logs the user out post-update! When I don't have the before_action :correct_user, it leaves the user logged in and redirects to edit page of the user. I tried manually signing the user in def update prior to redirecting to edit page, but it does not work. In fact, that isn't even the issue. When I compare the current_user and User.find(params[:id]), the current_user is logged in! But for some reason, having the before_action :correct_user there logs me out!
I've been banging my head on the wall for quite some time on this one. Can anyone help out? This is a Rails 4 app and am using the latest version of devise.
Thank you!
I'm not sure you really need the find_user method here.
class UsersController < ApplicationController
respond_to :html
def update
current_user.update_attributes user_params
respond_with current_user, location: [:edit, current_user]
end
private
def user_params
...
end
end
Seeing as you only let a user edit their own record, you can just use current_user in the update method.
Also, if you are happy to use the standard Rails convention for CRUD operations, then respond_to/with will save you a little time and code by implementing that for you. I used the location option otherwise respond_with defaults to the show page of the resource.
Related
Should I load and check the presence of the DB source in before filter?
In our application, we always load and check presence of DB source which has an id passed in by params. I'm not sure if this is a good pattern.
like:
before_action :set_org
private
def seg_org
#org ||= Organization.find params[:id]
resource_not_found unless #org
end
Yes, finding a record and setting it as an instance variable is a common convention for controller filters. Generally though, any piece of code that gets run for multiple actions is a good candidate. Say you want to redirect to the log in page if the current user is not logged in.
class UsersController < ApplicationController
before_action :require_login
before_action :set_user, only: [:show, :edit, :update, :destroy]
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
def set_user
#user = User.find(params[:id])
end
end
I have a jobs pages where users can create new jobs edit and destroy their jobs and I want to let user edit or destroy only their post only if are connected else they will returned to the job show page for this i have this code in my job controller
def require_login
#job = current_user.jobs.find_by_slug(params[:id])
redirect_to job_path if #job.nil?
end
before_action :login_required
def login_required
redirect_to new_user_session_path unless user_signed_in?
end
before_action :login_required, :require_login, only: [:edit, :update, :destroy]
the only part where this don't work is when i not connected and try to edit my job it redirect me to the log in form but after login it redirect me to the home page instead of the edit page
First of all, the code block you pasted looks weird, for three reasons:
Indentation is wrong
You have before_action :login_required twice and it's not even the same
From the methods names it's unclear what you want exactly
From what you wrote, you want a user to edit or destroy a job only if they are logged in - otherwise you want to send them to the jobs index page. If that's right, the code in your controller should look like this:
class JobsController < ApplicationController
before_filter :require_login, :only => [:edit, :update, :destroy]
def edit
# your code here
end
def update
# your code here
end
def destroy
# your code here
end
private
def require_login
redirect_to job_path unless user_signed_in?
end
end
i followed the tutorial in railstutorial.org. I noticed that if i use the url
localhost:3000/users/102
logged in or out, i can view the profile. If i change the 102 to another existing user i can also view it. How can i limit the access to this page? I want that only the current user can be able to view his/her profile.
I am new at rails.
i know its got to do something with users_controller.erb in the code
def show
#user = User.find(params[:id])
end
This does depend on how you define your current_user helper. For instance, if you were using Devise for authentication, it would be if current_user == #user, but I believe for the helper on that tutorial, the method below should work.
def show
#user = User.find(params[:id])
if current_user?(#user)
# show page
else
# render error page?
flash[:error] = "Nope" # Clearly here you can do whatever you want, from error pages to redirections
redirect_to root_path
end
end
seems like the right solution would be to add another function
at the top it would be
before_action :correct_user, only: [:edit, :update]
before_action :correct_user_return, only: [:show]
and then in the private section it would be
def correct_user_return
#user = User.find(params[:id])
redirect_to current_user unless current_user?(#user)
end
So I am having some trouble with my rails app and I think I went a little out of my own depth. I am creating a simple alumni application and I want users to be able to join organizations. For some reason in my new join page I get the error "Couldn't find User without an ID". I want to know why the ID isn't passing in, which would imply signed_in? = false. I don't know why everything worked find when I created other additions to my users controller but here it refuses to take on the logged in user id. I feel like I am missing something simple, let me know if updates are necessary!
Here is the relevant information in my Users controller:
class UsersController < ApplicationController
before_action :signed_in_user, only: [:edit, :update, :index, :show, :join]
before_action :correct_user, only: [:edit, :update, :join]
before_action :admin_user, only: :destroy
def join
end
def show
#user = User.find(params[:id])
#organization = #user.organization
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
...
private
def user_params
params.require(:user).permit(:name,:email, :password, :password_confirmation,:organization_id)
end
def signed_in_user
unless signed_in?
store_location
redirect_to signin_url, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
I included the def create method because I tried editing it to redirect users to the join page right after login but then I came across this error and I thought that was the problem so i switched it back. I guess it wasnt.... NOTE: I am basing a lot of this app off of the Hartl tutorial if that is helpful.
You should have a Memberships controller and model with a belongs_to :user (has_many :memberships for User & Organization), instead of defining a join method in the Users controller. The controller should be responsible for adding/deleting organization user-memberships. From that controller you fetch the user info by #user = User.find(:id) and don't forget to properly set the route file for nested resources.
resources :users do
resources :memberships
end
Also note that your join method doesn't create any instance variables for the view (#user). It looks like it properly goes through the signed_in_user action but nothing is instantiated in the join method.
Change line 3 to:
before_action :correct_user, only: [:edit, :update]
If the purpose of "join" is to create a user, then there should not be a user yet. However if your purpose for "join" is to get the current user you should add this to your join method:
#user = current_user
What about if you changed it to this:
def show
#users = User.all
#user = #users.find(params[:id])
#organization = #user.organization
end
Frstly my apologies if this is a duplicate question. I have tried to find the answer but as Im very new to Rails I did not know what to search for.
I have a controller that has some security on it. For the show, edit, update and destroy actions I need to check if the user owns the persona they are working on, like this:
if #persona.user_id != #current_user.id
flash[:notice] = "Sorry, we couldn't find that persona"
redirect_to '/personas/'
else
# do something else
This is relatively easy. However, how do I do this in a DRY way? The code before the else is repeated across all 4 actions, the code after the else statement will be different on a per controller basis.
Thanks in advance.
Richard
You will need to use a before_filter. Something like this:
class PersonasController < ApplicationController
before_filter :check_owner, :only => [:show, :edit, :update, :destroy]
def show
#...
end
#...etc.
protected
def check_owner
redirect_to personas_path unless params[:id] == current_user.id
end
end
Also, take #davidb's advice on writing a current_user method if you don't already have one, which will go in your application_controller.rb. Something like this:
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
#current_user ||= session[:user_id] ? User.find(session[:user_id]) : User.new
end
end
You may have to tweak all of this, since it will depend on how you have your models setup. This is just a general idea of what you need to/should do.
Use before_filter here is an overview:
http://guides.rubyonrails.org/action_controller_overview.html#filters
To dry it you should also write a current_user method that returns the logged in user!
You can move your security logic to the before_filter. It will run before your actions and make your security check.
Your controller file:
class TestController
before_filter :check_persona, :only => [:show, :edit, :update, :destroy]
private
def check_persona
if #persona.user_id != #current_user.id
flash[:notice] = "Sorry, we couldn't find that persona"
redirect_to '/personas/'
end
end
end