From bdea20c0c7619663c012a7500ba2fff497991b1f Mon Sep 17 00:00:00 2001 From: John Mulligan Date: Wed, 12 Mar 2025 14:28:15 -0400 Subject: [PATCH] mgr/smb: initial support for content conversions - password filtering Add the core of a basic support for converting the content of various resources. The current goal being filtering passwords to either obscure them (base64) or hide them (hidden). The default continues to be plain text as before. Signed-off-by: John Mulligan --- src/pybind/mgr/smb/enums.py | 8 ++++++++ src/pybind/mgr/smb/resources.py | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/pybind/mgr/smb/enums.py b/src/pybind/mgr/smb/enums.py index 7f7b04df55936..5606c3faf3c86 100644 --- a/src/pybind/mgr/smb/enums.py +++ b/src/pybind/mgr/smb/enums.py @@ -107,3 +107,11 @@ class SMBClustering(_StrEnum): class ShowResults(_StrEnum): FULL = 'full' COLLAPSED = 'collapsed' + + +class PasswordFilter(_StrEnum): + """Filter type for password values.""" + + NONE = 'none' + BASE64 = 'base64' + HIDDEN = 'hidden' diff --git a/src/pybind/mgr/smb/resources.py b/src/pybind/mgr/smb/resources.py index d91485f9992bb..13e4165bd8022 100644 --- a/src/pybind/mgr/smb/resources.py +++ b/src/pybind/mgr/smb/resources.py @@ -1,5 +1,6 @@ from typing import Dict, List, Optional, Tuple, Union, cast +import base64 import errno import json @@ -20,12 +21,15 @@ from .enums import ( JoinSourceType, LoginAccess, LoginCategory, + PasswordFilter, SMBClustering, UserGroupSourceType, ) from .proto import Self, Simplified from .utils import checked +ConversionOp = Tuple[PasswordFilter, PasswordFilter] + def _get_intent(data: Simplified) -> Intent: """Helper function that returns the intent value from a data dict.""" @@ -94,6 +98,9 @@ class _RBase: rc = getattr(self, '_resource_config') return rc.object_to_simplified(self) + def convert(self, operation: ConversionOp) -> Self: + return self + @resourcelib.component() class CephFSStorage(_RBase): @@ -240,6 +247,12 @@ class JoinAuthValues(_RBase): username: str password: str + def convert(self, operation: ConversionOp) -> Self: + return self.__class__( + username=self.username, + password=_password_convert(self.password, operation), + ) + @resourcelib.component() class JoinSource(_RBase): @@ -471,6 +484,14 @@ class JoinAuth(_RBase): rc.on_construction_error(InvalidResourceError.wrap) return rc + def convert(self, operation: ConversionOp) -> Self: + return self.__class__( + auth_id=self.auth_id, + intent=self.intent, + auth=None if not self.auth else self.auth.convert(operation), + linked_to_cluster=self.linked_to_cluster, + ) + @resourcelib.resource('ceph.smb.usersgroups') class UsersAndGroups(_RBase): @@ -537,3 +558,18 @@ def load(data: Simplified) -> List[SMBResource]: structured types. """ return resourcelib.load(data) + + +def _password_convert(pvalue: str, operation: ConversionOp) -> str: + if operation == (PasswordFilter.NONE, PasswordFilter.BASE64): + pvalue = base64.b64encode(pvalue.encode("utf8")).decode("utf8") + elif operation == (PasswordFilter.NONE, PasswordFilter.HIDDEN): + pvalue = "*" * 16 + elif operation == (PasswordFilter.BASE64, PasswordFilter.NONE): + pvalue = base64.b64decode(pvalue.encode("utf8")).decode("utf8") + else: + osrc, odst = operation + raise ValueError( + f"can not convert password value encoding from {osrc} to {odst}" + ) + return pvalue -- 2.39.5