Как программно создать новые группы с указанным c набором прав в XWiki? - PullRequest
1 голос
/ 22 марта 2020

Я пишу свой собственный XWiki Authenticator (тот extends XWikiAuthServiceImpl), и в нем я хочу создать несколько групп, каждая с разными наборами прав. Как мне сделать это программно?

Пример,

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

Также Я создаю пользователей программно. Как дать разные права доступа разным наборам пользователей?

На концептуальном уровне, как пользователи (с правами) работают со страницами (с правами)? Я пытался прочитать следующие документы:

Они не кажется, объясняют это - или, может быть, они делают, но написаны сложным образом без каких-либо конкретных примеров, что затрудняет понимание того, как права на разные объекты (страницы, пользователи и группы) работают вместе. Вот некоторый текст из Прав доступа , который нуждается в понимании примера:

  • Когда право разрешено на данном уровне, оно становится неявным отказано кому-либо еще на том же уровне . Это относится только к разрешенному праву. Если на этом уровне для пользователя / группы установлен только «Вид», все остальные права, такие как «Редактировать», по-прежнему наследуются. Использование этого неявного поведения запрета рекомендуется вместо применения явного отказа.

Что означает жирная часть? Я думаю, что термин level используется в разных смыслах для разных пунктов марки в разделе same Basi c rules section.

1 Ответ

2 голосов
/ 23 марта 2020

Я чувствую, что в этом посте три вопроса:

  1. Как программно создавать пользователей и группы?
  2. Как работает система прав доступа?
  3. Есть ли пример для текста, цитируемого со страницы прав доступа

Первый ответ на второй.

Как работает система прав доступа - на примере

В XWiki есть фиксированное количество прав, например view, edit и др. c.

Пользователи могут получить эти права, назначенные непосредственно им, или они могут быть членами группы, и группа имеет эти права, назначенные им. Это назначение прав может происходить в разных местах (которые в документации называются «уровнями»).

Структура «уровней» выглядит следующим образом:

Уровни вики

Во-первых, это основная вики (которая устанавливается предварительно при установке вики). Тогда может быть больше вики, называемых «под-вики», которые вы можете создать вручную (через раздел «Вики» из меню «Бургер» в правом верхнем углу каждой вики-страницы). Это простая двухуровневая иерархия:

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

Subwikis не может быть вложенным. Я не буду вдаваться в подробности, почему вы можете их хотеть; Часто они могут go без них. Пользователи и группы могут существовать в основной вики (что означает, что их страницы профиля находятся в основной вики), или они могут существовать в подвики (т.е. их страницы профиля есть.) Пользователи и группы из основной вики видны во всех подвики. (и может получить права, назначенные им), но не наоборот - пользователь, расположенный в подвики, не может получить специальные права в основной вики (а также не в другой подвики). Если такие пользователи получают доступ к основной вики, они рассматриваются как анонимные пользователи. Они могут только войти в подвики.

Уровни страниц

Во-вторых, (почти) все данные в вики хранятся на страницах. Эти страницы также являются вложенными, и, начиная с XWiki 7.x, они могут быть вложены сколь угодно глубоко. Это другая часть структуры «уровней».

Для каждой вики существует набор страниц «верхнего уровня», как предустановленных, так и созданных пользователем. Затем есть страницы, которые являются дочерними по отношению к этим страницам «верхнего уровня», которые, в свою очередь, могут иметь детей, и так далее. Как дополнительное осложнение, не все страницы могут иметь подстраницы. По историческому соглашению эти страницы с полным именем, оканчивающимся на WebHome, могут иметь дочерние страницы, другие не могут. Это, вероятно, прозрачно для пользователя, но важно для программиста.

Нет единой страницы "root" для запуска иерархии. Например, для одной вики структура может выглядеть следующим образом:

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)

....

Предоставление прав

