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
Related
currently, my edit action in my users controller is like so:
def edit
#user = User.find(params[:id])
end
This works if I want to access my own user profile. However, if I want to access someone else's user profile, this wouldn't work. How should I change this?
Add a before action check_right_user which checks whether the current user is trying to access his own profile.
before_action :check_admin, only: [:edit, :update, :destroy]
def check_admin
unless current_user.admin?
redirect_to root_path, alert: "You're not authorized"
end
end
I assume you have a current_user method defined in your application_controller or users_controller and an admin field in your user model.
i have a rails app with sorcery
everything work .
the problem is when edit a user like :
http://localhost:3000/users/1/edit
its work fine , but when i change the user id to 2 or 3 ..
i can update all users data
how can i restrict the edit page only if the current user is the one that logged in
here is my controller :
skip_before_action :require_login, only: [:new, :create, :show]
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
auto_login(#user)
flash[:info] = "Welcome."
redirect_to root_url
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
you can also do something like this
before_action :edit_rights?, only: [:update, :edit]
private
def edit_rights?
#user = User.find(params[:id])
redirect_to(root_path) unless current_user == #user
end
you won't need #user = User.find(params[:id]) in both update and edit actions then
There are (at least) two ways to do that. First and straightforward is detailed in another answer, fine-tune your controller.
A less obvious way is to create a singular resource and its own controller. In routes that could look like:
resource :profile, only: [:show, :edit, :update]
# generates:
# /profile (GET, PATCH, PUT)
# /profile/edit (GET)
Then create a controller that is responible solely for user's own profile and operates only on current_user.
Yes, it's okay for one model to have multiple controllers, if your model should behave really differently in different parts of your app.
Why would you do that?
User's own profile could show much more information than is available publicly, you can lay it out in a separate view
No "access denied" errors, as the resource is auto-selected via current_user, all you need is ensure the user is logged in in the entire controller.
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
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.
Hey guys I created some custom authentication thanks to railscasts.com but I'm somewhat stuck as I need to restrict my users from editing other users' profiles.
Here's my authenticate_user and current_user methods:
private
def current_user
#current_user ||= User.find_by_auth_token!(cookies[:auth_token]) if cookies[:auth_token]
end
def authenticate_user!
if current_user.nil?
redirect_to login_url, :alert => "You must first log in to access this page"
end
end
Here's the before_filter in my UsersController:
before_filter :authenticate_user!, :only => [:edit, :update, :destroy]`
EDIT: Fixed it thanks to alock27.
I had to edit my users_controller and modify the edit action as follows:
#user = User.find(params[:id]
redirect_to root_url unless current_user == #user
I think you want this:
Adding security on routes in Rails
you need to find the User by :id and check if current_user = #user
You don't have to provide an id for edit, update and destroy: you already have current_user.
Instead of editing #user = User.find(id), edit current_user. Thus, your authentication functions ensure the user will only edit its own profile.