bizproc 26.1200.0: псевдодокумент WORKFLOW закрыт для не-админов, у узловых шаблонов появились роли
Обновление безопасности
Закрыта уязвимость или ослабленная проверка прав. Ставить в первую очередь.
До bizproc 26.1200.0 псевдодокумент WORKFLOW разрешал CreateWorkflow и WriteDocument любому пользователю, и запись узловых шаблонов ничем не ограничивалась. Теперь эти операции доступны только администратору, а для узловых шаблонов появились роли на Main\Access, которые после обновления ни к кому не привязаны. Ещё из модуля убрали автоочистку хранилищ, и старые записи будут копиться, пока вы не удалите их сами.
Обновитесь, чтобы не-админы не могли записывать узловые шаблоны
В lib/Public/Entity/Document/Workflow.php методы canUserOperateDocument() и canUserOperateDocumentType() возвращали true на любую операцию, рядом стоял комментарий // TODO: some logic here.. Любой пользователь проходил проверки CreateWorkflow, CreateAutomation и WriteDocument, и WorkflowTemplateService на проверке CreateWorkflow пускал его к записи узловых шаблонов.
Теперь все три операции разрешены только администратору, решает CBPWorkflowTemplateUser::isAdmin() (он пропускает группу 1 и пользователей из опции wtu_admins). Просмотр и запуск для WORKFLOW по-прежнему открыты всем. Если ваш код спрашивал у этого псевдодокумента CreateWorkflow для обычного пользователя, после обновления он получит false.
Ещё три места закрыли в WorkflowTemplateService и контроллере AI-агентов:
- права проверяются по собственному типу документа шаблона (
canPersist(),canTransfer()), и запрос с чужим типом документа больше не даёт записать шаблон другого типа; - при обновлении черновика сервис сверяет, что
draftIdпринадлежит этому шаблону; - в контроллере
Infrastructure\Controller\Integration\AiAgent\TemplateэкшеныcopyAndStartиcheckExistingRunsпроверяют шаблон черезLaunchableTemplateResolver, аcheckExistingRunsпринимается только методом POST.
Раздайте роли, пока сотрудники не потеряли узловые шаблоны
Узловые шаблоны (TYPE = nodes) теперь закрыты ACL. Сохранение, черновик и константы существующего шаблона WorkflowTemplateService проверяет через TemplateAccessService::canPublish() и canEdit(). Чтобы создать новый узловой шаблон, нужно право CREATE. Без PUBLISH шаблон сохранится пустым, с TEMPLATE = [] и AUTO_EXECUTE = None.
Список шаблонов тоже собирается иначе. Раньше getList() отдавал пользователю шаблоны, которые он создал сам (CREATED_BY). Теперь фильтр строит TemplateAccessProvider::getEntityFilter(). Администратор видит все узловые шаблоны, а пользователь без роли получает ['=ID' => -1], то есть пустой список. Компонент bizproc.template.processes раньше пускал только админа, теперь он спрашивает canAccessTemplates() и canDelete().
Предустановленных ролей две (Internal/Access/Install/AccessInstaller.php). «Администратор БП» получает все пять прав, «Управляющий» получает CREATE и EDIT на все шаблоны. Ни к одному пользователю роли не привязаны, поэтому до ручной настройки прав узловые шаблоны доступны только администраторам портала.
Роли настраиваются на странице bizproc.config.permissions. Открыть её может пользователь с правом CONFIGURE_RIGHTS, но сохранить роли получится только при опции designer_v2 = Y, а по умолчанию она выключена. Если узловые процессы у вас собирают не админы, сразу после обновления включите опцию и раздайте роли, иначе эти сотрудники увидят пустой список.
Проверьте интеграции на bizproc.workflow.template.list
Из RestService::getWorkflowTemplates() убрали вызов checkAdminPermissions(). Вызов bizproc.workflow.template.list от не-администратора раньше падал с ошибкой доступа, а теперь возвращает только пользовательские узловые шаблоны (=TYPE = nodes, =SYSTEM_CODE = null) из области EDIT или PUBLISH вызывающего пользователя. Если роли нет, список пустой.
Интеграция, которая по ошибке доступа понимала, что вызов идёт не от админа, эту ошибку больше не получит. Вместо неё придёт урезанный или пустой список, и код должен уметь с ним работать.
Чистите хранилища своим агентом
Записи хранилищ бизнес-процессов больше не удаляются по возрасту. Раньше их через storage_items_cleanup_days дней (по умолчанию 90) удалял StorageCleanupAgent. Очистку по размеру таблиц (StorageSizeCleanupService) в 26.1100.0 никто не вызывал, теперь её удалили совсем. В 26.1200.0 удалены:
- классы
Public\Service\StorageItem\StorageCleanupServiceиStorageSizeCleanupService; - методы
Public\Provider\StorageItemProvider::findOldStorageItemIds()иfindOldestStorageItemIds(), а с ними одноимённые методы репозиториевStorageItemRepository*; - агент
Infrastructure\Agent\StorageCleanupAgentи его строка вinstall/migrations/agents.php; - опция
storage_items_cleanup_daysвoptions.php.
Что апдейтер делает с уже зарегистрированным агентом, из диффа не видно. Поищите в агентах модуля bizproc Bitrix\Bizproc\Infrastructure\Agent\StorageCleanupAgent::runAgent(); и, если остался, ...::execAgent();. Класса больше нет, и такой агент ничего не удалит. Судя по коду main (classes/general/agent.php), исключение при вызове агента пишется в лог как «Error in agent ...», а строка остаётся в b_agent. Агент main Bitrix\Main\Data\Infrastructure\StorageCleanupAgent::run(); не трогайте, это другой класс с тем же коротким именем.
Если старые записи нужно убирать, заведите свой агент. Методы поиска старых записей из StorageItemProvider удалены, поэтому в примере ниже выборка идёт по внутренней ORM-таблице Internal\Model\StorageRecordDataTable, а удаляет записи публичная команда DeleteStorageItemCommand. Классы из Internal могут поменяться в любом обновлении, так что после апдейтов bizproc агент придётся перепроверять.
<?php declare(strict_types=1);
namespace Vendor\Module\Infrastructure\Agent;
use Bitrix\Bizproc\Internal\Model\StorageRecordDataTable;
use Bitrix\Bizproc\Public\Command\StorageItem\DeleteStorageItemCommand;
use Bitrix\Main\Diag\Logger;
use Bitrix\Main\Loader;
use Bitrix\Main\Type\DateTime;
final class BizprocStorageCleanupAgent
{
private const KEEP_DAYS = 90;
private const BATCH_SIZE = 100;
private const MAX_BATCHES = 20;
public static function run(): string
{
if (!Loader::includeModule('bizproc')) {
return __METHOD__ . '();';
}
$cutoff = (new DateTime())->add('-' . self::KEEP_DAYS . ' days');
for ($batch = 0; $batch < self::MAX_BATCHES; $batch++) {
$rows = StorageRecordDataTable::getList([
'select' => ['ID'],
'filter' => ['<CREATED_TIME' => $cutoff, '!@WORKFLOW_ID' => ['dataview', 'dataview_manual']],
'order' => ['ID' => 'ASC'],
'limit' => self::BATCH_SIZE,
])->fetchAll();
$ids = array_map('intval', array_column($rows, 'ID'));
if ($ids === []) {
break;
}
// удаляем командой модуля, таблицу напрямую не трогаем
$result = (new DeleteStorageItemCommand($ids))->run();
if (!$result->isSuccess()) {
Logger::create('vendor.module.BizprocStorageCleanup')
?->error(implode('; ', $result->getErrorMessages()));
break;
}
if (count($ids) < self::BATCH_SIZE) {
break;
}
}
return __METHOD__ . '();';
}
}
Строки DataView агент пропускает, иначе команда удаления запишет их в DELETION_MARKS, и представление их больше не пересоберёт. За один запуск агент удаляет не больше 2000 записей, остальное подберёт следующий запуск. Регистрируется он при установке вашего модуля:
// install/index.php вашего модуля, DoInstall()
\CAgent::AddAgent(
'\\Vendor\\Module\\Infrastructure\\Agent\\BizprocStorageCleanupAgent::run();',
'vendor.module',
'N',
3600,
);
Не рассчитывайте на лимиты хранилищ в коробке
Internal\Service\Storage\StorageLimitsService переписан. Из него удалены canAddStorage(), getMaxStorages() с лимитом в 300 хранилищ, getCleanupDays(), getStorageItemsCleanupSizeBytes() и isDiskQuotaReadable(). Конструктор теперь требует RemainingDiskSpaceProviderInterface. Проверку на 300 хранилищ убрали и из StorageTypeTable.
Вместо этих лимитов появился shouldBlockWrite(). Он блокирует запись, когда до лимита места на портале остаётся меньше storage_disk_block_buffer, по умолчанию 1024 МБ. Лимит места берётся из Bitrix24\LicenseScanner, и без модуля bitrix24 проверка не срабатывает. В коробке без bitrix24 число хранилищ и возраст записей больше ничем не ограничены. Свободное место там не проверялось и в 26.1100.0 (вызовы isDiskQuotaReadable() были закомментированы). Вместо storage_items_cleanup_days в options.php теперь две опции, storage_disk_block_buffer и storage_disk_warning_buffer.
Поправьте вызовы внутренних классов
Остальные несовместимости касаются внутренних классов. Если вы на них опирались:
- у
CapabilityCatalogService::getAvailableBlocksForNode()иgetCatalogForNode()появился обязательный второй параметрbool $filterModuleSupported; - конструктор
ComplexActivityServiceтребуетSearcherиActionCatalogMap; StorageActivityService::getFilteringFieldsMap()вторым аргументом принимает?StorageFieldCollectionвместо?array;- у
ScheduledTriggerMessage::__construct()убралиscheduleId, аscheduledAtсериализуется вDATE_ATOM; - удалён
AiAgentsGrid::getOrmParams().
Проверяйте права на шаблоны через TemplateAccessService
Права на шаблоны собраны на Main\Access. Internal\Access\AccessController наследует BaseAccessController, а PermissionDictionary описывает пять прав. CREATE и CONFIGURE_RIGHTS работают как переключатели, EDIT, PUBLISH и DELETE выдаются на выбранные шаблоны, и значение -1 означает «все шаблоны».
Из своего кода права проверяет публичный фасад Public\Service\TemplateAccessService с методами canCreate(), canEdit(), canPublish(), canDelete(), canConfigureRights() и canAccessTemplates(). Право PUBLISH включает EDIT, так что canEdit() вернёт true и тому, у кого есть только публикация. Для выборок есть Public\Provider\TemplateAccessProvider::getEntityFilter(). Он возвращает ORM-фильтр по ID, а при большой области прав строит подзапрос.
Например, сервис, который отдаёт узловые шаблоны, доступные пользователю для правки, и проверяет право на конкретный шаблон:
<?php declare(strict_types=1);
namespace Vendor\Module\Application\Service;
use Bitrix\Bizproc\Api\Enum\Template\WorkflowTemplateType;
use Bitrix\Bizproc\Public\Provider\TemplateAccessProvider;
use Bitrix\Bizproc\Public\Service\TemplateAccessService;
use Bitrix\Bizproc\Workflow\Template\Entity\WorkflowTemplateTable;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\Result;
final class NodeTemplateAccess
{
public function checkEdit(int $templateId, int $userId): Result
{
Loader::requireModule('bizproc');
$result = new Result();
if (!(new TemplateAccessService())->canEdit($templateId, $userId)) {
$result->addError(new Error('Нет права на правку шаблона', 'ACCESS_DENIED'));
}
return $result;
}
public function listEditable(int $userId): array
{
Loader::requireModule('bizproc');
// админу вернётся [], пользователю без роли ['=ID' => -1]
$scope = (new TemplateAccessProvider())->getEntityFilter($userId);
return WorkflowTemplateTable::getList([
'select' => ['ID', 'NAME'],
'filter' => [
'=TYPE' => WorkflowTemplateType::Nodes->value,
'=SYSTEM_CODE' => null,
] + $scope,
'order' => ['ID' => 'ASC'],
])->fetchAll();
}
}
Матрицу прав отдаёт Public\Provider\PermissionMatrixProvider, сохраняет Public\Command\SaveRolePermissionsCommand. Страница bizproc.config.permissions собрана на UI AccessRights V2.
Включите DataView и снимки последних значений, если они нужны
Обе подсистемы выключены по умолчанию, как и designer_v2.
DataView (dataview_enabled = 'N') строит материализуемые представления над хранилищами и переменными, с join и агрегатами. Код лежит в lib/Public/DataView/* и lib/Internal/Service/DataView/*. Свой источник данных подключается через публичные интерфейсы DataSourceProvider, AggregationCapableProvider, PresentableProvider, StampConstantProvider, TemplateContextCapableProvider и реестр DataSourceProviderRegistry::register(string $moduleId, DataSourceProvider). Контроллер Infrastructure\Controller\DataView в пространстве v2 отвечает на save, preview, get, list, getSources, getSourceSchema, getStampConstants, listSourceStorages и delete. Даже с включённой опцией DataView доступен только администратору.
Снимки последних значений (last_values_capture = 'N') пишет CBPWorkflowPersister перед удалением экземпляра процесса, через Internal\Service\LastValues\CaptureService. Отладочные и брошенные запуски в снимки не попадают. Прочитать значения выражений последнего завершённого запуска шаблона можно через Public\Service\LastValues\LastValuesService::getValues(int $templateId, int $userId), если у пользователя есть доступ к исходному документу. Рядом с last_values_capture появились опции last_values_throttle, last_values_value_limit, last_values_snapshot_limit, last_values_key_limit и last_values_retention.
<?php declare(strict_types=1);
use Bitrix\Bizproc\Public\Service\LastValues\LastValuesService;
use Bitrix\Main\Config\Option;
use Bitrix\Main\Engine\CurrentUser;
use Bitrix\Main\Loader;
Loader::requireModule('bizproc');
Option::set('bizproc', 'dataview_enabled', 'Y');
Option::set('bizproc', 'last_values_capture', 'Y');
Option::set('bizproc', 'designer_v2', 'Y'); // сохранение прав
$lastValues = (new LastValuesService())->getValues(
templateId: 42,
userId: (int)CurrentUser::get()->getId(),
);
Не спешите встраивать FieldType::CONDITIONGROUP в свои действия
Новый тип поля FieldType::CONDITIONGROUP (BaseType\ConditionGroup) рисует контрол «фильтр по полям». Значение хранится как сериализованный Automation\Engine\ConditionGroup, а лимиты на in и !in те же, что в EntityFilter. Из коробки тип использует DeleteDataStorageActivity, в глобальных переменных он недоступен. У настроек этого поля в DeleteDataStorageActivity стоит пометка «Pilot-private (NOT part of DTO-01)», так что формат настроек может ещё поменяться.
Остальное новое
- Классификация действий узла.
Activity\Dto\NodeActionSettingsпринимает вsetNodeActionSettings()ключиACTION_GROUP,ACTION_AREA,ACTION_OBJECTSиCREATES_DOCUMENT. EnumActionAreaпривязан к модулю черезgetModuleId(),ActionGroupрасширен. ДобавленыDto\Complex\ActionClassification,ActionObjectи сборщикInternal\Service\Activity\ActionCatalogMap, уComplex\SettingsпоявилисьuseGlobalActionCatalogиrelationAction.CBPMailActivityобъявилSEND/MAIL. Public\Activity\Interface\RelationFieldResolverиRegistry\RelationFieldResolverRegistryсопоставляют поля связи между типами документов на этапе проектирования.- У
CBPActivityпоявилисьgetPropertiesTypes()иgetVariablesTypes(), аcreateConfigurator()принимаетarray $documentType = []. BaseTrigger::applyPropertiesDialogValues()можно переопределить в своём триггере, метод protected.- AI-агента можно перезапустить через триггер
AiAgentRestartTrigger.AbstractProcessStarterзапускает процесс подrestartRunIdи пишет вb_bp_trackingсобытиеAPPLIED_RESTART_TRIGGER. Рядом появилисьTemplate::checkExistingRunsAction(), параметрbool $asRestartуSystemTemplateActivationService::startTemplate(),AiAgentProvider::isRestartableLaunchedTemplate(),LaunchableTemplateResolverиExistingRunsWarningPolicy. ScheduledTriggerвозвращаетScheduledAt. Ещё добавленыUI\Helpers\TaskTextDecoder,StorageField::getActivityFieldsMapAction(),StorageProvider::doSearch(),WorkflowTemplateDraftTable::getLatestDraftDataByTemplateId()и провайдер entity-selectorbizproc-start-workflow-template.Starter\ModuleSettings::getInterchangeableTriggerPairs()в самом bizproc нигде не вызывается.- Новые JS-расширения
bizproc.a11y,bizproc.config-permissionsиbizproc.dataview.
БД
Новые таблицы и индексы из install/migrations/tables.php:
| Таблица / индекс | Что появилось |
|---|---|
b_bp_access_role, b_bp_access_role_relation, b_bp_access_permission |
Роли, привязки к кодам доступа и матрица прав |
b_bp_storage_data_view |
DEFINITION, STATUS, DELETION_MARKS (mediumtext), владелец-шаблон и активити; уникальный индекс по STORAGE_TYPE_ID |
b_bp_workflow_last_values |
PK TEMPLATE_ID, VERSION_KEY, VALUES_DATA; индекс по COMPLETED_AT |
ix_bp_wf_template_settings_name_value |
Индекс (NAME, VALUE(50)) для поиска копий AI-агента по ORIGIN_SYSTEM_CODE |
ix_storage_record_data_storage_time |
Индекс (STORAGE_ID, CREATED_TIME) |
При установке install/index.php вызывает Internal\Access\Install\AccessInstaller::install(). При обновлении роли создаёт AccessSeedStepper, потому что апдейтер, по комментарию в коде, «runs before the module files are copied».
В install/migrations/agents.php добавлен LastValuesCleanupAgent (записан как [class, 'runAgent'], хранит снимки 30 дней) и удалён StorageCleanupAgent. В install/migrations/events.php на bizproc:onAfterWorkflowTemplateDelete подписан LastValues\EventHandler.
Мелочи и находки
- При удалении шаблона
WorkflowTemplateTableчистит строки ACL с его ID и сбрасываетPermissionCache. Комментарий объясняет зачем: «so a later reuse of the ID cannot leak access to a foreign template». AccessController::isAdmin()не учитывает старыйCreateWorkflow. В комментарии написано, что для типаWORKFLOWон «returns true for everyone and would nullify this ACL». Комментарий описывает поведение до 26.1200.0.- В заданиях
Approve,ReviewиRequestInformation(Optional)Activityподпись комментария больше не экранируется дважды. Теперь она проходит черезTaskTextDecoder::decode()и один раз черезhtmlspecialcharsbx(), потому чтоCBPTaskService::GetList(), как поясняет комментарий в классе, уже прогоняетPARAMETERSчерезhtmlspecialcharsEx(). - Шаблон
bitrix_ai_project_pulseсильно урезали:template.jsonпохудел на 1093 строки,lang/ru/template.json.phpна 642, из шаблона ушлиTasksGetInfoActivityиCalendarGetInform. - В
bizproc.task,bizproc.task.list,bizproc.workflow.info,bizproc.workflow.startиBaseType\Baseпоявилисьrole,aria-*иdata-testidвродеtask-detail-action-btnиbizproc-field-clone-btn. Слайдер запуска передаёт скринридеру заголовок страницы черезSET_TITLE. - Комплексный узел
UnifiedNode, единственный сuseGlobalActionCatalog: true, по-прежнему появляется только приBitrix\Bizproc\Dev\ENV. - PNG-иконки таймлайна, которые добавили в 26.1100.0, удалены и заменены на webp.
- Порты
StateNodeв коде подписаны по-русски:'Вход','Выход','Ожидание'. - Иконки в
ActionAreaиActionGroupпомечены «Illustrative Outline names; final icon mapping to confirm with design». В комментариях попадаются ссылки на внутренние документы: «PRD § 14», «Errata E-1», «EVENT-01», «AC-016». - Ключи payload перезапуска AI-агента продублированы литералами в
AbstractProcessStarterс пояснением «bizproc must not depend on ai». - Кнопка «обсудить отчёт» (
BizprocBot::onCommandAdd()) теперь сама открывает чат у нажавшего руководителя черезIm\V2\Pull\Event\ApplicationOpenChat. Классы соседних модулей обёрнуты вclass_exists, потому что «module updates are not atomic». admin/bizproc_selector.phpподключаетepilog_admin_js.phpс комментарием «Task 722886».
Что делать
- Обновите bizproc. До 26.1200.0 любой пользователь проходил проверку на запись узловых шаблонов.
- Включите
designer_v2и раздайте роли на странице прав тем, кто собирает узловые процессы без прав администратора. - Проверьте интеграции, которые вызывают
bizproc.workflow.template.listне от админа. - Найдите в агентах bizproc
Bitrix\Bizproc\Infrastructure\Agent\StorageCleanupAgent(не путайте с агентом main) и, если записи хранилищ нужно чистить, поставьте свой агент. - Поищите в своём коде
StorageCleanupService,StorageSizeCleanupService,findOldStorageItemIds,findOldestStorageItemIdsи удалённые методыStorageLimitsService.
Устаревшие и удалённые API в этой версии
| Символ | Статус | Чем заменять |
|---|---|---|
Bitrix\Bizproc\Public\Service\StorageItem\StorageCleanupService
|
Удалено | — |
Bitrix\Bizproc\Public\Service\StorageItem\StorageSizeCleanupService
|
Удалено | — |
Bitrix\Bizproc\Public\Provider\StorageItemProvider::findOldStorageItemIds()
|
Удалено | — |
Bitrix\Bizproc\Public\Provider\StorageItemProvider::findOldestStorageItemIds()
|
Удалено | — |
Bitrix\Bizproc\Infrastructure\Agent\StorageCleanupAgent
|
Удалено | — |