anaconda/anaconda-40.22.3.13/pyanaconda/modules/common/structures/secret.py
2024-11-14 21:39:56 -08:00

188 lines
5.5 KiB
Python

#
# DBus structures for secret data.
#
# Copyright (C) 2020 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
import copy
from dasbus.structure import DBusData, get_fields, generate_string_from_data
from dasbus.typing import * # pylint: disable=wildcard-import
from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core.constants import SECRET_TYPE_NONE, SECRET_TYPE_TEXT, SECRET_TYPE_HIDDEN
log = get_module_logger(__name__)
__all__ = ["get_public_copy", "hide_secrets", "SecretData", "SecretDataList"]
def get_public_copy(obj: DBusData):
"""Get a public copy of the given DBus data object.
The function will create a copy of the given DBus
data object, automatically detect all fields that
are instances of SecretData and call their method
hide_secret.
The final DBus data object is safe to send to a DBus
client, because it doesn't contain sensitive information.
The automatic detection works only for first-level
fields of the type SecretData. Nested types of fields
are not supported at this moment. For example, a field
of the type List[SecretData] will be ignored. We will
also ignore types that are subclasses of DBusData (with
the exception of SecretData).
FIXME: Add support for nested types of fields.
:param obj: a data object with secrets
:type obj: an instance of DBusData
:return: a copy of data object with hidden secrets
:rtype: an instance of DBusData
"""
obj = copy.deepcopy(obj)
hide_secrets(obj)
return obj
def hide_secrets(obj: DBusData):
"""Hide all secrets in the given DBus data object.
The function will search fields of the given
DBus data object, check if the value of a field
is an instance of SecretData and call its method
hide_secret.
:param obj: a data object with secrets
:type obj: an instance of DBusData
"""
fields = get_fields(obj)
hidden = []
for field in fields.values():
value = getattr(obj, field.data_name)
if isinstance(value, SecretData):
value.hide_secret()
hidden.append(field.name)
if hidden:
log.debug("Hiding DBus fields %s.", ", ".join(hidden))
class SecretData(DBusData):
"""Data for a secret string value."""
def __init__(self):
self._type = SECRET_TYPE_NONE
self._value = self._get_initial_value()
@property
def type(self) -> Str:
"""The type of the secret.
Supported values:
NONE The secret is not set.
HIDDEN The secret is hidden.
TEXT The secret is in plain text.
:return: a string
"""
return self._type
@type.setter
def type(self, value: Str):
self._type = value
@property
def value(self) -> Str:
"""The value of the secret.
The value is set only if the secret
is set and not hidden.
:return: a string
"""
return self._value
@value.setter
def value(self, value: Str):
self._value = value
def _get_initial_value(self) -> Str:
"""Get the initial value of the secret.
:return: a value
"""
return ""
def set_secret(self, value):
"""Set the secret.
If the value is None, clear the secret. Otherwise,
set the secret to the given value.
:param value: a value of the secret or None
"""
if value is None:
self.type = SECRET_TYPE_NONE
self.value = self._get_initial_value()
else:
self.type = SECRET_TYPE_TEXT
self.value = value
def hide_secret(self):
"""Hide the secret.
If the secret is not set, do nothing. Otherwise,
hide the value of the secret.
"""
if self.type == SECRET_TYPE_NONE:
self.value = self._get_initial_value()
else:
self.type = SECRET_TYPE_HIDDEN
self.value = self._get_initial_value()
def __repr__(self):
"""Convert this data object to a string."""
return generate_string_from_data(
self, skip=["value"], add={"value_set": bool(self.value)}
)
class SecretDataList(SecretData):
"""Data for a secret list of string values."""
@property
def value(self) -> List[Str]:
"""The value of the secret.
:return: a list of strings
"""
return self._value
@value.setter
def value(self, value: List[Str]):
self._value = value
def _get_initial_value(self) -> List[Str]:
"""Get the initial value of the secret.
:return: a value
"""
return []