Теперь вы можете предоставить право пользователю или группе на каждой странице в этой иерархии, добавив объект типа XWiki.XWikiRights для самой страницы, с указанием списка прав для предоставления (запутанно хранящегося в атрибуте levels этого объекта), списка пользователей и / или групп, которым предоставляется право, и allow/deny флаг ... к которому мы придем позже. Как это сделать программно, обсуждается в вопросе: Установить права пользователя и группы на документ в XWiki

В этом случае право предоставляется только для страницы сама , не ее подстраницы. Если вы предоставите право edit на странице Main.WebHome группе XWiki.HomepageEditorsGroup, то редактировать страницу могут только члены этой группы, но это не влияет на подстраницы, такие как Main.Search или Main.SomePage.WebHome.

.

То, что атрибут levels здесь фактически хранит права, может сбивать с толку - опять же, это еще одно историческое решение. (Программное обеспечение разрабатывается примерно 15 лет назад, и разработчики обязаны поддерживать обратную совместимость). Что бы ни называли атрибутом, это права , а не уровней , о которых говорится в документации.

К go в управлении правами: Вы можете также предоставьте право на страницу и все ее подстраниц . Это работает только для страниц, которые могут иметь подстраницы. Технически это достигается добавлением объекта типа XWiki.XWikiGlobalRights ... но не на саму страницу, а на подстраницу с именем WebPreferences. (Историческое решение, опять же.)

Итак, если вы хотите предоставить view право группе XWiki.Topic1ViewerGroup на странице Documentation.Topic1.WebHome и ее подстраницах, таких как Documentation.Topic1.SubTopic1.WebHome или Documentation.Topic1.SubTopic3.EvenMore.WebHome, то вы возьмите страницу Documentation.Topic1.WebPreferences (создайте ее, если она не существует) и добавьте к ней объект типа XWiki.XWikiGlobalRights с атрибутами:

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

Как проверяются права

Теперь проверка определенного права c обычно просматривает саму данную страницу, затем просматривает WebPreferences этой страницы, затем WebPreferences родительской страницы и так далее. (Он «поднимается по уровням».) Проверка прекращается, как только обнаруживается «объект» прав, охватывающий рассматриваемое право.

Если не найден соответствующий объект «прав» до самого верха Уровень страницы, то вики проверяется. Права на уровне вики хранятся на специальной странице XWiki.XWikiPreferences, опять же, как объекты класса XWiki.XWikiGlobalRights.

Наконец, если вики оказывается подвики, можно обратиться к глобальным правам в основной вики. - снова на именах страниц XWiki.XWikiPreferences, но на этот раз в основной вики.

Пример 1: проверка на view прямо на Documentation.Topic1.SubTopic3.WebHome

  • Documentation.Topic1.SubTopic3.WebHome имеет нет XWiki.XWikiRights - нет решения
  • Documentation.Topic1.SubTopic3.WebPreferences не имеет XWiki.XWikiGlobalRights - нет решения
  • Documentation.Topic1.WebPreferences имеет XWiki.XWikiGlobalRights для view - прекратить принимать решение
  • Результат: если текущий пользователь находится в группе XWiki.Topic1ViewerGroup, он / она может просматривать страницу, в противном случае он не

Пример 2: проверка на edit прямо на Main.WebHome

  • Main.WebHome имеет XWiki.XWikiRights для edit - прекратить принимать решение
  • Результат: редактировать могут только пользователи в XWiki.HomepageEditorsGroup, другие не

