Документация COREmanager

Права доступа

 

В COREmanager доступ к функциям может быть определён двумя способами:

  • разработчиком на основании уровня доступа пользователя;
  • администратором при помощи модуля управления доступом.

Недоступные функции автоматически вырезаются из интерфейса. Вызов таких функций через InternalCall игнорируется и возвращает пустой XML, вызов через API — возвращает ошибку.

Доступ к функциям

Разработчик должен сам определить, какому пользователю будет доступна та или иная функция панели управления. Это может быть сделано в коде: в конструкторе класса Class_isp_api::Action или через переопределение виртуальных методов Authorize, CheckAccess, или через XML.

По умолчанию Authorize проверяет наличие текущего уровня пользователя в маске, переданной в конструкторе класса. Маска может быть дополнительно ограничена через атрибут @level в соответствующем элементе metadata. В случае успешной проверки вызывается метод CheckAccess, который по умолчанию выполняется успешно.

Все внешние функции по умолчанию доступны всем пользователям. Доступ к ним может быть определён только через атрибут @level.

Модуль управления доступом

В COREmanager встроена гибкая система контроля доступа. Она позволяет решать следующие задачи:

  • ограничение доступа к функциям панелей управления;
  • ограничение доступа к полям форм и списков;
  • ограничение доступа к записям списков.

Права могут быть установлены как для пользователя, так и для группы, в которую могут быть включены пользователи с одинаковым уровнем доступа.

Для того, чтобы в панели появилась возможность управления доступом конкретного пользователя, добавьте следующую кнопку в список ваших пользователей:

<toolbtn name="rights" func="userrights" type="editlist" img="t-rights"/>

Идентификатор записи должен содержать имя пользователя, которое используется при авторизации. В противном случае необходимо определить, каким образом идентификатор может быть преобразован в имя пользователя. Для этого вы можете написать собственный обработчик события RIGHTS_CALLBACK или воспользоваться классом Class_isp_api::RightsCallback.

Функции группы userrights проверяют достаточно ли прав у текущего пользователя для доступа к выбранной учётной записи при помощи вызова функции IsOwner.

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

Система накладывает ряд ограничений при работе с правами доступа:

  • пользователь не может менять права для самого себя;
  • нельзя менять права пользователю с уровнем доступа lvSuper (30) и выше;
  • нельзя менять права пользователю, уровень которого выше, чем у текущего пользователя.

Функции

COREmanager позволяет создавать:

  • пользователей, которые по умолчанию имеют доступ ко всем функциям согласно их уровню доступа;
  • пользователей, которые имеют доступ только к определённым функциям. Это настраивается через кнопку "Политики" в разделе "Доступ к функциям".

Функции, доступные для пользователей с уровнем доступа lvPublic, не зависят от настроек политик или доступа. Как правило, это служебные функции. Например, desktop, su, keepalive и т.п. Их запрет может привести к некорректной работе web-интерфейса.

Группы функций

Для удобства функции панели управления объединяются в группы по именам. Разделителем служит точка. Например, в группу функций с именем user попадут функции: user.edit, user.delete, user.delete.one, но не попадёт функция userrights. При этом user.delete.one входит как в группу user, так и в группу user.delete.

Если вы меняете доступ к функции, меняется доступ ко всем функциям, входящим в одноимённую группу. Например, если вы запрещаете доступ к функции user, этот запрет автоматически распространяется и на все функции, имя которых начинается со строки "user.".

Если вы устанавливаете доступ к функции, это действие автоматически отменяет настройки доступа, сделанные для группы, в которую эта функция входит.

Доступ к элементам списков

COREmanager позволяет ограничивать доступ не только к функциям, но и к отдельным записям списков. Для этого у списка должна быть реализована возможность установки фильтров. Модуль прав использует форму фильтра (берётся функция с именем .filter). Фильтр, установленный через модуль прав, не заметен для пользователя и не может быть снят. Поля, по которым был ограничен список, будут скрыты на форме фильтра при её открытии пользователем.

Если для списка применяется фильтр, то при редактировании элементов этого списка проверяется наличие фильтра. Для этого вызывается функция списка (имя списка берётся из имени функции формы путем отбрасывания правой части имени до точки) с параметром has_record=<идентификатор записи> (метод HasRecord класса ListAction). Результат должен быть аналогичен вызову функции списка, но может содержать только одну запись с запрошенным идентификатором, если такая запись доступна.

Вложенные списки

Для проверки доступа к элементам вложенных списков проверяются все базовые списки, имена которых вычисляются путём последовательного отбрасывания правой части имени функции до точки. Идентификатор записи в базовом списке вычисляется из параметра plid. Предполагается, что идентификаторы всех базовых списков перечислены в plid и разделены символом "/".

Групповые права

Помимо преобразования идентификатора пользователя в имя, функция userrights.user выполняет ряд других задач, позволяющих работать с группами пользователей. По умолчанию такая возможность отсутствует, т.к. COREmanager не позволяет работает с конкретными пользователями.

