From 627be9a4f14fa1d0c91ca673eee90efa17c62617 Mon Sep 17 00:00:00 2001 From: Untone Date: Mon, 26 May 2025 13:31:25 +0300 Subject: [PATCH] env-creds-mask --- panel/admin.tsx | 95 ++++++++++++++++++++++++++++++++++++---------- services/env.py | 24 +++++++++++- services/viewed.py | 2 +- 3 files changed, 98 insertions(+), 23 deletions(-) diff --git a/panel/admin.tsx b/panel/admin.tsx index 41555350..7f208034 100644 --- a/panel/admin.tsx +++ b/panel/admin.tsx @@ -861,6 +861,44 @@ const AdminPage: Component = (props) => { } } + /** + * Кнопка копирования значения переменной окружения + * @param value - значение для копирования + */ + function CopyButton({ value }: { value: string }) { + /** + * Копирует значение в буфер обмена + * @param e - событие клика + */ + const handleCopy = async (e: MouseEvent) => { + e.preventDefault() + try { + await navigator.clipboard.writeText(value) + // Можно добавить всплывающее уведомление + } catch (err) { + alert('Ошибка копирования: ' + (err as Error).message) + } + } + return ( + + ) + } + + /** + * Кнопка показать/скрыть значение переменной + * @param shown - показывать ли значение + * @param onToggle - обработчик переключения + */ + function ShowHideButton({ shown, onToggle }: { shown: boolean, onToggle: () => void }) { + return ( + + ) + } + /** * Компонент модального окна для редактирования переменной окружения */ @@ -909,6 +947,17 @@ const AdminPage: Component = (props) => { * Компонент для отображения переменных окружения */ const EnvVariablesTab: Component = () => { + // Сигналы для показа/скрытия значений по ключу + const [shownVars, setShownVars] = createSignal<{ [key: string]: boolean }>({}) + + /** + * Переключает показ значения переменной + * @param key - ключ переменной + */ + const toggleShow = (key: string) => { + setShownVars((prev) => ({ ...prev, [key]: !prev[key] })) + } + return (
@@ -928,7 +977,6 @@ const AdminPage: Component = (props) => {

{section.description}

-
@@ -941,25 +989,32 @@ const AdminPage: Component = (props) => { - {(variable) => ( - - - - - - - )} + {(variable) => { + const shown = shownVars()[variable.key] || false + return ( + + + + + + + ) + }}
{variable.key} - {variable.isSecret - ? '••••••••' - : (variable.value || не задано)} - {variable.description || '-'} - -
{variable.key} + {variable.isSecret && !shown + ? '••••••••' + : (variable.value || не задано)} + + {variable.isSecret && ( + toggleShow(variable.key)} /> + )} + {variable.description || '-'} + +
diff --git a/services/env.py b/services/env.py index c3a57218..2b6907d4 100644 --- a/services/env.py +++ b/services/env.py @@ -86,7 +86,9 @@ class EnvManager: # Переменные, которые следует всегда помечать как секретные SECRET_VARS_PATTERNS = [ r".*TOKEN.*", r".*SECRET.*", r".*PASSWORD.*", r".*KEY.*", - r".*PWD.*", r".*PASS.*", r".*CRED.*" + r".*PWD.*", r".*PASS.*", r".*CRED.*", + r".*JWT.*", r".*SESSION.*", r".*OAUTH.*", + r".*GITHUB.*", r".*GOOGLE.*", r".*FACEBOOK.*" ] def __init__(self): @@ -178,9 +180,27 @@ class EnvManager: def _is_secret_variable(self, key: str) -> bool: """ - Проверяет, является ли переменная секретной + Проверяет, является ли переменная секретной. + Секретными считаются: + - переменные, подходящие под SECRET_VARS_PATTERNS + - переменные с ключами DATABASE_URL, REDIS_URL, DB_URL (точное совпадение, без учета регистра) + + >>> EnvManager()._is_secret_variable('MY_SECRET_TOKEN') + True + >>> EnvManager()._is_secret_variable('database_url') + True + >>> EnvManager()._is_secret_variable('REDIS_URL') + True + >>> EnvManager()._is_secret_variable('DB_URL') + True + >>> EnvManager()._is_secret_variable('SOME_PUBLIC_KEY') + True + >>> EnvManager()._is_secret_variable('SOME_PUBLIC_VAR') + False """ key_upper = key.upper() + if key_upper in {"DATABASE_URL", "REDIS_URL", "DB_URL"}: + return True return any(re.match(pattern, key_upper) for pattern in self.SECRET_VARS_PATTERNS) def _determine_variable_type(self, value: str) -> str: diff --git a/services/viewed.py b/services/viewed.py index 44cfa74b..66599783 100644 --- a/services/viewed.py +++ b/services/viewed.py @@ -75,7 +75,7 @@ class ViewedStorage: await redis.connect() # Логируем настройки Redis соединения - logger.info(f" * Redis connection: {redis._client}") + logger.info("* Redis connected") # Получаем список всех ключей migrated_views_* и находим самый последний keys = await redis.execute("KEYS", "migrated_views_*")