具有复杂条件的数据库中的动态cancan

时间:2015-06-18 22:37:30

标签: ruby-on-rails permissions cancan user-roles

我尝试定义用户创建的角色,从权限列表中选择权限。 我想要这样的许可:

def initialize(user)
  user.projects_users.each do |project_user|
    project_user.role.privileges do |privilege|
      can :create, ProjectsUser, :project_id => project_user.project_id
    end
  end
end

但是我试图以这样的方式保存数据库中的权限,以便我可以这样做以获得上述结果

def initialize(user)
  user.projects_users.each do |project_user|
    project_user.role.privileges do |privilege|
      can privilege.action.to_sym, privilege.subject_class.constantize, privilege.conditions
    end
  end
end

问题出在' privilege.conditions'部分。我无法存储必须仅在Ability.rb文件中执行的条件。如果我试着存储:

{ :project_id => project_user.project_id }

可以说没有名为' project_user'的变量。我可以将它保存为字符串,并在我的Ability文件中执行eval(privilege.condition),但是我只需要对值进行此操作。我试过这样的事情:

def initialize(user)
  user.projects_users.each do |project_user|
    project_user.role.privileges do |privilege|
      can privilege.action.to_sym, privilege.subject_class.constantize, privilege.conditions.each do |subject, id|
        subject => eval(id)
      end
    end
  end
end

我得到的错误是语法错误,意外=>,期待关键字_end'对于' subject =>'片。

不确定如何准确地做到这一点......

我使用这一行命令来测试它:

@user_id = 4
@role = Role.create(name: "Tester", project_id: 4)
@priv = Privilege.create(:action => :create, :subject_class => 'ProjectsUser', :conditions => { :project_id => 'project_user.project_id' })
@role.privileges << @priv
@project_user = ProjectsUser.create(:user_id => @user_id, :role_id => @role.id, :project_id => @role.project_id)
@a = Ability.new(User.find(@user_id))
@a.can?(:create, ProjectsUser.new(:user_id => @user_id + 1, :role_id => @role.id, :project_id => @role.project_id))

有什么建议吗?

2 个答案:

答案 0 :(得分:2)

好的,所以我找到了一个非常简单的工作。基本上没有正确评估条件的do块。这是工作代码:

user.projects_users.each do |project_user|
  project_user.role.privileges.each do |privilege|
    can privilege.action.to_sym, privilege.subject_class.constantize, Hash[privilege.conditions.map {|subject, condition| [subject, eval(condition)] }]
  end
end

注意Hash[privilege.conditions.map {|subject, condition| [subject, eval(condition)] }]

这样做是将符号作为条件,例如:subject_id并将其映射到已评估的条件,该条件评估为特定的id。

在我的模特中我有

class Privilege < ActiveRecord::Base
    has_and_belongs_to_many :roles

  serialize :conditions, Hash
end

示例模型是:

Privilege.create(
  :action => :create, 
  :subject_class => 'ProjectsUser', 
  :conditions => { :project_id => 'project_user.project_id' }
)

<强>更新

此方法仅适用于深度为一级的条件。像这样的条件是行不通的。您将得到:TypeError:没有将Hash隐式转换为String

:conditions => { 
  :project => { 
    :location_id => 'project_user.project.location_id'
  }
}

这不是最佳解决方案,但解决此问题的方法是

:conditions => { 
  :project => "{ 
    :location_id => eval(\"project_user.project.location_id'\")
  }"
}

答案 1 :(得分:1)

我肯定会说你有点倒退了。从数据库记录动态创建一堆auth规则将非常难以测试,因为测试套件中的任何地方都需要大量的设置,而且过于复杂。

而是使用块来声明复杂的关系和角色来存储权限。

使用带有作用域资源的角色Rolify gem的示例:

can :moderate, Forum do |forum|
  user.has_role?(:moderator, forum)
end

您还可以使用角色为组分配权限:

if user.has_role? :admin
  can :crud, User
  # ...
end 

我想说如果你真的需要一个复杂的完全动态的解决方案,其中管理员从Web界面创建授权规则,角色定义等,那么你肯定已经超出了CanCan。但你真的需要吗?