代码改变世界

XCommunity权限控制和配置体系

2009-06-28 19:46  Ivony...  阅读(762)  评论(0编辑  收藏  举报

1、问题

我们知到一般说到权限管理配置,最常见的就是这样的形式:

in:某人、某事

out:允许或拒绝。

更强大一些无非加入用户组的概念,某用户组的人,某事。

但实际上,在稍微复杂的系统中,这样的模式是很糟糕的,比如说论坛。

 

比如说,我要定义一个这样的权限:

用户可以在自己发起的话题中修改自己发布的回复。

这里的某人是针对任何人的,而某事是:“在自己发起的话题中修改自己发布的回复”,最后控制级别是允许。

 

这里面有两个问题:

1、某人和某事是紧密联系在一起的,某事是针对特定的人的。而在传统的模式中,这两者是分开的,操作发起方应告诉权限控制系统完整的某事,即操作方还必须去判断这个是不是自己的话题和回复。

2、这个某事可以由几个基本条件组合出非常多的变化,不可能一开始都想到。

 

为了便于理解,我还是啰嗦两句,说明传统的方式会遇到多大的麻烦:

因为传统的权限控制方式只是简单的接受两个参数(某人,某事)然后查权限控制表,组合一下权限控制级别(Deny > Allow),然后告诉我们,你可不可以这么做。

那么,当用户点击修改按钮的时候,这个按钮必须自己判断出来,这个用户是不是在修改自己发表的回复,以及是不是在自己发起的话题里,然后告诉权限控制系统,这个用户正“在自己发起的话题中修改自己发布的回复”或者正“在不是自己发起的话题中修改自己发布的回复”或者。。。。。。

这样的组合我们不知道有多少种。

然后权限控制系统根据如此这些信息,查表得出结论,可以这么做。

 

所以,这种权限管理方式是不现实的。

 

2、权限项(ControlItem)

XCommunity的权限配置体系最小的配置项是权限控制项(ControlItem)。其定义为:

 

1public interface IControlItem
2{
3
4  bool IsSatisfied( IPrincipal user, string verb, params object[] frameObjects );
5
6  EntryControlType ControlType { get; } 
7
8  string Name { get; } 
9
10}
 
11

一个权限控制项就如同刚才我描述权限的那一个陈述句一样:“用户可以在自己发起的话题中修改自己发布的回复”。

将这个陈述句拆分成两个部分:“用户在自己发起的话题中修改自己发布的回复”,“可以”。

即条件(Condition)和控制类别(允许或拒绝)。

IsSatisfied方法用于判断当前是不是“用户在自己发起的话题中修改自己发布的回复”。

ControlType属性则告诉权限管理系统,如果是,则“可以”。

 

IsSatisfied方法的参数用于描述当前的情况(instance),使用了一个主谓宾结构来描述:

user,发起者,主语

verb,动词,谓语

frameObjects,所涉及到的框架对象,宾语。

 

这个结构中的核心是verb,动词。动词决定了宾语的结构,XCommunity预定义的动词与宾语结构的关系如下:

verb 动词描述 BoardFrame TopicFrame PostFrame PublishedDocument
ModifyPost 修改一个帖子   帖子所属的话题 被修改的帖子  
DeletePost 从话题删除一个帖子   帖子所属的话题 被删除的帖子  
ModifyTopicTitle 修改话题的标题 话题所在的版面 被修改的话题    
DeleteTopic 从版面删除一个话题 话题所在的版面 被修改的话题    
LaunchTopic 在版面发起一个话题 发起话题的版面     用于发起话题的文档
AttachTopic 加入一个话题的讨论   被参与的话题   用于发表回复的文档

 

BoardFrame、TopicFrame、PostFrame、PublishedDocument统称为框架对象(FrameObject)。frameObjects参数包含了所有相关的框架对象,由于在每一种宾语结构中,每个类型的对象都只有一个,所以用OfType方法来找出需要的框架对象。

 

用于描述“用户可以在自己发起的话题中修改自己发布的回复”的权限控制项的实现就可以是这样的:

 

1public class AllowModifyOwnResponseInOwnTopic : IControlItem
2{
3  public bool IsSatisfied( IPrincipal user, string verb, params object[] frameObjects )
4  {
5    if ( verb != "ModifyPost" )
6      return false;
7
8    var topic = frameObjects.OfType<ITopicFrame>().Single();
9    if ( !user.Identity.IsMySelf( topic.Meta.Launcher ) )
10      return false;
11
12    var post = frameObjects.OfType<IPostFrame>().Single();
13    if ( post.IsTheme )
14      return false;
15
16    if ( !user.Identity.IsMySelf( post.Author ) )
17      return false;
18
19    return true;
20
21  }
 
22
23  public EntryControlType ControlType
24  {
25    get { return EntryControlType.Allow; }
26  }
 
27
28  public string Name
29  {
30    get { return "AllowModifyOwnResponseInOwnTopic"; }
31  }

32}
 
33
34

 

这样,所有已想到和没想到的权限设置都可以用一个个的类型来描述了。

3、配置

但是如果所有的权限控制项都用代码来实现,是不现实的。所以,同样的,权限控制项可以通过XML文件来配置。

一个权限控制项典型的配置如下:

 

<control-item>
  
<condition verb=”DeletePost” />
  
<control-type>Deny</control-type>
</control-item>

 

 

其中condition元素对应为IsSatisfied方法,control-type则对应ControlType属性。该控制项表示任何情况下都禁止删除帖子的操作。

<condition>元素主要有两种配置方案:

设置一个类或者方法来处理,如:

<condition type=”XCommunity.Forums.Users.ControlConditions.ModifyOwnResponse” />

<condition method=”XCommunity.Forums.Users.ControlConditions.IsModifyOwnResponse” />

 

直接通过XML来配置一系列条件的组合,如上例的“用户可以在自己发起的话题中修改自己发布的回复”可以如此设置:

<control-item>
  
<condition verb=”ModifyPost” >
    
<topic launcher=”#current-logged” >
    
<post theme=”false” author=”#current-logged” >
  
</condition>
  
<control-type>Allow</control-type>
<control-item>

 

其中的topic和post会自动对应为frameObjects中的ITopicFrame对象和IPostFrame对象。当然,如果要对自定义类型的对象进行条件判断,也可以透过简单的语法实现:

<condition verb=”AttachTopic” >
  
<frame-object type=”MyTopic” >
    
<property name=”locked” value=”false” />
  
</frameObject>
</condition>

 

condition元素可以包含多个条件,各个条件之间都是与的关系,如果某个条件因为某些原因无法判断,例如找不到指定类型的对象,则直接判否。

 

4、控制组

权限控制项的粒度是很小的,很多时候我们需要设置一批权限控制项,所以我们需要将一些权限控制项组合在一起加以管理,这就是权限控制组,同样用XML文件来配置:

<control-group name=”DemoGroup” >

  
<control-item>
    
<condition verb=”ModifyPost” >
      
<topic launcher=”#current-logged” >
      
<post theme=”false” author=”#current-logged” >
    
</condition>
    
<control-type>Allow</control-type>
  
<control-item>

  
<control-item>
    
<condition verb=”AttachTopic”>
      
<frame-object type=”MyTopic”>
        
<property name=”locked” value=”false” />
      
</frameObject>
    
</condition>
  
</control-item>

</control-group>

 

但有时候同一个权限项可能被多个组包含,为了避免重复代码,所以还支持这样的语法。

<control-item name=”AllowModifyOwnResponseInOwnTopic” >
  
<condition verb=”ModifyPost” >
    
<topic launcher=”#current-logged” >
    
<post theme=”false” author=”#current-logged” >
  
</condition>
  
<control-type>Allow</control-type>
<control-item>

<control-group>
  
<control-item ref=”AllowModifyOwnResponseInOwnTopic” />
</control-group>

 

同样的,控制组也可以命名,并且被包含于其他控制组内。

权限控制组和控制项的定义都必须包含在授权配置元素(authorities)内。不包含在任何权限控制组内的权限控制项和组定义必须是命名的(name属性是必须的),而在控制组内的内联定义则不能命名(不能有name属性),即authorities的直接子元素的control-item和control-group是必须具备name属性的,而control-group内的control-item和control-group则是不能有name属性的。