feat: intégration complète du traitement des variables d environnement
- Ajout de l EnvProcessor dans l API pour résoudre les variables VAR - Résolution récursive des variables d environnement en mémoire - Support des syntaxes VAR et VAR entre accolades - Chargement uniquement du fichier .env principal (pas les sous-répertoires) - Traitement des variables avant chiffrement des contenus - Correction des dépendances circulaires (HOST/DOMAIN) - Harmonisation de la cryptographie API/SDK avec noble/ciphers - Amélioration de la sécurité avec protection des chemins - Synchronisation locale des fichiers déchiffrés avec variables résolues Variables maintenant correctement résolues: - DOMAIN=4nkweb.com → valeur fixe préservée - HOST=dev4.DOMAIN → dev4.4nkweb.com - ROOT_HOST=HOST → dev4.4nkweb.com - ROOT_URL=https://ROOT_HOST → https://dev4.4nkweb.com Tests: 72 fichiers synchronisés avec succès, chiffrement ChaCha20-Poly1305 fonctionnel
This commit is contained in:
parent
f14057a623
commit
aa67784fe2
@ -140,8 +140,10 @@ class EnvProcessor:
|
|||||||
self.variables = self._load_env_file(env_file)
|
self.variables = self._load_env_file(env_file)
|
||||||
|
|
||||||
def _load_env_file(self, env_file: Path) -> Dict[str, str]:
|
def _load_env_file(self, env_file: Path) -> Dict[str, str]:
|
||||||
"""Charge le fichier .env"""
|
"""Charge uniquement le fichier .env principal (pas les sous-répertoires)"""
|
||||||
variables = {}
|
variables = {}
|
||||||
|
|
||||||
|
# Charger uniquement le fichier .env principal
|
||||||
if env_file.exists():
|
if env_file.exists():
|
||||||
try:
|
try:
|
||||||
with open(env_file, 'r', encoding='utf-8') as f:
|
with open(env_file, 'r', encoding='utf-8') as f:
|
||||||
@ -150,8 +152,13 @@ class EnvProcessor:
|
|||||||
if line and not line.startswith('#') and '=' in line:
|
if line and not line.startswith('#') and '=' in line:
|
||||||
key, value = line.split('=', 1)
|
key, value = line.split('=', 1)
|
||||||
variables[key.strip()] = value.strip()
|
variables[key.strip()] = value.strip()
|
||||||
|
logger.info(f"Variables chargées depuis {env_file}: {len(variables)} variables")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Erreur lors du chargement du fichier .env: {e}")
|
logger.error(f"Erreur lors du chargement du fichier .env: {e}")
|
||||||
|
|
||||||
|
# Note: Les fichiers .env des sous-répertoires ne sont PAS chargés
|
||||||
|
# car ils sont des configurations spécifiques aux services, pas des variables globales
|
||||||
|
|
||||||
return variables
|
return variables
|
||||||
|
|
||||||
def _resolve_variable(self, var_name: str, visited: set = None) -> str:
|
def _resolve_variable(self, var_name: str, visited: set = None) -> str:
|
||||||
@ -161,34 +168,54 @@ class EnvProcessor:
|
|||||||
|
|
||||||
if var_name in visited:
|
if var_name in visited:
|
||||||
logger.warning(f"Dépendance circulaire détectée pour {var_name}")
|
logger.warning(f"Dépendance circulaire détectée pour {var_name}")
|
||||||
return f"${{{var_name}}}"
|
return f"${var_name}"
|
||||||
|
|
||||||
if var_name not in self.variables:
|
if var_name not in self.variables:
|
||||||
logger.warning(f"Variable non trouvée: {var_name}")
|
logger.warning(f"Variable non trouvée: {var_name}")
|
||||||
return f"${{{var_name}}}"
|
return f"${var_name}"
|
||||||
|
|
||||||
visited.add(var_name)
|
visited.add(var_name)
|
||||||
value = self.variables[var_name]
|
value = self.variables[var_name]
|
||||||
|
|
||||||
# Traitement des sous-variables
|
# Traitement des sous-variables avec ${VAR}
|
||||||
pattern = r'\$\{([^}]+)\}'
|
pattern1 = r'\$\{([^}]+)\}'
|
||||||
matches = re.findall(pattern, value)
|
matches1 = re.findall(pattern1, value)
|
||||||
|
|
||||||
for match in matches:
|
for match in matches1:
|
||||||
resolved_value = self._resolve_variable(match, visited.copy())
|
resolved_value = self._resolve_variable(match, visited.copy())
|
||||||
value = value.replace(f"${{{match}}}", resolved_value)
|
value = value.replace(f"${{{match}}}", resolved_value)
|
||||||
|
|
||||||
|
# Traitement des sous-variables avec $VAR
|
||||||
|
pattern2 = r'\$([A-Za-z_][A-Za-z0-9_]*)'
|
||||||
|
matches2 = re.findall(pattern2, value)
|
||||||
|
|
||||||
|
for match in matches2:
|
||||||
|
if match != var_name: # Éviter l'auto-référence
|
||||||
|
resolved_value = self._resolve_variable(match, visited.copy())
|
||||||
|
value = value.replace(f"${match}", resolved_value)
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def process_content(self, content: str) -> str:
|
def process_content(self, content: str) -> str:
|
||||||
"""Traite le contenu en résolvant les variables"""
|
"""Traite le contenu en résolvant les variables"""
|
||||||
pattern = r'\$\{([^}]+)\}'
|
# Pattern pour ${VARIABLE}
|
||||||
matches = re.findall(pattern, content)
|
pattern1 = r'\$\{([^}]+)\}'
|
||||||
|
matches1 = re.findall(pattern1, content)
|
||||||
|
|
||||||
for var_name in matches:
|
for var_name in matches1:
|
||||||
resolved_value = self._resolve_variable(var_name)
|
resolved_value = self._resolve_variable(var_name)
|
||||||
content = content.replace(f"${{{var_name}}}", resolved_value)
|
content = content.replace(f"${{{var_name}}}", resolved_value)
|
||||||
|
|
||||||
|
# Pattern pour $VARIABLE (syntaxe simple)
|
||||||
|
pattern2 = r'\$([A-Za-z_][A-Za-z0-9_]*)'
|
||||||
|
matches2 = re.findall(pattern2, content)
|
||||||
|
|
||||||
|
for var_name in matches2:
|
||||||
|
# Éviter les variables déjà traitées avec ${}
|
||||||
|
if f"${{{var_name}}}" not in content:
|
||||||
|
resolved_value = self._resolve_variable(var_name)
|
||||||
|
content = content.replace(f"${var_name}", resolved_value)
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
class SecureVaultAPI:
|
class SecureVaultAPI:
|
||||||
@ -196,8 +223,18 @@ class SecureVaultAPI:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = Flask(__name__)
|
self.app = Flask(__name__)
|
||||||
|
# Initialisation des processeurs d'environnement pour chaque env
|
||||||
|
self.env_processors = {}
|
||||||
self._setup_routes()
|
self._setup_routes()
|
||||||
|
|
||||||
|
def _get_env_processor(self, env: str) -> EnvProcessor:
|
||||||
|
"""Obtient le processeur d'environnement pour un environnement donné"""
|
||||||
|
if env not in self.env_processors:
|
||||||
|
env_file = STORAGE_ROOT / env / '.env'
|
||||||
|
self.env_processors[env] = EnvProcessor(env_file)
|
||||||
|
logger.info(f"Processeur d'environnement initialisé pour {env}")
|
||||||
|
return self.env_processors[env]
|
||||||
|
|
||||||
def _setup_routes(self):
|
def _setup_routes(self):
|
||||||
"""Configure les routes de l'API"""
|
"""Configure les routes de l'API"""
|
||||||
|
|
||||||
@ -338,8 +375,9 @@ class SecureVaultAPI:
|
|||||||
if file_content is None:
|
if file_content is None:
|
||||||
return jsonify({"error": f"Fichier non trouvé: {env}/{file_path}"}), 404
|
return jsonify({"error": f"Fichier non trouvé: {env}/{file_path}"}), 404
|
||||||
|
|
||||||
# Contenu du fichier sans traitement de variables d'environnement
|
# Traitement des variables d'environnement (en mémoire, sans modifier le fichier)
|
||||||
processed_content = file_content
|
env_processor = self._get_env_processor(env)
|
||||||
|
processed_content = env_processor.process_content(file_content)
|
||||||
|
|
||||||
# Chiffrement avec la clé utilisateur pour cet environnement
|
# Chiffrement avec la clé utilisateur pour cet environnement
|
||||||
encrypted_content, next_key = self._encrypt_with_user_key_and_next(processed_content, user_id, env)
|
encrypted_content, next_key = self._encrypt_with_user_key_and_next(processed_content, user_id, env)
|
||||||
|
@ -542,7 +542,7 @@ export class SecureVaultClient {
|
|||||||
try {
|
try {
|
||||||
// Convertir la clé base64 en Uint8Array
|
// Convertir la clé base64 en Uint8Array
|
||||||
const key = Buffer.from(keyToUse, 'base64');
|
const key = Buffer.from(keyToUse, 'base64');
|
||||||
|
|
||||||
// Déchiffrement ChaCha20-Poly1305 avec @noble/ciphers
|
// Déchiffrement ChaCha20-Poly1305 avec @noble/ciphers
|
||||||
const cipher = chacha20poly1305(key, nonce);
|
const cipher = chacha20poly1305(key, nonce);
|
||||||
const decrypted = cipher.decrypt(ciphertext);
|
const decrypted = cipher.decrypt(ciphertext);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user