catalog · Торговый каталог 26.500.0 Безопасность

Catalog 26.500.0: старый REST каталога строже проверяет права, а модуль получил REST V3

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

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

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

Catalog 26.500.0 (снят на БУС) закрывает дыры в правах старого REST catalog.*: цены, товары, свойства, складские документы и пакетная запись остатков. Обмен с 1С, синхронизация цен и внешний учёт, если они работают под пользователем с неполными правами, после обновления могут получать отказы там, где раньше всё проходило. Заодно у модуля появился полный REST V3 из 28 контроллеров.

Новое

REST V3 для всего каталога

В .settings.php модуля появилась секция rest с defaultNamespace = \Bitrix\Catalog\Infrastructure\Rest\Controller. По ней модуль rest сам находит наследников RestController и публикует их действия. Имя метода складывается из модуля, пути класса и действия в нижнем регистре: Product\Image::listAction() становится catalog.product.image.list.

Многие имена совпадают со старыми, catalog.product.get есть и там, и там. Различает их адрес. Старый REST отвечает на /rest/<id>/<секрет>/, V3 на /rest/api/<id>/<секрет>/ и ждёт параметры JSON-телом.

Что покрыто:

  • торговые каталоги (catalog.catalog.*), в том числе isoffers, который проверяет, каталог ли это предложений;
  • товары, SKU, предложения и услуги (catalog.product.*, catalog.product.{sku,offer,service}.*), их картинки и свойства; свойства передаются в properties с ключами PROPERTY_{id};
  • складские документы со строками, контрагентами, пользовательскими полями и статусом складского учёта (catalog.document.*), включая проведение и отмену пачкой;
  • цены, типы цен с группами и переводами, НДС, единицы измерения, коэффициенты, правила округления, разделы, наценки и контрагенты.

catalog.product.image.list требует фильтр по productId. Справочники catalog.product.type.list, catalog.product.property.type.list и catalog.document.type.list отклоняют filter, order и pagination. catalog.document.delete принимает id или фильтр только по id с операторами = и in. Типа TYPE_UNDO_RESERVE среди доступных типов документов нет.

Права V3 проверяет внутренний класс AccessSupport. Старый REST использует его же для складских документов и их контрагентов, остальные проверки из раздела ниже написаны в контроллерах старого REST. Сами типы цен (catalog.pricetype.add, update, delete) и ставки НДС (catalog.vat.*) V3 записывает через старые CCatalogGroup и CCatalogVat, поэтому ваши обработчики их событий сработают и на вызов из V3. Группы и переводы типов цен (catalog.pricetype.group.*, catalog.pricetype.lang.*) пишутся напрямую в GroupAccessTable и GroupLangTable, и события CCatalogGroup на них не срабатывают.

Из PHP-интеграции метод V3 вызывается обычным POST с JSON-телом:

        <?php

declare(strict_types=1);

use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;

// Адрес вебхука V3: https://<портал>/rest/api/<id пользователя>/<секрет>/
function callCatalogV3(string $webhook, string $method, array $params): array
{
    $http = new HttpClient(['socketTimeout' => 10, 'streamTimeout' => 30]);
    $http->setHeader('Content-Type', 'application/json');

    $body = $http->post($webhook . $method, Json::encode($params));
    if ($body === false) {
        throw new RuntimeException('REST V3 недоступен: ' . implode('; ', $http->getError()));
    }

    return Json::decode($body);
}

$answer = callCatalogV3(
    'https://portal.example/rest/api/1/0123456789abcdef/',
    'catalog.catalog.isoffers',
    ['id' => 3],
);

    

Если пишете свой метод V3, каталог годится как образец. DTO в lib/Infrastructure/Rest/Dto/ описаны атрибутами #[OrmEntity] или #[MappedBy], у полей стоят #[Editable], #[Filterable], #[Sortable] и #[Required].

Деньги в формате валюты

Bitrix\Catalog\Component\MoneyPropertyValueNormalizer::normalize($amount, $currency) приводит сумму к формату валюты (например, меняет десятичную точку на запятую) и склеивает её с валютой в строку сумма|валюта. Если у валюты десятичный разделитель — точка (так у RUB, USD и EUR в стандартных настройках) или сумму привести не удалось, вернёт исходную сумму, склеенную так же. Карточки товара и вариации теперь сохраняют свойства-деньги через него, а при битой структуре AMOUNT/CURRENCY бросают InvalidMoneyPropertyValueException и показывают ошибку у конкретного поля.

