Refonte authentification en crypté
This commit is contained in:
41
.gitignore
vendored
41
.gitignore
vendored
@@ -1,10 +1,41 @@
|
|||||||
.env
|
# ----- Secrets / configs locales -----
|
||||||
*.txt
|
# Ne versionne JAMAIS de .env (mets un .env.example à la place)
|
||||||
|
*.env
|
||||||
|
|
||||||
|
# ----- Python -----
|
||||||
|
.venv/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
|
||||||
# Ignorer tous les fichiers de travail dans Excel/dev
|
# ----- PyInstaller (builds) -----
|
||||||
Excel/dev/*
|
build/
|
||||||
|
dist/
|
||||||
|
*.spec
|
||||||
|
*.toc
|
||||||
|
*.pkg
|
||||||
|
*.pyz
|
||||||
|
*.zip
|
||||||
|
|
||||||
|
# ----- Excel -----
|
||||||
|
# Fichiers temporaires d'Excel (~$fichier.xlsm)
|
||||||
|
~$*.xls*
|
||||||
|
~$*.xlsm
|
||||||
|
# On versionne 1 seul fichier prod "pivot" ; on ignore les exports datés
|
||||||
Excel/prod/Ratio_prod_20*.xlsm
|
Excel/prod/Ratio_prod_20*.xlsm
|
||||||
!Excel/prod/Ratio_prod.xlsm
|
!Excel/prod/Ratio_prod.xlsm
|
||||||
|
|
||||||
|
# ----- Dossiers Auth livrables -----
|
||||||
|
# On n’embarque pas les exe ni .env dans le repo (tu livreras ça en zip)
|
||||||
|
Excel/**/Auth/*.exe
|
||||||
|
Excel/**/Auth/*.dll
|
||||||
|
Excel/**/Auth/*.pdb
|
||||||
|
Excel/**/Auth/.env
|
||||||
|
|
||||||
|
# ----- Systèmes / IDE -----
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
2
.idea/Ratio_Inventaires.iml
generated
2
.idea/Ratio_Inventaires.iml
generated
@@ -4,7 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.11 virtualenv at C:\Users\miche\PycharmProjects\Gestion_sondes\.venv" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
12
.idea/dataSources.xml
generated
Normal file
12
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="domo91" uuid="d8bef1d2-2e67-4363-960b-e8d2ef8041bd">
|
||||||
|
<driver-ref>mariadb</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:mariadb://162.19.78.131:3306/Acces</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -3,5 +3,5 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.13 (Ratio & inventaires)" />
|
<option name="sdkName" value="Python 3.13 (Ratio & inventaires)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 virtualenv at C:\Users\miche\PycharmProjects\Gestion_sondes\.venv" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
||||||
86
Excel/dev/Auth/auth_cli.py
Normal file
86
Excel/dev/Auth/auth_cli.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os, sys, argparse, datetime
|
||||||
|
import mysql.connector, bcrypt
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
def read_stdin() -> str:
|
||||||
|
data = sys.stdin.read()
|
||||||
|
return data.rstrip("\r\n")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
ap = argparse.ArgumentParser(description="Auth bcrypt locale pour Excel")
|
||||||
|
ap.add_argument("--user", required=True, help="NomUtilisateur (clé primaire)")
|
||||||
|
ap.add_argument("--password-stdin", action="store_true",
|
||||||
|
help="Lire le mot de passe via STDIN (recommandé)")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not args.password_stdin:
|
||||||
|
print("ERR|Utiliser --password-stdin et fournir le mot de passe via STDIN", flush=True)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
password = read_stdin()
|
||||||
|
if not password:
|
||||||
|
print("ERR|Mot de passe vide", flush=True)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
# Récupération des infos de connexion depuis .env
|
||||||
|
host = os.getenv("DB_HOST")
|
||||||
|
db = os.getenv("DB_NAME")
|
||||||
|
user = os.getenv("DB_USER")
|
||||||
|
pwd = os.getenv("DB_PASSWORD")
|
||||||
|
|
||||||
|
try:
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=host, database=db, user=user, password=pwd, connection_timeout=5
|
||||||
|
)
|
||||||
|
cur = conn.cursor(dictionary=True)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT NomUtilisateur, Nom_complet, Site, MotDePasseHash, DateExpiration
|
||||||
|
FROM Utilisateurs
|
||||||
|
WHERE NomUtilisateur = %s
|
||||||
|
LIMIT 1
|
||||||
|
""", (args.user,))
|
||||||
|
row = cur.fetchone()
|
||||||
|
except Exception as e:
|
||||||
|
print("ERR|Connexion base impossible", flush=True)
|
||||||
|
return 3
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not row or not row.get("MotDePasseHash"):
|
||||||
|
print("ERR|Utilisateur ou mot de passe invalide", flush=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
try:
|
||||||
|
ok = bcrypt.checkpw(password.encode("utf-8"),
|
||||||
|
row["MotDePasseHash"].encode("ascii"))
|
||||||
|
except Exception:
|
||||||
|
ok = False
|
||||||
|
|
||||||
|
if not ok:
|
||||||
|
print("ERR|Utilisateur ou mot de passe invalide", flush=True)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# Contrôle expiration (si renseignée)
|
||||||
|
exp = row.get("DateExpiration")
|
||||||
|
if exp is not None:
|
||||||
|
today = datetime.date.today()
|
||||||
|
exp_date = exp if isinstance(exp, datetime.date) else getattr(exp, "date", lambda: today)()
|
||||||
|
if exp_date < today:
|
||||||
|
print(f"ERR|Compte expiré le {exp_date.isoformat()}", flush=True)
|
||||||
|
return 2
|
||||||
|
|
||||||
|
site = row.get("Site") or ""
|
||||||
|
nom = row.get("Nom_complet") or ""
|
||||||
|
print(f"OK|Site={site}|NomComplet={nom}", flush=True)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
BIN
Excel/dev/Ratio_dev.xlsm
Normal file
BIN
Excel/dev/Ratio_dev.xlsm
Normal file
Binary file not shown.
Binary file not shown.
@@ -1,39 +1,112 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal EnableExtensions EnableDelayedExpansion
|
||||||
|
|
||||||
REM === Définition des chemins ===
|
REM === PARAMÈTRES A ADAPTER UNE FOIS ===
|
||||||
set "DEV=C:\Users\miche\PycharmProjects\Ratio_Inventaires\Excel\dev\Ratio_dev.xlsm"
|
set "ROOT=C:\Users\miche\PycharmProjects\Ratio_Inventaires"
|
||||||
set "PROD_DIR=C:\Users\miche\PycharmProjects\Ratio_Inventaires\Excel\prod"
|
set "DEV_XLSM=%ROOT%\Excel\dev\Ratio_dev.xlsm"
|
||||||
set "INSTALL_CLIENT=C:\Users\miche\PycharmProjects\Fichiers_Install_Clients\Ratio_Inventaires\Excel"
|
set "DEV_AUTH_PY=%ROOT%\Excel\dev\Auth\auth_cli.py"
|
||||||
|
set "VENV_PY=%ROOT%\.venv\Scripts\python.exe"
|
||||||
|
|
||||||
REM === Création du nom de fichier daté ===
|
set "PROD_DIR=%ROOT%\Excel\prod"
|
||||||
set "DATESTAMP=%date:~6,4%_%date:~3,2%_%date:~0,2%"
|
set "PROD_XLSM=%PROD_DIR%\Ratio_prod.xlsm"
|
||||||
|
set "PROD_AUTH_DIR=%PROD_DIR%\Auth"
|
||||||
|
|
||||||
|
set "INSTALL_CLIENT=%USERPROFILE%\Desktop\Install_Ratio_Inv" REM <- dossier prêt à livrer au client
|
||||||
|
set "CLIENT_AUTH_DIR=%INSTALL_CLIENT%\Auth"
|
||||||
|
|
||||||
|
REM === DATE ISO robuste (indépendant des paramètres régionaux) ===
|
||||||
|
for /f %%i in ('powershell -NoProfile -Command "(Get-Date).ToString(\"yyyy_MM_dd\")"') do set "DATESTAMP=%%i"
|
||||||
set "ARCHIVE_FILE=%PROD_DIR%\Ratio_prod_%DATESTAMP%.xlsm"
|
set "ARCHIVE_FILE=%PROD_DIR%\Ratio_prod_%DATESTAMP%.xlsm"
|
||||||
|
|
||||||
echo Sauvegarde de la version actuelle de PROD...
|
echo.
|
||||||
if exist "%PROD_DIR%\Ratio_prod.xlsm" copy /Y "%PROD_DIR%\Ratio_prod.xlsm" "%ARCHIVE_FILE%"
|
echo === [1/6] Préparation des dossiers ========================================
|
||||||
|
if not exist "%PROD_DIR%" mkdir "%PROD_DIR%"
|
||||||
echo Mise à jour du fichier Ratio_prod.xlsm depuis DEV...
|
if not exist "%PROD_AUTH_DIR%" mkdir "%PROD_AUTH_DIR%"
|
||||||
copy /Y "%DEV%" "%PROD_DIR%\Ratio_prod.xlsm"
|
if not exist "%INSTALL_CLIENT%" mkdir "%INSTALL_CLIENT%"
|
||||||
|
if not exist "%CLIENT_AUTH_DIR%" mkdir "%CLIENT_AUTH_DIR%"
|
||||||
echo Copie vers le dossier INSTALL_CLIENT (version sans date)...
|
|
||||||
copy /Y "%PROD_DIR%\Ratio_prod.xlsm" "%INSTALL_CLIENT%\Ratio_prod.xlsm"
|
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ✔️ Mise à jour complète effectuée (archive, prod, client)
|
echo === [2/6] (Optionnel) Build de auth_cli.exe avec PyInstaller ==============
|
||||||
pause
|
if exist "%VENV_PY%" (
|
||||||
echo Nettoyage des anciennes archives (on garde les 5 plus récentes)...
|
echo Compilation via venv: %VENV_PY%
|
||||||
|
"%VENV_PY%" -m PyInstaller --onefile --clean --distpath "%PROD_AUTH_DIR%" "%DEV_AUTH_PY%"
|
||||||
pushd "%PROD_DIR%"
|
if errorlevel 1 (
|
||||||
setlocal EnableDelayedExpansion
|
echo [WARN] Echec de compilation. On continue avec l'exe existant s'il est présent.
|
||||||
set count=0
|
)
|
||||||
|
) else (
|
||||||
for /f "delims=" %%f in ('dir /b /o-d "Ratio_prod_????_??_??.xlsm"') do (
|
echo [WARN] VENV introuvable: %VENV_PY% -> build ignore
|
||||||
set /a count+=1
|
)
|
||||||
if !count! gtr 5 (
|
|
||||||
echo Suppression de l'ancienne archive : %%f
|
echo.
|
||||||
del "%%f"
|
echo === [3/6] Sauvegarde de la prod courante ==================================
|
||||||
)
|
if exist "%PROD_XLSM%" (
|
||||||
|
echo Sauvegarde -> "%ARCHIVE_FILE%"
|
||||||
|
copy /Y "%PROD_XLSM%" "%ARCHIVE_FILE%" >nul
|
||||||
|
) else (
|
||||||
|
echo Pas de prod courante a sauvegarder.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === [4/6] Mise a jour de la prod depuis le dev ============================
|
||||||
|
if not exist "%DEV_XLSM%" (
|
||||||
|
echo [ERREUR] Fichier DEV introuvable: %DEV_XLSM%
|
||||||
|
goto :EOF
|
||||||
|
)
|
||||||
|
copy /Y "%DEV_XLSM%" "%PROD_XLSM%" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERREUR] Copie du XLSM vers PROD echouee.
|
||||||
|
goto :EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
REM On s'assure que l'exe est bien present et on deblocque Windows (SmartScreen)
|
||||||
|
if exist "%PROD_AUTH_DIR%\auth_cli.exe" (
|
||||||
|
powershell -NoProfile -Command "Unblock-File -Path '%PROD_AUTH_DIR%\auth_cli.exe'" 2>nul
|
||||||
|
) else (
|
||||||
|
echo [ERREUR] auth_cli.exe absent dans %PROD_AUTH_DIR%
|
||||||
|
echo Lance d'abord la compilation ou copie l'exe manuellement.
|
||||||
|
goto :EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
REM Le .env doit exister dans PROD\Auth (modele ou reel)
|
||||||
|
if not exist "%PROD_AUTH_DIR%\.env" (
|
||||||
|
echo [WARN] Aucun .env dans %PROD_AUTH_DIR% -> Pense a y mettre DB_HOST/DB_NAME/DB_USER/DB_PASSWORD
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === [5/6] Préparation du dossier client ===================================
|
||||||
|
copy /Y "%PROD_XLSM%" "%INSTALL_CLIENT%\Ratio_prod.xlsm" >nul
|
||||||
|
if errorlevel 1 (
|
||||||
|
echo [ERREUR] Copie du XLSM vers INSTALL_CLIENT echouee.
|
||||||
|
goto :EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
copy /Y "%PROD_AUTH_DIR%\auth_cli.exe" "%CLIENT_AUTH_DIR%\auth_cli.exe" >nul
|
||||||
|
powershell -NoProfile -Command "Unblock-File -Path '%CLIENT_AUTH_DIR%\auth_cli.exe'" 2>nul
|
||||||
|
|
||||||
|
if exist "%PROD_AUTH_DIR%\.env" (
|
||||||
|
copy /Y "%PROD_AUTH_DIR%\.env" "%CLIENT_AUTH_DIR%\.env" >nul
|
||||||
|
) else (
|
||||||
|
echo [INFO] Pas de .env a copier (client). Tu pourras le remplir a la main.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo === [6/6] Nettoyage: on garde 5 archives les plus recentes ===============
|
||||||
|
pushd "%PROD_DIR%"
|
||||||
|
set count=0
|
||||||
|
for /f "delims=" %%f in ('dir /b /o-d "Ratio_prod_20??_??_??.xlsm"') do (
|
||||||
|
set /a count+=1
|
||||||
|
if !count! gtr 5 (
|
||||||
|
echo Suppression: %%f
|
||||||
|
del "%%f"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
endlocal
|
|
||||||
popd
|
popd
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo [OK] Release terminee :
|
||||||
|
echo - PROD : "%PROD_XLSM%"
|
||||||
|
echo - AUTH : "%PROD_AUTH_DIR%\auth_cli.exe" + ".env"
|
||||||
|
echo - CLIENT: "%INSTALL_CLIENT%\Ratio_prod.xlsm" + "Auth\*"
|
||||||
|
echo.
|
||||||
|
pause
|
||||||
|
endlocal
|
||||||
|
|||||||
26
bcrypt_check.py
Normal file
26
bcrypt_check.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys, argparse, bcrypt
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--hash", required=True)
|
||||||
|
ap.add_argument("--password-stdin", action="store_true")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
if not args.password_stdin:
|
||||||
|
print("ERR")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
password = sys.stdin.read().rstrip("\r\n")
|
||||||
|
try:
|
||||||
|
ok = bcrypt.checkpw(password.encode("utf-8"),
|
||||||
|
args.hash.encode("ascii"))
|
||||||
|
except Exception:
|
||||||
|
print("ERR")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
print("OK" if ok else "ERR")
|
||||||
|
return 0 if ok else 1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
3
last_ids.txt
Normal file
3
last_ids.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Meudon:3
|
||||||
|
Roissy:8
|
||||||
|
Saclay:12
|
||||||
44
migre_bcrypt.py
Normal file
44
migre_bcrypt.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import os, sys, bcrypt, mysql.connector
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
conn = mysql.connector.connect(
|
||||||
|
host=os.getenv("DB_HOST"),
|
||||||
|
database=os.getenv("DB_NAME"),
|
||||||
|
user=os.getenv("DB_USER"),
|
||||||
|
password=os.getenv("DB_PASSWORD"),
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
cur = conn.cursor(dictionary=True)
|
||||||
|
cur.execute("""
|
||||||
|
SELECT NomUtilisateur, MotDePasse
|
||||||
|
FROM Utilisateurs
|
||||||
|
WHERE MotDePasseHash IS NULL OR MotDePasseHash = ''
|
||||||
|
""")
|
||||||
|
rows = cur.fetchall()
|
||||||
|
print(f"{len(rows)} utilisateur(s) à migrer…")
|
||||||
|
|
||||||
|
for r in rows:
|
||||||
|
login = r["NomUtilisateur"]
|
||||||
|
plain = (r["MotDePasse"] or "").encode("utf-8")
|
||||||
|
if not plain:
|
||||||
|
print(f"- {login}: mot de passe vide — ignoré")
|
||||||
|
continue
|
||||||
|
hashed = bcrypt.hashpw(plain, bcrypt.gensalt(rounds=12)).decode("ascii")
|
||||||
|
cur.execute("""
|
||||||
|
UPDATE Utilisateurs
|
||||||
|
SET MotDePasseHash = %s
|
||||||
|
WHERE NomUtilisateur = %s
|
||||||
|
""", (hashed, login))
|
||||||
|
print(f"- {login}: OK")
|
||||||
|
conn.commit()
|
||||||
|
print("Migration terminée.")
|
||||||
|
except Exception as e:
|
||||||
|
conn.rollback()
|
||||||
|
print("Erreur:", e)
|
||||||
|
sys.exit(1)
|
||||||
|
finally:
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
13
mkhash.py
Normal file
13
mkhash.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys, getpass, bcrypt
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) > 1:
|
||||||
|
password = sys.argv[1]
|
||||||
|
else:
|
||||||
|
password = getpass.getpass("Mot de passe: ")
|
||||||
|
h = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt(rounds=12)).decode("ascii")
|
||||||
|
print(h)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user