本文介绍了如何在XWiki上以编程方式创建具有特定权限集的新组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写自己的XWiki身份验证器(扩展了XWikiAuthServiceImpl ),在其中,我希望创建几个组,每个组具有不同的权限集。如何以编程方式进行?



示例




  • XWiki.MyStandardGroup -视图编辑评论

  • XWiki.MyClassicGroup - view 注释脚本

  • XWiki.MyAdminGroup -视图编辑提交脚本删除 admin



此外,我还以编程方式创建了用户。如何为不同的用户组赋予不同的访问权限?



从概念上讲,用户(具有权限)如何处理页面(具有权限)?我尝试阅读以下文档:








他们似乎没有解释这些—或许,他们确实这样做了,但是写起来很复杂,没有任何具体的例子,这使得很难理解不同实体(页面,用户和组)的权限如何协同工作。以下是中的一些文字需要理解的示例:

粗体部分甚至意味着什么?我认为 level 一词在相同 基本规则部分下的不同项目符号上使用的含义不同。

解决方案

我觉得这篇文章中有三个问题:


  1. 如何如何以编程方式创建用户和组?

  2. 访问权限系统如何工作?

  3. 是否有示例引用访问权限中的文本页面

第一个是第二个的答案。



访问权限系统如何工作-例如



XWiki中有固定数量的权限,例如视图编辑等。



用户可以将这些权限直接分配给他们,或者它们可以是组的成员,并且该组将这些权限分配给他们。权限分配可以发生在不同的位置(在文档中称为级别)。



级别结构如下:



Wiki级别



首先是主Wiki(在安装Wiki时会预先安装)。然后可能会有更多的Wiki,称为子Wiki,您可以手动创建(通过每个Wiki页面右上方的 Burger菜单中的 Wiki部分)。这是一个简单的两层层次结构:

 主Wiki(始终存在) 
| --- subwiki1
|
| --- subwiki2
|
| --- subwiki3

子维基不能嵌套。我不会详细说明为什么您会想要它们。没有他们,他们常常会走。
用户和组可以存在于主Wiki中(这意味着其个人资料页面位于主Wiki中),也可以存在于子Wiki中(即其个人资料页面位于其中)。主Wiki在所有子Wiki中都是可见的(并且可以为其分配权限),但是反之则不行-子Wiki中的用户不能在主Wiki中获得特殊权限(并且在另一个子Wiki中也不能)。如果此类用户访问主Wiki,则将其视为匿名用户。他们只能登录到子Wiki。



页面级别



第二,(几乎)所有数据Wiki存储在页面中。这些页面也是嵌套的,从XWiki 7.x开始,它们可以任意深度嵌套。这是级别结构的另一部分。



对于每个Wiki,都有一组预安装页面和用户创建的顶层页面。
然后是这些顶层页面的子级页面,这些页面又可以有子级,依此类推。
另一个麻烦之处在于,并非所有页面都可以包含子页面。按照历史惯例,这些全名以 WebHome 结尾的页面可以有子页面,其他页面则不能。这可能对用户是透明的,但对程序员而言很重要。



没有一个单独的根页面可以启动层次结构。例如,对于一个Wiki,其结构可能类似于:

 顶级第三级
第二级

Main.WebHome(预安装的开始页面)
| ------ Main.Search(预安装的搜索页面,无子页面)
| ------ Main.SomePage.WebHome(用户创建的页面,可以有孩子)

Sandbox.WebHome(预安装的游乐场页面)
|
| ------ Sandbox.TestPage1(预安装的演示页,无子页)
| ------ Sandbox.TestPage2(预安装的演示页,无子页)
| ------ Sandbox.TestPage3(预安装的演示页,无子页)
| -------Sandbox.SomePage.WebHome(用户创建的第二级页面,可以有孩子)

Documentation.WebHome(用户创建的顶级页面)
| ------ Documentation.Topic1.WebHome(用户创建的第二级页面,可以有孩子) |
| | ------ Documentation.Topic1.SubTopic1.WebHome(用户创建的第3个lvl页,也可以有子级)
| |
| | ------ Documentation.Topic1.SubTopic2.WebHome(用户创建的第3个lvl页,也可以有子级)
| |
| | ------ Documentation.Topic1.SubTopic3.WebHome(用户创建的第3个lvl页,也可以有子级)
| | |
| | | ------ Documentation.Topic1.SubTopic3.EvenMore.WebHome(用户创建的第4个lvl页,可以有子级)
| |
| 。
| 。
| |
| | ------ Documentation.Topic1.SubTopicN.WebHome(用户创建的第3个lvl页,也可以有子级)
|
| ------ Documentation.Topic2.WebHome(用户创建的第二个lvl页面,可以有子级)



|
| ------ Documentation.TopicN.WebHome(用户创建的第二个lvl页面,可以有孩子)

....



授予权利



