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.
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 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
Visiting users/1/edit when I'm signed in as different user does not raise an AccessDenied error, and I have no idea why:
authorize_resource only: [:edit, :update]
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to #user
else
render 'edit'
end
end
Ability class:
class Ability
include CanCan::Ability
def initialize(user)
user ||= User.new
can :read, :all
can :create, User
can :create, Group
can :update, User, id: user.id
end
end
If I change authorize_resource to load_and_authorize_resource then it works as expected. But this should not be relevant, surely?
Your code only authorize the user to access edit and update action not the #user object
you have to manually authorize the object like this
Try this,
def edit
#user = User.find(params[:id])
authorize! :update, #user
end
def update
#user = User.find(params[:id])
authorize! :update, #user
if #user.update_attributes(params[:user])
redirect_to #user
else
render 'edit'
end
end
I'm facing the same issues like you,but for me,I'm using devise with cancan. Therefore ,in my controller, i will put
before_filter :authenticate_user!, :except=>[:create]
it will authenticate user except the create.
def index
#user = User.all
authorize! :index, #user
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #user }
end
end
each of your controller function that you want to authorize the access of user, you can do like this, it seems you have to do lots of works by putting every single in the function that you need to authorize instead just using load_and_authorize_resource, but hope can help u a little from what i have completed. here is the resource:https://github.com/ryanb/cancan/wiki/authorizing-controller-actions. If you get an answer and why the load_and_authorize_resource is not working, post to here too :)
I don't have an answer for you (yet) on why this happens, but I encountered essentially the same issue. My situation was only different in that manually authorizing each action (instead of relying on either "authorize resource" or "load_and_authorize" was the key.
I am running into this issue as well, and here is what I have found.
If I'm reading the source code right, during an :update action, load_and_authorize does a find_by to load the resource, then calls authorize! on it. However, I don't see where it authorizes it after the incoming parameters have been applied. (Someone please correct me if I'm reading this wrong.)
The use case I am seeing this is when someone edits a resource, and in the edit, updates a value in the resource that makes it no longer eligible to pass authorization on save. (Granted, I am setting up the UI to help avoid this situation, but obviously I still want to protect the resource.) Running a functional test, I was able to set attributes that I expected not to pass authorization on the controller :update action, presumably because the check happens before the attributes are parsed.
So far, the way I have worked around it is to call authorize! again after I have set the attributes, which means I can't use update_attributes since I want to authorize before saving:
class FooController < ApplicationControlller
load_and_authorize_resource
def update
# slurp the mass assignable params
#foo.attributes = params[:foo]
# set some other params
#foo.some_other_attr = 'bar'
# authorize again, now that we have updated the params
authorize! :update, #foo
if #foo.save!
flash[:notice] = I18n.t(...)
respond_with(#foo)
# ...
end
end
end
An alternative is to create a before_filter, load the #foo instance yourself, then call authorize as above, but that doesn't really make things much cleaner, IMHO. It would just save on one authorize! call.
I'm curious as to how others are handling this. I am fairly new to CanCan, so I am presuming I am doing something wrong. :)
In Rails 2, how can I prevent a user from just changing the id # and accessing other Objects?
For example :
website.com/users/1231/edit
How do I prevent a user from changing the 1231 and accessing another account?
#user = User.find params[:id]
redirect_to :back unless current_user == #user
Use a before_filter in your controllers.
class Users < ApplicationController
before_filter :require_user, :only => [:show]
private
def require_user
#user = User.find_by_id(params[:id])
redirect_to root_url if #user.nil?
end
end
Use a permissions-checking gem like CanCan or Aegis. Both have conventions that add permissions checking to every method on every controller automatically.
Just as a disclaimer I am new to rails and programming in general so apologize for misunderstanding something obvious.
I have Authlogic with activation up and running. So for my site I would like my users who are logged in to be able to register other users. The new user would pick their login and password through the activation email, but the existing user needs to put them in by email, position and a couple other attributes. I want that to be done by the existing user.
The problem I am running into, if I am logged in and then try and create a new user it just tries to update the existing user and doesn't create a new one. I am not sure if there is some way to fix this by having another session start??? If that is even right/possible I wouldn't know how to go about implementing it.
I realize without knowing fully about my application it may be difficult to answer this, but does this even sound like the right way to go about this? Am I missing something here?
Users Controller:
class UsersController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
before_filter :require_user, :only => [:show, :edit, :update]
def new
#user = User.new
end
def create
#user = User.new
if #user.signup!(params)
#user.deliver_activation_instructions!
flash[:notice] = "Your account has been created. Please check your e-mail for your account activation instructions!"
redirect_to profile_url
else
render :action => :new
end
end
def show
#user = #current_user
end
def edit
#user = #current_user
end
def update
#user = #current_user # makes our views "cleaner" and more consistent
if #user.update_attributes(params[:user])
flash[:notice] = "Account updated!"
redirect_to profile_url
else
render :action => :edit
end
end
end
My User_Session Controller:
class UserSessionsController < ApplicationController
before_filter :require_no_user, :only => [:new, :create]
before_filter :require_user, :only => :destroy
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
flash[:notice] = "Login successful!"
if #user_session.user.position == 'Battalion Commander' : redirect_to battalion_path(#user_session.user.battalion_id)
else
end
else
render :action => :new
end
end
def destroy
current_user_session.destroy
flash[:notice] = "Logout successful!"
redirect_back_or_default new_user_session_url
end
end
Could you paste your users and users_session controller code?
I suggest using Ryan Bates' nifty_authentication gem. You can use authologic instead of default restful_authentication with
script/generate nifty_authentication --authlogic
Works like a charm.
I've done this with no probs, but know how hard it can be to port yourself to a new language and many new libraries! Hang in there! :)
I think that it might be the before_filter :require_no_user on new and create that blocks you.
What do you mean with this? Does it render the edit view? Or is this a result of a post/put?
it just tries to update the existing
user and doesn't create a new one.