我有一个安全架构,通过拥有SecureEntity引用来保护某些实体。 SecureEntity有一组RolePermissions,每个RolePermissions都有一个Allow标志和一个Priority。我们的想法是将用户的角色与SecureEntity上的RolePermissions相匹配。例如,用户可能被其最低优先级权限允许但被较高权限拒绝,因此它是我们感兴趣的最高权限。在此示例中,我查询的根实体称为ProcessCategory。
(SecureRoleId是用户角色的匹配; SecureRoleName只是一个字符串描述。)
假设用户具有角色(1,2)并且SecureEntity具有RolePermissions:
SecureRoleId = 1, Priority = 0, Allow = true
SecureRoleId = 2, Priority = 1, Allow = false
在这种情况下,不会选择实体。但如果用户只有角色1,则会选择该实体。当然,SecureEntity可能包含一堆用户没有的其他角色,并且无关紧要。
下面的sql代码可以工作并执行此操作:'如果用户也拥有的最高优先级角色权限是Allow = true,则选择实体'。因此,它基本上过滤了用户自己角色(IN子句)上的RolePermission,按优先级排序,如果是允许,则取最高值。
这是Sql:
select pc.* from ProcessCategory pc
join SecureEntity se
join RolePermission rp on se.SecureEntityId = rp.SecureEntityId
on pc.SecureEntityId = se.SecureEntityId
where rp.RolePermissionId = (select top 1 RolePermissionId
from RolePermission
where Allow = 1
and SecureEntityId = se.SecureEntityId
and SecureRoleId in(0,1)
order by Priority desc)
可能有另一种方法来编写上面的Sql,但它可以满足我的需要。理想情况下,我想使用NHibernate Linq或Criteria实现这一目标。我花了几个小时试图让Linq工作,并且在内部联接到RolePermission时出现了各种“无效操作”异常。我对ICriteria或MultiCriteria没有太多经验,如果有人可以帮助我,我会感兴趣。
请注意,对象的Fluent映射非常简单:
<some-entity>.References(x => x.SecureEntity)
和
SecureEntity.HasMany(x => x.RolePermissions).Not.Inverse();
答案 0 :(得分:0)
好。我无法使用原生 NH Linq来实现这一点,尽管这并不意味着它不可能。但我查看了Linq的所有NH单元测试,但找不到任何相同的东西。
为了使它工作,我创建了一个名为UserHasPermission的数据库函数,它可以执行以下任何操作:
on pc.SecureEntityId = se.SecureEntityId
where rp.RolePermissionId = (select top 1 RolePermissionId
from RolePermission
where Allow = 1
and SecureEntityId = se.SecureEntityId
and SecureRoleId in(0,1)
order by Priority desc)
这适用于任何类型的安全实体。然后按照本页中的说明将该函数映射为NH Linq函数:http://wordpress.primordialcode.com/index.php/2010/10/01/nhibernate-customize-linq-provider-user-defined-sql-functions/。
如果您按照这些说明操作,则必须在C#中创建一个普通的LinqToObjects扩展,该扩展与您的数据库签名具有相同的签名。然后,您可以执行NH Linq查询,如:
return base.Query<T>().Where(c => ((ISecureEntity)c)
.SecureEntity.Id
.UserHasPermissions(user.SecureRoleIdsCsv) == 1);
我发现的唯一问题是我的原始Sql函数返回了一点,我将其映射到NH布尔类型。然而,这产生了一个非常奇怪的sql,其中有几个“Where''True''=''True''”子句在Sql Server中爆炸了。所以我将结果更改为整数,一切正常。有点违反直觉,但......
这样做可以让我在不影响现有代码的情况下透明地使用Linq进行所有查询,因为它会自动为每个查询添加安全检查。
请注意,我查看了Rhino Security源代码,它使用了一个过于复杂的多重标准,我无法理解我的NH知识。如果我使用CreateCriteria完成它,我可以将它与Linq结合使用吗?