Пример 3: проверка на edit прямо на Main.SomePage.WebHome

  • Main.SomePage.WebHome не имеет XWiki.XWikiRights - нет решения
  • Main.SomePage.WebPreferences имеет нет XWiki.XWikiGlobalRights - нет решения
  • вверх по странице иерархия: Main.WebPreferences не имеет XWiki.XWikiGlobalRights - также не принято решение
  • (что Main.WebHome имеет XWiki.XWikiRights, не учитывается, поскольку это право распространяется только на саму страницу)
  • вверх по иерархии страниц: мы уже на странице верхнего уровня, поэтому go для вики вместо
  • т.е. проверяем XWiki.XWikiPreferences для XWiki.XWikiGlobalRights для edit
  • обычно allow : 1 для XWiki.XWikiAllGroup означает, что edit разрешено для всех пользователей
  • , если таких настроек нет, и мы находимся в подвики: go вверх по иерархии вики и проверьте XWiki.XWikiPreferences из main wiki
  • , если даже решение не принято, право edit не разрешено

admin является особым случаем

Как упрощение для пользователей, но усложнение концепции, право admin работает наоборот: если право admin предоставляется на уровне вики , это действительно на всех страницах. Более того, он косвенно предоставляет все остальные права, такие как view и edit. (Причина этого в том, что пользователи слишком часто блокировали себя до того, как было введено это специальное правило.)

Как работает «неявное запрещение»?

Теперь к цитате:

  • Когда право разрешено на данном уровне, оно неявно отказывается кому-либо еще на том же уровне . Это относится только к разрешенному праву. Если на этом уровне для пользователя / группы установлен только «Вид», все остальные права, такие как «Редактировать», по-прежнему наследуются. Использование этого неявного поведения запрета рекомендуется вместо применения явного отказа.

Я также пытаюсь объяснить примером:

В Примере 1 выше Я написал:

  • Documentation.Topic1.WebPreferences имеет XWiki.XWikiGlobalRights для view - прекратить принимать решение
  • Результат: если текущий пользователь находится в группе XWiki.Topic1ViewerGroup, он / она может просматривать страницу, в противном случае не

Здесь результат либо:

  • позволяет пользователю просматривать страницу (и ее подстраницы), если пользователь является членом XWiki.Topic1ViewerGroup
  • лишает пользователя права на просмотр страницы (и ее подстраниц), если пользователь не является членом XWiki.Topic1ViewerGroup (то есть является "всеми остальными")

То есть независимо от того, какие права пользователь может иметь в противном случае - как только это право будет установлено здесь, только пользователям, отвечающим критерию в настройках, будет разрешено просматривать. Все остальные отсутствуют. Это " неявное запрещение ".

В качестве альтернативы предположим, что кто-то установил объект прав на Sandbox.WebPreferences (то есть влияет на "Песочницу" и все подстраницы):

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

и Sandbox.SomePage.WebHome (т.е. влияет только на эту подстраницу):

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

Настройка allow: 0 - это " явный отказ": как только вы станете участником XWiki.Topic1ViewerGroup, вы не сможете редактировать эту страницу. Тот факт, что существует allow: 1 на более высоком уровне в иерархии страниц (в «Песочнице» и на всех вложенных страницах), не имеет значения, поскольку он не на том же уровне .

Как это сделать программно?

Во-первых, группы должны быть созданы как «терминальные» подстраницы (т. Е. Страницы, не имеющие дочерних элементов) в пространстве XWiki, например XWiki.MyCustomGroup. Однако кажется, что они работают везде, где вы хотите их создать.

С другой стороны, пользователи должны быть созданы в виде страниц XWiki.<LoginName>, к сожалению, существует много кода, который ожидает пользователей находиться в этом месте и нигде больше.

После создания страницы (в API они называются Document), добавьте объект соответствующего класса на страницу, установите нужные атрибуты и сохраните страницы.

При рассмотрении ваших требований не похоже, что вы хотите предоставить права группам в каком-либо особом месте в иерархии страниц; поэтому я предполагаю, что они будут установлены на уровне вики. Таким образом, нет необходимости понимать все другие объяснения; просто возьмите страницу XWiki.XWikiPreferences и добавьте туда XWiki.XWikiGlobalRights.

