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
|
# 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
|
## [0.1.3] - 2026-09-08
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import base64
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -19,6 +20,20 @@ def _ps_quote(value: str) -> str:
|
|||||||
return value.replace("'", "''")
|
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):
|
class WindowsAddComputerBackend(DomainJoinBackend):
|
||||||
def is_available(self) -> tuple[bool, str]:
|
def is_available(self) -> tuple[bool, str]:
|
||||||
if shutil.which("powershell") is None and shutil.which("powershell.exe") is None:
|
if shutil.which("powershell") is None and shutil.which("powershell.exe") is None:
|
||||||
@@ -76,8 +91,16 @@ class WindowsAddComputerBackend(DomainJoinBackend):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
process = subprocess.Popen(
|
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,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
text=True,
|
text=True,
|
||||||
@@ -90,12 +113,6 @@ class WindowsAddComputerBackend(DomainJoinBackend):
|
|||||||
finally:
|
finally:
|
||||||
env[_PASSWORD_ENV_VAR] = ""
|
env[_PASSWORD_ENV_VAR] = ""
|
||||||
|
|
||||||
try:
|
|
||||||
process.stdin.write(script)
|
|
||||||
process.stdin.close()
|
|
||||||
except BrokenPipeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
output_lines: list[str] = []
|
output_lines: list[str] = []
|
||||||
if process.stdout:
|
if process.stdout:
|
||||||
for line in 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)
|
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)
|
return JoinResult(success=False, message=message)
|
||||||
|
|||||||
Reference in New Issue
Block a user