您现在可以在此页面的每个页面上授予用户或组的权限通过向页面本身添加类型为 XWiki.XWikiRights 的对象,指定要授予的权限列表(混淆存储在属性 levels中),要授予该权限的用户和/或组的列表以及 allow / deny 标志...我们将来到以后。问题中讨论了如何以编程方式执行此操作:



在这种情况下,仅授予页面 自身的权限,不授予其子页面的权限。如果您在页面 Main.WebHome 上将正确的 edit 授予组 XWiki.HomepageEditorsGroup ,那么只有该组的成员才能编辑页面,但这不会影响 Main.Search Main等子页面.SomePage.WebHome



此处的 levels 属性实际上存储的权限是也许令人困惑-这又是另一个历史决定。 (该软件的开发已有15年左右,开发人员承诺保持向后兼容性)。无论该属性的名称是什么,这些都是权限,而不是文档所讨论的级别



到继续进行权限管理:您还可以在页面所有 子页面上授予权限。这仅适用于可以包含子页面的页面。从技术上讲,这是通过将类型为 XWiki.XWikiGlobalRights 的对象添加到页面本身,而不是添加到名为 WebPreferences 的子页面来完成的。 code>。 (再次是历史决定。)



因此,如果您要授予组<$ c的视图权限$ c> XWiki.Topic1ViewerGroup 在页面 Documentation.Topic1.WebHome 及其子页面上,例如 Documentation.Topic1.SubTopic1。 WebHome Documentation.Topic1.SubTopic3.EvenMore.WebHome ,然后进入页面 Documentation.Topic1.WebPreferences (如果不存在,则创建它),并向其中添加类型为 XWiki.XWikiGlobalRights 的对象,其属性为:




  • level:视图

  • 组:XWiki.Topic1ViewerGroup

  • 允许:1



如何检查权限



现在,对特定权限的检查通常会查看给定页面本身,然后查看该页面的 WebPreferences ,然后在父页面的 WebPreferences ,依此类推。 (这是上升水平。)
支票一旦发现涵盖所涉权利的权利对象,便立即停止。



如果直到顶层页面都没有找到匹配的权利对象,然后检查了Wiki。 Wiki级别的权限再次存储在特殊页面 XWiki.XWikiPreferences 中,作为类 XWiki.XWikiGlobalRights 的对象。



最后,如果Wiki恰好是子Wiki,则可以参考主Wiki上的全局权限-再次在页面名称 XWiki.XWikiPreferences上进行。 ,但这次是在主Wiki中。



示例1:检查视图直接在 Documentation.Topic1.SubTopic3.WebHome




  • Documentation.Topic1.SubTopic3.WebHome 没有 XWiki.XWikiRights -没有决定

  • Documentation.Topic1.SubTopic3.WebPreferences 没有 XWiki.XWikiGlobalRights -没有决定

  • Documentation.Topic1.WebPreferences 具有 XWiki.XWikiGlobalRights 用于视图-停止做出决定

  • 结果:如果当前用户在组 XWiki.Topic1ViewerGroup ,他/他可以查看页面,否则不能查看



示例2:检查编辑 Main.WebHome




  • Main.WebHome 具有 XWiki.XWikiRights 用于编辑-停止做出决定

  • 结果:只有 XWiki.HomepageEditorsGroup 中的用户可以编辑,其他用户则不能编辑



示例3:在 Main.SomePage.WebHome 上检查编辑




  • Main.SomePage.WebHome 没有 XWiki。 XWikiRights -没有决定

  • Main.SomePage.WebPreferences 没有 XWiki。 XWikiGlobalRights -没有决定

  • 在页面层次结构中: Main.WebPreferences 没有 XWiki.XWikiGlobalRights -也没有决定

  • Main.WebHome XWiki.XWikiRights 未被参考,因为该权利仅适用进入页面本身)

  • 页面层次结构:我们已经在顶层页面上,因此请转到Wiki

  • 即。检查 XWiki.XWikiPreferences 以获得 XWiki.XWikiGlobalRights 以获得 edit

  • 通常,对于 XWiki.XWikiAllGroup ,有一个 allow:1 表示所有用户都允许 edit

  • 如果没有这样的设置,子Wiki:进入Wiki层次结构,并检查 Wiki的 XWiki.XWikiPreferences

  • 没有决定,不允许编辑权限



admin 是特例



为用户简化,但使概念复杂化, admin 权限反之亦然:如果 admin 权限在Wiki级别授予,则在所有页面上均有效。更何况,它隐式授予所有其他权限,例如 view edit 。 (这样做的原因是,在引入此特殊规则之前,用户经常将自己锁定在外。)



隐式拒绝如何工作?



现在引用:

我尝试举例说明,也是:



在上面的示例1 中,我写道:

此处是结果可以是:




  • 如果用户是<$ c $的成员,则允许用户查看页面(及其子页面) c> XWiki.Topic1ViewerGroup

  • 如果用户不是<$ c的成员,则拒绝用户查看该页面(及其子页面)的权限$ c> XWiki.Topic1ViewerGroup (即其他人)



