From 705cd53b86d0a32be2e7cc8063c042882ac67f83 Mon Sep 17 00:00:00 2001 From: Wontek Hong Date: Mon, 9 Oct 2023 15:45:38 +0200 Subject: [PATCH] openapi/permissions: introduce OkayToIgnorePerm Change-Id: I3fe82638ae8607c805d1d00e919588060581138b --- cmk/gui/openapi/restful_objects/decorators.py | 18 +++++++++--- .../openapi/restful_objects/permissions.py | 28 ++++++++++++++----- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/cmk/gui/openapi/restful_objects/decorators.py b/cmk/gui/openapi/restful_objects/decorators.py index 1ca62261af7..24fc1653d94 100644 --- a/cmk/gui/openapi/restful_objects/decorators.py +++ b/cmk/gui/openapi/restful_objects/decorators.py @@ -1256,7 +1256,10 @@ def to_operation_dict( # pylint: disable=too-many-branches if self.permissions_required is not None: # Check that all the names are known to the system. for perm in self.permissions_required.iter_perms(): - if perm not in permission_registry: + if isinstance(perm, permissions.OkayToIgnorePerm): + continue + + if perm.name not in permission_registry: # NOTE: # See rest_api.py. dynamic_permission() have to be loaded before request # for this to work reliably. @@ -1596,16 +1599,23 @@ def _permission_descriptions( def _count_perms(_perms): return len([p for p in _perms if not isinstance(p, permissions.Undocumented)]) - def _add_desc(permission: permissions.BasePerm, indent: int, desc_list: list[str]) -> None: + def _add_desc( # pylint: disable=too-many-branches + permission: permissions.BasePerm, indent: int, desc_list: list[str] + ) -> None: if isinstance(permission, permissions.Undocumented): # Don't render return # We indent by two spaces, as is required by markdown. prefix = " " * indent - if isinstance(permission, permissions.Perm): + if isinstance(permission, (permissions.Perm, permissions.OkayToIgnorePerm)): perm_name = permission.name - desc = description_map.get(perm_name) or permission_registry[perm_name].description + try: + desc = description_map.get(perm_name) or permission_registry[perm_name].description + except KeyError: + if isinstance(permission, permissions.OkayToIgnorePerm): + return + raise _description.append(f"{prefix} * `{perm_name}`: {desc}") elif isinstance(permission, permissions.AllPerm): # If AllOf only contains one permission, we don't need to show the AllOf diff --git a/cmk/gui/openapi/restful_objects/permissions.py b/cmk/gui/openapi/restful_objects/permissions.py index 3fe72108e8f..6d655c54a6c 100644 --- a/cmk/gui/openapi/restful_objects/permissions.py +++ b/cmk/gui/openapi/restful_objects/permissions.py @@ -57,7 +57,7 @@ def has_permission(self, user: UserLike) -> bool: raise NotImplementedError() @abc.abstractmethod - def iter_perms(self) -> Iterable[str]: + def iter_perms(self) -> Iterable[Perm]: raise NotImplementedError def validate(self, permissions: Sequence[str]) -> bool: @@ -65,7 +65,7 @@ def validate(self, permissions: Sequence[str]) -> bool: return self.has_permission(FakeUser(permissions)) def __contains__(self, item): - return item in list(self.iter_perms()) + return item in (p.name for p in self.iter_perms()) class Optional(BasePerm): @@ -86,7 +86,7 @@ def has_permission(self, user: UserLike) -> bool: It's okay if we don't have the permission, so we accept it all the time.""" return True - def iter_perms(self) -> Iterable[str]: + def iter_perms(self) -> Iterable[Perm]: return self.perm.iter_perms() @@ -103,7 +103,7 @@ def __init__(self, perms: list[BasePerm]) -> None: def __repr__(self) -> str: return f"{self.__class__.__name__}([{', '.join([repr(o) for o in self.perms])})" - def iter_perms(self) -> Iterable[str]: + def iter_perms(self) -> Iterable[Perm]: return itertools.chain(*[perm.iter_perms() for perm in self.perms]) @@ -120,7 +120,7 @@ def has_permission(self, user: UserLike) -> bool: """ return False - def iter_perms(self) -> Iterable[str]: + def iter_perms(self) -> Iterable[Perm]: return iter([]) @@ -139,8 +139,8 @@ def has_permission(self, user: UserLike) -> bool: This method asks the user object if it has said permission.""" return user.has_permission(self.name) - def iter_perms(self) -> Iterable[str]: - return iter([self.name]) + def iter_perms(self) -> Iterable[Perm]: + return iter([self]) class AllPerm(MultiPerm): @@ -219,3 +219,17 @@ def has_permission(self, user: UserLike) -> bool: Is verified if any one of the child permissions is verified.""" return any(perm.has_permission(user) for perm in self.perms) + + +class OkayToIgnorePerm(Perm): + """A permission which does not raise an error if it is not present in Checkmk during built-time. + + Introduced mainly since some components were removed in the CSE edition. Removing a + component also removes the associating permissions. Some general endpoints make use of + those permissions beyond its component specific endpoints and this would lead to an error + if the permission is not present. This permission will also not get rendered in the + documentation. + + Consider this as a workaround since clear separation of edition specific permissions would + require a restructure of the entire endpoint specific permissions specification system. + """