diff --git a/.claude/skills/role-compile/SKILL.md b/.claude/skills/role-compile/SKILL.md index 9fcf5b54b..33b3052cf 100644 --- a/.claude/skills/role-compile/SKILL.md +++ b/.claude/skills/role-compile/SKILL.md @@ -129,10 +129,16 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-compile.ps1" - Права на части объекта задаются точечным путём: `Catalog.Контрагенты.Attribute.ИНН: View, Edit`, `WebService.Обмен.Operation.Загрузить: Use`, `HTTPService.ЭДО.URLTemplate.ЕстьНовыеДокументы.Method.POST: Use`. -Реквизиты, табличные части, измерения и ресурсы по умолчанию наследуют права своего объекта: -выдавать их отдельно бессмысленно, платформа такую запись не сохранит. Поштучные права на них -имеют смысл только при `"setForAttributesByDefault": false`; ограничить реквизит в обычной роли -можно запретом — `{"name": "Catalog.Товары.Attribute.Цена", "rights": {"View": false}}`. +Роль хранит только то, что отличается от её умолчаний, — всё совпавшее навык не пишет и говорит +об этом. При умолчаниях (`setForNewObjects: false`, `setForAttributesByDefault: true`) это значит: +объектам верхнего уровня права выдают, а реквизитам, ТЧ, измерениям и ресурсам — только +запрещают, потому что те наследуют права своего объекта: + +```json +{"name": "Catalog.Товары.Attribute.Цена", "rights": {"View": false}} +``` + +Запрет тянет за собой права, которым запрещённое нужно: `View: false` у реквизита уносит `Edit`. Роль расширения, включённая в основные (`DefaultRoles`), прав на заимствованные объекты давать не может — платформа это запрещает. Такие права выноси в отдельную роль вне основных. diff --git a/.claude/skills/role-compile/scripts/role-compile.ps1 b/.claude/skills/role-compile/scripts/role-compile.ps1 index d61319dc6..0dcb35ea9 100644 --- a/.claude/skills/role-compile/scripts/role-compile.ps1 +++ b/.claude/skills/role-compile/scripts/role-compile.ps1 @@ -1,4 +1,4 @@ -# role-compile v1.41 — Compile 1C role from JSON +# role-compile v1.42 — Compile 1C role from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( @@ -901,32 +901,73 @@ $script:configurationLegacyDeps = @("AnalyticsSystemClient","MainWindowModeEmbed $script:configurationLegacyRank = 218 # Замыкание набора прав объекта. Возвращает @{ Rights = <итог>; Added = <что дописано> }. +# Платформа хранит только то, что ОТЛИЧАЕТСЯ от значения по умолчанию для роли: при +# setForNewObjects=false на верхнем уровне живут разрешения, при true — запреты; у реквизитных +# вложенных объектов ту же роль играет setForAttributesByDefault. Совпавшее с умолчанием +# платформа выбрасывает при первой же загрузке, поэтому не пишем его и сами. +$script:attributeKinds = @( + "Attribute","StandardAttribute","TabularSection","StandardTabularSection", + "Dimension","Resource","AccountingFlag","ExtDimensionAccountingFlag","AddressingAttribute" +) + +function Get-DefaultRightValue { + param([string]$objName, [string]$setForNewObjects, [string]$setForAttributesByDefault) + $parts = $objName -split '\.' + if ($parts.Count -lt 3) { return $setForNewObjects } + # Внешние источники данных под это правило не проверялись — трогаем только то, что замерено. + if ($parts[0] -eq 'ExternalDataSource') { return "false" } + $kind = $parts[$parts.Count-2] + if ($script:attributeKinds -contains $kind) { return $setForAttributesByDefault } + # Команды, подсистемы, операции сервисов флагами роли не управляются — там живут разрешения. + return "false" +} + function Close-RightsDependencies { param([string]$objName, $rights, [int]$formatRank) $parts = $objName -split '\.' - # У вложенных объектов (реквизит, ТЧ, измерение) зависимостей нет — платформа их не трогает. - if ($parts.Count -ge 3) { return @{ Rights = $rights; Added = @() } } + $nested = $parts.Count -ge 3 $objectType = $parts[0] - $allowed = $script:knownRights[$objectType] + $allowed = if ($nested) { Get-NestedRights -objectType $objectType -kind (Get-NestedKind $objName) } + else { $script:knownRights[$objectType] } if (-not $allowed) { return @{ Rights = $rights; Added = @() } } $have = [ordered]@{} foreach ($r in $rights) { if (-not $have.Contains($r.Name)) { $have[$r.Name] = $r } } $byType = $script:rightDepsByType[$objectType] $added = @() - $queue = @($have.Keys) + # Вперёд — только от РАЗРЕШЁННЫХ прав: платформа замыкает выданное, а не запрещённое. + $queue = @($have.Keys | Where-Object { $have[$_].Value -eq "true" }) while ($queue.Count -gt 0) { $name = $queue[0] $queue = @($queue | Select-Object -Skip 1) $need = if ($byType -and $byType.Contains($name)) { $byType[$name] } else { $script:rightDeps[$name] } if (-not $need) { continue } foreach ($dep in $need) { - if ($have.Contains($dep)) { continue } if ($allowed -notcontains $dep) { continue } + if ($have.Contains($dep)) { + # Разрешение перебивает запрет — так поступает и платформа при загрузке. + if ($have[$dep].Value -ne "true") { $have[$dep].Value = "true"; $added += $dep; $queue += $dep } + continue + } $have[$dep] = @{ Name = $dep; Value = "true"; Condition = $null } $added += $dep $queue += $dep } } + # Назад — от ЗАПРЕТОВ: право, которому запрещённое нужно, платформа запрещает следом. + $denyQueue = @($have.Keys | Where-Object { $have[$_].Value -ne "true" }) + while ($denyQueue.Count -gt 0) { + $name = $denyQueue[0] + $denyQueue = @($denyQueue | Select-Object -Skip 1) + foreach ($candidate in $allowed) { + if ($candidate -eq $name) { continue } + $need = if ($byType -and $byType.Contains($candidate)) { $byType[$candidate] } else { $script:rightDeps[$candidate] } + if (-not $need -or $need -notcontains $name) { continue } + if ($have.Contains($candidate)) { continue } + $have[$candidate] = @{ Name = $candidate; Value = "false"; Condition = $null } + $added += $candidate + $denyQueue += $candidate + } + } if ($objectType -eq 'Configuration' -and $formatRank -le $script:configurationLegacyRank -and $have.Count -gt 0) { foreach ($dep in $script:configurationLegacyDeps) { if ($have.Contains($dep)) { continue } @@ -1479,6 +1520,19 @@ foreach ($o in $parsedObjects) { $parsedObjects = @(Sort-ObjectsByUuid -objects $parsedObjects -configRoot $resolvedOutputDir) foreach ($o in $parsedObjects) { $o.Rights = @(Sort-RightsCanonical -objName $o.Name -rights $o.Rights) } +# Записи, равные умолчанию роли, платформа не хранит — отбрасываем их сами и говорим об этом. +$droppedByDefault = @() +foreach ($obj in $parsedObjects) { + $defaultValue = Get-DefaultRightValue $obj.Name $sfno $sfab + $kept = @() + foreach ($right in $obj.Rights) { + if ($right.Value -eq $defaultValue) { $droppedByDefault += "$($obj.Name).$($right.Name)"; continue } + $kept += ,$right + } + $obj.Rights = $kept +} +$parsedObjects = @($parsedObjects | Where-Object { $_.Rights.Count -gt 0 }) + # Object blocks $totalRights = 0 foreach ($obj in $parsedObjects) { @@ -1748,6 +1802,9 @@ Write-Host " UUID: $uuid" Write-Host " Metadata: $metadataPath" Write-Host " Rights: $rightsPath" Write-Host " Objects: $($parsedObjects.Count), Rights: $totalRights, Templates: $templateCount" +if ($droppedByDefault.Count -gt 0) { + [Console]::Error.WriteLine("[role-compile] Не записаны права, совпадающие с умолчанием роли (платформа их не хранит): $($droppedByDefault -join ', ')") +} foreach ($note in $closureNotes) { Write-Host $note } switch ($regResult) { "added" { Write-Host " Configuration.xml: $roleName added to ChildObjects" } diff --git a/.claude/skills/role-compile/scripts/role-compile.py b/.claude/skills/role-compile/scripts/role-compile.py index 6b5dd5136..09b54abc1 100644 --- a/.claude/skills/role-compile/scripts/role-compile.py +++ b/.claude/skills/role-compile/scripts/role-compile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# role-compile v1.41 — Compile 1C role from JSON +# role-compile v1.42 — Compile 1C role from JSON # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -839,14 +839,37 @@ CONFIGURATION_LEGACY_DEPS = ["AnalyticsSystemClient", "MainWindowModeEmbeddedWor CONFIGURATION_LEGACY_RANK = 218 +# Платформа хранит только то, что ОТЛИЧАЕТСЯ от значения по умолчанию для роли: при +# setForNewObjects=false на верхнем уровне живут разрешения, при true — запреты; у реквизитных +# вложенных объектов ту же роль играет setForAttributesByDefault. Совпавшее с умолчанием +# платформа выбрасывает при первой же загрузке, поэтому не пишем его и сами. +ATTRIBUTE_KINDS = [ + "Attribute", "StandardAttribute", "TabularSection", "StandardTabularSection", + "Dimension", "Resource", "AccountingFlag", "ExtDimensionAccountingFlag", "AddressingAttribute", +] + + +def get_default_right_value(object_name, set_for_new_objects, set_for_attributes_by_default): + parts = object_name.split('.') + if len(parts) < 3: + return set_for_new_objects + # Внешние источники данных под это правило не проверялись — трогаем только то, что замерено. + if parts[0] == 'ExternalDataSource': + return "false" + kind = parts[-2] + if kind in ATTRIBUTE_KINDS: + return set_for_attributes_by_default + # Команды, подсистемы, операции сервисов флагами роли не управляются — там живут разрешения. + return "false" + + def close_rights_dependencies(object_name, rights, format_rank): """Замыкание набора прав объекта. Возвращает (итоговые права, что дописано).""" parts = object_name.split('.') - # У вложенных объектов (реквизит, ТЧ, измерение) зависимостей нет — платформа их не трогает. - if len(parts) >= 3: - return rights, [] + nested = len(parts) >= 3 object_type = parts[0] - allowed = KNOWN_RIGHTS.get(object_type) + allowed = (get_nested_rights(object_type, get_nested_kind(object_name)) if nested + else KNOWN_RIGHTS.get(object_type)) if not allowed: return rights, [] have = {} @@ -854,18 +877,39 @@ def close_rights_dependencies(object_name, rights, format_rank): have.setdefault(r['Name'], r) by_type = RIGHT_DEPS_BY_TYPE.get(object_type, {}) added = [] - queue = list(have.keys()) + # Вперёд — только от РАЗРЕШЁННЫХ прав: платформа замыкает выданное, а не запрещённое. + queue = [n for n in have if have[n]['Value'] == 'true'] while queue: name = queue.pop(0) need = by_type[name] if name in by_type else RIGHT_DEPS.get(name) if not need: continue for dep in need: - if dep in have or dep not in allowed: + if dep not in allowed: + continue + if dep in have: + # Разрешение перебивает запрет — так поступает и платформа при загрузке. + if have[dep]['Value'] != 'true': + have[dep]['Value'] = 'true' + added.append(dep) + queue.append(dep) continue have[dep] = {'Name': dep, 'Value': 'true', 'Condition': None} added.append(dep) queue.append(dep) + # Назад — от ЗАПРЕТОВ: право, которому запрещённое нужно, платформа запрещает следом. + deny_queue = [n for n in have if have[n]['Value'] != 'true'] + while deny_queue: + name = deny_queue.pop(0) + for candidate in allowed: + if candidate == name or candidate in have: + continue + need = by_type[candidate] if candidate in by_type else RIGHT_DEPS.get(candidate) + if not need or name not in need: + continue + have[candidate] = {'Name': candidate, 'Value': 'false', 'Condition': None} + added.append(candidate) + deny_queue.append(candidate) if object_type == 'Configuration' and format_rank <= CONFIGURATION_LEGACY_RANK and have: for dep in CONFIGURATION_LEGACY_DEPS: if dep in have: @@ -1730,6 +1774,19 @@ def main(): for o in parsed_objects: o['Rights'] = sort_rights_canonical(o['Name'], o['Rights']) + # Записи, равные умолчанию роли, платформа не хранит — отбрасываем их сами и говорим об этом. + dropped_by_default = [] + for obj in parsed_objects: + default_value = get_default_right_value(obj['Name'], sfno, sfab) + kept = [] + for right in obj['Rights']: + if right['Value'] == default_value: + dropped_by_default.append(f"{obj['Name']}.{right['Name']}") + continue + kept.append(right) + obj['Rights'] = kept + parsed_objects = [o for o in parsed_objects if o['Rights']] + # Object blocks total_rights = 0 for obj in parsed_objects: @@ -1799,6 +1856,9 @@ def main(): print(f" Metadata: {metadata_path}") print(f" Rights: {rights_path}") print(f" Objects: {len(parsed_objects)}, Rights: {total_rights}, Templates: {template_count}") + if dropped_by_default: + print("[role-compile] Не записаны права, совпадающие с умолчанием роли " + f"(платформа их не хранит): {', '.join(dropped_by_default)}", file=sys.stderr) for note in closure_notes: print(note) if reg_result == 'added': diff --git a/.claude/skills/role-edit/SKILL.md b/.claude/skills/role-edit/SKILL.md index eb2821e4b..077949a71 100644 --- a/.claude/skills/role-edit/SKILL.md +++ b/.claude/skills/role-edit/SKILL.md @@ -87,10 +87,17 @@ powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/role-edit.ps1" -Rol отчёта — `Use`. Снятие работает в обратную сторону: снимаешь `Read` — уходят и права, которые его требуют. Дописанное и снятое каскадом перечисляется в выводе. -Реквизиты, табличные части, измерения и ресурсы по умолчанию наследуют права своего объекта, и -выдавать их отдельно бессмысленно — платформа такую запись не сохранит. Ограничивают их через -`deny-rights`. Выдавать права реквизитам поштучно имеет смысл только в роли с -`setForAttributesByDefault=false`. +Роль хранит только то, что отличается от её умолчаний, — всё совпавшее навык не пишет и говорит +об этом: + +| Где | Что имеет смысл | +|-----|-----------------| +| объект верхнего уровня, `setForNewObjects=false` (умолчание) | выдача прав; запрет бессмыслен | +| объект верхнего уровня, `setForNewObjects=true` | запрет; выдача бессмысленна | +| реквизит, ТЧ, измерение, ресурс, `setForAttributesByDefault=true` (умолчание) | запрет: права наследуются от объекта | +| они же при `setForAttributesByDefault=false` | и выдача, и запрет | + +Запрет тянет за собой права, которым запрещённое нужно: `View=false` у реквизита уносит `Edit`. Тип или имя права вне допустимого списка — отказ: файл не меняется, ошибки печатаются все разом. Полные таблицы «тип → права» и виды вложенности — в справке `/role-compile`. diff --git a/.claude/skills/role-edit/scripts/role-edit.ps1 b/.claude/skills/role-edit/scripts/role-edit.ps1 index ddf7e7952..e54e39d64 100644 --- a/.claude/skills/role-edit/scripts/role-edit.ps1 +++ b/.claude/skills/role-edit/scripts/role-edit.ps1 @@ -1,4 +1,4 @@ -# role-edit v1.2 — Edit existing 1C role rights in place +# role-edit v1.3 — Edit existing 1C role rights in place # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills [CmdletBinding(PositionalBinding=$false)] param( @@ -854,32 +854,73 @@ $script:configurationLegacyDeps = @("AnalyticsSystemClient","MainWindowModeEmbed $script:configurationLegacyRank = 218 # Замыкание набора прав объекта. Возвращает @{ Rights = <итог>; Added = <что дописано> }. +# Платформа хранит только то, что ОТЛИЧАЕТСЯ от значения по умолчанию для роли: при +# setForNewObjects=false на верхнем уровне живут разрешения, при true — запреты; у реквизитных +# вложенных объектов ту же роль играет setForAttributesByDefault. Совпавшее с умолчанием +# платформа выбрасывает при первой же загрузке, поэтому не пишем его и сами. +$script:attributeKinds = @( + "Attribute","StandardAttribute","TabularSection","StandardTabularSection", + "Dimension","Resource","AccountingFlag","ExtDimensionAccountingFlag","AddressingAttribute" +) + +function Get-DefaultRightValue { + param([string]$objName, [string]$setForNewObjects, [string]$setForAttributesByDefault) + $parts = $objName -split '\.' + if ($parts.Count -lt 3) { return $setForNewObjects } + # Внешние источники данных под это правило не проверялись — трогаем только то, что замерено. + if ($parts[0] -eq 'ExternalDataSource') { return "false" } + $kind = $parts[$parts.Count-2] + if ($script:attributeKinds -contains $kind) { return $setForAttributesByDefault } + # Команды, подсистемы, операции сервисов флагами роли не управляются — там живут разрешения. + return "false" +} + function Close-RightsDependencies { param([string]$objName, $rights, [int]$formatRank) $parts = $objName -split '\.' - # У вложенных объектов (реквизит, ТЧ, измерение) зависимостей нет — платформа их не трогает. - if ($parts.Count -ge 3) { return @{ Rights = $rights; Added = @() } } + $nested = $parts.Count -ge 3 $objectType = $parts[0] - $allowed = $script:knownRights[$objectType] + $allowed = if ($nested) { Get-NestedRights -objectType $objectType -kind (Get-NestedKind $objName) } + else { $script:knownRights[$objectType] } if (-not $allowed) { return @{ Rights = $rights; Added = @() } } $have = [ordered]@{} foreach ($r in $rights) { if (-not $have.Contains($r.Name)) { $have[$r.Name] = $r } } $byType = $script:rightDepsByType[$objectType] $added = @() - $queue = @($have.Keys) + # Вперёд — только от РАЗРЕШЁННЫХ прав: платформа замыкает выданное, а не запрещённое. + $queue = @($have.Keys | Where-Object { $have[$_].Value -eq "true" }) while ($queue.Count -gt 0) { $name = $queue[0] $queue = @($queue | Select-Object -Skip 1) $need = if ($byType -and $byType.Contains($name)) { $byType[$name] } else { $script:rightDeps[$name] } if (-not $need) { continue } foreach ($dep in $need) { - if ($have.Contains($dep)) { continue } if ($allowed -notcontains $dep) { continue } + if ($have.Contains($dep)) { + # Разрешение перебивает запрет — так поступает и платформа при загрузке. + if ($have[$dep].Value -ne "true") { $have[$dep].Value = "true"; $added += $dep; $queue += $dep } + continue + } $have[$dep] = @{ Name = $dep; Value = "true"; Condition = $null } $added += $dep $queue += $dep } } + # Назад — от ЗАПРЕТОВ: право, которому запрещённое нужно, платформа запрещает следом. + $denyQueue = @($have.Keys | Where-Object { $have[$_].Value -ne "true" }) + while ($denyQueue.Count -gt 0) { + $name = $denyQueue[0] + $denyQueue = @($denyQueue | Select-Object -Skip 1) + foreach ($candidate in $allowed) { + if ($candidate -eq $name) { continue } + $need = if ($byType -and $byType.Contains($candidate)) { $byType[$candidate] } else { $script:rightDeps[$candidate] } + if (-not $need -or $need -notcontains $name) { continue } + if ($have.Contains($candidate)) { continue } + $have[$candidate] = @{ Name = $candidate; Value = "false"; Condition = $null } + $added += $candidate + $denyQueue += $candidate + } + } if ($objectType -eq 'Configuration' -and $formatRank -le $script:configurationLegacyRank -and $have.Count -gt 0) { foreach ($dep in $script:configurationLegacyDeps) { if ($have.Contains($dep)) { continue } @@ -1452,6 +1493,18 @@ $script:ns = New-Object System.Xml.XmlNamespaceManager($script:xmlDoc.NameTable) $script:ns.AddNamespace("rt", $script:mdNs) $script:formatVersion = if ($script:root.HasAttribute("version")) { $script:root.GetAttribute("version") } else { "2.17" } $script:formatRank = Get-FormatRank $script:formatVersion +# Умолчания роли решают, какие записи платформа хранит: совпавшее с умолчанием она выбрасывает. +$script:roleSfno = $script:root.SelectSingleNode("rt:setForNewObjects", $script:ns).InnerText +$script:roleSfab = $script:root.SelectSingleNode("rt:setForAttributesByDefault", $script:ns).InnerText +$script:droppedByDefault = @() + +function Test-RightStored { + param([string]$objName, [string]$rightName, [string]$value) + $default = Get-DefaultRightValue $objName $script:roleSfno $script:roleSfab + if ($value -ne $default) { return $true } + $script:droppedByDefault += "$objName.$rightName" + return $false +} $script:addCount = 0 $script:removeCount = 0 @@ -1737,6 +1790,7 @@ function Apply-AddRights($spec) { $added = @() $indent = Get-ChildIndent $objNode foreach ($rightName in $final) { + if (-not (Test-RightStored $spec.Name $rightName 'true')) { continue } $node = Find-RightNode $objNode $rightName if ($node) { if ((Get-RightNodeValue $node) -ne 'true') { @@ -1790,6 +1844,7 @@ function Apply-SetRights($spec) { $closure = Close-RightsDependencies -objName $spec.Name -rights @($spec.Rights | ForEach-Object { @{ Name = $_; Value = "true"; Condition = $null } }) -formatRank $script:formatRank $indent = Get-ChildIndent $objNode foreach ($rightName in @($closure.Rights | ForEach-Object { $_.Name })) { + if (-not (Test-RightStored $spec.Name $rightName 'true')) { continue } $new = New-RightNode $rightName 'true' $indent Insert-RightCanonical $objNode $new $spec.Name $script:addCount++ @@ -1873,6 +1928,7 @@ function Apply-DenyRights($spec) { $denied = @() $indent = Get-ChildIndent $objNode foreach ($rightName in $toDeny) { + if (-not (Test-RightStored $spec.Name $rightName 'false')) { continue } $node = Find-RightNode $objNode $rightName if ($node) { if ((Get-RightNodeValue $node) -eq 'false') { continue } @@ -1887,7 +1943,8 @@ function Apply-DenyRights($spec) { $script:rightsDirty = $true } if ($denied.Count -eq 0) { - Add-Note " $($spec.Name): права уже запрещены, изменений нет" + $reason = if ($script:droppedByDefault -match [regex]::Escape($spec.Name)) { "запрет совпадает с умолчанием роли и платформой не хранится" } else { "права уже запрещены" } + Add-Note " $($spec.Name): $reason, изменений нет" return } $cascade = @($denied | Where-Object { $spec.Rights -notcontains $_ }) @@ -2301,6 +2358,9 @@ Write-Host "[OK] Роль '$($script:paths.RoleName)' обновлена" Write-Host " Rights: $($script:rightsPath)" foreach ($note in $script:notes) { Write-Host $note } Write-Host " Added: $($script:addCount), Removed: $($script:removeCount), Modified: $($script:modifyCount)" +if ($script:droppedByDefault.Count -gt 0) { + [Console]::Error.WriteLine("[role-edit] Не записаны права, совпадающие с умолчанием роли (платформа их не хранит): $($script:droppedByDefault -join ', ')") +} if (-not $NoValidate) { $validateScript = Join-Path (Join-Path $PSScriptRoot "..\..\role-validate") "scripts\role-validate.ps1" diff --git a/.claude/skills/role-edit/scripts/role-edit.py b/.claude/skills/role-edit/scripts/role-edit.py index 763c6cfa7..783c219f7 100644 --- a/.claude/skills/role-edit/scripts/role-edit.py +++ b/.claude/skills/role-edit/scripts/role-edit.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# role-edit v1.2 — Edit existing 1C role rights in place +# role-edit v1.3 — Edit existing 1C role rights in place # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse import json @@ -794,14 +794,37 @@ CONFIGURATION_LEGACY_DEPS = ["AnalyticsSystemClient", "MainWindowModeEmbeddedWor CONFIGURATION_LEGACY_RANK = 218 +# Платформа хранит только то, что ОТЛИЧАЕТСЯ от значения по умолчанию для роли: при +# setForNewObjects=false на верхнем уровне живут разрешения, при true — запреты; у реквизитных +# вложенных объектов ту же роль играет setForAttributesByDefault. Совпавшее с умолчанием +# платформа выбрасывает при первой же загрузке, поэтому не пишем его и сами. +ATTRIBUTE_KINDS = [ + "Attribute", "StandardAttribute", "TabularSection", "StandardTabularSection", + "Dimension", "Resource", "AccountingFlag", "ExtDimensionAccountingFlag", "AddressingAttribute", +] + + +def get_default_right_value(object_name, set_for_new_objects, set_for_attributes_by_default): + parts = object_name.split('.') + if len(parts) < 3: + return set_for_new_objects + # Внешние источники данных под это правило не проверялись — трогаем только то, что замерено. + if parts[0] == 'ExternalDataSource': + return "false" + kind = parts[-2] + if kind in ATTRIBUTE_KINDS: + return set_for_attributes_by_default + # Команды, подсистемы, операции сервисов флагами роли не управляются — там живут разрешения. + return "false" + + def close_rights_dependencies(object_name, rights, format_rank): """Замыкание набора прав объекта. Возвращает (итоговые права, что дописано).""" parts = object_name.split('.') - # У вложенных объектов (реквизит, ТЧ, измерение) зависимостей нет — платформа их не трогает. - if len(parts) >= 3: - return rights, [] + nested = len(parts) >= 3 object_type = parts[0] - allowed = KNOWN_RIGHTS.get(object_type) + allowed = (get_nested_rights(object_type, get_nested_kind(object_name)) if nested + else KNOWN_RIGHTS.get(object_type)) if not allowed: return rights, [] have = {} @@ -809,18 +832,39 @@ def close_rights_dependencies(object_name, rights, format_rank): have.setdefault(r['Name'], r) by_type = RIGHT_DEPS_BY_TYPE.get(object_type, {}) added = [] - queue = list(have.keys()) + # Вперёд — только от РАЗРЕШЁННЫХ прав: платформа замыкает выданное, а не запрещённое. + queue = [n for n in have if have[n]['Value'] == 'true'] while queue: name = queue.pop(0) need = by_type[name] if name in by_type else RIGHT_DEPS.get(name) if not need: continue for dep in need: - if dep in have or dep not in allowed: + if dep not in allowed: + continue + if dep in have: + # Разрешение перебивает запрет — так поступает и платформа при загрузке. + if have[dep]['Value'] != 'true': + have[dep]['Value'] = 'true' + added.append(dep) + queue.append(dep) continue have[dep] = {'Name': dep, 'Value': 'true', 'Condition': None} added.append(dep) queue.append(dep) + # Назад — от ЗАПРЕТОВ: право, которому запрещённое нужно, платформа запрещает следом. + deny_queue = [n for n in have if have[n]['Value'] != 'true'] + while deny_queue: + name = deny_queue.pop(0) + for candidate in allowed: + if candidate == name or candidate in have: + continue + need = by_type[candidate] if candidate in by_type else RIGHT_DEPS.get(candidate) + if not need or name not in need: + continue + have[candidate] = {'Name': candidate, 'Value': 'false', 'Condition': None} + added.append(candidate) + deny_queue.append(candidate) if object_type == 'Configuration' and format_rank <= CONFIGURATION_LEGACY_RANK and have: for dep in CONFIGURATION_LEGACY_DEPS: if dep in have: @@ -1501,10 +1545,20 @@ class Editor: self.modify_count = 0 self.notes = [] self.pending = [] + # Умолчания роли решают, какие записи платформа хранит: совпавшее с умолчанием она выбрасывает. + self.role_sfno = node_text(self.root, "setForNewObjects") + self.role_sfab = node_text(self.root, "setForAttributesByDefault") + self.dropped_by_default = [] def note(self, text): self.notes.append(text) + def right_stored(self, obj_name, right_name, value): + if value != get_default_right_value(obj_name, self.role_sfno, self.role_sfab): + return True + self.dropped_by_default.append(f"{obj_name}.{right_name}") + return False + # --- Разбор значений операций --- def parse_batch(self, value): @@ -1751,6 +1805,8 @@ class Editor: indent = self.child_indent(obj_node) for right in closed: name = right["Name"] + if not self.right_stored(spec["Name"], name, "true"): + continue node = self.find_right(obj_node, name) if node is not None: if node_text(node, "value") != "true": @@ -1792,6 +1848,8 @@ class Editor: self.format_rank) indent = self.child_indent(obj_node) for right in closed: + if not self.right_stored(spec["Name"], right["Name"], "true"): + continue new_el = self.make_right(right["Name"], "true", indent) self.insert_right_canonical(obj_node, new_el, spec["Name"]) self.add_count += 1 @@ -1852,6 +1910,8 @@ class Editor: denied = [] indent = self.child_indent(obj_node) for right_name in to_deny: + if not self.right_stored(spec["Name"], right_name, "false"): + continue node = self.find_right(obj_node, right_name) if node is not None: if node_text(node, "value") == "false": @@ -1865,7 +1925,10 @@ class Editor: denied.append(right_name) self.rights_dirty = True if not denied: - self.note(f" {spec['Name']}: права уже запрещены, изменений нет") + reason = ("запрет совпадает с умолчанием роли и платформой не хранится" + if any(d.startswith(spec['Name'] + '.') for d in self.dropped_by_default) + else "права уже запрещены") + self.note(f" {spec['Name']}: {reason}, изменений нет") return cascade = [r for r in denied if r not in spec["Rights"]] note = f" {spec['Name']}: запрещено — {', '.join(denied)}" @@ -2218,6 +2281,9 @@ def main(): for note in ed.notes: print(note) print(f" Added: {ed.add_count}, Removed: {ed.remove_count}, Modified: {ed.modify_count}") + if ed.dropped_by_default: + print("[role-edit] Не записаны права, совпадающие с умолчанием роли " + f"(платформа их не хранит): {', '.join(ed.dropped_by_default)}", file=sys.stderr) if not args.NoValidate: validate_script = os.path.normpath(os.path.join( diff --git a/tests/skills/cases/role-compile/default-value-filter.json b/tests/skills/cases/role-compile/default-value-filter.json new file mode 100644 index 000000000..93caaaf45 --- /dev/null +++ b/tests/skills/cases/role-compile/default-value-filter.json @@ -0,0 +1,27 @@ +{ + "name": "Права, совпадающие с умолчанием роли, не пишутся — платформа их не хранит", + "setup": "fixture:view-preset-fx", + "input": { + "name": "УмолчанияРоли", + "synonym": "Умолчания роли", + "objects": [ + { + "name": "Catalog.Номенклатура", + "preset": "view", + "rights": { + "Delete": false + } + } + ] + }, + "validatePath": "Roles/УмолчанияРоли", + "expect": { + "stderrContains": [ + "Не записаны права, совпадающие с умолчанием роли" + ], + "fileNotContains": { + "file": "Roles/УмолчанияРоли/Ext/Rights.xml", + "text": "false" + } + } +} diff --git a/tests/skills/cases/role-compile/nested-and-services.json b/tests/skills/cases/role-compile/nested-and-services.json index a091488bd..bd8f3336e 100644 --- a/tests/skills/cases/role-compile/nested-and-services.json +++ b/tests/skills/cases/role-compile/nested-and-services.json @@ -14,7 +14,8 @@ "ExternalDataSource.Источник.Table.Заказы: Read, View", "ExternalDataSource.Источник.Table.Заказы.Field.Сумма: View, Edit", "ExternalDataSource.Источник.Cube.Продажи.Dimension.Период: View" - ] + ], + "setForAttributesByDefault": false }, "expect": { "files": [ diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Catalogs/Номенклатура.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Catalogs/Номенклатура.xml new file mode 100644 index 000000000..0daed60e9 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Catalogs/Номенклатура.xml @@ -0,0 +1,91 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + + Номенклатура + + + ru + Номенклатура + + + + false + HierarchyFoldersAndItems + false + 2 + true + true + + ToItems + 9 + 25 + String + Variable + WholeCatalog + false + true + AsDescription + + Auto + InDialog + false + BothWays + + Catalog.Номенклатура.StandardAttribute.Description + Catalog.Номенклатура.StandardAttribute.Code + + Begin + DontUse + Directly + + + + + + + + + + + false + + + Managed + Use + + + + + + Use + Auto + DontUse + false + false + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Catalogs/Номенклатура/Ext/ObjectModule.bsl b/tests/skills/cases/role-compile/snapshots/default-value-filter/Catalogs/Номенклатура/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Configuration.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Configuration.xml new file mode 100644 index 000000000..b706f2440 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Configuration.xml @@ -0,0 +1,254 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + UUID-012 + UUID-013 + + + UUID-014 + UUID-015 + + + + TestConfig + + + ru + TestConfig + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + УмолчанияРоли + Номенклатура + Загрузка + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка.xml new file mode 100644 index 000000000..fa5caaf26 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка.xml @@ -0,0 +1,32 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + + Загрузка + + + ru + Загрузка + + + + true + + + false + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка/Ext/ManagerModule.bsl b/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка/Ext/ManagerModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка/Ext/ObjectModule.bsl b/tests/skills/cases/role-compile/snapshots/default-value-filter/DataProcessors/Загрузка/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Ext/ClientApplicationInterface.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Ext/ClientApplicationInterface.xml new file mode 100644 index 000000000..3c1161b2d --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + UUID-002 + + + + + UUID-004 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Languages/Русский.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Languages/Русский.xml new file mode 100644 index 000000000..37c60d786 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли.xml new file mode 100644 index 000000000..f8a4fb260 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли.xml @@ -0,0 +1,15 @@ + + + + + УмолчанияРоли + + + ru + Умолчания роли + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли/Ext/Rights.xml b/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли/Ext/Rights.xml new file mode 100644 index 000000000..7cccfe3b5 --- /dev/null +++ b/tests/skills/cases/role-compile/snapshots/default-value-filter/Roles/УмолчанияРоли/Ext/Rights.xml @@ -0,0 +1,21 @@ + + + false + true + false + + Catalog.Номенклатура + + Read + true + + + View + true + + + InputByString + true + + + \ No newline at end of file diff --git a/tests/skills/cases/role-compile/snapshots/nested-and-services/Roles/Интеграция/Ext/Rights.xml b/tests/skills/cases/role-compile/snapshots/nested-and-services/Roles/Интеграция/Ext/Rights.xml index c0b3e410b..192882641 100644 --- a/tests/skills/cases/role-compile/snapshots/nested-and-services/Roles/Интеграция/Ext/Rights.xml +++ b/tests/skills/cases/role-compile/snapshots/nested-and-services/Roles/Интеграция/Ext/Rights.xml @@ -1,7 +1,7 @@ false - true + false false WebService.Обмен.Operation.Загрузить diff --git a/tests/skills/cases/role-edit/deny-rights-default-filter.json b/tests/skills/cases/role-edit/deny-rights-default-filter.json new file mode 100644 index 000000000..ca239014f --- /dev/null +++ b/tests/skills/cases/role-edit/deny-rights-default-filter.json @@ -0,0 +1,21 @@ +{ + "name": "Запрет на объекте верхнего уровня не пишется: платформа его не хранит", + "setup": "fixture:role-base", + "params": { + "rolePath": "Roles/Менеджер" + }, + "input": [ + { + "operation": "deny-rights", + "value": "Catalog.Товары: Delete" + } + ], + "expect": { + "stdoutContains": [ + "запрет совпадает с умолчанием роли" + ], + "stderrContains": [ + "Не записаны права, совпадающие с умолчанием роли" + ] + } +} diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Catalogs/Товары.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Catalogs/Товары.xml new file mode 100644 index 000000000..292de84c4 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Catalogs/Товары.xml @@ -0,0 +1,137 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + + Товары + + + ru + Товары + + + + false + HierarchyFoldersAndItems + false + 2 + true + true + + ToItems + 9 + 25 + String + Variable + WholeCatalog + false + true + AsDescription + + Auto + InDialog + false + BothWays + + Catalog.Товары.StandardAttribute.Description + Catalog.Товары.StandardAttribute.Code + + Begin + DontUse + Directly + + + + + + + + + + + false + + + Managed + Use + + + + + + Use + Auto + DontUse + false + false + + + + + Цена + + + ru + Цена + + + + + xs:decimal + + 15 + 2 + Any + + + false + + + + false + + false + false + + + false + 0 + DontCheck + Items + + + Auto + Auto + + + Auto + ForItem + DontIndex + Use + Use + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Catalogs/Товары/Ext/ObjectModule.bsl b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Catalogs/Товары/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Configuration.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Configuration.xml new file mode 100644 index 000000000..880e20b87 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Configuration.xml @@ -0,0 +1,256 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + UUID-012 + UUID-013 + + + UUID-014 + UUID-015 + + + + TestConfig + + + ru + TestConfig + + + + + Version8_3_24 + ManagedApplication + + PlatformApplication + + Russian + + + + + false + false + false + + + + + + + + + + + + + + + + + + + + + + Biometrics + true + + + Location + false + + + BackgroundLocation + false + + + BluetoothPrinters + false + + + WiFiPrinters + false + + + Contacts + false + + + Calendars + false + + + PushNotifications + false + + + LocalNotifications + false + + + InAppPurchases + false + + + PersonalComputerFileExchange + false + + + Ads + false + + + NumberDialing + false + + + CallProcessing + false + + + CallLog + false + + + AutoSendSMS + false + + + ReceiveSMS + false + + + SMSLog + false + + + Camera + false + + + Microphone + false + + + MusicLibrary + false + + + PictureAndVideoLibraries + false + + + AudioPlaybackAndVibration + false + + + BackgroundAudioPlaybackAndVibration + false + + + InstallPackages + false + + + OSBackup + true + + + ApplicationUsageStatistics + false + + + BarcodeScanning + false + + + BackgroundAudioRecording + false + + + AllFilesAccess + false + + + Videoconferences + false + + + NFC + false + + + DocumentScanning + false + + + SpeechToText + false + + + Geofences + false + + + IncomingShareRequests + false + + + AllIncomingShareRequestsTypesProcessing + false + + + + + + Normal + + + Language.Русский + + + + + + Managed + NotAutoFree + DontUse + DontUse + TaxiEnableVersion8_2 + DontUse + Version8_3_24 + + + + Русский + Менеджер + Товары + Заказ + Загрузка + Цены + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка.xml new file mode 100644 index 000000000..fa5caaf26 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка.xml @@ -0,0 +1,32 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + + Загрузка + + + ru + Загрузка + + + + true + + + false + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка/Ext/ManagerModule.bsl b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка/Ext/ManagerModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка/Ext/ObjectModule.bsl b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/DataProcessors/Загрузка/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Documents/Заказ.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Documents/Заказ.xml new file mode 100644 index 000000000..32fa05878 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Documents/Заказ.xml @@ -0,0 +1,82 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + + Заказ + + + ru + Заказ + + + + true + + String + 11 + Variable + Year + true + true + + + + Document.Заказ.StandardAttribute.Number + + Use + Begin + DontUse + Directly + + + + + + + Allow + Deny + AutoDelete + WriteSelected + AutoFill + + true + true + false + + Managed + Use + + + + + + Auto + DontUse + false + false + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Documents/Заказ/Ext/ObjectModule.bsl b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Documents/Заказ/Ext/ObjectModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Ext/ClientApplicationInterface.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Ext/ClientApplicationInterface.xml new file mode 100644 index 000000000..3c1161b2d --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Ext/ClientApplicationInterface.xml @@ -0,0 +1,18 @@ + + + + + UUID-002 + + + + + UUID-004 + + + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/InformationRegisters/Цены.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/InformationRegisters/Цены.xml new file mode 100644 index 000000000..092bf1d11 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/InformationRegisters/Цены.xml @@ -0,0 +1,261 @@ + + + + + + UUID-002 + UUID-003 + + + UUID-004 + UUID-005 + + + UUID-006 + UUID-007 + + + UUID-008 + UUID-009 + + + UUID-010 + UUID-011 + + + UUID-012 + UUID-013 + + + UUID-014 + UUID-015 + + + + Цены + + + ru + Цены + + + + true + InDialog + + + + + + + + DontCheck + false + false + Auto + + + false + + + Auto + Auto + + false + Use + false + + + + Use + + + + + + + + DontCheck + false + false + Auto + + + false + + + Auto + Auto + + false + Use + false + + + + Use + + + + + + + + DontCheck + false + false + Auto + + + false + + + Auto + Auto + + false + Use + false + + + + Use + + + + + + + + DontCheck + false + false + Auto + + + false + + + Auto + Auto + + false + Use + false + + + + Use + + + + + + + Nonperiodical + Independent + false + false + Managed + Use + false + false + + + + + + DontUse + false + false + + + + + Цена + + + ru + Цена + + + + + xs:decimal + + 15 + 2 + Any + + + false + + + + false + + false + false + + + false + 0 + DontCheck + Items + + + Auto + Auto + + + Auto + DontIndex + Use + Use + + + + + Товар + + + ru + Товар + + + + + cfg:CatalogRef.Товары + + false + + + + false + + false + false + + + false + + DontCheck + Items + + + Auto + Auto + + + Auto + false + false + false + DontIndex + Use + Use + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/InformationRegisters/Цены/Ext/RecordSetModule.bsl b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/InformationRegisters/Цены/Ext/RecordSetModule.bsl new file mode 100644 index 000000000..e69de29bb diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Languages/Русский.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Languages/Русский.xml new file mode 100644 index 000000000..37c60d786 --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Languages/Русский.xml @@ -0,0 +1,16 @@ + + + + + Русский + + + ru + Русский + + + + ru + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер.xml new file mode 100644 index 000000000..001d4535b --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер.xml @@ -0,0 +1,15 @@ + + + + + Менеджер + + + ru + Менеджер + + + + + + \ No newline at end of file diff --git a/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер/Ext/Rights.xml b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер/Ext/Rights.xml new file mode 100644 index 000000000..3d7a6b6fd --- /dev/null +++ b/tests/skills/cases/role-edit/snapshots/deny-rights-default-filter/Roles/Менеджер/Ext/Rights.xml @@ -0,0 +1,92 @@ + + + false + true + false + + Catalog.Товары + + Read + true + + + View + true + + + InputByString + true + + + + Document.Заказ + + Read + true + + + Insert + true + + + Update + true + + + Delete + true + + + Posting + true + + + UndoPosting + true + + + View + true + + + InteractiveInsert + true + + + Edit + true + + + InteractiveSetDeletionMark + true + + + InteractiveClearDeletionMark + true + + + InteractivePosting + true + + + InteractivePostingRegular + true + + + InteractiveUndoPosting + true + + + InteractiveChangeOfPosted + true + + + InputByString + true + + + + ДляОбъекта(Мод) + ГДЕ Организация = &ТекОрг + + \ No newline at end of file diff --git a/tests/skills/check-inline-drift.mjs b/tests/skills/check-inline-drift.mjs index 4c60e6e71..544d4f179 100644 --- a/tests/skills/check-inline-drift.mjs +++ b/tests/skills/check-inline-drift.mjs @@ -409,6 +409,16 @@ const FAMILIES = [ // Существует только в PY: PowerShell регистронезависим сам по себе (свойства PSObject, ключи // Hashtable, -eq/-contains, имена параметров, ValidateSet), поэтому в .ps1 копии нет и быть // не должно — ps1: null. + // Замыкание прав и фильтр значений по умолчанию: обе роли обязаны считать одинаково, иначе + // созданная и отредактированная роль разойдутся между собой и с платформой. + { + name: 'права роли: close_rights_dependencies', py: 'close_rights_dependencies', ps1: 'Close-RightsDependencies', + variants: [{ id: 'base', authority: 'role-compile', consumers: ['role-edit'] }], + }, + { + name: 'права роли: get_default_right_value', py: 'get_default_right_value', ps1: 'Get-DefaultRightValue', + variants: [{ id: 'base', authority: 'role-compile', consumers: ['role-edit'] }], + }, // Значение операции может быть "@путь" — текст читается из файла. Правило поиска общее: // абсолютный путь как есть, относительный — рядом с DSL (или с редактируемым объектом), // затем в текущем каталоге. Разъедется — и один навык начнёт искать не там, где другой.