fix(role-compile,role-validate): закрытый белый список типов и прав вместо предупреждений

Неизвестный тип объекта обрабатывался веткой «warning» и всё равно попадал в
Rights.xml: и генератор, и валидатор рапортовали успех. Блок прав на тип, который
прав не имеет, платформа не отвергает — конфигурация с правами на перечисление не
загружается в информационную базу, конфигуратор зависает без сообщений.

Белый список — дерево редактора ролей: 27 типов (добавлен ExternalDataSource,
которого не было ни в одной таблице) плюс таблица видов вложенности с наборами прав
и привязкой вида к типу-родителю. Значения сняты с корпуса (acc/erp/ut/unf, ~2750
ролей) и с выгрузок роли со всеми проставленными правами — для внешнего источника
данных и перерасчёта регистра расчёта, которых в корпусе нет.

- role-compile: отказ ДО записи файлов, все причины разом, exit 1; ни файлов роли,
  ни записи в Configuration.xml. Русские алиасы для типов без прав — ради внятного
  отказа, а не ради генерации.
- role-validate: те же случаи — ошибка вместо предупреждения.
- Побочно снято 424 ложных предупреждения на корпусе: права Use у операций
  веб-сервисов и методов HTTP-сервисов считались недопустимыми для вложенных объектов.
- Проверка имён прав и вложенных путей больше не пропускает мусор: неизвестное право
  и путь вида Enum.Х.Attribute.Y раньше не проверялись вовсе.
- docs/1c-role-spec.md: ExternalDataSource ошибочно числился типом без прав.
- dsl-reference.md переписан под «что можно написать» (тип → права, виды
  вложенности) вместо таблиц прощающего ввода.