В своём импорте нормализатор пригодится, когда в настройках валюты разделитель — запятая, а суммы приходят с точкой:

        <?php

declare(strict_types=1);

use Bitrix\Catalog\Component\MoneyPropertyValueNormalizer;
use Bitrix\Main\Loader;

Loader::requireModule('catalog');
Loader::requireModule('currency');

// В выгрузке сумма с точкой: "1500.5". Если в настройках валюты десятичный разделитель — запятая,
// normalize вернёт "1500,50|RUB"; при стандартной точке — "1500.5|RUB" без изменений.
function saveMoneyProperty(int $iblockId, int $elementId, string $code, string $amount, string $currency): void
{
    \CIBlockElement::SetPropertyValuesEx($elementId, $iblockId, [
        $code => MoneyPropertyValueNormalizer::normalize($amount, $currency),
    ]);
}

saveMoneyProperty(2, 1045, 'OLD_PRICE', '1500.5', 'RUB');

    

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

Количества в складских отчётах теперь округляет QuantityNormalizer, до 6 знаков и не больше 15 значащих цифр. Хвосты вроде 2.9999999999 из таблиц уйдут.

В ReportStoreList методы getMeasures(), prepareItemColumn() и prepareOverallTotalRow() стали protected, в CatalogReportStoreProfitGridComponent так же открыли prepareMeasureField(), getMeasures() и getDefaultMeasureId(). В ReportProductList появился protected applyRowQuantities(), он считает количества строки товара. Колонку или итоговую строку отчёта теперь можно поменять в наследнике, не копируя класс целиком.

Что сломается

Проверьте пользователя интеграции

В catalog 26.400.0 правами начали резать выдачу остатков по складам, теперь очередь цен, товаров, свойств и документов. Всё ниже относится к старому REST (Bitrix\Catalog\Controller\*). Прогоните обмен и выгрузки под тем пользователем, под которым они работают, а не под администратором.

  • Цены. catalog.price.modify сначала проверяет право на правку цен элемента (element_edit_price) и то, что все PRICES[].ID принадлежат этому товару, и только потом удаляет цены, которых нет в списке. Раньше лишние цены удалялись ещё до проверки прав. Чужой, нулевой или нечисловой ID цены даёт Access Denied с кодом 200040300030. catalog.price.update берёт товар из сохранённой цены, а не из fields.PRODUCT_ID. catalog.price.delete теперь тоже требует element_edit_price на товар, которому принадлежит цена.
  • Товары. catalog.product.get, update, delete и download больше не находят элементы обычных инфоблоков, только товары торговых каталогов. download вдобавок требует право чтения элемента.
  • Свойства. catalog.productproperty.update и delete, catalog.productpropertyenum.update и delete, catalog.productpropertysection.set требуют право iblock_edit на инфоблок свойства. Если update меняет IBLOCK_ID, новый инфоблок должен быть каталогом («The specified iblock is not a product catalog»), и права нужны на него тоже.
  • Поля документов. catalog.userfield.document.update берёт тип документа из БД. DOC_TYPE стал необязательным, а несовпадающий даёт «The DOC_TYPE field does not match the document». Раньше при неизвестном DOC_TYPE метод молча возвращал [], теперь отвечает этой ошибкой.
  • Складские документы. catalog.document.conduct, cancel, confirm, unconfirm, conductList и cancelList требуют ACTION_CATALOG_READ и ACTION_INVENTORY_MANAGEMENT_ACCESS. В пакетных вызовах недоступный документ приходит как «не найден», и его название в текст ошибки не попадает. catalog.documentcontractor.list видит только документы, которые пользователь может смотреть, а add и delete — только те, которые он может и смотреть, и менять.
  • Остатки из 1С. catalog.storeproduct.bulkSave требует ACTION_CATALOG_SETTINGS_ACCESS и право ACTION_STORE_VIEW на все склады из items. Без ACTION_CATALOG_SETTINGS_ACCESS приходит «Недостаточно прав для доступа к настройкам разделов каталога», без права на какой-то из складов — Access Denied. Пользователю обмена могут понадобиться дополнительные права.
  • Картинки. catalog.productimage.add для DETAIL_PICTURE и PREVIEW_PICTURE отвечает «Product image was not saved.», если файл не сменился. Раньше метод возвращал старую картинку как успех.

