From 1c8e18e109f1c50828ebfef2ef93f0caa8a6510c Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Mon, 20 Jul 2026 20:03:11 +0200 Subject: [PATCH] fix(permissions): preserve canonical granted scopes --- src/govoplan_access/backend/permissions/catalog.py | 7 ++++++- tests/test_permission_catalog_contract.py | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/govoplan_access/backend/permissions/catalog.py b/src/govoplan_access/backend/permissions/catalog.py index a818e0e..0b0f792 100644 --- a/src/govoplan_access/backend/permissions/catalog.py +++ b/src/govoplan_access/backend/permissions/catalog.py @@ -100,7 +100,12 @@ def expand_scopes(scopes: Iterable[str], *, include_unknown: bool = True) -> lis for alias in compatible_required_scopes(scope): if alias != scope: expanded.add(alias) - if include_unknown and not matched: + # Preserve explicitly granted scopes in the presentation set. A + # canonical module scope can still match its legacy compatibility + # alias when the providing module is not loaded; treating that match + # as proof that the original scope is known would otherwise replace + # the canonical grant with only the deprecated alias. + if include_unknown or scope in catalog: expanded.add(scope) return sorted(expanded) diff --git a/tests/test_permission_catalog_contract.py b/tests/test_permission_catalog_contract.py index 2d2b4c9..4129944 100644 --- a/tests/test_permission_catalog_contract.py +++ b/tests/test_permission_catalog_contract.py @@ -24,6 +24,12 @@ class PermissionCatalogContractTests(unittest.TestCase): self.assertIn("files:upload", scopes) self.assertIn("mail_servers:test", scopes) + def test_expand_scopes_preserves_explicit_canonical_scope_with_legacy_alias(self) -> None: + scopes = access_catalog.expand_scopes(("files:file:read",)) + + self.assertIn("files:file:read", scopes) + self.assertIn("files:read", scopes) + if __name__ == "__main__": unittest.main()