Для того, чтобы воспользоваться этой возможностью, необходимо добавить свой обработчик функции/события RIGHTS_CALLBACK. Эта функция служит для:

  • преобразования идентификатора пользователя в имя пользователя;
  • получения списка уровней доступа, задействованных в панели управления;
  • получения списка пользователей с заданным уровнем доступа.

Для упрощения реализации подобных обработчиков в состав API включен класс Class_isp_api::RightsCallback.

Например, у вас есть список пользователей-администраторов, реализованный функцией admin. Тогда вы можете создать экземпляр класса Class_isp_api::RightsCallback, передав ему необходимые параметры:

#include <api/module.h>
#include <api/rights.h>
 
namespace {
...
 
MODULE_INIT(admin, "") {
  new isp_api::RightsCallback(isp_api::lvAdmin, "admin", "name"); 
}
} // end of private namespace
Пояснения

Смешанные списки пользователей

В некоторых случаях функция отдаёт список пользователей сразу для нескольких уровней доступа. Чтобы использовать такой список в качестве источника для класса Class_isp_api::RightsCallback, необходимо указать значения уровней доступа для записей. Для этого можно использовать последний параметр конструктора класса Class_isp_api::RightsCallback. Это выражение XPath, которое используется для выбора записей из XML-документа, возвращаемого функцией, реализующей работу со списком пользователей.

Например, у вас есть список users, в который включены как пользователи с уровнем доступа lvUser, так и администраторы с уровнем доступа lvAdmin. Реализовать работу как с группой пользователей, так и с группой администраторов можно следующим образом:

#include <api/module.h>
#include <api/rights.h>
 
namespace {
...
 
MODULE_INIT(users, "") {
  // Предположим, что в списке есть столбец level, в котором указан уровень записи
  new isp_api::RightsCallback(isp_api::lvAdmin, "users", "name", "", "/doc/elem[string(level)='admin']/"); 
  new isp_api::RightsCallback(isp_api::lvUser, "users", "name", "", "/doc/elem[string(level)='user']/");
}
} // end of private namespace

Собственные группы

Если архитектура вашей панели использует собственные группы пользователей, синхронизируйте информацию о группах с COREmanager.

Для этого необходимо реализовать:

  • создание/удаление/переименование группы;
    // создание группы с именем "NewGroup" для пользователей с уровнем доступа 16
    // уровень доступа задаётся при создании группы и не может быть изменён
    InternalCall(ses, "userrights.group.edit", "level=level_16&name=NewGroup&sok=ok");
    // переименование группы с именем "NewGroup" в "MyGroup"
    InternalCall(ses, "userrights.group.edit", "elid=#NewGroup&name=MyGroup&sok=ok");
    // удаление группы "MyGroup"
    InternalCall(ses, "userrights.group.delete", "elid=#MyGroup");
  • добавление/удаление пользователя из группы;
    // добавление пользователя "user" в группу "MyGroup"
    InternalCall(ses, "userrights.group.users.resume", "elid=user&plid=MyGroup");
    // исключение пользователя "user" из группы "MyGroup"
    InternalCall(ses, "userrights.group.users.suspend", "elid=user&plid=MyGroup");
  • изменение свойств группы.
    // Включать всех пользователей с подходящим уровнем доступа в группу "MyGroup"
    // Запрещающие/разрешающие правила, установленные на эту группу, будут действовать
    // на всех пользователей с уровнем доступа 16 (см. предыдущие примеры)
    // Внимание! Данное действие автоматически исключает всех ранее включённых пользователей из группы.
    // Если с группы будет снят флаг "default", она не будет содержать ни одного пользователя
    InternalCall(ses, "userrights.group.edit", "elid=MyGroup&default=on&sok=ok");

Вместе с этими внутренними вызовами соответствующие изменения должны быть сделаны и в ваших собственных группах.

Чтобы избежать рассинхронизации, необходимо также отслеживать изменения, которые делаются через функции userrights. Например, следующим образом:

class AddUserToGroup : public Event {
public:
  AddUserToGroup() : Event("userrights.group.users.resume", "mygroup") {}
  virtual void AfterExecute(Session &ses) const {
    if (ses.conn.isInternal())
      return; // внутренний вызов
    const string &user = ses.Param(ELEM_ID);
    const string &group = ses.Param(PARENT_ID);
    AddUserToGroup(group, user);
  }
};

Преобразование идентификатора пользователя в имя

Когда пользователь авторизуется в системе, он вводит имя пользователя и пароль. Это имя в дальнейшем используется для сохранения настроек пользователя и его прав доступа. Иногда при работе со списком пользователей, хранящемся в базе данных, проще использовать в качестве ключа Id записи вместо имени. Тогда ключевое поле не совпадает с именем пользователя, и пример описанный выше не работает. В этом случае нужно изменить обработчик функции/события RIGHTS_CALLBACK:

#include <api/module.h>
#include <api/rights.h>
 
namespace {
...
 
MODULE_INIT(admin, "") {
  new isp_api::RightsCallback(isp_api::lvAdmin, "admin", "name", "id"); 
}
} // end of private namespace
Пояснения