Таблицы остатков в карточке товара стали общими

Таблицы остатков по складам в карточке товара больше не хранят настройки отдельно для каждого товара. Гриды называются productcard_store_amount и productcard_store_amount_details, без ID товара, поэтому колонки и фильтр, настроенные на одном товаре, пользователь увидит на всех. Настройки, сделанные раньше на конкретных товарах, перестанут применяться. Склад из URL (storeId) действует только на текущее открытие и в сохранённый фильтр больше не попадает.

БД

Схема не менялась. Для чистки старых настроек таблиц остатков добавлен степпер Bitrix\Catalog\Update\StoreAmountGridOptionsCleaner. Он удаляет из b_user_option записи гридов и фильтров с именами productcard_store_amount_<ID> и productcard_store_amount_details_<ID>. Размер порции задаёт опция store_amount_grid_options_cleanup_limit модуля catalog: по умолчанию 500, максимум 10000.

Мелочи и находки

  • total в старых list-методах теперь считается по тому же обработанному фильтру, что и выборка, и с ней совпадает.
  • Множественное свойство-деньги в таблице вариаций стало только для чтения и выводится списком.
  • catalog.compilation размечен role="list" и сообщает скринридеру о подгруженных товарах через BX.UI.Accessibility.LiveAnnouncer.announce(). Выбор SKU в шаблоне bootstrap_v4 компонента catalog.product.subscribe.list стал radiogroup с управлением с клавиатуры. Если вы копировали эти шаблоны к себе, новой разметки в копиях нет.
  • У Controller\Userfield\Document удалён protected exists(). Это касается только тех, кто наследовал контроллер.

Устаревшие и удалённые API в этой версии

Символ Статус Чем заменять
Bitrix\Catalog\Controller\Userfield\Document::exists() Удалено —

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

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

Подписаться

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

catalog 26.400.0 Безопасность

Catalog 26.400.0: остатки по складам в REST теперь режутся правами

Catalog 26.400.0 затрагивает 145 файлов и больше двадцати тысяч строк, но почти весь объём дали пересобранные JS-бандлы формы товаров и дубли файлов из `install/`. Содержательных изменений три. REST-выдача остатков и строк складских документов начала учитывать права на склады, установщик модуля пере…

3 мин
landing 26.1600.0 Безопасность Свежее

Landing 26.1300.200–26.1600.0: закрыты дыры с PHP и правами групп, поля редактора стали ES-классами

С прошлого разбора вышли четыре версии landing: 26.1300.200, 26.1400.0, 26.1500.0 и 26.1600.0. В каждой закрыты дыры, от сборки PHP-тега при импорте сайта до лишних прав у участников групп соцсети. Попутно в режиме Битрикс24 превью черновика переехало на маршрут раздела «Сайты», а поля редактора ста…

7 мин
calendar 26.300.0 Безопасность Свежее

Calendar 26.300.0: закрыты дыры в правах на календари и переговорки, дельта в REST

Calendar 26.300.0 закрывает три дыры в правах: перенос события в чужой календарь без права на добавление, подмену чужой брони переговорки через `LOCATION[’OLD’]` или `room_event_id` и добавление в участники проектов, где пользователь не состоит. Для интеграций в `calendar.event.get` появилось инкрем…

2 мин
blog 26.600.0 Заметное Свежее

Blog 26.600.0: тег кеша для важных сообщений и исправленные права в уведомлениях

Blog 26.600.0 ничего не ломает и базу не трогает. Разработчику он даёт тег кеша `blogpost_important_all` и метод, который считает, сколько секунд осталось до ближайшей отложенной публикации важного сообщения. `bitrix:blog.blog` в папке модуля больше не перебирает все посты блога ради списка авторов,…

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

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

на связи

пишет…

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

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

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

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

Войти