bizproc · Бизнес-процессы 26.1200.0 Безопасность

bizproc 26.1200.0: псевдодокумент WORKFLOW закрыт для не-админов, у узловых шаблонов появились роли

5 мин чтения Устаревших API: 5
Узнавайте о ломающих изменениях Битрикса раньше, чем они доедут до прода: свежие разборы — в канале @bxmax_updates

Обновление безопасности

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

До 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. Enum ActionArea привязан к модулю через 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-selector bizproc-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».

Что делать

  1. Обновите bizproc. До 26.1200.0 любой пользователь проходил проверку на запись узловых шаблонов.
  2. Включите designer_v2 и раздайте роли на странице прав тем, кто собирает узловые процессы без прав администратора.
  3. Проверьте интеграции, которые вызывают bizproc.workflow.template.list не от админа.
  4. Найдите в агентах bizproc Bitrix\Bizproc\Infrastructure\Agent\StorageCleanupAgent (не путайте с агентом main) и, если записи хранилищ нужно чистить, поставьте свой агент.
  5. Поищите в своём коде 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 Удалено —

Новая версия вышла — разбор уже в Telegram

В канале @bxmax_updates публикуем каждый свежий разбор: что сломается, что устарело и что появилось в API. Узнаете до того, как обновите прод.

Подписаться

Читайте дальше

bizproc 26.1400.0 Ломающее Свежее

bizproc 26.1400.0 + bizprocdesigner 26.800.0: новые REST-методы внешнего AI-агента, дочерние действия в CBPActivity, история версий шаблонов

bizproc 26.1400.0 и bizprocdesigner 26.800.0 ставятся одним шагом. Внешнему AI-агенту дизайнера придётся сменить имена трёх методов и формат запросов, потому что его V3-контроллеры перевели с `ArrayResponse` на типизированные запросы и DTO. Свои действия из `/local/activities` стоит проверить на обн…

4 мин
bizproc 26.1100.0 Безопасность Свежее

Bizproc 26.1100.0: агент удаляет процессы, зависшие на паузе, а импорт смарт-скриптов проверяет действия

В bizproc 26.1100.0 импорт смарт-скриптов через REST-конфигурацию начал проверять действия шаблона от имени импортирующего пользователя. До этого шаблон сохранялся без `ValidateProperties`, и через импорт в него могло попасть действие `CBPCodeActivity`, которое эта проверка разрешает только админист…

2 мин
bizproc 26.1300.0 Безопасность Свежее

bizproc 26.1300.0 + bizprocdesigner 26.700.0: дизайнер проверяет права на присланный documentType, внешнего AI-агента вернули владельцу его копии

bizprocdesigner 26.700.0 ставится одним шагом с bizproc 26.1300.0 и закрывает экшен, который без проверки прав отдавал категории и стадии любого присланного клиентом типа документа. Внешнего AI-агента на псевдотипе `Workflow`, которого с bizproc 26.1200 подключал только администратор, снова может по…

2 мин
bizproc 26.1075.0 Рутинное Свежее

Bizproc 26.1075.0: строже ответ AI-коуча, кеш хранилищ без нулей

Почти весь релиз про встроенного AI-агента `bitrix_ai_coach`, который проводит тесты для сотрудников. В JSON-схеме ответа модели появились обязательные `questions_review`, `correct_count` и `total_count`, а промпт велит сверять их между собой перед выводом итога. Промпты при этом разошлись. Английск…

1 мин
Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.
AI Домовой

AI Домовой История

на связи

пишет…

Можно закрыть чат и перейти на другую страницу. Когда ответ будет готов, на кнопке Домового появится отметка.

Нет истории чатов
AI Домовой

Нужна авторизация

Войдите, чтобы задавать вопросы AI Домовому.

Войти