Я рекомендую использовать для этого MandatoryDocumentInitializer ; в базе кода есть хороший пример , который гарантирует, что XWikiAllGroup всегда присутствует. Этот интерфейс предназначен для того, чтобы гарантировать, что в вики присутствует одна страница, но никто не мешает вам также проверить, правильно ли настроены другие страницы. Единственное, что вам нужно иметь в виду, это то, что другие страницы не сохраняются автоматически, но вы можете сделать это вручную с помощью метода XWiki.saveDocument.

Для создания пользователя в классе XWiki существует удобный метод XWiki.createUser(String userName, Map values, XWikiContext context). Карта values содержит значения атрибутов, которые будут установлены для нового пользователя; Вы можете проверить, какие атрибуты доступны на странице XWiki.XWikiUsers в вашей вики.

Чтобы создать группу, вы можете позаимствовать код из приведенного выше примера. Обратите внимание, что для создания новой пустой группы добавляется объект типа XWiki.XWikiGroups; Чтобы добавить участников в группу, необходимо добавить еще один объект типа XWiki.XWikiGroups для каждого пользователя и установить для атрибута member полное имя пользователя (т. е. включая префикс 'XWiki`).

Таким образом, класс может начинаться с:

@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;

* @Named содержит условно название страницы, о которой заботится инициализатор. Это позволяет избежать столкновения имен между инициализаторами, с одной стороны, и позволяет перезаписать существующий инициализатор для страницы, если это необходимо. Здесь вы можете выбрать другое имя, если хотите.

* * * * * * * * * * * * Компоненты @Inject являются средством доступа к текущему «контексту», что позволяет нам получать доступ к данным в текущей вики и отображать соединение с базой данных в фон. Регистратор также не может повредить.

Поскольку нам нужно реализовать MandatoryDocumentInitializer, нам сначала нужно указать местоположение одной из страниц, которые нас интересуют:

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

Это делает XWiki передать нам на странице в качестве параметра в следующем методе; мы должны вернуть true здесь, если эту страницу нужно сохранить позже. Поскольку мы делаем все сами, мы всегда можем вернуть false.

    @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;
    }

Вот и все, в основном. О, некоторые, возможно, полезные помощники:

Добавление пользователей относительно просто:

    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);
        }
    }

хорошо, возможно, не , что просто, но вы можете начать с этого.

Это почти то же самое для групп:

    // 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);
    }

и добавить пользователей в группу также легко:

    // 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;
    }

... если бы не было настройки прав, которые я перенес в отдельный помощник

    // 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);
        }
    }

Я также использовал помощник checkDocument, который в основном совпадает с updateDocument в XWikiAllGroupInitializer , за исключением что имя вводится, а утомительно новая настроенная страница является возвращаемым значением.

Возможно, вы захотите прочитать Руководство по компонентам , чтобы понять, как вводятся необходимые зависимости. Особенно вам нужно будет добавить полное имя класса инициализатора в src/main/resources/META-INF/components.txt, чтобы инициализатор активировался.

Сделайте резервную копию базы данных, прежде чем попробовать это. За исключением нескольких попыток, прежде чем все будет правильно настроено, и при каждом перезапуске вики ничего не будет сохранено без необходимости. Также возьмите WEB-INF/classes/logback.xml для установки уровня INFO для вашего пакета, если вы хотите видеть сообщения журнала.


Несколько других случайных советов

Вместо управления своим пользователям программно вы можете подумать о том, чтобы затем сохранить его на сервере LDAP и использовать его для аутентификации с помощью LDAP Authenticator . (Тем не менее, вам все еще нужно создавать группы и управлять их правами)

При разработке я обнаружил, что очень полезно иметь расширение Справочная документация по сценариям , установленное в моей вики-разработке. Он не является заменой какой-либо документации, но возможность интерактивного использования API Javado c мне как-то очень помогает.

Расширение Admin Tools имеет страницу, которая показывает вам все Права предоставляются в текущей вики, где установлено это расширение. (Go до .../xwiki/bin/view/Admin/ и нажмите «Показать права».)

...