即不管用户可能拥有什么权利-此处设置了权利后,只有符合设置中条件的用户才能查看。其他人都出去了。这是 隐式拒绝



作为替代,假设有人在沙盒上设置了权限对象。 WebPreferences (即影响沙箱和所有子页面):




  • level:编辑

  • groups:XWiki.Topic1ViewerGroup

  • allow:1



Sandbox.SomePage.WebHome (即仅影响此子页面):




  • level:编辑

  • groups:XWiki.Topic1ViewerGroup

  • allow:0



设置 allow:0 明确拒绝:只要您是 XWiki.Topic1ViewerGroup 的成员,便不能编辑此页面。在页面层次结构中较高级别(在沙盒和所有子页面上)存在 allow:1 的事实并不重要,因为它是



如何以编程方式做到这一点?



首先,小组应该在 XWiki 空间中作为终端子页面(即没有子页面)创建,例如 XWiki.MyCustomGroup 。但是,它们似乎可以在您要创建它们的任何地方工作。



另一方面,必须将用户必须创建为页面 XWiki。< LoginName> ,因为不幸的是,周围有很多代码希望用户位于此位置,而无处不在。



创建页面后(在API中,它们称为 Document ),向页面添加适当类的对象,设置所需的属性并保存页面。 / p>

在查看您的需求时,您似乎不想向页面层次结构中任何特殊位置的组授予权限;因此,我认为它们将在Wiki级别设置。因此,无需了解所有其他解释;只需获取 XWiki.XWikiPreferences 页,然后在其中添加所需的 XWiki.XWikiGlobalRights



我建议使用;有一个很好的,可确保始终存在 XWikiAllGroup
此界面旨在确保Wiki中存在单个页面,但是没有人阻止您检查其他页面是否也正确设置。您唯一需要记住的是其他页面不会自动保存,但是您可以使用 XWiki.saveDocument 方法手动保存。



要创建用户,有一种便捷方法 XWiki.createUser(String userName,Map values,XWikiContext context) XWiki 类中。 映射包含要在新用户上设置的属性的值;您可以在Wiki的 XWiki.XWikiUsers 页上查看可用的属性。



要创建组,您可以从上面的示例中借用代码。请注意,要创建一个新的空组,可以添加一个 XWiki.XWikiGroups 类型的对象;要将成员添加到组,应为每个用户再添加一个类型为 XWiki.XWikiGroups 的对象,并设置成员属性赋予用户的全名(即包括 XWiki。前缀)。



所以该类可能始于:

  @Component 
