fix: la jointure Windows n'executait jamais le script PowerShell
Le script etait envoye sur stdin de `powershell -Command -`, qui analyse l'entree ligne par ligne. Le `}` fermant le try/catch n'etait suivi d'aucun saut de ligne : le parseur gardait l'instruction incomplete en attente et la jetait a la fermeture de stdin. PowerShell rendait la main avec le code 0 sans avoir appele Add-Computer, et l'utilisateur ne voyait qu'un « echec de la jointure (code retour 0) » sans explication. - Transmission du script d'un seul bloc via `-EncodedCommand` (base64 UTF-16LE), ce qui supprime toute analyse ligne par ligne et propage correctement les codes de sortie. Le mot de passe reste hors de la ligne de commande : il ne transite que par l'environnement du sous-processus. - Report de la sortie brute de PowerShell quand aucun marqueur n'est trouve, pour qu'un echec inattendu reste diagnosticable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
# Changelog
|
||||
|
||||
## [Non publié]
|
||||
|
||||
### Fixed
|
||||
- La jointure de domaine sous Windows échouait sans aucun message, avec un code
|
||||
retour 0. Le script PowerShell était transmis sur l'entrée standard de
|
||||
`powershell -Command -`, qui analyse stdin ligne par ligne : le bloc
|
||||
`try`/`catch` final, dépourvu de saut de ligne terminal, restait en attente
|
||||
dans le parseur et n'était jamais exécuté. PowerShell rendait donc la main
|
||||
sans avoir tenté la jointure. Le script est désormais transmis d'un bloc via
|
||||
`-EncodedCommand`.
|
||||
- En l'absence de marqueur de succès comme d'échec, le message d'erreur se
|
||||
réduisait à « code retour 0 », impossible à diagnostiquer. La sortie brute de
|
||||
PowerShell est maintenant reportée à l'utilisateur (le mot de passe ne
|
||||
transitant que par l'environnement, il n'y apparaît jamais).
|
||||
|
||||
## [0.1.3] - 2026-09-08
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
@@ -19,6 +20,20 @@ def _ps_quote(value: str) -> str:
|
||||
return value.replace("'", "''")
|
||||
|
||||
|
||||
def _encode_command(script: str) -> str:
|
||||
"""Encode un script pour `powershell -EncodedCommand` (base64 d'UTF-16LE).
|
||||
|
||||
Le script est ainsi transmis comme un bloc unique et atomique. L'alternative
|
||||
`-Command -` fait analyser l'entrée standard ligne par ligne : la dernière
|
||||
instruction d'un bloc `try`/`catch` restait en attente dans le parseur et
|
||||
n'était jamais exécutée à la fermeture de stdin — PowerShell rendait alors
|
||||
la main avec le code 0 sans avoir rien fait, et la jointure echouait sans
|
||||
le moindre message. Le mot de passe reste hors du script (variable
|
||||
d'environnement) : la ligne de commande ne l'expose pas.
|
||||
"""
|
||||
return base64.b64encode(script.encode("utf-16-le")).decode("ascii")
|
||||
|
||||
|
||||
class WindowsAddComputerBackend(DomainJoinBackend):
|
||||
def is_available(self) -> tuple[bool, str]:
|
||||
if shutil.which("powershell") is None and shutil.which("powershell.exe") is None:
|
||||
@@ -76,8 +91,16 @@ class WindowsAddComputerBackend(DomainJoinBackend):
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
["powershell", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", "-"],
|
||||
stdin=subprocess.PIPE,
|
||||
[
|
||||
"powershell",
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-EncodedCommand",
|
||||
_encode_command(script),
|
||||
],
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
@@ -90,12 +113,6 @@ class WindowsAddComputerBackend(DomainJoinBackend):
|
||||
finally:
|
||||
env[_PASSWORD_ENV_VAR] = ""
|
||||
|
||||
try:
|
||||
process.stdin.write(script)
|
||||
process.stdin.close()
|
||||
except BrokenPipeError:
|
||||
pass
|
||||
|
||||
output_lines: list[str] = []
|
||||
if process.stdout:
|
||||
for line in process.stdout:
|
||||
@@ -114,5 +131,15 @@ class WindowsAddComputerBackend(DomainJoinBackend):
|
||||
)
|
||||
|
||||
failure_detail = next((l for l in output_lines if l.strip().startswith(_FAILURE_PREFIX)), None)
|
||||
message = failure_detail or f"Échec de la jointure au domaine (code retour {returncode})."
|
||||
if failure_detail:
|
||||
message = failure_detail
|
||||
else:
|
||||
# Sans ce report de la sortie brute, un echec inattendu se resumait a
|
||||
# « code retour 0 », indiagnosticable. Le mot de passe n'apparait
|
||||
# jamais dans cette sortie : il ne transite que par l'environnement.
|
||||
detail = full_output.strip() or "aucune sortie produite par PowerShell"
|
||||
message = (
|
||||
f"Échec de la jointure au domaine (code retour {returncode}).\n"
|
||||
f"Sortie de PowerShell : {detail}"
|
||||
)
|
||||
return JoinResult(success=False, message=message)
|
||||
|
||||
Reference in New Issue
Block a user