Rails 3 Limiting Access so user can update only th

2019-06-20 11:23发布

问题:

This seems like something that should be fairly simple since it would be needed a lot. I check to see if a user is logged in fine but once a user is logged in they could potentially alter other peoples accounts. For example: say user with ID 1 was logged in and they put /users/2/edit as the url. This would show them user 2s data and allow them to modify it. Of course I can alter the edit action in the controller to use something like this...

  def edit
    @user = User.find(current_user.id)
  end

where current_user is set in the controller so the user is always that person who is logged in. This is fine if you only have a couple controllers with a couple actions but could be a pain if you have many. It seems like there should be a way to limit this globally so the user can only update their own data no matter what action or controller they use.

Is there a way to restrict the user to their own data for all actions?

回答1:

I think your question is really about how to do authorization. Also, I suspect you might be conflating the "belongs_to" model association name with authorization. The "belongs_to" association name describes how one model relates to another, but it does not imply anything about how an application controls access to any model.

Hishalv is correct that using a controller filter is the right way to manage authorization. It's the correct MVC way. Give the Rails Guide a good read so you understand that.

Then, you can consider using an authorization gem like CanCan, or rolling your own per Hishalv's suggestion. CanCan works well in tandem with an authentication gem like Devise. If you do roll your own, it's a simple thing to put a helper method in ApplicationController and write it in such a way that it works regardless of the model.



回答2:

In your application_controller.rb you can try this:

class ApplicationController < ActionController::Base
  before_filter :validate_user

  private

  def validate_user() #might need to pass id and current_user
    if current_user.id == params[:id]
      # continue to current_user url
    else
        flash[:error] = "Please access one of your own pages"
        redirect_to(:back)
    end
  end
end

by putting the stuff in the application controller it should be available in all the controllers, however if you do not need to confirm that this is the current user (say maybe home page) then you may need to use a skip_before_filter in any specific controller(for a specific action) that needs it, like this in the pages controller for example

class PagesController < ApplicationController
  skip_before_filter :validate_user, :only => [:home, :about]
end

For more info checkout this link to rails guides on filters. There could be more efficient ways of achieving this as well.

Hope it helps



回答3:

I have always found it a bit strange that end users were exposed to Rails "internals" such as /users/2/edit when all they wanted to do was update their own account.

So I prefer the method of calling it

resource :account

instead of

resources :users

Then you have paths like /account, /account/edit and so on, and need only handle data for the logged in user.



回答4:

If you are using cancan then

def validate_user
 unless current_user.id == params[:id].to_i
  raise CanCan::AccessDenied
 end
end

And in application_controller write

   rescue_from CanCan::AccessDenied do |exception|
    flash[:error] = "Access denied."
    redirect_to access_denied_path
  end