@Named( XWiki.MyStandardGroup)
公共类MyUserAndGroupsInitializer实现MandatoryDocumentInitializer
{
private static final String GROUP_CLASS_NAME = XWikiGroups;
private static final String MEMBER_ATTR =成员;

私有静态最终字符串RIGHTS_CLASS_NAME = XWikiGlobalRights;
private static final String GROUPS_ATTR =组;
private static final String USERS_ATTR =用户;
private static final String RIGHTS_ATTR =级别; //;)
私有静态最终字符串ALLOW_ATTR = allow;

@Inject
Provider< XWikiContext> contextProvider;

@Inject
org.slf4j.Logger记录器;

@Named 按照惯例包含名称初始化程序关心的页面的大小。这样一方面可以避免在初始化程序之间发生名称冲突,并且可以根据需要覆盖页面的现有初始化程序。您可以在这里选择一个不同的名称。



@Inject 组件是当前组件的访问者上下文,它允许我们访问当前Wiki中的数据,并在后台维护数据库连接。记录器也不会受伤。



由于我们需要实现 MandatoryDocumentInitializer ,因此我们首先需要告知我们关心的页面之一:

  @Override 
public EntityReference getDocumentReference()
{
返回新的LocalDocumentReference(XWiki.SYSTEM_SPACE, MyStandardGroup);
}

这使得XWiki在下一个方法中将我们作为参数传递给页面;如果此后需要保存该页面,则应在此处返回 true 。当我们自己做某事时,我们也总是可以返回 false

  @Override 
public boolean updateDocument(XWikiDocument document)
{
logger.info(尝试创建用户/组);
try {
//在这里创建用户
//和您的组
} catch(XWikiException xe){
//因为我们不允许这样做通过:
logger.error(创建组失败,xe);
}
返回false;
}

基本上就是这样。哦,一些可能有用的助手:



添加用户相对容易:

  private void createUser(String userFullName)throws XWikiException 
{
XWikiContext context = contextProvider.get();
XWiki xwiki = context.getWiki();

Map< String,String>值=新的HashMap<>();
values.put( last_name,userFullName);
values.put( password,装订电池马正确);

int结果= xwiki.createUser(用户名,值,上下文);
if(结果> 0){
logger.info(用户[{}]已创建,userFullName);
} else {
logger.debug( user [{}] aleady存在,userFullName);
}
}

好的,也许不是那个简单,但是您可以从那开始。



对于组来说,几乎是相同的:

  //以逗号分隔的字符串形式传递权限,例如: view,comment,edit 
//成员应该是用户的完整页面名称,包括 XWiki。部分
私有void createGroup(字符串组,字符串权限,字符串...成员)引发XWikiException
{
logger.info(尝试创建组[{}],group);
XWikiDocument groupDoc = checkDocument(XWiki.SYSTEM_SPACE +‘。’+组);
if(groupDoc.isNew()){
addUserToGroup(groupDoc,);
表示(字符串成员:成员){
addUserToGroup(groupDoc,member);
}
XWikiContext context = contextProvider.get();
XWiki xwiki = context.getWiki();
xwiki.saveDocument(groupDoc, created,false,上下文);
logger.info( group [{}] created,group);
}
setRightsForGroup(groupDoc,rights);
}

将用户添加到组中也很容易:

  //如果随后需要保存组,则返回true。
私有布尔型addUserToGroup(XWikiDocument groupDoc,String userName)抛出XWikiException
{
XWikiContext context = contextProvider.get();
LocalDocumentReference groupClassReference =新的LocalDocumentReference(XWiki.SYSTEM_SPACE,GROUP_CLASS_NAME);

//首先检查用户是否已经是组
if(groupDoc.getXObject(groupClassReference,MEMBER_ATTR,userName,false)!= null){
//已经是成员,无需更改
logger.debug(用户[{}]已经是组[{}]的成员,userName,groupDoc.getFullName());
返回false;
}

logger.info(将用户[{}]添加到组[{}],userName,groupDoc.getFullName());
BaseObject newGroupEntry = groupDoc.newXObject(groupClassReference,context);
newGroupEntry.setStringValue(MEMBER_ATTR,userName);
返回true;
}

...如果不是因为我已进入的权限设置

  //为组设置权限设置(如果尚未设置);立即保存结果
private void setRightsForGroup(XWikiDocument groupDoc,String rights)抛出XWikiException
{
XWikiContext context = contextProvider.get();
XWiki xwiki = context.getWiki();

LocalDocumentReference rightsClassReference = new LocalDocumentReference(XWiki.SYSTEM_SPACE,RIGHTS_CLASS_NAME);
字符串groupName = groupDoc.getFullName();

//检查XWikiPreferences中是否已经设置了权限。
// //我们需要遍历所有值,而不是
XWikiDocument xwikiPrefDocument = xwiki.getDocument(new DocumentReference(context.getWikiId(),XWiki.SYSTEM_SPACE, XWikiPreferences),context);
boolean found = false;
for(BaseObject rightsSetting:xwikiPrefDocument.getXObjects(rightsClassReference)){
if(rights.contentEquals(rightsSetting.getStringValue(RIGHTS_ATTR))
&& rightsSetting.getIntValue(ALLOW_ATTR)== 1){
//这是正确的设置!
字符串组= rightsSetting.getStringValue(GROUPS_ATTR);
if(!groups.contains(groupName)){
//我们的组丢失:添加组并保存
rightsSetting.setStringValue(GROUPS_ATTR,groups +’,’+ groupName);
xwiki.saveDocument(xwikiPrefDocument,为组[ + groupName +]添加权限,true,上下文);
logger.info( [[}}组的修订权限,groupName);
} else {
logger.info(组[{}]的权限已设置,groupName);
}
found = true;
休息时间;
}
}
if(!found){
BaseObject newRightsSetting = xwikiPrefDocument.newXObject(rightsClassReference,context);
newRightsSetting.setStringValue(RIGHTS_ATTR,权利);
newRightsSetting.setIntValue(ALLOW_ATTR,1);
newRightsSetting.setLargeStringValue(GROUPS_ATTR,groupName);
if(newRightsSetting.getIntValue(ALLOW_ATTR)!= 1){
logger.error(为组[{}]添加类[{}]的权限失败!,rightsClassReference,上下文);
}
xwiki.saveDocument(xwikiPrefDocument,为组[ + groupName +]添加权限,true,上下文);
logger.info(为组[{}]添加了新权限,groupName);
}
}

我也使用了 checkDocument 助手,它与updateDocument 基本上相同platform / blob / master / xwiki-platform-core / xwiki-platform-oldcore / src / main / java / com / xpn / xwiki / internal / mandatory / XWikiAllGroupDocumentInitializer.java rel = nofollow noreferrer> XWikiAllGroupInitializer ,只是输入了名称,而繁琐的新设置页面是返回值。



您可能需要阅读,以了解如何注入必要的依赖项。尤其是,您需要将初始化程序的完整类名添加到 src / main / resources / META-INF / components.txt 中,以便激活初始化程序。



在尝试之前,请备份数据库。除了在正确设置所有内容之前进行几次尝试之外,每次重新启动Wiki时都不会保存不必要的内容。
还摆弄 WEB-INF / classes / logback.xml 将级别设置为 INFO 您的包裹,如果您想查看日志消息。






一些其他随机建议



您可以考虑将其存储在LDAP服务器中,然后通过。 (不过,您仍然需要创建组并管理其权限)



在开发过程中,我发现拥有扩展。它不能替代任何文档,但是能够以交互方式浏览API Javadoc对我有很大帮助。



扩展程序中的页面向您显示了当前Wiki中该扩展程序所授予的所有权限。已安装。 (转到 ... / xwiki / bin / view / Admin / ,然后单击显示权限。)


I'm writing my own XWiki Authenticator (that extends XWikiAuthServiceImpl) and therein, I want to create few groups, each with different sets of rights. How do I do it programmatically?

Example,

  • XWiki.MyStandardGroup - view, edit, comment
  • XWiki.MyClassicGroup - view, edit, comment, script
  • XWiki.MyAdminGroup - view, edit, commit, script, delete, admin

Also, I create the users programmatically. How do I give different access rights to different sets of users?

On the conceptual level, how do users (with rights) work with pages (with rights)? I tried to read the following docs:

They dont seem to explain these — or maybe, they do but written in a complex way without any concrete examples which makes it difficult to get the idea of how rights on different entities (pages, users and groups) work together. Here are some text from the Access Rights which needs example to be understood:

What does the bold part even mean? I think the term level is used in different sense on different bullet points under the same Basic rules section.

解决方案

I feel there are three questions in this post:

  1. How do I create Users and Groups programatically?
  2. How does the Access Rights system work?
  3. Is there an example for the text quoted from the access rights page

First an answer to the second one.

How does the Access Rights system work - with example

There are a fixed number of rights in XWiki, like view, edit, etc.

Users can get these rights assigned directly to them, or they can be member of a group, and the group has these rights assigned to them. This assignment of rights can happen in different places (which are called "levels" in the documentation).

The "level" structure is as follows:

Wiki levels

First there is the main wiki (that gets pre-installed when you install the wiki). Then there might be more wikis, called "sub-wikis", which you can create manually (via the "Wikis" secton from the "Burger" menu on the top right of every wiki page). This is a simple two layer hierarchy:

main wiki (always exists)
   |
   |--- subwiki1
   |
   |--- subwiki2
   |
   |--- subwiki3

Subwikis cannot be nested. I am not going into details why you might want them; oen can often go without them.Users and groups can exist in the main wiki (which means their profile pages are located in the main wiki), or they can exist in subwikis (i.e. their profile pages are there.)Users and Groups from the main wiki are visible in all subwikis (and can get rights assigned to them), but not the other way round - a user located in a subwiki cannot get special rights in the main wiki (and also not in another subwiki). If such users access the main wiki, they are treated as the anonymous user. They can only log in to the subwiki.

Page levels

Second, (nearly) all data in the wiki is stored in pages. These pages are also nested, and since XWiki 7.x they can be nested arbitrarily deep. This is the other part of the "levels" structure.

For every wiki, there is a set of "top level" pages, both preinstalled and user created.Then there are pages which are children of these "top level" pages, which in turn can have children, and so on.As an additional complication, not all pages can have subpages. By historical convention these pages with a full name ending in WebHome can have children pages, others can not. This is probably transparent to the user, but important for the programmer.

There is no single "root" page to start the hierarchy. As an example, for one wiki the structure might look like:

Top level                Third Level
            Second Level                    Fourth Level

Main.WebHome                                             (preinstalled "Start" page)
   |
   |------ Main.Search                                   (preinstalled search page, no subpages)
   |
   |------ Main.SomePage.WebHome                         (user created page, can have children)

Sandbox.WebHome                                          (preinstalled playground page)
   |
   |------ Sandbox.TestPage1                             (preinstalled demo page, no subpages)
   |
   |------ Sandbox.TestPage2                             (preinstalled demo page, no subpages)
   |
   |------ Sandbox.TestPage3                             (preinstalled demo page, no subpages)
   |
   |------ Sandbox.SomePage.WebHome                      (user created 2nd level page, can have children)

Documentation.WebHome                                    (user created top level page)
   |
   |------ Documentation.Topic1.WebHome                  (user created 2nd level page, can have children)
   |           |
   |           |------ Documentation.Topic1.SubTopic1.WebHome   (user created 3rd lvl page, can have children, too)
   |           |
   |           |------ Documentation.Topic1.SubTopic2.WebHome   (user created 3rd lvl page, can have children, too)
   |           |
   |           |------ Documentation.Topic1.SubTopic3.WebHome   (user created 3rd lvl page, can have children, too)
   |           |                  |
   |           |                  |------ Documentation.Topic1.SubTopic3.EvenMore.WebHome   (user created 4th lvl page, can have children)
   |           |
   |           .
   |           .
   |           |
   |           |------ Documentation.Topic1.SubTopicN.WebHome   (user created 3rd lvl page, can have children, too)
   |
   |------ Documentation.Topic2.WebHome                  (user created 2nd lvl page, can have children)
   .
   .
   .
   |
   |------ Documentation.TopicN.WebHome                  (user created 2nd lvl page, can have children)

....

Granting rights

You now can grant a right to user or group on every page in this hierarchy by adding an Object of type XWiki.XWikiRights to the page itself, specifying the list of rights to grant (confusingly stored in the attribute levels of that object), the list of users and/or groups to grant the right to, and a allow/deny flag ... which we will come to later. How to do that programatically is discussed in the question: Set user and group rights to document in XWiki

In that case the right is only granted for the page itself, not its subpages. If you give the right edit on the page Main.WebHome to the group XWiki.HomepageEditorsGroup, then only members of this group can edit the page, but this does not affect subpages like Main.Search or Main.SomePage.WebHome.

That the attribute levels here actually stores the rights is maybe confusing - again this is another historical decision. (The software is developed since 15 years or so and the developers are commited to keep backwards compatibility). What ever the attribute is named, these are rights, and not the levels the documentation talks about.

To go on with the rights management: You can also grant a right on a page and all its subpages. This only works for pages which can have subpages. Technically this is done by adding an object of type XWiki.XWikiGlobalRights ... but not to the page itself, but to a subpage named WebPreferences. (Historical decision, again.)

So if you want to grant the view right to the group XWiki.Topic1ViewerGroup on the page Documentation.Topic1.WebHome and its subpages like Documentation.Topic1.SubTopic1.WebHome or Documentation.Topic1.SubTopic3.EvenMore.WebHome, then you take the page Documentation.Topic1.WebPreferences (creating it if it does not exist), and add an object of type XWiki.XWikiGlobalRights to it, with the attributes:

  • level : view
  • groups : XWiki.Topic1ViewerGroup
  • allow: 1

How the rights are checked

Now the check for a specific right usually looks at a given page itself, then looks at the WebPreferences for that page, then at the WebPreferences of the parent page, and so on. (It is "going up the levels".)The check stops as soon as it finds a "rights" object covering the right in question.

If no matching "rights" object has been found up to the top level page, then the wiki is checked. Rights on the wiki level are stored in the special page XWiki.XWikiPreferences, again as objects of class XWiki.XWikiGlobalRights.

Finally if the wiki happens to be a subwiki, the global righs on the main wiki might be consulted - again on the page names XWiki.XWikiPreferences, but this time in the main wiki.

Example 1: check for view right on Documentation.Topic1.SubTopic3.WebHome

  • Documentation.Topic1.SubTopic3.WebHome has no XWiki.XWikiRights - no decision
  • Documentation.Topic1.SubTopic3.WebPreferences has no XWiki.XWikiGlobalRights - no decision
  • Documentation.Topic1.WebPreferences has an XWiki.XWikiGlobalRights for view - stop to make a decision
  • Result: if the current user is in the group XWiki.Topic1ViewerGroup, she/he can view the page, otherwise not

Example 2: check for edit right on Main.WebHome

  • Main.WebHome has a XWiki.XWikiRights for edit - stop to make a decision
  • Result: only users in the XWiki.HomepageEditorsGroup can edit, others do not

Example 3: check for edit right on Main.SomePage.WebHome

  • Main.SomePage.WebHome has no XWiki.XWikiRights - no decision
  • Main.SomePage.WebPreferences has no XWiki.XWikiGlobalRights - no decision
  • up the page hierarchy: Main.WebPreferences has no XWiki.XWikiGlobalRights - no decision either
  • (that Main.WebHome has a XWiki.XWikiRights is not consulted, as the right applies only to the page itself)
  • up the page hierarchy: we are already at a top-level page, so go to the wiki instead
  • i.e. check XWiki.XWikiPreferences for a XWiki.XWikiGlobalRights for edit
  • usually there is an allow : 1 for the XWiki.XWikiAllGroup which means edit is allowed for all users
  • if there is no such settings, and we are in a subwiki: go up the wiki hierarchy and check the XWiki.XWikiPreferences of the main wiki
  • if even there is no decision made, the edit right is not allowed

admin is a special case

As a simplification for the users, but complication for the concept, the admin right works the other way round: if the admin right is granted on the wiki level, it is valid on all pages. Even more, it implicitely grants all the other rights, like view and edit. (The reason for this is that users too often locked themselves out before this special rule was introduced.)

How does the "implicit deny" work?

Now to the quote:

I try to explain by example, too:

In the Example 1 above I wrote:

Here the result is either:

  • allow the user to view the page (and its sub pages), if the user is member of the XWiki.Topic1ViewerGroup
  • deny the user the right to view the page (and its sub pages), if the user is not member of the XWiki.Topic1ViewerGroup (i.e is "everyone else")

That is, no matter what rights the user might have otherwise - as soon as the right is set here, then only the users fulfilling the criterion in the settings are allowed to view. Everybody else is out. This is an "implicit deny".

As an alternative, assume someone has set a rights object on Sandbox.WebPreferences (i.e. affecting the "Sandbox" and all subpages):

  • level : edit
  • groups : XWiki.Topic1ViewerGroup
  • allow: 1

and onSandbox.SomePage.WebHome (i.e. affecting this sub page only):

  • level : edit
  • groups : XWiki.Topic1ViewerGroup
  • allow: 0

The setting allow: 0 is an "explicit deny": as soon as you are member of the XWiki.Topic1ViewerGroup, you are not allowed to edit this page. The fact that there is an allow: 1 on a higher level in the page hierarchy (on "Sandbox" and all sub pages) does not matter, beacuse it is not on the same level.

How to do that programatically?

First, the groups should be created as "terminal" sub pages (i.e. pages not having children) in the XWiki space, like XWiki.MyCustomGroup. However they seem to work wherever you want to create them.

On the other hand, users must be created as pages XWiki.<LoginName> as unfortunately there is a lot of code around that expects users to be in this location and nowhere else.

After having created the page (in the API they are called Document), add an object of the proper class to the page, set the attributes you want and save the page.

When looking at your requirements, it does not look like you want to grant the rights to the groups in any special place in the page hierarchy; so I assume they will be set on the wiki level. Thus no need to understand all the other explanations; just grab the XWiki.XWikiPreferences page and add the required XWiki.XWikiGlobalRights there.

I recommend using an MandatoryDocumentInitializer for this; there is a nice example in the code base which makes sure the XWikiAllGroup is always present.This interface is meant to ensure that a single page is present in the wiki, but nobody keeps you from checking that other pages are set up properly, too. The only thing you need to have in mind is that the other pages are not saved automatically, but you can do that manually with the XWiki.saveDocument method.

To create a user, there is a convenience method XWiki.createUser(String userName, Map values, XWikiContext context) in the XWiki class. The values map contains the values for the attributes to be set on the new user; you can check which attributes are available on the XWiki.XWikiUsers page in your wiki.

To create a group, you can borrow code from the example above. Note that to create a new empty group, one adds an object of type XWiki.XWikiGroups; to add members to the group one should add one more object of type XWiki.XWikiGroups for each user and set the member attribute to the full name of the user (i.e. including the 'XWiki.` prefix).

So the class might start with:

@Component
@Named("XWiki.MyStandardGroup")
public class MyUserAndGroupsInitializer implements MandatoryDocumentInitializer
{
    private static final String GROUP_CLASS_NAME = "XWikiGroups";
    private static final String MEMBER_ATTR = "member";

    private static final String RIGHTS_CLASS_NAME = "XWikiGlobalRights";
    private static final String GROUPS_ATTR = "groups";
    private static final String USERS_ATTR = "users";
    private static final String RIGHTS_ATTR = "levels"; // ;)
    private static final String ALLOW_ATTR = "allow";

    @Inject
    Provider<XWikiContext> contextProvider;

    @Inject
    org.slf4j.Logger logger;

The @Named contains by convention the name of the page the initializer cares about. That avoids name clashes between initializers on the one hand and allows to overwrite an existing initializer for a page, if wanted. You can choose a differetn name here if you prefer.

The @Injected compontents are an accessor to the current "context", which allows us to access the data in the current wiki and maintans a database connection in the background. A logger cannot hurt either.

As we need to implement the MandatoryDocumentInitializer, we first need to tell the location of one of the pages we care about:

    @Override
    public EntityReference getDocumentReference()
    {
        return new LocalDocumentReference(XWiki.SYSTEM_SPACE, "MyStandardGroup");
    }

This makes XWiki pass us in the page as a parameter in the next method; we should return true here if that page needs to be saved afterwards. As we do e verything by ourselves, we can as well return false always.

    @Override
    public boolean updateDocument(XWikiDocument document)
    {
        logger.info("try to create users/groups");
        try {
            // here create your users
            // and your groups
        } catch (XWikiException xe) {
            // as we are not allowed to let this through:
            logger.error("failed to create groups", xe);
        }
        return false;
    }

That is it, basically. Oh, some possibly useful helpers:

Adding users is relatively easy:

    private void createUser(String userFullName)  throws XWikiException
    {
        XWikiContext context = contextProvider.get();
        XWiki xwiki = context.getWiki();

        Map<String,String> values = new HashMap<>();
        values.put("last_name", userFullName);
        values.put("password", "staple battery horses correct");

        int result = xwiki.createUser(userName, values, context);
        if (result > 0) {
            logger.info("user [{}] created", userFullName);
        } else {
            logger.debug("user [{}] aleady exists", userFullName);
        }
    }

ok, maybe not that simple, but you can start with that one.

It is nearly the same for groups:

    // pass in rights as comma separated string, e.g.: "view,comment,edit"
    // members should be the full page name of the user, including the "XWiki." part
    private void createGroup(String group, String rights, String... members)  throws XWikiException
    {
        logger.info("try to create group [{}]", group);
        XWikiDocument groupDoc = checkDocument(XWiki.SYSTEM_SPACE + '.' + group);
        if (groupDoc.isNew()) {
            addUserToGroup(groupDoc, "");
            for (String member : members) {
                addUserToGroup(groupDoc, member);
            }
            XWikiContext context = contextProvider.get();
            XWiki xwiki = context.getWiki();
            xwiki.saveDocument(groupDoc, "created", false, context);
            logger.info("group [{}] created", group);
        }
        setRightsForGroup(groupDoc, rights);
    }

and adding users to the group is also easy:

    // return true if group needs to be saved afterwards
    private boolean addUserToGroup(XWikiDocument groupDoc, String userName) throws XWikiException
    {
        XWikiContext context = contextProvider.get();
        LocalDocumentReference groupClassReference = new LocalDocumentReference(XWiki.SYSTEM_SPACE, GROUP_CLASS_NAME);

        // first check if the user is already member of the group
        if (groupDoc.getXObject(groupClassReference, MEMBER_ATTR, userName, false) != null) {
            // is already member, no changes necessary
            logger.debug("user [{}] is already member of group [{}]", userName, groupDoc.getFullName());
            return false;
        }

        logger.info("add user [{}] to group [{}]", userName, groupDoc.getFullName());
        BaseObject newGroupEntry = groupDoc.newXObject(groupClassReference, context);
        newGroupEntry.setStringValue(MEMBER_ATTR, userName);
        return true;
    }

... if it were not for the rights settings that I have moved into a separate helper

    // set rights settings for group if it is not set yet; saves the result right away
    private void setRightsForGroup(XWikiDocument groupDoc, String rights) throws XWikiException
    {
        XWikiContext context = contextProvider.get();
        XWiki xwiki = context.getWiki();

        LocalDocumentReference rightsClassReference = new LocalDocumentReference(XWiki.SYSTEM_SPACE, RIGHTS_CLASS_NAME);
        String groupName = groupDoc.getFullName();

        // check if the right is already set in the XWikiPreferences.
        // here we need to loop over all values instead
        XWikiDocument xwikiPrefDocument = xwiki.getDocument(new DocumentReference(context.getWikiId(), XWiki.SYSTEM_SPACE, "XWikiPreferences"), context);
        boolean found = false;
        for (BaseObject rightsSetting : xwikiPrefDocument.getXObjects(rightsClassReference)) {
            if (rights.contentEquals(rightsSetting.getStringValue(RIGHTS_ATTR))
                && rightsSetting.getIntValue(ALLOW_ATTR) == 1) {
                // this is the right setting!
                String groups = rightsSetting.getStringValue(GROUPS_ATTR);
                if (!groups.contains(groupName)) {
                    // our group is missing: add group and save
                    rightsSetting.setStringValue(GROUPS_ATTR, groups + ',' + groupName);
                    xwiki.saveDocument(xwikiPrefDocument, "add rights for group [" + groupName + "]", true, context);
                    logger.info("amended rights for group [{}]", groupName);
                } else {
                    logger.info("rights for group [{}] already set", groupName);
                }
                found = true;
                break;
            }
        }
        if (!found) {
            BaseObject newRightsSetting = xwikiPrefDocument.newXObject(rightsClassReference, context);
            newRightsSetting.setStringValue(RIGHTS_ATTR, rights);
            newRightsSetting.setIntValue(ALLOW_ATTR, 1);
            newRightsSetting.setLargeStringValue(GROUPS_ATTR, groupName);
            if (newRightsSetting.getIntValue(ALLOW_ATTR) != 1) {
                logger.error("adding rights of class [{}] for group [{}] failed!", rightsClassReference, context);
            }
            xwiki.saveDocument(xwikiPrefDocument, "add rights for group [" + groupName + "]", true, context);
            logger.info("added new rights for group [{}]", groupName);
        }
    }

I have also used a checkDocument helper, which is basically the same as the updateDocument in the XWikiAllGroupInitializer, except that the name is input and the tediously newly set up page is the return value.

You might want to read the Component Guide to understand how the necessary dependencies get injected. Especially you will need to add the full class name of the initializer to the src/main/resources/META-INF/components.txt for the initializer to get activated.

Backup your database before you try this out. Except a few tries before everything is properly set up, and nothing gets saved unnecessarily on each wiki restart.Also fiddle with the WEB-INF/classes/logback.xml to set the level to INFO for your package, if you want to see the log messages.


Some random other advice

Instead of managing your users programatically you might consider storing then in a LDAP Server and use this for authentication with the LDAP Authenticator. (You still need to create the groups and manage their rights, however)

While developing I found it very useful to have the Scripting Reference Documentation extension installed in my development wiki. It is no a replacement for any documentation, but being able to brwose the API Javadoc interactively somehow helps me a lot.

The Admin Tools extension has a page that shows you all rights granted in the current wiki where this extension is installed. (Go to .../xwiki/bin/view/Admin/ and click "Show Rights".)

这篇关于如何在XWiki上以编程方式创建具有特定权限集的新组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-27 11:50