mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-14 07:23:22 +03:00
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:
co-authored by
Claude Opus 5
parent
30c8901615
commit
9b0bff1386
@@ -64,7 +64,7 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-compile.ps1" -
|
||||
|
||||
### Русские синонимы
|
||||
|
||||
Поддерживаются русские типы (`Справочник`→Catalog, `Документ`→Document) и права (`Чтение`→Read, `Просмотр`→View). Смешивание допустимо: `"Справочник.Контрагенты: Чтение, View"`.
|
||||
Поддерживаются русские типы (`Справочник`→Catalog, `Документ`→Document) и права (`Чтение`→Read, `Просмотр`→View). Каноничная форма — английская.
|
||||
|
||||
### Шаблоны RLS
|
||||
|
||||
@@ -99,7 +99,13 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-compile.ps1" -
|
||||
}
|
||||
```
|
||||
|
||||
Подробные таблицы пресетов, русских синонимов и дополнительные примеры — в `dsl-reference.md`.
|
||||
## Что можно писать в `objects`
|
||||
|
||||
Права имеют 27 типов объектов; тип или имя права вне списка — ошибка: роль не создаётся, файлы не пишутся, `Configuration.xml` не меняется. Права нельзя назначить на `Enum`, `CommonModule`, `DefinedType`, `CommonPicture`, `CommonTemplate`, `Language`, `FunctionalOption`, `EventSubscription`, `ScheduledJob`, `StyleItem`, `SettingsStorage` и подобные — в дереве редактора ролей их нет.
|
||||
|
||||
Права на части объекта задаются точечным путём: `Catalog.Контрагенты.Attribute.ИНН: View, Edit`, `WebService.Обмен.Operation.Загрузить: Use`.
|
||||
|
||||
Полные таблицы «тип → права», виды вложенности (включая внешние источники данных), список типов без прав, таблицы пресетов и дополнительные примеры — в `dsl-reference.md`.
|
||||
|
||||
## Верификация
|
||||
|
||||
|
||||
@@ -2,49 +2,86 @@
|
||||
|
||||
Подробная справка по JSON DSL для `/role-compile`. Компактное описание — в [SKILL.md](SKILL.md).
|
||||
|
||||
## Структура верхнего уровня
|
||||
## Какие объекты можно писать в `objects`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ИмяРоли",
|
||||
"synonym": "Отображаемое имя роли",
|
||||
"comment": "",
|
||||
"setForNewObjects": false,
|
||||
"setForAttributesByDefault": true,
|
||||
"independentRightsOfChildObjects": false,
|
||||
"objects": [ ... ],
|
||||
"templates": [ ... ]
|
||||
}
|
||||
```
|
||||
Права в роли имеют ровно 27 типов. Тип вне этого списка — ошибка компиляции: роль не
|
||||
создаётся, файлы не пишутся.
|
||||
|
||||
- `name` — программное имя роли (обязательно)
|
||||
- `synonym` — отображаемое имя (по умолчанию = name)
|
||||
- `comment` — комментарий (по умолчанию пусто)
|
||||
- Глобальные флаги — по умолчанию `false`, `true`, `false`
|
||||
| Тип | Права |
|
||||
|-----|-------|
|
||||
| `Configuration` | Administration, DataAdministration, UpdateDataBaseConfiguration, ConfigurationExtensionsAdministration, ActiveUsers, EventLog, ExclusiveMode, ThinClient, ThickClient, WebClient, MobileClient, ExternalConnection, Automation, Output, SaveUserData, TechnicalSpecialistMode, InteractiveOpenExtDataProcessors, InteractiveOpenExtReports, AnalyticsSystemClient, CollaborationSystemInfoBaseRegistration, MainWindowModeNormal, MainWindowModeWorkplace, MainWindowModeEmbeddedWorkplace, MainWindowModeFullscreenWorkplace, MainWindowModeKiosk |
|
||||
| `Catalog`, `ChartOfCharacteristicTypes` | Read, Insert, Update, Delete, View, Edit, InputByString, Interactive* (Insert, SetDeletionMark, ClearDeletionMark, Delete, DeleteMarked), *PredefinedData, *DataHistory* |
|
||||
| `Document` | то же + Posting, UndoPosting, InteractivePosting, InteractivePostingRegular, InteractiveUndoPosting, InteractiveChangeOfPosted |
|
||||
| `ChartOfAccounts`, `ChartOfCalculationTypes`, `ExchangePlan` | Read, Insert, Update, Delete, View, Edit, InputByString, Interactive*, *DataHistory* (без InteractiveDeleteMarked у ChartOfAccounts) |
|
||||
| `BusinessProcess` | Read, Insert, Update, Delete, View, Edit, InputByString, Start, InteractiveInsert, InteractiveSetDeletionMark, InteractiveClearDeletionMark, InteractiveDelete, InteractiveActivate, InteractiveStart |
|
||||
| `Task` | то же, но вместо Start/InteractiveStart — Execute, InteractiveExecute |
|
||||
| `InformationRegister` | Read, Update, View, Edit, TotalsControl, *DataHistory* |
|
||||
| `AccumulationRegister`, `AccountingRegister` | Read, Update, View, Edit, TotalsControl |
|
||||
| `CalculationRegister` | Read, View |
|
||||
| `Constant` | Read, Update, View, Edit, *DataHistory* |
|
||||
| `DocumentJournal` | Read, View |
|
||||
| `Sequence` | Read, Update |
|
||||
| `DataProcessor`, `Report` | Use, View |
|
||||
| `WebService`, `HTTPService`, `IntegrationService` | Use |
|
||||
| `CommonForm`, `CommonCommand`, `Subsystem`, `FilterCriterion` | View |
|
||||
| `CommonAttribute` | View, Edit |
|
||||
| `SessionParameter` | Get, Set |
|
||||
| `ExternalDataSource` | Use, Administration, StandardAuthenticationChange, SessionStandardAuthenticationChange, SessionOSAuthenticationChange |
|
||||
|
||||
## Объекты: два формата
|
||||
Полные списки прав по каждому типу — в коде навыка (`scripts/role-compile.ps1`,
|
||||
таблица `$knownRights`); навык проверяет каждое имя права и отвергает несуществующее.
|
||||
|
||||
### Вложенные объекты
|
||||
|
||||
Путь длиннее двух сегментов задаёт права на часть объекта. Вид вложенности —
|
||||
предпоследний сегмент, он определяет набор прав.
|
||||
|
||||
| Вид | Права |
|
||||
|-----|-------|
|
||||
| `Attribute`, `StandardAttribute`, `TabularSection`, `StandardTabularSection`, `Dimension`, `Resource`, `AccountingFlag`, `ExtDimensionAccountingFlag`, `AddressingAttribute` | View, Edit |
|
||||
| `Command`, вложенная `Subsystem` | View |
|
||||
| `Operation` (веб-сервис), `Method` (HTTP-сервис, под `URLTemplate`), `IntegrationServiceChannel` | Use |
|
||||
| `Recalculation` (регистр расчёта) | Read, Update |
|
||||
|
||||
Виды `Operation`, `Method`, `IntegrationServiceChannel`, `Recalculation` и виды внешнего
|
||||
источника допустимы только у своего типа-родителя. Графы журнала документов
|
||||
(`DocumentJournal.Ж.Column.Г`) собственных прав не имеют.
|
||||
|
||||
Примеры: `Catalog.Контрагенты.Attribute.ИНН: View, Edit`,
|
||||
`Document.Реализация.TabularSection.Товары.Attribute.Цена: View`,
|
||||
`WebService.Обмен.Operation.Загрузить: Use`,
|
||||
`HTTPService.API.URLTemplate.Файлы.Method.get: Use`.
|
||||
|
||||
### Вложенные объекты внешнего источника данных
|
||||
|
||||
Виды `Table`, `Cube`, `Function`, `Field`, `DimensionTable` бывают **только** под
|
||||
`ExternalDataSource`.
|
||||
|
||||
| Путь | Права |
|
||||
|------|-------|
|
||||
| `ExternalDataSource.И.Table.Т` | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveDelete |
|
||||
| `ExternalDataSource.И.Table.Т.Field.П` | View, Edit |
|
||||
| `ExternalDataSource.И.Cube.К` | Read, View |
|
||||
| `ExternalDataSource.И.Cube.К.Dimension.И`, `…Resource.Р` | View |
|
||||
| `ExternalDataSource.И.Cube.К.DimensionTable.Т` | Read, View |
|
||||
| `ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П` | View, Edit |
|
||||
| `ExternalDataSource.И.Function.Ф` | Use, View |
|
||||
| `…Table.Т.Command.К`, `…Cube.К.Command.К`, `…DimensionTable.Т.Command.К` | View |
|
||||
|
||||
### Типы объектов без прав в ролях
|
||||
|
||||
Права нельзя назначить на `Enum`, `CommonModule`, `DefinedType`, `CommonPicture`,
|
||||
`CommonTemplate`, `Language`, `FunctionalOption`, `FunctionalOptionsParameter`,
|
||||
`EventSubscription`, `ScheduledJob`, `StyleItem`, `Style`, `SettingsStorage`,
|
||||
`XDTOPackage`, `WSReference`, `DocumentNumerator` — в дереве редактора ролей этих типов нет.
|
||||
|
||||
Попытка — ошибка компиляции. Права на перечисления особенно опасны: конфигурация с таким
|
||||
блоком не загружается в информационную базу, конфигуратор зависает без сообщений.
|
||||
|
||||
## Формат записи объектов
|
||||
|
||||
Массив `objects` принимает строки (shorthand) и объекты (полная форма).
|
||||
|
||||
### Строковый shorthand
|
||||
|
||||
```
|
||||
"ОбъектМетаданных: @пресет"
|
||||
"ОбъектМетаданных: Право1, Право2"
|
||||
```
|
||||
|
||||
Примеры:
|
||||
```json
|
||||
"objects": [
|
||||
"Catalog.Номенклатура: @view",
|
||||
"Document.Реализация: @edit",
|
||||
"InformationRegister.Цены: Read, Update",
|
||||
"DataProcessor.Загрузка: @view"
|
||||
]
|
||||
```
|
||||
|
||||
### Объектная форма (для RLS и переопределений)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Document.Реализация",
|
||||
@@ -58,6 +95,9 @@
|
||||
- `rights` — переопределения: dict `{"Right": true/false}` или массив `["Right1", "Right2"]`
|
||||
- `rls` — RLS-ограничения: `{"ИмяПрава": "текст условия"}`
|
||||
|
||||
Русские имена типов и прав тоже принимаются (`Справочник.Контрагенты: Чтение`), каноничная
|
||||
форма — английская.
|
||||
|
||||
## Пресеты — подробные таблицы
|
||||
|
||||
Пресеты обозначаются `@` в строковом формате. В объектной форме ключ `preset` без `@`.
|
||||
@@ -88,127 +128,8 @@
|
||||
| SessionParameter | Get, Set |
|
||||
| CommonAttribute | View, Edit |
|
||||
|
||||
Для сервисов (WebService, HTTPService, IntegrationService) пресеты не определены — используй явные права: `"WebService.Имя: Use"`.
|
||||
|
||||
Если пресет не определён для типа объекта — предупреждение с подсказкой доступных.
|
||||
|
||||
## Русские синонимы
|
||||
|
||||
Скрипт автоматически транслирует русские имена в английские. Можно смешивать: `"Справочник.Контрагенты: Чтение, View"` — работает.
|
||||
|
||||
### Типы объектов
|
||||
|
||||
| Русский | English |
|
||||
|---------|---------|
|
||||
| `Справочник` | Catalog |
|
||||
| `Документ` | Document |
|
||||
| `РегистрСведений` | InformationRegister |
|
||||
| `РегистрНакопления` | AccumulationRegister |
|
||||
| `РегистрБухгалтерии` | AccountingRegister |
|
||||
| `РегистрРасчета` | CalculationRegister |
|
||||
| `Константа` | Constant |
|
||||
| `ПланСчетов` | ChartOfAccounts |
|
||||
| `ПланВидовХарактеристик` | ChartOfCharacteristicTypes |
|
||||
| `ПланВидовРасчета` | ChartOfCalculationTypes |
|
||||
| `ПланОбмена` | ExchangePlan |
|
||||
| `БизнесПроцесс` | BusinessProcess |
|
||||
| `Задача` | Task |
|
||||
| `Обработка` | DataProcessor |
|
||||
| `Отчет` | Report |
|
||||
| `ОбщаяФорма` | CommonForm |
|
||||
| `ОбщаяКоманда` | CommonCommand |
|
||||
| `Подсистема` | Subsystem |
|
||||
| `КритерийОтбора` | FilterCriterion |
|
||||
| `ЖурналДокументов` | DocumentJournal |
|
||||
| `Последовательность` | Sequence |
|
||||
| `ВебСервис` | WebService |
|
||||
| `HTTPСервис` | HTTPService |
|
||||
| `СервисИнтеграции` | IntegrationService |
|
||||
| `ПараметрСеанса` | SessionParameter |
|
||||
| `ОбщийРеквизит` | CommonAttribute |
|
||||
| `Конфигурация` | Configuration |
|
||||
| `Перечисление` | Enum |
|
||||
|
||||
### Вложенные типы
|
||||
|
||||
| Русский | English |
|
||||
|---------|---------|
|
||||
| `Реквизит` | Attribute |
|
||||
| `СтандартныйРеквизит` | StandardAttribute |
|
||||
| `ТабличнаяЧасть` | TabularSection |
|
||||
| `Измерение` | Dimension |
|
||||
| `Ресурс` | Resource |
|
||||
| `Команда` | Command |
|
||||
| `РеквизитАдресации` | AddressingAttribute |
|
||||
|
||||
### Права (основные)
|
||||
|
||||
| Русский | English |
|
||||
|---------|---------|
|
||||
| `Чтение` | Read |
|
||||
| `Добавление` | Insert |
|
||||
| `Изменение` | Update |
|
||||
| `Удаление` | Delete |
|
||||
| `Просмотр` | View |
|
||||
| `Редактирование` | Edit |
|
||||
| `ВводПоСтроке` | InputByString |
|
||||
| `Проведение` | Posting |
|
||||
| `ОтменаПроведения` | UndoPosting |
|
||||
| `Использование` | Use |
|
||||
| `Получение` | Get |
|
||||
| `Установка` | Set |
|
||||
| `Старт` | Start |
|
||||
| `Выполнение` | Execute |
|
||||
| `УправлениеИтогами` | TotalsControl |
|
||||
|
||||
### Права (интерактивные)
|
||||
|
||||
| Русский | English |
|
||||
|---------|---------|
|
||||
| `ИнтерактивноеДобавление` | InteractiveInsert |
|
||||
| `ИнтерактивнаяПометкаУдаления` | InteractiveSetDeletionMark |
|
||||
| `ИнтерактивноеСнятиеПометкиУдаления` | InteractiveClearDeletionMark |
|
||||
| `ИнтерактивноеУдаление` | InteractiveDelete |
|
||||
| `ИнтерактивноеУдалениеПомеченных` | InteractiveDeleteMarked |
|
||||
| `ИнтерактивноеПроведение` | InteractivePosting |
|
||||
| `ИнтерактивноеПроведениеНеоперативное` | InteractivePostingRegular |
|
||||
| `ИнтерактивнаяОтменаПроведения` | InteractiveUndoPosting |
|
||||
| `ИнтерактивноеИзменениеПроведенных` | InteractiveChangeOfPosted |
|
||||
| `ИнтерактивныйСтарт` | InteractiveStart |
|
||||
| `ИнтерактивнаяАктивация` | InteractiveActivate |
|
||||
| `ИнтерактивноеВыполнение` | InteractiveExecute |
|
||||
|
||||
### Права (конфигурация)
|
||||
|
||||
| Русский | English |
|
||||
|---------|---------|
|
||||
| `Администрирование` | Administration |
|
||||
| `АдминистрированиеДанных` | DataAdministration |
|
||||
| `ТонкийКлиент` | ThinClient |
|
||||
| `ТолстыйКлиент` | ThickClient |
|
||||
| `ВебКлиент` | WebClient |
|
||||
| `МобильныйКлиент` | MobileClient |
|
||||
| `ВнешнееСоединение` | ExternalConnection |
|
||||
| `Вывод` | Output |
|
||||
| `СохранениеДанныхПользователя` | SaveUserData |
|
||||
|
||||
## Типы объектов без прав в ролях
|
||||
|
||||
Следующие типы 1С **не могут** иметь права в ролях (не добавляются в `objects`):
|
||||
|
||||
| Тип | Причина |
|
||||
|-----|---------|
|
||||
| Enum (Перечисление) | Права наследуются от конфигурации, явное назначение невозможно |
|
||||
| CommonModule (ОбщийМодуль) | Не имеет собственных прав в роли |
|
||||
| DefinedType (ОпределяемыйТип) | Тип данных, не объект прав |
|
||||
| CommonPicture (ОбщаяКартинка) | Ресурс, не объект прав |
|
||||
| CommonTemplate (ОбщийМакет) | Ресурс, не объект прав |
|
||||
| Language (Язык) | Конфигурационный элемент |
|
||||
| FunctionalOption (ФункциональнаяОпция) | Не объект прав |
|
||||
| FunctionalOptionsParameter | Не объект прав |
|
||||
| EventSubscription (ПодпискаНаСобытие) | Не объект прав |
|
||||
| ScheduledJob (РегламентноеЗадание) | Не объект прав |
|
||||
| StyleItem (ЭлементСтиля) | Ресурс оформления |
|
||||
Для сервисов (WebService, HTTPService, IntegrationService) и внешних источников данных
|
||||
пресеты не определены — задавай права явно: `"WebService.Имя: Use"`.
|
||||
|
||||
## Шаблоны ограничений (RLS templates)
|
||||
|
||||
@@ -227,36 +148,7 @@
|
||||
|
||||
## Примеры
|
||||
|
||||
### 1. Простая роль (только пресеты)
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ЧтениеНоменклатуры",
|
||||
"synonym": "Чтение номенклатуры",
|
||||
"objects": [
|
||||
"Catalog.Номенклатура: @view",
|
||||
"Catalog.Контрагенты: @view",
|
||||
"DataProcessor.Загрузка: @view"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Роль для регламентного задания
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ОбновлениеЦен",
|
||||
"synonym": "Обновление цен номенклатуры",
|
||||
"objects": [
|
||||
"Catalog.Номенклатура: Read",
|
||||
"Catalog.Валюты: Read",
|
||||
"InformationRegister.ЦеныНоменклатуры: Read, Update",
|
||||
"Constant.ОсновнаяВалюта: Read"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Роль с RLS
|
||||
### Роль с RLS
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -281,22 +173,7 @@
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Роль с русскими синонимами
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "ПросмотрДанных",
|
||||
"synonym": "Просмотр данных",
|
||||
"objects": [
|
||||
"Справочник.Контрагенты: @view",
|
||||
"Документ.Реализация: Чтение, Просмотр",
|
||||
"РегистрСведений.Цены: @edit",
|
||||
"Обработка.ЗагрузкаДанных: @view"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Роль с переопределением прав из пресета
|
||||
### Роль с переопределением прав из пресета
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -311,3 +188,16 @@
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Роль с правами на части объекта
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "МенеджерБезЦен",
|
||||
"synonym": "Менеджер без доступа к ценам",
|
||||
"objects": [
|
||||
"Catalog.Номенклатура: @view",
|
||||
{ "name": "Catalog.Номенклатура.Attribute.Цена", "rights": { "View": false, "Edit": false } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# role-compile v1.24 — Compile 1C role from JSON (+русские алиасы типов: формы с ё и без)
|
||||
# role-compile v1.25 — Compile 1C role from JSON (+русские алиасы типов: формы с ё и без)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -217,7 +217,24 @@ $script:typeAliases = @{
|
||||
"ПараметрСеанса" = "SessionParameter"
|
||||
"ОбщийРеквизит" = "CommonAttribute"
|
||||
"Конфигурация" = "Configuration"
|
||||
"ВнешнийИсточникДанных" = "ExternalDataSource"
|
||||
# Типы без прав в ролях: алиасы нужны не ради генерации, а ради отказа по делу —
|
||||
# иначе на русскую запись навык ответит «неизвестный тип 'ОбщийМодуль'».
|
||||
"Перечисление" = "Enum"
|
||||
"ОбщийМодуль" = "CommonModule"
|
||||
"ОпределяемыйТип" = "DefinedType"
|
||||
"ОбщаяКартинка" = "CommonPicture"
|
||||
"ОбщийМакет" = "CommonTemplate"
|
||||
"Язык" = "Language"
|
||||
"ФункциональнаяОпция" = "FunctionalOption"
|
||||
"ПараметрФункциональныхОпций" = "FunctionalOptionsParameter"
|
||||
"ПодпискаНаСобытие" = "EventSubscription"
|
||||
"РегламентноеЗадание" = "ScheduledJob"
|
||||
"ЭлементСтиля" = "StyleItem"
|
||||
"ХранилищеНастроек" = "SettingsStorage"
|
||||
"ПакетXDTO" = "XDTOPackage"
|
||||
"WSСсылка" = "WSReference"
|
||||
"Нумератор" = "DocumentNumerator"
|
||||
# Nested
|
||||
"Реквизит" = "Attribute"
|
||||
"СтандартныйРеквизит" = "StandardAttribute"
|
||||
@@ -403,11 +420,72 @@ $script:knownRights = @{
|
||||
"IntegrationService" = @("Use")
|
||||
"SessionParameter" = @("Get","Set")
|
||||
"CommonAttribute" = @("View","Edit")
|
||||
"ExternalDataSource" = @(
|
||||
"Use","Administration","StandardAuthenticationChange",
|
||||
"SessionStandardAuthenticationChange","SessionOSAuthenticationChange"
|
||||
)
|
||||
}
|
||||
|
||||
# Nested objects: Attribute, StandardAttribute, TabularSection, Dimension, Resource, AddressingAttribute
|
||||
$script:nestedRights = @("View","Edit")
|
||||
$script:commandRights = @("View")
|
||||
# Виды вложенности (предпоследний сегмент пути) → допустимые права. Списки сняты с корпуса
|
||||
# типовых конфигураций и с выгрузки роли, где права проставлены по всему дереву редактора:
|
||||
# догадкам тут не место — закрытый список превращает промах в ложный отказ.
|
||||
$script:nestedKindRights = @{
|
||||
"Attribute" = @("View","Edit")
|
||||
"StandardAttribute" = @("View","Edit")
|
||||
"TabularSection" = @("View","Edit")
|
||||
"StandardTabularSection" = @("View","Edit")
|
||||
"Dimension" = @("View","Edit")
|
||||
"Resource" = @("View","Edit")
|
||||
"AccountingFlag" = @("View","Edit")
|
||||
"ExtDimensionAccountingFlag" = @("View","Edit")
|
||||
"AddressingAttribute" = @("View","Edit")
|
||||
"Field" = @("View","Edit")
|
||||
"Command" = @("View")
|
||||
"Subsystem" = @("View")
|
||||
"Operation" = @("Use")
|
||||
"Method" = @("Use")
|
||||
"IntegrationServiceChannel" = @("Use")
|
||||
"Recalculation" = @("Read","Update")
|
||||
"Cube" = @("Read","View")
|
||||
"DimensionTable" = @("Read","View")
|
||||
"Function" = @("Use","View")
|
||||
"Table" = @(
|
||||
"Read","Insert","Update","Delete","View","Edit","InputByString",
|
||||
"InteractiveInsert","InteractiveDelete"
|
||||
)
|
||||
}
|
||||
|
||||
# Виды, существующие только у одного типа-родителя: без этой привязки
|
||||
# `Catalog.Товары.Field.Цена` прошёл бы как валидный вложенный объект.
|
||||
$script:kindOwners = @{
|
||||
"Table" = "ExternalDataSource"
|
||||
"Cube" = "ExternalDataSource"
|
||||
"Function" = "ExternalDataSource"
|
||||
"Field" = "ExternalDataSource"
|
||||
"DimensionTable" = "ExternalDataSource"
|
||||
"Recalculation" = "CalculationRegister"
|
||||
"Operation" = "WebService"
|
||||
"Method" = "HTTPService"
|
||||
"IntegrationServiceChannel" = "IntegrationService"
|
||||
}
|
||||
|
||||
# Один и тот же вид под разными родителями имеет разный набор: измерение регистра —
|
||||
# View + Edit, измерение куба внешнего источника — только View. Объединять нельзя,
|
||||
# объединение молча разрешило бы Edit там, где платформа его не даёт.
|
||||
$script:nestedKindRightsByType = @{
|
||||
"ExternalDataSource" = @{
|
||||
"Dimension" = @("View")
|
||||
"Resource" = @("View")
|
||||
}
|
||||
}
|
||||
|
||||
# Типы без прав в ролях (в дереве редактора ролей их нет). Таблица НЕ управляет поведением —
|
||||
# отказ даёт отсутствие типа в $knownRights; здесь только причина для сообщения.
|
||||
$script:noRightsTypes = @(
|
||||
"Enum","CommonModule","DefinedType","CommonPicture","CommonTemplate","Language",
|
||||
"FunctionalOption","FunctionalOptionsParameter","EventSubscription","ScheduledJob",
|
||||
"StyleItem","Style","SettingsStorage","XDTOPackage","WSReference","DocumentNumerator"
|
||||
)
|
||||
|
||||
# --- 4. Presets (@view, @edit) ---
|
||||
|
||||
@@ -472,6 +550,66 @@ function Is-NestedObject {
|
||||
return ($objectName.Split(".").Count -ge 3)
|
||||
}
|
||||
|
||||
# Вид вложенности — предпоследний сегмент: путь бывает и восьмисегментным
|
||||
# (ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П), считать от конца.
|
||||
function Get-NestedKind {
|
||||
param([string]$objectName)
|
||||
$parts = $objectName.Split(".")
|
||||
if ($parts.Count -lt 3) { return $null }
|
||||
return $parts[$parts.Count - 2]
|
||||
}
|
||||
|
||||
function Get-NestedRights {
|
||||
param([string]$objectType, [string]$kind)
|
||||
if ($script:nestedKindRightsByType.ContainsKey($objectType) -and
|
||||
$script:nestedKindRightsByType[$objectType].ContainsKey($kind)) {
|
||||
return @($script:nestedKindRightsByType[$objectType][$kind])
|
||||
}
|
||||
if ($script:nestedKindRights.ContainsKey($kind)) { return @($script:nestedKindRights[$kind]) }
|
||||
return $null
|
||||
}
|
||||
|
||||
# Отказ копится, а не печатается сразу: роль пишется целиком, поэтому единственный
|
||||
# безопасный момент отказа — до первой записи, и показать надо все причины сразу.
|
||||
$script:validationErrors = @()
|
||||
|
||||
function Add-ValidationError {
|
||||
param([string]$message)
|
||||
$script:validationErrors += $message
|
||||
}
|
||||
|
||||
# Проверка имени объекта: тип по белому списку (всегда, включая вложенные пути) и вид
|
||||
# вложенности. Запрещённый и незнакомый тип — разные диагнозы.
|
||||
function Validate-ObjectName {
|
||||
param([string]$objectName)
|
||||
|
||||
$objectType = Get-ObjectType $objectName
|
||||
if (-not $script:knownRights.ContainsKey($objectType)) {
|
||||
if ($script:noRightsTypes -contains $objectType) {
|
||||
Add-ValidationError "${objectName}: тип '$objectType' не имеет прав в роли — уберите объект из списка"
|
||||
} else {
|
||||
$similar = @($script:knownRights.Keys | Where-Object { $_ -like "*$objectType*" -or $objectType -like "*$_*" })
|
||||
$sug = if ($similar.Count -gt 0) { " Возможно: $(($similar | Select-Object -First 3) -join ', ')?" } else { "" }
|
||||
Add-ValidationError "${objectName}: неизвестный тип объекта '$objectType'.$sug"
|
||||
}
|
||||
return $false
|
||||
}
|
||||
|
||||
if (Is-NestedObject $objectName) {
|
||||
$kind = Get-NestedKind $objectName
|
||||
if ($script:kindOwners.ContainsKey($kind) -and $objectType -ne $script:kindOwners[$kind]) {
|
||||
Add-ValidationError "${objectName}: вид '$kind' бывает только у $($script:kindOwners[$kind])"
|
||||
return $false
|
||||
}
|
||||
if ($null -eq (Get-NestedRights $objectType $kind)) {
|
||||
Add-ValidationError "${objectName}: неизвестный вид вложенности '$kind'"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
function Resolve-Preset {
|
||||
param([string]$objectType, [string]$presetName)
|
||||
|
||||
@@ -503,23 +641,18 @@ function Validate-RightName {
|
||||
|
||||
$objectType = Get-ObjectType $objectName
|
||||
|
||||
if (Is-NestedObject $objectName) {
|
||||
if ($objectName -match '\.Command\.') {
|
||||
if ($rightName -notin $script:commandRights) {
|
||||
Write-Warning "${objectName}: '$rightName' not valid for commands (only: View)"
|
||||
return $false
|
||||
}
|
||||
} else {
|
||||
if ($rightName -notin $script:nestedRights) {
|
||||
Write-Warning "${objectName}: '$rightName' not valid for nested objects (only: View, Edit)"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
return $true
|
||||
}
|
||||
# Тип уже проверен Validate-ObjectName — здесь только права, иначе про один
|
||||
# запрещённый тип напечатается столько строк, сколько у него перечислено прав.
|
||||
if (-not $script:knownRights.ContainsKey($objectType)) { return $false }
|
||||
|
||||
if (-not $script:knownRights.ContainsKey($objectType)) {
|
||||
Write-Warning "${objectName}: unknown object type '$objectType'"
|
||||
if (Is-NestedObject $objectName) {
|
||||
$kind = Get-NestedKind $objectName
|
||||
$validNested = Get-NestedRights $objectType $kind
|
||||
if ($null -eq $validNested) { return $false }
|
||||
if ($rightName -notin $validNested) {
|
||||
Add-ValidationError "${objectName}: право '$rightName' недопустимо для вида '$kind' (допустимо: $($validNested -join ', '))"
|
||||
return $false
|
||||
}
|
||||
return $true
|
||||
}
|
||||
|
||||
@@ -528,8 +661,8 @@ function Validate-RightName {
|
||||
$suggestions = @($validRights | Where-Object {
|
||||
$_ -like "*$rightName*" -or $rightName -like "*$_*"
|
||||
})
|
||||
$sugStr = if ($suggestions.Count -gt 0) { " Did you mean: $($suggestions -join ', ')?" } else { "" }
|
||||
Write-Warning "${objectName}: unknown right '$rightName'.$sugStr"
|
||||
$sugStr = if ($suggestions.Count -gt 0) { " Возможно: $(($suggestions | Select-Object -First 3) -join ', ')?" } else { "" }
|
||||
Add-ValidationError "${objectName}: право '$rightName' не существует у типа '$objectType'.$sugStr"
|
||||
return $false
|
||||
}
|
||||
|
||||
@@ -550,6 +683,9 @@ function Parse-ObjectEntry {
|
||||
}
|
||||
$objName = Translate-ObjectName ($entry.Substring(0, $colonIdx).Trim())
|
||||
$rightsStr = $entry.Substring($colonIdx + 1).Trim()
|
||||
# Объект с непроходным именем дальше не разбираем: пресет для несуществующего типа
|
||||
# добавил бы к отказу ещё и бессмысленное предупреждение.
|
||||
if (-not (Validate-ObjectName $objName)) { return $null }
|
||||
$objectType = Get-ObjectType $objName
|
||||
|
||||
if ($rightsStr.StartsWith('@')) {
|
||||
@@ -575,6 +711,8 @@ function Parse-ObjectEntry {
|
||||
return $null
|
||||
}
|
||||
|
||||
if (-not (Validate-ObjectName $objName)) { return $null }
|
||||
|
||||
$objectType = Get-ObjectType $objName
|
||||
$rightsMap = [ordered]@{}
|
||||
|
||||
@@ -648,6 +786,16 @@ if ($def.objects) {
|
||||
}
|
||||
}
|
||||
|
||||
# Отказ ДО записи: роль пишется тремя файлами (метаданные, права, регистрация в
|
||||
# Configuration.xml), и частично записанная роль хуже отсутствующей. Печатаем все причины
|
||||
# разом — иначе пользователь чинит их по одной. Console.Error, а не Write-Error: под
|
||||
# ErrorActionPreference=Stop последний бросает исключение (см. комментарий в support-guard).
|
||||
if ($script:validationErrors.Count -gt 0) {
|
||||
[Console]::Error.WriteLine("[role-compile] Роль не создана: $($script:validationErrors.Count) ошибок в описании прав.")
|
||||
foreach ($e in $script:validationErrors) { [Console]::Error.WriteLine(" ERROR: $e") }
|
||||
exit 1
|
||||
}
|
||||
|
||||
# --- Detect format version ---
|
||||
|
||||
function Detect-FormatVersion([string]$dir) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-compile v1.24 — Compile 1C role from JSON (+русские алиасы типов: формы с ё и без)
|
||||
# role-compile v1.25 — Compile 1C role from JSON (+русские алиасы типов: формы с ё и без)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
import argparse
|
||||
import json
|
||||
@@ -354,7 +354,24 @@ TYPE_ALIASES = {
|
||||
"ПараметрСеанса": "SessionParameter",
|
||||
"ОбщийРеквизит": "CommonAttribute",
|
||||
"Конфигурация": "Configuration",
|
||||
"ВнешнийИсточникДанных": "ExternalDataSource",
|
||||
# Типы без прав в ролях: алиасы нужны не ради генерации, а ради отказа по делу —
|
||||
# иначе на русскую запись навык ответит «неизвестный тип 'ОбщийМодуль'».
|
||||
"Перечисление": "Enum",
|
||||
"ОбщийМодуль": "CommonModule",
|
||||
"ОпределяемыйТип": "DefinedType",
|
||||
"ОбщаяКартинка": "CommonPicture",
|
||||
"ОбщийМакет": "CommonTemplate",
|
||||
"Язык": "Language",
|
||||
"ФункциональнаяОпция": "FunctionalOption",
|
||||
"ПараметрФункциональныхОпций": "FunctionalOptionsParameter",
|
||||
"ПодпискаНаСобытие": "EventSubscription",
|
||||
"РегламентноеЗадание": "ScheduledJob",
|
||||
"ЭлементСтиля": "StyleItem",
|
||||
"ХранилищеНастроек": "SettingsStorage",
|
||||
"ПакетXDTO": "XDTOPackage",
|
||||
"WSСсылка": "WSReference",
|
||||
"Нумератор": "DocumentNumerator",
|
||||
# Nested
|
||||
"Реквизит": "Attribute",
|
||||
"СтандартныйРеквизит": "StandardAttribute",
|
||||
@@ -516,10 +533,72 @@ KNOWN_RIGHTS = {
|
||||
"IntegrationService": ["Use"],
|
||||
"SessionParameter": ["Get", "Set"],
|
||||
"CommonAttribute": ["View", "Edit"],
|
||||
"ExternalDataSource": [
|
||||
"Use", "Administration", "StandardAuthenticationChange",
|
||||
"SessionStandardAuthenticationChange", "SessionOSAuthenticationChange",
|
||||
],
|
||||
}
|
||||
|
||||
NESTED_RIGHTS = ["View", "Edit"]
|
||||
COMMAND_RIGHTS = ["View"]
|
||||
# Виды вложенности (предпоследний сегмент пути) → допустимые права. Списки сняты с корпуса
|
||||
# типовых конфигураций и с выгрузки роли, где права проставлены по всему дереву редактора:
|
||||
# догадкам тут не место — закрытый список превращает промах в ложный отказ.
|
||||
NESTED_KIND_RIGHTS = {
|
||||
"Attribute": ["View", "Edit"],
|
||||
"StandardAttribute": ["View", "Edit"],
|
||||
"TabularSection": ["View", "Edit"],
|
||||
"StandardTabularSection": ["View", "Edit"],
|
||||
"Dimension": ["View", "Edit"],
|
||||
"Resource": ["View", "Edit"],
|
||||
"AccountingFlag": ["View", "Edit"],
|
||||
"ExtDimensionAccountingFlag": ["View", "Edit"],
|
||||
"AddressingAttribute": ["View", "Edit"],
|
||||
"Field": ["View", "Edit"],
|
||||
"Command": ["View"],
|
||||
"Subsystem": ["View"],
|
||||
"Operation": ["Use"],
|
||||
"Method": ["Use"],
|
||||
"IntegrationServiceChannel": ["Use"],
|
||||
"Recalculation": ["Read", "Update"],
|
||||
"Cube": ["Read", "View"],
|
||||
"DimensionTable": ["Read", "View"],
|
||||
"Function": ["Use", "View"],
|
||||
"Table": [
|
||||
"Read", "Insert", "Update", "Delete", "View", "Edit", "InputByString",
|
||||
"InteractiveInsert", "InteractiveDelete",
|
||||
],
|
||||
}
|
||||
|
||||
# Виды, существующие только у одного типа-родителя: без этой привязки
|
||||
# `Catalog.Товары.Field.Цена` прошёл бы как валидный вложенный объект.
|
||||
KIND_OWNERS = {
|
||||
'Table': 'ExternalDataSource',
|
||||
'Cube': 'ExternalDataSource',
|
||||
'Function': 'ExternalDataSource',
|
||||
'Field': 'ExternalDataSource',
|
||||
'DimensionTable': 'ExternalDataSource',
|
||||
'Recalculation': 'CalculationRegister',
|
||||
'Operation': 'WebService',
|
||||
'Method': 'HTTPService',
|
||||
'IntegrationServiceChannel': 'IntegrationService',
|
||||
}
|
||||
|
||||
# Один и тот же вид под разными родителями имеет разный набор: измерение регистра —
|
||||
# View + Edit, измерение куба внешнего источника — только View. Объединять нельзя,
|
||||
# объединение молча разрешило бы Edit там, где платформа его не даёт.
|
||||
NESTED_KIND_RIGHTS_BY_TYPE = {
|
||||
"ExternalDataSource": {
|
||||
"Dimension": ["View"],
|
||||
"Resource": ["View"],
|
||||
},
|
||||
}
|
||||
|
||||
# Типы без прав в ролях (в дереве редактора ролей их нет). Список НЕ управляет поведением —
|
||||
# отказ даёт отсутствие типа в KNOWN_RIGHTS; здесь только выбор формулировки.
|
||||
NO_RIGHTS_TYPES = [
|
||||
"Enum", "CommonModule", "DefinedType", "CommonPicture", "CommonTemplate", "Language",
|
||||
"FunctionalOption", "FunctionalOptionsParameter", "EventSubscription", "ScheduledJob",
|
||||
"StyleItem", "Style", "SettingsStorage", "XDTOPackage", "WSReference", "DocumentNumerator",
|
||||
]
|
||||
|
||||
# --- Presets ---
|
||||
|
||||
@@ -594,6 +673,56 @@ def is_nested_object(object_name):
|
||||
return len(object_name.split('.')) >= 3
|
||||
|
||||
|
||||
def get_nested_kind(object_name):
|
||||
"""Вид вложенности — предпоследний сегмент: путь бывает и восьмисегментным
|
||||
(ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П), считать от конца."""
|
||||
parts = object_name.split('.')
|
||||
if len(parts) < 3:
|
||||
return None
|
||||
return parts[-2]
|
||||
|
||||
|
||||
def get_nested_rights(object_type, kind):
|
||||
by_type = NESTED_KIND_RIGHTS_BY_TYPE.get(object_type)
|
||||
if by_type and kind in by_type:
|
||||
return by_type[kind]
|
||||
return NESTED_KIND_RIGHTS.get(kind)
|
||||
|
||||
|
||||
# Отказ копится, а не печатается сразу: роль пишется целиком, поэтому единственный
|
||||
# безопасный момент отказа — до первой записи, и показать надо все причины сразу.
|
||||
VALIDATION_ERRORS = []
|
||||
|
||||
|
||||
def add_validation_error(message):
|
||||
VALIDATION_ERRORS.append(message)
|
||||
|
||||
|
||||
def validate_object_name(object_name):
|
||||
"""Тип по белому списку (всегда, включая вложенные пути) и вид вложенности.
|
||||
Запрещённый и незнакомый тип — разные диагнозы."""
|
||||
object_type = get_object_type(object_name)
|
||||
if object_type not in KNOWN_RIGHTS:
|
||||
if object_type in NO_RIGHTS_TYPES:
|
||||
add_validation_error(f"{object_name}: тип '{object_type}' не имеет прав в роли — уберите объект из списка")
|
||||
else:
|
||||
similar = [t for t in KNOWN_RIGHTS if object_type in t or t in object_type][:3]
|
||||
sug = f" Возможно: {', '.join(similar)}?" if similar else ''
|
||||
add_validation_error(f"{object_name}: неизвестный тип объекта '{object_type}'.{sug}")
|
||||
return False
|
||||
|
||||
if is_nested_object(object_name):
|
||||
kind = get_nested_kind(object_name)
|
||||
if kind in KIND_OWNERS and object_type != KIND_OWNERS[kind]:
|
||||
add_validation_error(f"{object_name}: вид '{kind}' бывает только у {KIND_OWNERS[kind]}")
|
||||
return False
|
||||
if get_nested_rights(object_type, kind) is None:
|
||||
add_validation_error(f"{object_name}: неизвестный вид вложенности '{kind}'")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def resolve_preset(object_type, preset_name):
|
||||
preset = preset_name.lstrip('@')
|
||||
if preset not in PRESETS:
|
||||
@@ -614,26 +743,26 @@ def resolve_preset(object_type, preset_name):
|
||||
def validate_right_name(object_name, right_name):
|
||||
object_type = get_object_type(object_name)
|
||||
|
||||
if is_nested_object(object_name):
|
||||
if '.Command.' in object_name:
|
||||
if right_name not in COMMAND_RIGHTS:
|
||||
print(f"WARNING: {object_name}: '{right_name}' not valid for commands (only: View)", file=sys.stderr)
|
||||
return False
|
||||
else:
|
||||
if right_name not in NESTED_RIGHTS:
|
||||
print(f"WARNING: {object_name}: '{right_name}' not valid for nested objects (only: View, Edit)", file=sys.stderr)
|
||||
return False
|
||||
return True
|
||||
|
||||
# Тип уже проверен validate_object_name — здесь только права, иначе про один
|
||||
# запрещённый тип напечатается столько строк, сколько у него перечислено прав.
|
||||
if object_type not in KNOWN_RIGHTS:
|
||||
print(f"WARNING: {object_name}: unknown object type '{object_type}'", file=sys.stderr)
|
||||
return False
|
||||
|
||||
if is_nested_object(object_name):
|
||||
kind = get_nested_kind(object_name)
|
||||
valid_nested = get_nested_rights(object_type, kind)
|
||||
if valid_nested is None:
|
||||
return False
|
||||
if right_name not in valid_nested:
|
||||
add_validation_error(f"{object_name}: право '{right_name}' недопустимо для вида '{kind}' (допустимо: {', '.join(valid_nested)})")
|
||||
return False
|
||||
return True
|
||||
|
||||
valid_rights = KNOWN_RIGHTS[object_type]
|
||||
if right_name not in valid_rights:
|
||||
suggestions = [r for r in valid_rights if right_name in r or r in right_name]
|
||||
sug_str = f" Did you mean: {', '.join(suggestions)}?" if suggestions else ""
|
||||
print(f"WARNING: {object_name}: unknown right '{right_name}'.{sug_str}", file=sys.stderr)
|
||||
suggestions = [r for r in valid_rights if right_name in r or r in right_name][:3]
|
||||
sug_str = f" Возможно: {', '.join(suggestions)}?" if suggestions else ""
|
||||
add_validation_error(f"{object_name}: право '{right_name}' не существует у типа '{object_type}'.{sug_str}")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -648,6 +777,10 @@ def parse_object_entry(entry):
|
||||
return None
|
||||
obj_name = translate_object_name(entry[:colon_idx].strip())
|
||||
rights_str = entry[colon_idx + 1:].strip()
|
||||
# Объект с непроходным именем дальше не разбираем: пресет для несуществующего типа
|
||||
# добавил бы к отказу ещё и бессмысленное предупреждение.
|
||||
if not validate_object_name(obj_name):
|
||||
return None
|
||||
object_type = get_object_type(obj_name)
|
||||
|
||||
if rights_str.startswith('@'):
|
||||
@@ -668,6 +801,9 @@ def parse_object_entry(entry):
|
||||
print("WARNING: Object entry missing 'name' field", file=sys.stderr)
|
||||
return None
|
||||
|
||||
if not validate_object_name(obj_name):
|
||||
return None
|
||||
|
||||
object_type = get_object_type(obj_name)
|
||||
# Use a list of tuples to preserve insertion order
|
||||
rights_map = {} # name -> {Value, Condition}
|
||||
@@ -761,6 +897,15 @@ def main():
|
||||
if parsed:
|
||||
parsed_objects.append(parsed)
|
||||
|
||||
# Отказ ДО записи: роль пишется тремя файлами (метаданные, права, регистрация в
|
||||
# Configuration.xml), и частично записанная роль хуже отсутствующей. Печатаем все
|
||||
# причины разом — иначе пользователь чинит их по одной.
|
||||
if VALIDATION_ERRORS:
|
||||
print(f"[role-compile] Роль не создана: {len(VALIDATION_ERRORS)} ошибок в описании прав.", file=sys.stderr)
|
||||
for e in VALIDATION_ERRORS:
|
||||
print(f" ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# --- 3. Generate UUID ---
|
||||
uid = new_uuid()
|
||||
|
||||
|
||||
@@ -25,3 +25,14 @@ allowed-tools:
|
||||
```powershell
|
||||
powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-validate.ps1" -RightsPath "Roles/МояРоль"
|
||||
```
|
||||
|
||||
## Что считается ошибкой
|
||||
|
||||
Ошибка (код возврата 1), а не предупреждение:
|
||||
|
||||
- тип объекта, не имеющий прав в ролях (`Enum`, `CommonModule`, `CommonPicture`, `ScheduledJob` и т.п.) — конфигурация с блоком прав на перечисление не загружается в базу, конфигуратор зависает без сообщений;
|
||||
- незнакомый тип объекта (опечатка);
|
||||
- имя права, которого у этого типа нет;
|
||||
- вид вложенности, невозможный у данного типа, и недопустимое право у вложенного объекта.
|
||||
|
||||
Полные списки «тип → права» и виды вложенности — в `role-compile/dsl-reference.md`.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# role-validate v1.2 — Validate 1C role structure
|
||||
# role-validate v1.3 — Validate 1C role structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
@@ -127,11 +127,72 @@ $script:knownRights = @{
|
||||
"IntegrationService" = @("Use")
|
||||
"SessionParameter" = @("Get","Set")
|
||||
"CommonAttribute" = @("View","Edit")
|
||||
"ExternalDataSource" = @(
|
||||
"Use","Administration","StandardAuthenticationChange",
|
||||
"SessionStandardAuthenticationChange","SessionOSAuthenticationChange"
|
||||
)
|
||||
}
|
||||
|
||||
$script:nestedRights = @("View","Edit")
|
||||
$script:channelRights = @("Use")
|
||||
$script:commandRights = @("View")
|
||||
# Виды вложенности (предпоследний сегмент пути) → допустимые права. Списки сняты с корпуса
|
||||
# типовых конфигураций и с выгрузки роли, где права проставлены по всему дереву редактора:
|
||||
# догадкам тут не место — закрытый список превращает промах в ложный отказ.
|
||||
$script:nestedKindRights = @{
|
||||
"Attribute" = @("View","Edit")
|
||||
"StandardAttribute" = @("View","Edit")
|
||||
"TabularSection" = @("View","Edit")
|
||||
"StandardTabularSection" = @("View","Edit")
|
||||
"Dimension" = @("View","Edit")
|
||||
"Resource" = @("View","Edit")
|
||||
"AccountingFlag" = @("View","Edit")
|
||||
"ExtDimensionAccountingFlag" = @("View","Edit")
|
||||
"AddressingAttribute" = @("View","Edit")
|
||||
"Field" = @("View","Edit")
|
||||
"Command" = @("View")
|
||||
"Subsystem" = @("View")
|
||||
"Operation" = @("Use")
|
||||
"Method" = @("Use")
|
||||
"IntegrationServiceChannel" = @("Use")
|
||||
"Recalculation" = @("Read","Update")
|
||||
"Cube" = @("Read","View")
|
||||
"DimensionTable" = @("Read","View")
|
||||
"Function" = @("Use","View")
|
||||
"Table" = @(
|
||||
"Read","Insert","Update","Delete","View","Edit","InputByString",
|
||||
"InteractiveInsert","InteractiveDelete"
|
||||
)
|
||||
}
|
||||
|
||||
# Виды, существующие только у одного типа-родителя: без этой привязки
|
||||
# `Catalog.Товары.Field.Цена` прошёл бы как валидный вложенный объект.
|
||||
$script:kindOwners = @{
|
||||
"Table" = "ExternalDataSource"
|
||||
"Cube" = "ExternalDataSource"
|
||||
"Function" = "ExternalDataSource"
|
||||
"Field" = "ExternalDataSource"
|
||||
"DimensionTable" = "ExternalDataSource"
|
||||
"Recalculation" = "CalculationRegister"
|
||||
"Operation" = "WebService"
|
||||
"Method" = "HTTPService"
|
||||
"IntegrationServiceChannel" = "IntegrationService"
|
||||
}
|
||||
|
||||
# Один и тот же вид под разными родителями имеет разный набор: измерение регистра —
|
||||
# View + Edit, измерение куба внешнего источника — только View. Объединять нельзя,
|
||||
# объединение молча разрешило бы Edit там, где платформа его не даёт.
|
||||
$script:nestedKindRightsByType = @{
|
||||
"ExternalDataSource" = @{
|
||||
"Dimension" = @("View")
|
||||
"Resource" = @("View")
|
||||
}
|
||||
}
|
||||
|
||||
# Типы без прав в ролях (в дереве редактора ролей их нет). Таблица НЕ управляет поведением —
|
||||
# отказ даёт отсутствие типа в $knownRights; здесь только причина для сообщения.
|
||||
$script:noRightsTypes = @(
|
||||
"Enum","CommonModule","DefinedType","CommonPicture","CommonTemplate","Language",
|
||||
"FunctionalOption","FunctionalOptionsParameter","EventSubscription","ScheduledJob",
|
||||
"StyleItem","Style","SettingsStorage","XDTOPackage","WSReference","DocumentNumerator"
|
||||
)
|
||||
|
||||
# --- 2. Output helpers ---
|
||||
|
||||
@@ -179,6 +240,37 @@ function Is-NestedObject {
|
||||
return ($name.Split(".").Count -ge 3)
|
||||
}
|
||||
|
||||
# Вид вложенности — предпоследний сегмент: путь бывает и восьмисегментным
|
||||
# (ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П), считать от конца.
|
||||
function Get-NestedKind {
|
||||
param([string]$name)
|
||||
$parts = $name.Split(".")
|
||||
if ($parts.Count -lt 3) { return $null }
|
||||
return $parts[$parts.Count - 2]
|
||||
}
|
||||
|
||||
function Get-NestedRights {
|
||||
param([string]$objectType, [string]$kind)
|
||||
if ($script:nestedKindRightsByType.ContainsKey($objectType) -and
|
||||
$script:nestedKindRightsByType[$objectType].ContainsKey($kind)) {
|
||||
return @($script:nestedKindRightsByType[$objectType][$kind])
|
||||
}
|
||||
if ($script:nestedKindRights.ContainsKey($kind)) { return @($script:nestedKindRights[$kind]) }
|
||||
return $null
|
||||
}
|
||||
|
||||
# Запрещённый тип и незнакомый тип — разные диагнозы: первый документирован, второй
|
||||
# скорее опечатка или пробел в таблице.
|
||||
function Format-TypeError {
|
||||
param([string]$objName, [string]$objectType)
|
||||
if ($script:noRightsTypes -contains $objectType) {
|
||||
return "${objName}: тип '$objectType' не имеет прав в роли — блок нужно удалить"
|
||||
}
|
||||
$similar = Find-Similar -needle $objectType -haystack @($script:knownRights.Keys)
|
||||
$sug = if ($similar.Count -gt 0) { " Возможно: $($similar -join ', ')?" } else { "" }
|
||||
return "${objName}: неизвестный тип объекта '$objectType'.$sug"
|
||||
}
|
||||
|
||||
function Find-Similar {
|
||||
param([string]$needle, [string[]]$haystack)
|
||||
$result = @($haystack | Where-Object {
|
||||
@@ -303,9 +395,16 @@ foreach ($obj in $objects) {
|
||||
$objectType = Get-ObjectType $objName
|
||||
$isNested = Is-NestedObject $objName
|
||||
|
||||
# Check object type is known
|
||||
if (-not $isNested -and -not $script:knownRights.ContainsKey($objectType)) {
|
||||
Report-Warn "${objName}: unknown object type '$objectType'"
|
||||
# Тип проверяется ВСЕГДА, включая вложенные пути: раньше `Enum.Х.Attribute.Y` не проверялся
|
||||
# вообще, потому что ветка «unknown» стояла под `-not $isNested`.
|
||||
$nestedKind = if ($isNested) { Get-NestedKind $objName } else { $null }
|
||||
$typeKnown = $script:knownRights.ContainsKey($objectType)
|
||||
if (-not $typeKnown) {
|
||||
Report-Error (Format-TypeError $objName $objectType)
|
||||
} elseif ($isNested -and $script:kindOwners.ContainsKey($nestedKind) -and $objectType -ne $script:kindOwners[$nestedKind]) {
|
||||
Report-Error "${objName}: вид '$nestedKind' бывает только у $($script:kindOwners[$nestedKind])"
|
||||
} elseif ($isNested -and $null -eq (Get-NestedRights $objectType $nestedKind)) {
|
||||
Report-Error "${objName}: неизвестный вид вложенности '$nestedKind'"
|
||||
}
|
||||
|
||||
# Check rights
|
||||
@@ -345,27 +444,20 @@ foreach ($obj in $objects) {
|
||||
|
||||
$rightCount++
|
||||
|
||||
# Validate right name
|
||||
if ($isNested) {
|
||||
if ($objName -match '\.Command\.') {
|
||||
if ($rName -notin $script:commandRights) {
|
||||
Report-Warn "${objName}: '$rName' not valid for commands (only: View)"
|
||||
}
|
||||
} elseif ($objName -match '\.IntegrationServiceChannel\.') {
|
||||
if ($rName -notin $script:channelRights) {
|
||||
Report-Warn "${objName}: '$rName' not valid for channels (only: Use)"
|
||||
}
|
||||
} else {
|
||||
if ($rName -notin $script:nestedRights) {
|
||||
Report-Warn "${objName}: '$rName' not valid for nested objects (only: View, Edit)"
|
||||
}
|
||||
# Validate right name. Тип уже отвергнут выше — второй раз про него не пишем.
|
||||
if (-not $typeKnown) {
|
||||
# уже сообщено
|
||||
} elseif ($isNested) {
|
||||
$validNested = Get-NestedRights $objectType $nestedKind
|
||||
if ($null -ne $validNested -and $rName -notin $validNested) {
|
||||
Report-Error "${objName}: право '$rName' недопустимо для вида '$nestedKind' (допустимо: $($validNested -join ', '))"
|
||||
}
|
||||
} elseif ($script:knownRights.ContainsKey($objectType)) {
|
||||
} else {
|
||||
$validRights = $script:knownRights[$objectType]
|
||||
if ($rName -notin $validRights) {
|
||||
$similar = Find-Similar -needle $rName -haystack $validRights
|
||||
$sugStr = if ($similar.Count -gt 0) { " Did you mean: $($similar -join ', ')?" } else { "" }
|
||||
Report-Warn "${objName}: unknown right '$rName'.$sugStr"
|
||||
$sugStr = if ($similar.Count -gt 0) { " Возможно: $($similar -join ', ')?" } else { "" }
|
||||
Report-Error "${objName}: право '$rName' не существует у типа '$objectType'.$sugStr"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# role-validate v1.2 — Validate 1C role Rights.xml structure
|
||||
# role-validate v1.3 — Validate 1C role Rights.xml structure
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
"""Validates role Rights.xml: root element, global flags, objects, rights, RLS, templates."""
|
||||
import sys, os, argparse, re
|
||||
@@ -145,11 +145,72 @@ KNOWN_RIGHTS = {
|
||||
'IntegrationService': ['Use'],
|
||||
'SessionParameter': ['Get', 'Set'],
|
||||
'CommonAttribute': ['View', 'Edit'],
|
||||
'ExternalDataSource': [
|
||||
'Use', 'Administration', 'StandardAuthenticationChange',
|
||||
'SessionStandardAuthenticationChange', 'SessionOSAuthenticationChange',
|
||||
],
|
||||
}
|
||||
|
||||
NESTED_RIGHTS = ['View', 'Edit']
|
||||
CHANNEL_RIGHTS = ['Use']
|
||||
COMMAND_RIGHTS = ['View']
|
||||
# Виды вложенности (предпоследний сегмент пути) → допустимые права. Списки сняты с корпуса
|
||||
# типовых конфигураций и с выгрузки роли, где права проставлены по всему дереву редактора:
|
||||
# догадкам тут не место — закрытый список превращает промах в ложный отказ.
|
||||
NESTED_KIND_RIGHTS = {
|
||||
'Attribute': ['View', 'Edit'],
|
||||
'StandardAttribute': ['View', 'Edit'],
|
||||
'TabularSection': ['View', 'Edit'],
|
||||
'StandardTabularSection': ['View', 'Edit'],
|
||||
'Dimension': ['View', 'Edit'],
|
||||
'Resource': ['View', 'Edit'],
|
||||
'AccountingFlag': ['View', 'Edit'],
|
||||
'ExtDimensionAccountingFlag': ['View', 'Edit'],
|
||||
'AddressingAttribute': ['View', 'Edit'],
|
||||
'Field': ['View', 'Edit'],
|
||||
'Command': ['View'],
|
||||
'Subsystem': ['View'],
|
||||
'Operation': ['Use'],
|
||||
'Method': ['Use'],
|
||||
'IntegrationServiceChannel': ['Use'],
|
||||
'Recalculation': ['Read', 'Update'],
|
||||
'Cube': ['Read', 'View'],
|
||||
'DimensionTable': ['Read', 'View'],
|
||||
'Function': ['Use', 'View'],
|
||||
'Table': [
|
||||
'Read', 'Insert', 'Update', 'Delete', 'View', 'Edit', 'InputByString',
|
||||
'InteractiveInsert', 'InteractiveDelete',
|
||||
],
|
||||
}
|
||||
|
||||
# Виды, существующие только у одного типа-родителя: без этой привязки
|
||||
# `Catalog.Товары.Field.Цена` прошёл бы как валидный вложенный объект.
|
||||
KIND_OWNERS = {
|
||||
'Table': 'ExternalDataSource',
|
||||
'Cube': 'ExternalDataSource',
|
||||
'Function': 'ExternalDataSource',
|
||||
'Field': 'ExternalDataSource',
|
||||
'DimensionTable': 'ExternalDataSource',
|
||||
'Recalculation': 'CalculationRegister',
|
||||
'Operation': 'WebService',
|
||||
'Method': 'HTTPService',
|
||||
'IntegrationServiceChannel': 'IntegrationService',
|
||||
}
|
||||
|
||||
# Один и тот же вид под разными родителями имеет разный набор: измерение регистра —
|
||||
# View + Edit, измерение куба внешнего источника — только View. Объединять нельзя,
|
||||
# объединение молча разрешило бы Edit там, где платформа его не даёт.
|
||||
NESTED_KIND_RIGHTS_BY_TYPE = {
|
||||
'ExternalDataSource': {
|
||||
'Dimension': ['View'],
|
||||
'Resource': ['View'],
|
||||
},
|
||||
}
|
||||
|
||||
# Типы без прав в ролях (в дереве редактора ролей их нет). Таблица НЕ управляет поведением —
|
||||
# отказ даёт отсутствие типа в KNOWN_RIGHTS; здесь только причина для сообщения.
|
||||
NO_RIGHTS_TYPES = [
|
||||
'Enum', 'CommonModule', 'DefinedType', 'CommonPicture', 'CommonTemplate', 'Language',
|
||||
'FunctionalOption', 'FunctionalOptionsParameter', 'EventSubscription', 'ScheduledJob',
|
||||
'StyleItem', 'Style', 'SettingsStorage', 'XDTOPackage', 'WSReference', 'DocumentNumerator',
|
||||
]
|
||||
|
||||
|
||||
def get_object_type(name):
|
||||
@@ -163,6 +224,22 @@ def is_nested_object(name):
|
||||
return name.count('.') >= 2
|
||||
|
||||
|
||||
def get_nested_kind(name):
|
||||
"""Вид вложенности — предпоследний сегмент: путь бывает и восьмисегментным
|
||||
(ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П), считать от конца."""
|
||||
parts = name.split('.')
|
||||
if len(parts) < 3:
|
||||
return None
|
||||
return parts[-2]
|
||||
|
||||
|
||||
def get_nested_rights(object_type, kind):
|
||||
by_type = NESTED_KIND_RIGHTS_BY_TYPE.get(object_type)
|
||||
if by_type and kind in by_type:
|
||||
return by_type[kind]
|
||||
return NESTED_KIND_RIGHTS.get(kind)
|
||||
|
||||
|
||||
def find_similar(needle, haystack):
|
||||
result = []
|
||||
needle_lower = needle.lower()
|
||||
@@ -175,6 +252,16 @@ def find_similar(needle, haystack):
|
||||
return result
|
||||
|
||||
|
||||
def format_type_error(obj_name, object_type):
|
||||
"""Запрещённый тип и незнакомый тип — разные диагнозы: первый документирован,
|
||||
второй скорее опечатка или пробел в таблице."""
|
||||
if object_type in NO_RIGHTS_TYPES:
|
||||
return f"{obj_name}: тип '{object_type}' не имеет прав в роли — блок нужно удалить"
|
||||
similar = find_similar(object_type, list(KNOWN_RIGHTS.keys()))
|
||||
sug = f" Возможно: {', '.join(similar)}?" if similar else ''
|
||||
return f"{obj_name}: неизвестный тип объекта '{object_type}'.{sug}"
|
||||
|
||||
|
||||
def get_child_text(parent, local_name, ns):
|
||||
"""Get text of first child element with given local name in namespace."""
|
||||
for child in parent:
|
||||
@@ -339,9 +426,16 @@ def main():
|
||||
object_type = get_object_type(obj_name)
|
||||
is_nested = is_nested_object(obj_name)
|
||||
|
||||
# Check object type is known
|
||||
if not is_nested and object_type not in KNOWN_RIGHTS:
|
||||
report_warn(f"{obj_name}: unknown object type '{object_type}'")
|
||||
# Тип проверяется ВСЕГДА, включая вложенные пути: раньше `Enum.Х.Attribute.Y`
|
||||
# не проверялся вообще — ветка «unknown» стояла под `not is_nested`.
|
||||
nested_kind = get_nested_kind(obj_name) if is_nested else None
|
||||
type_known = object_type in KNOWN_RIGHTS
|
||||
if not type_known:
|
||||
report_error(format_type_error(obj_name, object_type))
|
||||
elif is_nested and nested_kind in KIND_OWNERS and object_type != KIND_OWNERS[nested_kind]:
|
||||
report_error(f"{obj_name}: вид '{nested_kind}' бывает только у {KIND_OWNERS[nested_kind]}")
|
||||
elif is_nested and get_nested_rights(object_type, nested_kind) is None:
|
||||
report_error(f"{obj_name}: неизвестный вид вложенности '{nested_kind}'")
|
||||
|
||||
# Check rights
|
||||
for child in obj:
|
||||
@@ -384,23 +478,19 @@ def main():
|
||||
|
||||
right_count += 1
|
||||
|
||||
# Validate right name
|
||||
if is_nested:
|
||||
if '.Command.' in obj_name:
|
||||
if r_name not in COMMAND_RIGHTS:
|
||||
report_warn(f"{obj_name}: '{r_name}' not valid for commands (only: View)")
|
||||
elif '.IntegrationServiceChannel.' in obj_name:
|
||||
if r_name not in CHANNEL_RIGHTS:
|
||||
report_warn(f"{obj_name}: '{r_name}' not valid for channels (only: Use)")
|
||||
else:
|
||||
if r_name not in NESTED_RIGHTS:
|
||||
report_warn(f"{obj_name}: '{r_name}' not valid for nested objects (only: View, Edit)")
|
||||
elif object_type in KNOWN_RIGHTS:
|
||||
# Validate right name. Тип уже отвергнут выше — второй раз про него не пишем.
|
||||
if not type_known:
|
||||
pass
|
||||
elif is_nested:
|
||||
valid_nested = get_nested_rights(object_type, nested_kind)
|
||||
if valid_nested is not None and r_name not in valid_nested:
|
||||
report_error(f"{obj_name}: право '{r_name}' недопустимо для вида '{nested_kind}' (допустимо: {', '.join(valid_nested)})")
|
||||
else:
|
||||
valid_rights = KNOWN_RIGHTS[object_type]
|
||||
if r_name not in valid_rights:
|
||||
similar = find_similar(r_name, valid_rights)
|
||||
sug_str = f' Did you mean: {", ".join(similar)}?' if similar else ''
|
||||
report_warn(f"{obj_name}: unknown right '{r_name}'.{sug_str}")
|
||||
sug_str = f" Возможно: {', '.join(similar)}?" if similar else ''
|
||||
report_error(f"{obj_name}: право '{r_name}' не существует у типа '{object_type}'.{sug_str}")
|
||||
|
||||
report_ok(f'{obj_count} objects, {right_count} rights')
|
||||
if rls_count > 0:
|
||||
|
||||
+46
-7
@@ -542,18 +542,44 @@ Subsystem.Администрирование.Subsystem.Пользователи
|
||||
| `SessionParameter` | Get, Set |
|
||||
| `CommonAttribute` | View, Edit |
|
||||
|
||||
#### ExternalDataSource
|
||||
|
||||
Источник ошибки в прежних редакциях спецификации: внешние источники данных права **имеют** —
|
||||
узел «Внешние источники данных» есть в дереве редактора ролей. Значения ниже сняты с выгрузки
|
||||
роли со всеми проставленными правами (8.3.25).
|
||||
|
||||
| Объект | Права |
|
||||
|--------|-------|
|
||||
| `ExternalDataSource.И` | Use, Administration, StandardAuthenticationChange, SessionStandardAuthenticationChange, SessionOSAuthenticationChange |
|
||||
| `ExternalDataSource.И.Table.Т` | Read, Insert, Update, Delete, View, Edit, InputByString, InteractiveInsert, InteractiveDelete |
|
||||
| `ExternalDataSource.И.Table.Т.Field.П` | View, Edit |
|
||||
| `ExternalDataSource.И.Cube.К` | Read, View |
|
||||
| `ExternalDataSource.И.Cube.К.Dimension.И` | View |
|
||||
| `ExternalDataSource.И.Cube.К.Resource.Р` | View |
|
||||
| `ExternalDataSource.И.Cube.К.DimensionTable.Т` | Read, View |
|
||||
| `ExternalDataSource.И.Cube.К.DimensionTable.Т.Field.П` | View, Edit |
|
||||
| `ExternalDataSource.И.Function.Ф` | Use, View |
|
||||
| `…Table.Т.Command.К`, `…Cube.К.Command.К`, `…DimensionTable.Т.Command.К` | View |
|
||||
|
||||
#### Типы объектов БЕЗ прав в ролях
|
||||
|
||||
Следующие типы не фигурируют в Rights.xml (права не применимы или управляются иначе):
|
||||
Следующие типы не фигурируют в Rights.xml — в дереве редактора ролей их нет:
|
||||
|
||||
- `Enum` (перечисления)
|
||||
- `FunctionalOption`
|
||||
- `DefinedType`
|
||||
- `Enum` (перечисления) — блок прав на перечисление приводит к зависанию загрузки
|
||||
конфигурации в информационную базу: конфигуратор не завершается и не выдаёт сообщений
|
||||
- `CommonModule`
|
||||
- `DefinedType`
|
||||
- `CommonPicture`
|
||||
- `CommonTemplate`
|
||||
- `Language`
|
||||
- `FunctionalOption`, `FunctionalOptionsParameter`
|
||||
- `EventSubscription`
|
||||
- `ScheduledJob`
|
||||
- `StyleItem`, `Style`
|
||||
- `SettingsStorage`
|
||||
- `ExternalDataSource`
|
||||
- `XDTOPackage`
|
||||
- `WSReference`
|
||||
- `DocumentNumerator`
|
||||
|
||||
---
|
||||
|
||||
@@ -646,8 +672,21 @@ Subsystem.Администрирование.Subsystem.Пользователи
|
||||
| `TabularSection.*.Attribute` | (все с TabularSection) | View, Edit |
|
||||
| `Dimension` | InformationRegister, AccumulationRegister, AccountingRegister | View, Edit |
|
||||
| `Resource` | InformationRegister, AccumulationRegister, AccountingRegister | View, Edit |
|
||||
| `Command` | Catalog, Document, DataProcessor, Report, *Register, DocumentJournal, ExchangePlan, BusinessProcess, Task | View |
|
||||
| `AddressingAttribute` | Task | View, Edit |
|
||||
| `Command` | Catalog, Document, DataProcessor, Report, *Register, DocumentJournal, ExchangePlan, BusinessProcess, Task, ExternalDataSource (Table/Cube/DimensionTable) | View |
|
||||
| `AddressingAttribute` | Task, BusinessProcess | View, Edit |
|
||||
| `StandardTabularSection` | Catalog, Document, ChartOf* | View, Edit |
|
||||
| `AccountingFlag`, `ExtDimensionAccountingFlag` | ChartOfAccounts | View, Edit |
|
||||
| `Subsystem` | Subsystem (вложенная) | View |
|
||||
| `Operation` | WebService | Use |
|
||||
| `URLTemplate.*.Method` | HTTPService | Use |
|
||||
| `IntegrationServiceChannel` | IntegrationService | Use |
|
||||
| `Recalculation` | CalculationRegister | Read, Update |
|
||||
| `Column` (графа журнала) | — | прав не имеет |
|
||||
| `Table`, `Cube`, `Function`, `Field`, `DimensionTable` | только ExternalDataSource | см. раздел ExternalDataSource |
|
||||
|
||||
Виды, встречающиеся в выгрузках типовых конфигураций, сверены с корпусом
|
||||
(`acc`, `erp`, `ut`, `unf` — ~2750 ролей); внешние источники данных — с выгрузкой роли,
|
||||
где права проставлены по всему дереву.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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>
|
||||
+18
@@ -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>
|
||||
+96
@@ -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>
|
||||
+5
-1
@@ -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]"]
|
||||
}
|
||||
}
|
||||
+205
@@ -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>
|
||||
+64
@@ -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>
|
||||
+83
@@ -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": {
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user