diff --git a/.gitignore b/.gitignore index e3fa3c5..3ac9e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ dist/ *.spec .build-venv-linux/ .build-venv-windows/ +.build-wine/ +.build-wine-cache/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..99e2ec1 --- /dev/null +++ b/Makefile @@ -0,0 +1,87 @@ +# JoinDomainGUI — diagnostic reseau AD + jointure de domaine +# `make` seul (ou `make help`) affiche les cibles disponibles. + +.DEFAULT_GOAL := help + +VENV := .build-venv-linux +PY := $(VENV)/bin/python +PIP := $(VENV)/bin/pip +VERSION := $(shell sed -n 's/^__version__ = "\(.*\)"/\1/p' domainjoin/__init__.py) +DOMAIN ?= h3adm.lan +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +REMOTES := $(shell git remote) + +##@ 📦 Installation + +$(VENV)/bin/python: + python3 -m venv --system-site-packages $(VENV) + $(PIP) install --upgrade pip -q + $(PIP) install -q -r requirements.txt + +.PHONY: install +install: $(VENV)/bin/python ## Cree l'environnement virtuel et installe les dependances + @echo "✅ Environnement pret : $(VENV)" + +##@ 🚀 Utilisation + +.PHONY: run +run: install ## Lance l'interface graphique + $(PY) main.py + +.PHONY: diagnose +diagnose: install ## Lance le diagnostic reseau en CLI (make diagnose DOMAIN=exemple.lan) + $(PY) main.py diagnose --domain $(DOMAIN) + +##@ 🏗️ Compilation + +.PHONY: build +build: build-linux build-windows ## Compile les binaires Linux et Windows + +.PHONY: build-linux +build-linux: ## Compile le binaire Linux autonome (dist/JoinDomainGUI) + ./packaging/build_linux.sh + +.PHONY: build-windows +build-windows: ## Compile le .exe Windows depuis Linux via Wine (dist/windows/JoinDomainGUI.exe) + ./packaging/build_windows_wine.sh + +##@ 🚢 Publication + +.PHONY: remotes +remotes: ## Liste les depots distants configures + @git remote -v | awk '$$3 == "(push)" { printf " %-10s %s\n", $$1, $$2 }' + +.PHONY: publish +publish: ## Pousse la branche courante et les tags vers tous les depots (Gitea) + @git diff --quiet HEAD || { echo "❌ Modifications non commitees — commit avant de publier."; exit 1; } + @for remote in $(REMOTES); do \ + echo "🚀 $$remote ← $(BRANCH)"; \ + git push $$remote $(BRANCH) || exit 1; \ + git push $$remote --tags || exit 1; \ + done + @echo "✅ Publie sur : $(REMOTES)" + +##@ 🧹 Nettoyage + +.PHONY: clean +clean: ## Supprime les artefacts de compilation et les caches Python + rm -rf build dist + find . -path ./$(VENV) -prune -o -name '__pycache__' -type d -print0 | xargs -0 rm -rf + +.PHONY: distclean +distclean: clean ## Nettoyage complet : + environnements virtuels, prefixe Wine et telechargements + rm -rf $(VENV) .build-venv-windows .build-wine .build-wine-cache + +##@ ℹ️ Aide + +.PHONY: version +version: ## Affiche la version courante + @echo "JoinDomainGUI $(VERSION)" + +.PHONY: help +help: ## Affiche cette aide + @echo "JoinDomainGUI $(VERSION)" + @awk 'BEGIN {FS = ":.*?## "} \ + /^[a-zA-Z0-9_.-]+:.*?##/ { printf " \033[36m%-16s\033[0m %s\n", $$1, $$2 } \ + /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) }' $(MAKEFILE_LIST) + @echo "" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2597de --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +# JoinDomainGUI + +Outil de **diagnostic réseau Active Directory** et de **jointure de domaine**, +avec interface graphique (PySide6) et interface en ligne de commande. +Fonctionne sous **Linux** (`realmd`) et sous **Windows** (PowerShell `Add-Computer`), +le backend étant choisi automatiquement selon la plateforme. + +## Fonctionnalités + +- **Diagnostic réseau AD** : résolution DNS du domaine, enregistrements SRV + (LDAP / Kerberos), ping des contrôleurs de domaine, test des ports AD + (53, 88, 389, 445, 464, 636, 3268), synchronisation NTP et présence des + outils système requis. +- **Jointure de domaine** avec unité d'organisation et nom de machine + optionnels, et détection du besoin de redémarrage. +- **Installation des prérequis Linux** manquants en un clic ou une commande + (`realmd`, `sssd`, `adcli`, `samba-common-bin`, `packagekit` via `apt-get`). +- **Interface graphique** à trois onglets : Diagnostic réseau, Rejoindre le + domaine, À propos. +- **Interface en ligne de commande** utilisable sans environnement graphique + (SSH, déploiement scripté) : la CLI n'importe jamais PySide6. +- **Le mot de passe ne transite jamais par la ligne de commande** : il est + transmis au sous-processus via stdin ou son environnement, et n'apparaît + donc pas dans `ps` ni dans l'historique du shell. + +## Installation + +```bash +make install # crée l'environnement virtuel et installe les dépendances +``` + +Dépendances : Python 3.11+, `PySide6`, `dnspython`, `ntplib`. + +## Utilisation + +### Interface graphique + +```bash +make run # depuis les sources +sudo ./dist/JoinDomainGUI # binaire autonome Linux +``` + +La jointure de domaine nécessite les droits root / administrateur ; le +diagnostic, non. + +### Ligne de commande + +```bash +# Diagnostic réseau +python main.py diagnose --domain exemple.lan +make diagnose DOMAIN=exemple.lan + +# Jointure de domaine (root / administrateur requis) +echo 'motdepasse' | sudo python main.py join \ + --domain exemple.lan --username administrateur --password-stdin + +# Installation des prérequis système (Linux / apt uniquement) +sudo python main.py install-prereqs +``` + +Sans mot de passe fourni, il est demandé de façon interactive et masquée. +`--password` existe mais est déconseillé (visible dans `ps` et l'historique). + +Code de sortie : `0` si tout va bien, `1` si un test échoue ou si la jointure +échoue, `2` en cas de droits insuffisants ou de backend indisponible. + +## Compilation des binaires + +```bash +make build-linux # -> dist/JoinDomainGUI (ELF autonome) +make build-windows # -> dist/windows/JoinDomainGUI.exe (PE64 autonome) +make build # les deux +``` + +PyInstaller ne fait pas de cross-compilation. Le binaire Windows est donc +produit depuis Linux en exécutant un vrai PyInstaller Windows dans un +préfixe **Wine** dédié (`packaging/build_windows_wine.sh`) : le `.exe` obtenu +est un binaire Windows natif. Le premier appel télécharge un Python Windows +embarqué et les dépendances (plusieurs minutes) ; les suivants sont +incrémentaux. + +Sur une machine Windows, `packaging/build_windows.bat` fait le même travail +nativement. + +> Le `.exe` s'appuie sur l'ICU fourni par le système (`icuuc.dll` dans +> `System32`), présent depuis Windows 10 1703 et Windows Server 2019. Il ne +> peut pas être testé sous Wine, qui ne fournit pas cette bibliothèque. + +## Publication + +```bash +make publish # pousse la branche courante et les tags vers tous les dépôts +``` + +Le projet est publié sur deux instances Gitea : +[git.tuxtech.fr](https://git.tuxtech.fr/tuxgyver/JoinDomainGUI) (`origin`) et +[git.h3campus.fr](https://git.h3campus.fr/Johnny/JoinDomainGUI) (`h3campus`). + +## Structure + +``` +main.py Point d'entrée : GUI sans argument, CLI sinon +domainjoin/ +├── cli.py Interface en ligne de commande +├── gui.py Interface PySide6 +├── diagnostics.py Moteur de diagnostic réseau +├── worker.py Exécution en arrière-plan pour la GUI +├── privileges.py Détection des droits root/administrateur +└── backend/ + ├── base.py Interface commune de jointure + ├── linux_realmd.py Backend Linux (realmd / adcli) + └── windows_addcomputer.py Backend Windows (Add-Computer) +packaging/ Scripts de compilation PyInstaller +``` + +Voir [CHANGELOG.md](CHANGELOG.md) pour l'historique des versions. diff --git a/packaging/build_windows_wine.sh b/packaging/build_windows_wine.sh new file mode 100755 index 0000000..18cc4d2 --- /dev/null +++ b/packaging/build_windows_wine.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# Fabrique le binaire Windows autonome (.exe) de JoinDomainGUI DEPUIS LINUX, +# en pilotant un Python Windows embarque dans un prefixe Wine dedie. +# +# PyInstaller ne cross-compile pas : on execute donc un vrai PyInstaller Windows +# sous Wine. Le .exe produit est un binaire Windows natif. +# +# Prerequis : wine (>= 8), curl, unzip. +set -euo pipefail + +cd "$(dirname "$0")/.." + +PYTHON_VERSION="${PYTHON_VERSION:-3.12.10}" +WINEPREFIX="${WINEPREFIX:-$PWD/.build-wine}" +export WINEPREFIX +export WINEARCH=win64 +export WINEDEBUG="${WINEDEBUG:--all}" + +PY_DIR="$WINEPREFIX/drive_c/python" +WPY="c:\\python\\python.exe" +CACHE=".build-wine-cache" + +command -v wine >/dev/null || { echo "wine est requis (apt install wine)" >&2; exit 1; } + +mkdir -p "$CACHE" + +if [ ! -d "$WINEPREFIX" ]; then + echo ">> Initialisation du prefixe Wine ($WINEPREFIX)" + wineboot --init >/dev/null 2>&1 + wineserver -w +fi + +if [ ! -f "$PY_DIR/python.exe" ]; then + ZIP="$CACHE/python-$PYTHON_VERSION-embed-amd64.zip" + [ -f "$ZIP" ] || curl -fL --retry 3 -o "$ZIP" \ + "https://www.python.org/ftp/python/$PYTHON_VERSION/python-$PYTHON_VERSION-embed-amd64.zip" + echo ">> Installation de Python $PYTHON_VERSION (Windows) dans le prefixe" + rm -rf "$PY_DIR" + mkdir -p "$PY_DIR" + unzip -q "$ZIP" -d "$PY_DIR" + # La distribution "embeddable" desactive site-packages : on le reactive + # pour que pip et les paquets installes soient importables. + sed -i 's/^#import site/import site/' "$PY_DIR"/python*._pth +fi + +if [ ! -f "$PY_DIR/Scripts/pip.exe" ]; then + GETPIP="$CACHE/get-pip.py" + [ -f "$GETPIP" ] || curl -fL --retry 3 -o "$GETPIP" https://bootstrap.pypa.io/get-pip.py + echo ">> Installation de pip" + cp "$GETPIP" "$PY_DIR/get-pip.py" + wine "$WPY" c:\\python\\get-pip.py --no-warn-script-location +fi + +# pip sous Wine est lent (plusieurs minutes pour le wheel PySide6) : on ne +# reinstalle que si le prefixe est neuf. FORCE_DEPS=1 pour forcer une mise a jour. +if [ ! -d "$PY_DIR/Lib/site-packages/PyInstaller" ] || [ "${FORCE_DEPS:-0}" = "1" ]; then + echo ">> Installation des dependances Windows (peut prendre plusieurs minutes)" + wine "$WPY" -m pip install -q --no-warn-script-location \ + -r requirements.txt pyinstaller +else + echo ">> Dependances Windows deja presentes (FORCE_DEPS=1 pour les mettre a jour)" +fi + +echo ">> Compilation du .exe" +wine "$WPY" -m PyInstaller \ + --noconfirm \ + --onefile \ + --windowed \ + --name JoinDomainGUI \ + --distpath dist/windows \ + --workpath build/windows \ + --specpath build/windows \ + main.py + +wineserver -w +echo +echo "Binaire produit : dist/windows/JoinDomainGUI.exe" +echo "Lancement : clic droit -> Executer en tant qu'administrateur"