【问题标题】:Devise + CanCan just prevent other users from editing objectsDevise + Can 只能阻止其他用户编辑对象
【发布时间】:2011-11-30 19:50:20
【问题描述】:

您将如何防止其他用户编辑对象,例如不属于自己的配置文件对象?

大多数在线示例都是具有多个用户角色的复合体,我无法使其正常工作,但一定很简单:

  def initialize(user)

      can :update, Profile do |profile|
        profile.try(:user) == current_user
      end

  end

在我的 ProfilesController#edit 中

authorize! :update, @profile

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 devise cancan


    【解决方案1】:

    第一个问题是,您是否为User 设置了角色?

    app/models/user.rb

    class User < ActiveRecord::Base
      attr_accessible :email, :password, :remember_me
      devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, # regular devise stuff
      before_create :setup_default_role_for_new_users
    
      ROLES = %w[admin default banned]
    
      private
    
      def setup_default_role_for_new_users
        if self.role.blank?
          self.role = "default"
        end
      end
    end
    

    如您所见,我在这里有 3 个不同的角色,当创建新用户时,他们始终是 default 用户。现在有了 CanCan 设置,假设您想让 admin 能够做任何事情,default 用户能够使用他们自己的配置文件做任何事情,banned 用户不能做任何事情而访客用户能够查看个人资料:

    class Ability
      include CanCan::Ability
      # Remember that CanCan is for a resource, meaning it must have a class(model).
    
      def initialize(user)
        user ||= User.new # guest user (not logged in)
    
        if user.role == "admin"
          can :manage, :all
        elsif user.role == "default"
          can :manage, Profile, :user_id => user.id
        elsif user.role == "banned"
          cannot :manage, :all
        else
          can :read, Profile # guest user
        end
      end
    end
    

    这就是您让用户只编辑他们自己的个人资料而不是其他人的方式。


    其他一些方便的注意事项:确保您的Profile 表中有一个user_id 列。此外,如果您可能需要让猜测用户看到这样的个人资料:

    class ProfileController < ApplicationController
        before_filter :authenticate_user!, :except => :show
        load_and_authorize_resource
    end
    

    他们将无法使用任何其他操作,CanCan 仍会检查除 show 之外的所有其他内容的身份验证。

    祝你好运!


    更新:为用户制作 :role 属性

    我所做的是运行迁移,将role 列添加到设计users 表中:

    rails generate migration add_role_to_users role:string
    

    然后是rake db:migrate。新的迁移文件应如下所示,并检查您的 db/schema.rb 文件以确保其正确地与用户表分开。如果不是,则rake db:drop,然后rake db:create,然后再次rake db:migrate

    class AddRoleToUsers < ActiveRecord::Migration
      def self.up
        add_column :users, :role, :string
      end
    
      def self.down
        remove_column :users, :role
      end
    end
    

    这就是您成功使user.role 工作的方法。

    注意:请确保您离开:can :manage, Profile, :user_id =&gt; user.id,保持原样不变。将role 列添加到user 后应该可以工作。

    重要!如果您使用 Rails 3,请制作role attr_accessible 或者每个人都可以编辑他们的角色! Rails 4 默认使用Strong Parameters 并且不受此问题的影响,因为您可以选择允许的参数。

    【讨论】:

    • 感谢信息,自从 cancan 升级以来,我有一些旧信息,导致一些问题,仍然没有得到这个 - 看起来很简单 - 事情已修复。我从上面的示例中复制了代码。 user.role 部分对我来说并不完全清楚,user.role 来自哪里?用户表中的列?仍然收到“您无权访问此页面”。由于我使用的是设计 current_user 应该返回当前登录的用户 ID。也许“可以:管理,配置文件,:user_id => user.id”是问题所在,我尝试将其更改为current_user,而不是重新启动应用程序但结果相同。
    • 即使在用户模型 self.role = "admin" 硬编码和重新启动应用程序中设置也不允许我编辑任何配置文件或查看它们,很奇怪我在这里没有看到我想念的东西,任何建议都会欢迎:) thx
    • @Rubytastic 查看编辑,如果您需要更多信息,可以访问此处:github.com/ryanb/cancan/wiki/Role-Based-Authorization,但基本相同。
    • 我修复了它,但完全忘记将其标记为获胜答案、很好的答案和文章。似乎我在控制器中有一些非默认逻辑把事情搞砸了,因此我对为什么即使遵循文档也没有工作感到困惑。对于未来遇到问题的人来说可能是一个很好的选择
    • @Rubytastic 酷,很高兴一切都为你工作。祝您申请顺利。
    【解决方案2】:

    试试这样的东西......

    can :update, Profile, :user_id => user.id
    

    【讨论】:

    • 这给出了相同的结果:您无权访问此页面。
    • 只是检查,但 Profile 是否在数据库中设置了 user_id 列(belongs_to :user)?我刚刚检查了我的项目(这正是你想要做的),我将它设置为'can [:edit,:update],User,:id => user.id',所以你可能必须玩弄上述值以使其正确适应您的设置。
    • 另外,我的一个疏忽:您的设置与我的不同之处在于我正在直接编辑用户,您正在编辑属于用户的个人资料,我接受吗?没有承诺,因为我只是把这个从我的脑海中抛开,但是试一试这样的事情:'can :update, Profile, :user => { :id => user.id }'
    • 仍然没有找到任何使用 cancan 设计并知道正确语法的解决方案?
    【解决方案3】:

    更新

    看起来像上面的代码示例是正确的。在阅读了 cancan rtfm 的所有文档后,我发现了您需要添加的角色列。

    由于我组织个人资料更新操作的方式,CanCan 似乎不起作用!我解决了如下:

      def edit
    
        @profile = Profile.find params[:id]
        what = params[:what]
    
        if can? :update, @profile
          if ["basics", "location", "details", "photos", "interests"].member?(what)
            render :action => "edit_#{what}"
          else
            render :action => "edit_basics"
          end
        else
          raise CanCan::AccessDenied.new("Not authorized!", :update, Profile)
        end
      end
    

    也许不是最干净的方法,但却是让它工作的唯一方法。欢迎任何关于改进的建议,我确实有

      load_and_authorize_resource
    

    内部配置文件控制器!也许是一个错误

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-25
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多