【问题标题】:Check if current_user is the owner of a resource and allow edit/delete actions检查 current_user 是否是资源的所有者并允许编辑/删除操作
【发布时间】:2013-07-11 13:39:00
【问题描述】:

示例:

用户A(id=10)创建了一个照片资源

photo: (id: 1 user_id = 10, url: "http://...")

现在,如果用户 B (id=20) 转到此网址:/photos/1/edit 它可以编辑用户 A 的照片!!!

Rails+Devise 默认为此提供了一些东西?这似乎是一个非常普遍的问题

我只需要允许任何用户只能编辑/删除它创建的资源(其中 current_user == resource.user)

使用:Rails 4,设计

更新:

我认为 CanCan 太高级了。我不需要角色或将某些操作限制为某些用户

【问题讨论】:

  • cancan 不再维护得很好。上次提交 2 个月前。 189 个未解决的问题。它也是一个非常臃肿的简单授权工具。就个人而言,我更喜欢github.com/elabs/pundit
  • @Deefour thx 指出这一点,专家看起来很棒
  • @deefour +1,谢谢,我试试
  • cancan 的延续宝石正在积极维护中,cancancan 任何感兴趣的人

标签: ruby-on-rails activerecord devise ruby-on-rails-4


【解决方案1】:

在您的照片控制器中:

before_filter :require_permission, only: :edit

def require_permission
  if current_user != Photo.find(params[:id]).user
    redirect_to root_path
    #Or do something else here
  end
end

【讨论】:

  • 您需要在此处处理 ActiveRecord::RecordNotFound 异常。
  • @NiravGandhi AFAIK,只有 ActiveRecord 方法的! 版本会抛出该异常。否则,结果将只是nil,当然也应该处理。
  • @ChrisCirefice On Rails 4.2.8 和 ActiveRecord 2.5.1,没有find! - find 本身肯定会抛出ActiveRecord::RecordNotFound 异常。
  • 这不会让您再次在控制器操作方法中再次获取记录吗?
【解决方案2】:

你可以利用 Rails 的关联,这样写:

def edit
  @photo = current_user.photos.find(params[:id])

  # ... do everything else
end

只有当提供的 ID 的照片属于当前用户时,才会找到记录。如果没有,Rails 将引发 ActiveRecord::RecordNotFound 异常。

当然,我假设current_user 方法可用并且您的User 模型包含语句has_many :photos

【讨论】:

  • 这是解决这个简单问题的最优雅的解决方案。 +1
【解决方案3】:

检查这个 railscasts,

http://railscasts.com/episodes/192-authorization-with-cancan

您会遇到的并发症,

  1. 当您想要对 Devise gem 用于身份验证的用户模型进行 cancan 授权时

  2. 当您想将角色存储在数据库中时

  3. 当您想从 webUI 以管理员身份将权限分配给角色时

  4. 还有更多..

如果您需要这些功能中的任何一个,请发表评论,我很乐意提供帮助,因为我最近在其他人的大力帮助下完成了这些功能,并且将其传递下去总是令人惊叹。

您的资源的示例能力可以如下所示,

class Ability
  include CanCan::Ability

  def initialize(user)

      user ||= User.new # guest users
      send(user.role.name)

        if user.role.blank?
          can :read, User #for guest without roles
        end

  end

  def man
    can :manage, Photo
  end


  def boy
    can :read, Photo
  end

  def kid
    can :read, Article
  end

end

【讨论】:

  • 1) 我的设计使用了用户模型。就这个
  • 你想知道怎么做吗?
  • 我在想我需要一些非常简单的东西。我只需要允许任何用户只能编辑/删除它创建的资源(其中 current_user == resource.user)。 Cancan 是更高级的东西
【解决方案4】:

我从 before_filter 操作中捕获了异常:

before_action :set_photo, only: [:edit, :update, :destroy]

def set_photo
  @photo = current_user.photos.find(params[:id])

  rescue ActiveRecord::RecordNotFound
    redirect_to(root_url, :notice => 'Record not found')
end

希望这对某人有所帮助。我正在使用 Rails 4 和 Ruby 2。

【讨论】:

    【解决方案5】:

    所以您使用的是gem devise

    此 gem 为当前登录的用户提供 current_user

    在您的 PhotosController#edit 方法中。我会做类似下面的事情。

    def edit
      @photo = Photo.find(params[:id])
      redirect_to root_path, notice: 'Thou Shalt Nought duuu dat :(' unless current_user.id == @photo.user_id
      ...
    end
    

    这种方法更便宜,因为您已经有 2 个对象要比较,而不是在比较中运行查询。

    【讨论】:

      【解决方案6】:

      最简单的方法是修改 routes.rb。

      将照片分配到 current_user 路径中。

      例如,

      devise_for :users
      
      resources 'users' do 
        resources 'photos'
      end
      

      【讨论】:

        【解决方案7】:

        康康舞既困难又复杂 我有编码is_onwer 方法 很简单,很容易

        https://gist.github.com/x1wins/0d3f0058270cef37b2d3f25a56a3745d

        应用程序控制器

         def is_owner user_id
            unless user_id == current_user.id
              render json: nil, status: :forbidden
              return
            end
          end
          def is_owner_object data
            if data.nil? or data.user_id.nil?
              return render status: :not_found
            else
              is_owner data.user_id
            end
          end
        

        你的控制器

          before_action only: [:edit, :update, :destroy] do
            is_owner_object @article ##your object
          end
        

        【讨论】:

          【解决方案8】:

          如果 CanCan 太高级,您应该仔细检查控制器中访问器的 ID,使用...

          if @user.id == @photo.user_id
            # edit photo details
          else
            redirect_to root_path, notice: "You! Shall! Not! Edit!"
          

          ...或类似的东西

          【讨论】:

            【解决方案9】:

            在application_controller中再写一个before_filter:

            before_filter :has_permission?
            
            has_permission?
            controllers=["articles", "photos", "..."]
            actions=["edit", "destroy", "..."]
            id = params[:id] if (controllers.include?(params[:controller] && actions.include?(params[:action]) end
            if id && (current_user.id==(params[:controller][0...1].capitalize!+params[:controller].singularize[1...-1] + ".find(#{id}).user_id").send)
            return true
            else
            redirect_to root_url, :notice=>"no permission for this action"
            end
            
            helper_method :has_permission?
            

            您可以在视图中使用它,而不是向用户显示他们无法关注的链接。

            有些这样,当然你需要修改它以满足你的需要。

            【讨论】:

            • 这是个坏主意。有太多经过测试、更灵活、更易读、编写良好的 gem 来处理授权。没有必要用这种脆弱的东西重新发明轮子。
            • @Deefour,这只是示例。由于某些原因,在可以避免的情况下不使用宝石是有道理的。最好的方法是没有数据库表的模型,但整个问题教程更适合文章,而不是答案。
            • 这个想法似乎很有趣。 @Deefour 哪些是做这样的事情的宝石? (CanCan对于这个问题来说太高级了)
            • @user1028100 正如我在问题的 cmets 中提到的,我使用 pundit
            猜你喜欢
            • 1970-01-01
            • 2010-11-02
            • 2021-12-13
            • 2012-08-26
            • 2021-07-29
            • 1970-01-01
            • 2011-09-06
            • 1970-01-01
            • 2013-08-11
            相关资源
            最近更新 更多