业务需求是,高级用户可以使用新的或现有的“普通”用户“分享”层次结构中的任何单个元素.没有与他们共享的对象,普通用户无权查看(或做任何其他事情)与层次结构的任何级别中的任何对象.
共享过程包括选择共享是否授予对目标对象的只读,读取更新或完整CRUD的权限.
共享任何对象向该对象和层次结构中的所有较低级别对象授予R / O,R / W或CRUD权限,并向对象的所有直接祖先授予R / O权限.对象集合有机地增长,所以许可系统只需要记录user_id,share_id的object_id和共享的性质(R / O,CRUD等),就可以工作.随着该层次结构中的对象总体不断增长,在DB中为每个用户/对象组合创建一个显式权限记录是不切实际的.
相反,在用户请求周期开始时,ApplicationController收集所有权限记录(用户X具有部门#5的CRUD权限),并将它们保存在内存中的哈希中.一个权限模型知道当任何对象传递给它时如何评估哈希 – Permission.allow?(:show,Department#5)将根据用户权限哈希的内容返回true或false.
例如,我们来看看部门模式:
# app/models/department.rb class Department < ActiveRecord::Base after_initialize :check_permission private def check_permission # some code that returns true or false end end
当check_permission方法返回true时,我希望Department.first正常恢复数据库中的第一条记录,但如果check_permission返回false,我想返回nil.
现在,我有一个解决方案,默认范围触发权限检查,但是这导致了查询数量的2倍,而对于具有大量对象的类,内存问题和时间/性能问题肯定会在视野中.
我的目标是使用after_initialize回调来预先对对象进行许可.
但是,after_initialize将无法阻止返回原始对象.它允许我重置对象的属性的值,但不要放弃它.
有人知道如何实现吗?
编辑:
非常感谢所有提供的答案和意见;希望这个扩展版本的问题澄清了一些事情.
解决方法
这是可能的,但不是您在问题中描述的设计.在ActiveRecord适配器方法(如第一个,所有,最后一个等等)中直接实现它并不干净.你需要重新思考你的设计.
(如果这太多阅读,请跳到“D”)
您有几个选择,这些选择都取决于您的权限的定义方式.我们来看几例:
A.用户列出了他拥有的部门,只有他可以访问他们
您可以简单地将其实现为与Active Record Associations的has_many / belongs_to关联
B.用户和部门是独立的(换句话说:没有上述情况下所述的所有权),并且可以为每个用户和每个部门单独设置权限.
您再次可以实现与Active Record Associations的has_and_belongs_to_many关联.您将需要创建Web逻辑,以便您的应用程序的管理员可以添加/编辑/删除访问权限.
C.更复杂的情况:现有的授权库
大多数人会转向授权解决方案,如cancan,pundit或other
D.当这些授权库对您的需求过大时(实际上是我的大部分项目中的情况),我发现通过rails scoping实现授权可以满足我的所有需求.
让我们通过一个简单的例子看看.我希望管理员能够访问整个数据库记录;并且常规用户仅访问状态=开放且仅在操作时间(例如上午8点至下午6点)的部门.我写了一个实现我的权限逻辑的范围
# Class definition class Department scope :accessible_by -> (user) do # admin user have all access,always if user.is_admin? all # Regular user can access only 'open' departments,and only # if their request is done between 8am and 6pm elsif Time.now.hour >= 8 and Time.now.hour <= 18 where status: 'open' # Fallback to return ActiveRecord empty result set else none end end end # Fetching without association Department.accessible_by(current_user) # Fetching through association Building.find(5).departments.accessible_by(current_user)
定义范围要求我们在我们的代码中使用它.您可以考虑“忘记”范围并直接访问模型的风险(即写入Department.all而不是Department.accessible_by(current_user)).所以这就是为什么你必须坚定地测试您的规范(在控制器或功能级别)的权限.
注意在这个例子中,当权限失败(如你在你的问题中提到),而不是一个空的结果集,我们不返回nil.通常情况下,您可以保持ActiveRecord方法链接功能.但是您也可以引发异常并将其从控制器中救出,然后重定向到“未授权”页面.