Тесты: expect.filesAbsent в раннере; новые кейсы на отказ и на вложенные объекты.
Починены кейсы, которые ничего не проверяли: три задавали права одной строкой
"Read View" (навык писал в эталон несуществующее право), valid-role собирал роль
неизвестным ключом и валидировал пустую, bad-root проходил на «файл не найден».

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-13 20:12:15 +03:00
co-authored by Claude Opus 5
parent 30c8901615
commit 9b0bff1386
35 changed files with 1646 additions and 330 deletions
@@ -16,7 +16,7 @@
"input": {
"name": "Кладовщик",
"objects": [
"Catalog.Товары: Read View"
"Catalog.Товары: Read, View"
]
},
"validatePath": "Roles/Кладовщик",
@@ -0,0 +1,19 @@
{
"name": "Отказ: тип без прав в ролях (Enum), файлы не созданы",
"input": {
"name": "Кладовщик",
"objects": [
"Enum.ЮрФизЛицо: Read",
"Перечисление.ЮрФизЛицо: Чтение"
]
},
"expectError": "не имеет прав в роли",
"noSnapshot": "негативный кейс: навык не должен создать ни одного файла",
"expect": {
"filesAbsent": [
"Roles/Кладовщик.xml",
"Roles/Кладовщик/Ext/Rights.xml"
],
"fileNotContains": { "file": "Configuration.xml", "text": "Кладовщик" }
}
}
@@ -0,0 +1,12 @@
{
"name": "Отказ: вид вложенности не от того типа",
"input": {
"name": "Кладовщик",
"objects": ["Catalog.Товары.Field.Цена: View"]
},
"expectError": "бывает только у ExternalDataSource",
"noSnapshot": "негативный кейс: навык не должен создать ни одного файла",
"expect": {
"filesAbsent": ["Roles/Кладовщик/Ext/Rights.xml"]
}
}
@@ -0,0 +1,12 @@
{
"name": "Отказ: несуществующее право у известного типа",
"input": {
"name": "Кладовщик",
"objects": ["Catalog.Товары: Reed"]
},
"expectError": "право 'Reed' не существует у типа 'Catalog'",
"noSnapshot": "негативный кейс: навык не должен создать ни одного файла",
"expect": {
"filesAbsent": ["Roles/Кладовщик/Ext/Rights.xml"]
}
}
@@ -0,0 +1,13 @@
{
"name": "Отказ: незнакомый тип объекта с подсказкой",
"input": {
"name": "Кладовщик",
"objects": ["Catalogg.Товары: Read"]
},
"expectError": "неизвестный тип объекта 'Catalogg'",
"noSnapshot": "негативный кейс: навык не должен создать ни одного файла",
"expect": {
"stderrContains": "Возможно: Catalog?",
"filesAbsent": ["Roles/Кладовщик/Ext/Rights.xml"]
}
}
@@ -0,0 +1,25 @@
{
"name": "Позитив: операции сервисов, вложенные объекты и внешний источник данных",
"input": {
"name": "Интеграция",
"synonym": "Интеграция",
"objects": [
"WebService.Обмен.Operation.Загрузить: Use",
"HTTPService.ПередачаДанных.URLTemplate.Файлы.Method.get: Use",
"IntegrationService.Канал.IntegrationServiceChannel.Основной: Use",
"Catalog.Товары.Attribute.Цена: View, Edit",
"Subsystem.Продажи.Subsystem.Опт: View",
"CalculationRegister.Начисления.Recalculation.Перерасчет: Read, Update",
"ExternalDataSource.Источник: Use, Administration",
"ExternalDataSource.Источник.Table.Заказы: Read, View",
"ExternalDataSource.Источник.Table.Заказы.Field.Сумма: View, Edit",
"ExternalDataSource.Источник.Cube.Продажи.Dimension.Период: View"
]
},
"expect": {
"files": [
"Roles/Интеграция/Ext/Rights.xml"
]
},
"skipValidation": true
}
@@ -6,7 +6,11 @@
<object>
<name>Catalog.Товары</name>
<right>
<name>Read View</name>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
@@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<Configuration uuid="UUID-001">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>UUID-002</xr:ClassId>
<xr:ObjectId>UUID-003</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-004</xr:ClassId>
<xr:ObjectId>UUID-005</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-006</xr:ClassId>
<xr:ObjectId>UUID-007</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-008</xr:ClassId>
<xr:ObjectId>UUID-009</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-010</xr:ClassId>
<xr:ObjectId>UUID-011</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-012</xr:ClassId>
<xr:ObjectId>UUID-013</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>UUID-014</xr:ClassId>
<xr:ObjectId>UUID-015</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<Name>TestConfig</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>TestConfig</v8:content>
</v8:item>
</Synonym>
<Comment/>
<NamePrefix/>
<ConfigurationExtensionCompatibilityMode>Version8_3_24</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles/>
<Vendor/>
<Version/>
<UpdateCatalogAddress/>
<IncludeHelpInContents>false</IncludeHelpInContents>
<UseManagedFormInOrdinaryApplication>false</UseManagedFormInOrdinaryApplication>
<UseOrdinaryFormInManagedApplication>false</UseOrdinaryFormInManagedApplication>
<AdditionalFullTextSearchDictionaries/>
<CommonSettingsStorage/>
<ReportsUserSettingsStorage/>
<ReportsVariantsStorage/>
<FormDataSettingsStorage/>
<DynamicListsUserSettingsStorage/>
<URLExternalDataStorage/>
<Content/>
<DefaultReportForm/>
<DefaultReportVariantForm/>
<DefaultReportSettingsForm/>
<DefaultReportAppearanceTemplate/>
<DefaultDynamicListSettingsForm/>
<DefaultSearchForm/>
<DefaultDataHistoryChangeHistoryForm/>
<DefaultDataHistoryVersionDataForm/>
<DefaultDataHistoryVersionDifferencesForm/>
<DefaultCollaborationSystemUsersChoiceForm/>
<RequiredMobileApplicationPermissions/>
<UsedMobileApplicationFunctionalities>
<app:functionality>
<app:functionality>Biometrics</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Location</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundLocation</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BluetoothPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>WiFiPrinters</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Contacts</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Calendars</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PushNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>LocalNotifications</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InAppPurchases</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PersonalComputerFileExchange</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Ads</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NumberDialing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>CallLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AutoSendSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ReceiveSMS</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SMSLog</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Camera</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Microphone</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>MusicLibrary</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>PictureAndVideoLibraries</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioPlaybackAndVibration</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>InstallPackages</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>OSBackup</app:functionality>
<app:use>true</app:use>
</app:functionality>
<app:functionality>
<app:functionality>ApplicationUsageStatistics</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BarcodeScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>BackgroundAudioRecording</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllFilesAccess</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Videoconferences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>NFC</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>DocumentScanning</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>SpeechToText</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>Geofences</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>IncomingShareRequests</app:functionality>
<app:use>false</app:use>
</app:functionality>
<app:functionality>
<app:functionality>AllIncomingShareRequestsTypesProcessing</app:functionality>
<app:use>false</app:use>
</app:functionality>
</UsedMobileApplicationFunctionalities>
<StandaloneConfigurationRestrictionRoles/>
<MobileApplicationURLs/>
<AllowedIncomingShareRequestTypes/>
<MainClientApplicationWindowMode>Normal</MainClientApplicationWindowMode>
<DefaultInterface/>
<DefaultStyle/>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation/>
<DetailedInformation/>
<Copyright/>
<VendorInformationAddress/>
<ConfigurationInformationAddress/>
<DataLockControlMode>Managed</DataLockControlMode>
<ObjectAutonumerationMode>NotAutoFree</ObjectAutonumerationMode>
<ModalityUseMode>DontUse</ModalityUseMode>
<SynchronousPlatformExtensionAndAddInCallUseMode>DontUse</SynchronousPlatformExtensionAndAddInCallUseMode>
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
<DatabaseTablespacesUseMode>DontUse</DatabaseTablespacesUseMode>
<CompatibilityMode>Version8_3_24</CompatibilityMode>
<DefaultConstantsForm/>
</Properties>
<ChildObjects>
<Language>Русский</Language>
<Role>Интеграция</Role>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<ClientApplicationInterface xmlns="http://v8.1c.ru/8.2/managed-application/core" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="InterfaceLayouter">
<top>
<panel id="UUID-001">
<uuid>UUID-002</uuid>
</panel>
</top>
<left>
<panel id="UUID-003">
<uuid>UUID-004</uuid>
</panel>
</left>
<panelDef id="UUID-004"/>
<panelDef id="UUID-005"/>
<panelDef id="UUID-006"/>
<panelDef id="UUID-002"/>
<panelDef id="UUID-007"/>
</ClientApplicationInterface>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<Language uuid="UUID-001">
<Properties>
<Name>Русский</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Русский</v8:content>
</v8:item>
</Synonym>
<Comment/>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<Role uuid="UUID-001">
<Properties>
<Name>Интеграция</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Интеграция</v8:content>
</v8:item>
</Synonym>
<Comment/>
</Properties>
</Role>
</MetaDataObject>
@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<Rights xmlns="http://v8.1c.ru/8.2/roles" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Rights" version="2.17">
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>true</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>WebService.Обмен.Operation.Загрузить</name>
<right>
<name>Use</name>
<value>true</value>
</right>
</object>
<object>
<name>HTTPService.ПередачаДанных.URLTemplate.Файлы.Method.get</name>
<right>
<name>Use</name>
<value>true</value>
</right>
</object>
<object>
<name>IntegrationService.Канал.IntegrationServiceChannel.Основной</name>
<right>
<name>Use</name>
<value>true</value>
</right>
</object>
<object>
<name>Catalog.Товары.Attribute.Цена</name>
<right>
<name>View</name>
<value>true</value>
</right>
<right>
<name>Edit</name>
<value>true</value>
</right>
</object>
<object>
<name>Subsystem.Продажи.Subsystem.Опт</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>CalculationRegister.Начисления.Recalculation.Перерасчет</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>Update</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.Источник</name>
<right>
<name>Use</name>
<value>true</value>
</right>
<right>
<name>Administration</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.Источник.Table.Заказы</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.Источник.Table.Заказы.Field.Сумма</name>
<right>
<name>View</name>
<value>true</value>
</right>
<right>
<name>Edit</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.Источник.Cube.Продажи.Dimension.Период</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
</Rights>
@@ -6,7 +6,11 @@
<object>
<name>Catalog.Товары</name>
<right>
<name>Read View</name>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
@@ -16,7 +16,7 @@
"input": {
"name": "Кладовщик",
"rights": [
"Catalog.Товары: Read View"
"Catalog.Товары: Read, View"
]
},
"validatePath": "Roles/Кладовщик",
@@ -0,0 +1,13 @@
{
"name": "Ошибка: блоки прав на типы без прав в ролях",
"setup": "fixture:no-rights-types",
"params": { "rightsPath": "Roles/РольВсеТипы/Ext/Rights.xml" },
"expectError": true,
"noSnapshot": "негативный кейс: навык ничего не пишет, проверяется текст и код возврата",
"expect": {
"stdoutContains": [
"Enum.ЮрФизЛицо: тип 'Enum' не имеет прав в роли",
"CommonModule.ОбщегоНазначения: тип 'CommonModule' не имеет прав в роли"
]
}
}
@@ -0,0 +1,10 @@
{
"name": "Внешний источник данных: все уровни дерева проходят без замечаний",
"setup": "fixture:external-data-source",
"params": { "rightsPath": "Roles/ПраваНаВнешнийИсточник/Ext/Rights.xml" },
"noSnapshot": "валидатор не пишет файлов, проверяется чистый вывод",
"expect": {
"stdoutContains": "Validation OK",
"stdoutNotContains": ["[WARN]", "[ERROR]"]
}
}
@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<Rights xmlns="http://v8.1c.ru/8.2/roles" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Rights" version="2.18">
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>false</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.Resource.Ресурс1</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Table.Таблица1</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>Insert</name>
<value>true</value>
</right>
<right>
<name>Update</name>
<value>true</value>
</right>
<right>
<name>Delete</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
<right>
<name>InteractiveInsert</name>
<value>true</value>
</right>
<right>
<name>Edit</name>
<value>true</value>
</right>
<right>
<name>InteractiveDelete</name>
<value>true</value>
</right>
<right>
<name>InputByString</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Table.Таблица1.Command.Команда1</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1</name>
<right>
<name>Use</name>
<value>true</value>
</right>
<right>
<name>Administration</name>
<value>true</value>
</right>
<right>
<name>StandardAuthenticationChange</name>
<value>true</value>
</right>
<right>
<name>SessionStandardAuthenticationChange</name>
<value>true</value>
</right>
<right>
<name>SessionOSAuthenticationChange</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.DimensionTable.ТаблицаИзмерения1.Command.Команда1</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.Dimension.Измерение1</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Table.Таблица1.Field.Поле1</name>
<right>
<name>View</name>
<value>true</value>
</right>
<right>
<name>Edit</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.DimensionTable.ТаблицаИзмерения1</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Function.Функция1</name>
<right>
<name>Use</name>
<value>true</value>
</right>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.DimensionTable.ТаблицаИзмерения1.Field.Поле1</name>
<right>
<name>View</name>
<value>true</value>
</right>
<right>
<name>Edit</name>
<value>true</value>
</right>
</object>
<object>
<name>ExternalDataSource.ВнешнийИсточникДанных1.Cube.Куб1.Command.Команда1</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>Configuration.Конфигурация</name>
<right>
<name>ThinClient</name>
<value>true</value>
</right>
<right>
<name>WebClient</name>
<value>true</value>
</right>
<right>
<name>MobileClient</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeNormal</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeEmbeddedWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeFullscreenWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeKiosk</name>
<value>true</value>
</right>
<right>
<name>AnalyticsSystemClient</name>
<value>true</value>
</right>
<right>
<name>SaveUserData</name>
<value>true</value>
</right>
<right>
<name>Output</name>
<value>true</value>
</right>
</object>
</Rights>
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<Rights xmlns="http://v8.1c.ru/8.2/roles" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Rights" version="2.18">
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>true</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>Configuration.Конфигурация</name>
<right>
<name>ThinClient</name>
<value>true</value>
</right>
<right>
<name>WebClient</name>
<value>true</value>
</right>
<right>
<name>MobileClient</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeNormal</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeEmbeddedWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeFullscreenWorkplace</name>
<value>true</value>
</right>
<right>
<name>MainWindowModeKiosk</name>
<value>true</value>
</right>
<right>
<name>AnalyticsSystemClient</name>
<value>true</value>
</right>
<right>
<name>SaveUserData</name>
<value>true</value>
</right>
<right>
<name>Output</name>
<value>true</value>
</right>
</object>
<object>
<name>CalculationRegister.РегистрРасчета1.Recalculation.Перерасчет1</name>
<right>
<name>Read</name>
<value>true</value>
</right>
<right>
<name>Update</name>
<value>true</value>
</right>
</object>
</Rights>
@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<Rights xmlns="http://v8.1c.ru/8.2/roles" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Rights" version="2.18">
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>true</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>Enum.ЮрФизЛицо</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
<object>
<name>CommonModule.ОбщегоНазначения</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
<object>
<name>DefinedType.Контрагент</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
<object>
<name>CommonPicture.Логотип</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>CommonTemplate.Макет</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>Language.Русский</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>FunctionalOption.ИспользоватьВалюты</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>FunctionalOptionsParameter.Организация</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>EventSubscription.ЗаписатьВерсиюОбъекта</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>ScheduledJob.ОбновлениеИндекса</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
<object>
<name>StyleItem.ВажнаяНадписьШрифт</name>
<right>
<name>View</name>
<value>true</value>
</right>
</object>
</Rights>
@@ -0,0 +1,10 @@
{
"name": "Перерасчёт регистра расчёта проходит без замечаний",
"setup": "fixture:external-data-source",
"params": { "rightsPath": "Roles/ПраваНаПерерасчет/Ext/Rights.xml" },
"noSnapshot": "валидатор не пишет файлов, проверяется чистый вывод",
"expect": {
"stdoutContains": "Validation OK",
"stdoutNotContains": ["[WARN]", "[ERROR]"]
}
}
@@ -3,4 +3,11 @@
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>true</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>Catalog.Товары</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
</Rights>
@@ -16,13 +16,8 @@
"script": "role-compile/scripts/role-compile",
"input": {
"name": "Тест",
"rights": [
{
"object": "Catalog.Товары",
"rights": [
"Read"
]
}
"objects": [
"Catalog.Товары: Read"
]
},
"args": {
+4 -2
View File
@@ -60,8 +60,10 @@ const MAPS = [
// вокабуляры: значения — канонические имена, полнота не требуется
{
skill: 'role-compile', file: 'role-compile', kind: 'alias', py: 'TYPE_ALIASES', ps1: null,
extraTargets: ['Configuration', 'Attribute', 'StandardAttribute', 'TabularSection',
'Dimension', 'Resource', 'Command', 'AddressingAttribute'],
// ExternalDataSource — объект метаданных с правами в ролях, но в таблице порядка
// ChildObjects его нет: позиция в порядке не измерена (в корпусе внешних источников нет).
extraTargets: ['Configuration', 'ExternalDataSource', 'Attribute', 'StandardAttribute',
'TabularSection', 'Dimension', 'Resource', 'Command', 'AddressingAttribute'],
},
{ skill: 'interface-edit', file: 'interface-edit', kind: 'alias', py: 'TYPE_NORM_MAP', ps1: null },
{ skill: 'subsystem-edit', file: 'subsystem-edit', kind: 'alias', py: 'CONTENT_TYPE_MAP', ps1: null },
@@ -238,11 +238,11 @@ export const steps = [
input: {
name: 'Кладовщик',
objects: [
'Catalog.Контрагенты: Read View',
'Catalog.Номенклатура: Read View',
'Document.ПриходнаяНакладная: Read View Add Update',
'Catalog.Контрагенты: Read, View',
'Catalog.Номенклатура: Read, View',
'Document.ПриходнаяНакладная: Read, View, Insert, Update',
'AccumulationRegister.ОстаткиТоваров: Read',
'Report.ОстаткиТоваров: Use View',
'Report.ОстаткиТоваров: Use, View',
],
},
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
@@ -2027,20 +2027,20 @@ export const steps = [
input: {
name: 'Администратор',
objects: [
'Catalog.Организации: Read View Add Update Delete',
'Catalog.Контрагенты: Read View Add Update Delete',
'Catalog.КонтактныеЛица: Read View Add Update Delete',
'Catalog.Номенклатура: Read View Add Update Delete',
'Document.ПриходнаяНакладная: Read View Add Update Delete Posting UnPosting',
'InformationRegister.КурсыВалют: Read View Add Update Delete',
'Report.ОстаткиТоваров: Use View',
'DataProcessor.ДеревоНоменклатуры: Use View',
'DataProcessor.МножественныйВыбор: Use View',
'DataProcessor.БезшапочнаяТаблица: Use View',
'DataProcessor.МногострочнаяШапка: Use View',
'DataProcessor.ГруппыКолонок: Use View',
'DataProcessor.ПроверкаДоступности: Use View',
'DataProcessor.СтраницаНастроек: Use View',
'Catalog.Организации: Read, View, Insert, Update, Delete',
'Catalog.Контрагенты: Read, View, Insert, Update, Delete',
'Catalog.КонтактныеЛица: Read, View, Insert, Update, Delete',
'Catalog.Номенклатура: Read, View, Insert, Update, Delete',
'Document.ПриходнаяНакладная: Read, View, Insert, Update, Delete, Posting, UndoPosting',
'InformationRegister.КурсыВалют: Read, View, Update, Edit',
'Report.ОстаткиТоваров: Use, View',
'DataProcessor.ДеревоНоменклатуры: Use, View',
'DataProcessor.МножественныйВыбор: Use, View',
'DataProcessor.БезшапочнаяТаблица: Use, View',
'DataProcessor.МногострочнаяШапка: Use, View',
'DataProcessor.ГруппыКолонок: Use, View',
'DataProcessor.ПроверкаДоступности: Use, View',
'DataProcessor.СтраницаНастроек: Use, View',
],
},
args: { '-JsonPath': '{inputFile}', '-OutputDir': '{workDir}' },
+21 -1
View File
@@ -411,7 +411,7 @@ function checkFileContains(workDir, spec, expectPresent) {
// который молча ничего не проверяет (так уже было с 9 кейсами meta-edit) —
// поэтому он ошибка, а не игнор.
const KNOWN_EXPECT_KEYS = new Set([
'files', 'stdoutContains', 'stdoutNotContains', 'stderrContains', 'preserves',
'files', 'filesAbsent', 'stdoutContains', 'stdoutNotContains', 'stderrContains', 'preserves',
'fileContains', 'fileNotContains', 'filesEqual',
]);
@@ -841,6 +841,16 @@ async function runCaseAsync(testCase, opts) {
if (!stderr.includes(needle)) errors.push(`stderr does not contain "${needle}"`);
}
}
// Отсутствие файла — тоже утверждение, и нужно оно чаще всего НЕГАТИВНОМУ кейсу:
// «отказ произошёл до записи». В позитивной ветке (где живёт expect.files) такой
// проверки не было бы ровно там, где она единственная содержательная.
if (caseData.expect?.filesAbsent) {
const paths = Array.isArray(caseData.expect.filesAbsent)
? caseData.expect.filesAbsent : [caseData.expect.filesAbsent];
for (const p of paths) {
if (existsSync(join(workDir, p))) errors.push(`File must not exist: ${p}`);
}
}
}
if (!caseData.expectError) {
if (caseData.expect?.preserves) {
@@ -1066,6 +1076,16 @@ function runCase(testCase, opts) {
if (!stderr.includes(needle)) errors.push(`stderr does not contain "${needle}"`);
}
}
// Отсутствие файла — тоже утверждение, и нужно оно чаще всего НЕГАТИВНОМУ кейсу:
// «отказ произошёл до записи». В позитивной ветке (где живёт expect.files) такой
// проверки не было бы ровно там, где она единственная содержательная.
if (caseData.expect?.filesAbsent) {
const paths = Array.isArray(caseData.expect.filesAbsent)
? caseData.expect.filesAbsent : [caseData.expect.filesAbsent];
for (const p of paths) {
if (existsSync(join(workDir, p))) errors.push(`File must not exist: ${p}`);
}
}
}
if (!caseData.expectError) {