From b6ffb45ad7a8fb8fc810aaf91167aff65f850041 Mon Sep 17 00:00:00 2001 From: Bruno Charest Date: Mon, 23 Mar 2026 15:56:35 -0400 Subject: [PATCH] feat: Introduce optional JWT and Argon2id authentication with user management and access control, updating documentation and backend dependencies. --- README.md | 204 ++++++++++++++++++++++++++++++++------- backend/requirements.txt | 1 + 2 files changed, 170 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index f7c66f5..fde6f2a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive. -[![Version](https://img.shields.io/badge/Version-1.2.0-blue.svg)]() +[![Version](https://img.shields.io/badge/Version-1.4.0-blue.svg)]() [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Docker](https://img.shields.io/badge/Docker-Ready-blue.svg)](https://www.docker.com/) [![Python](https://img.shields.io/badge/Python-3.11+-green.svg)](https://www.python.org/) @@ -31,6 +31,7 @@ - [Installation rapide](#-installation-rapide) - [Configuration détaillée](#-configuration-détaillée) - [Variables d'environnement](#-variables-denvironnement) +- [🔒 Authentification](#-authentification) - [Ajouter une nouvelle vault](#-ajouter-une-nouvelle-vault) - [Build multi-platform](#-build-multi-platform) - [Utilisation](#-utilisation) @@ -55,11 +56,12 @@ - **🖼️ Images Obsidian** : Support complet des syntaxes d'images Obsidian avec résolution intelligente - **🎨 Syntax highlight** : Coloration syntaxique des blocs de code - **🌓 Thème clair/sombre** : Toggle persisté en localStorage -- **� Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index +- **📡 Synchronisation temps réel** : Surveillance automatique des fichiers via watchdog avec mise à jour incrémentale de l'index - **📡 Server-Sent Events** : Notifications SSE pour les changements d'index avec reconnexion automatique - **➕ Gestion dynamique des vaults** : Ajout/suppression de vaults via API sans redémarrage -- **�🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386 -- **🔒 Sécurité** : Protection contre le path traversal, utilisateur non-root dans Docker +- **🐳 Docker multi-platform** : linux/amd64, linux/arm64, linux/arm/v7, linux/386 +- **🔒 Authentification** : JWT + Argon2id, sessions persistantes, contrôle d'accès par vault +- **🛡️ Sécurité** : Headers de sécurité (CSP, X-Frame-Options…), protection path traversal, utilisateur non-root - **❤️ Healthcheck** : Endpoint `/api/health` intégré pour Docker et monitoring --- @@ -192,6 +194,96 @@ Les vaults sont configurées par paires de variables `VAULT_N_NAME` / `VAULT_N_P --- +## 🔒 Authentification + +> **Désactivée par défaut** — Compatible avec toutes les installations existantes. + +ObsiGate supporte un système d'authentification optionnel basé sur **JWT + Argon2id** avec contrôle d'accès par vault. + +### Activer l'authentification + +Dans `docker-compose.yml`, décommentez les variables d'auth : + +```yaml +environment: + # ... vaults ... + - OBSIGATE_AUTH_ENABLED=true + # - OBSIGATE_ADMIN_USER=admin # Défaut: admin + # - OBSIGATE_ADMIN_PASSWORD= # Vide = mot de passe auto-généré (voir logs) + # - OBSIGATE_SECURE_COOKIES=false # true si derrière un reverse-proxy HTTPS +``` + +### Premier démarrage + +Si aucun utilisateur n'existe, ObsiGate crée automatiquement un compte admin et **affiche le mot de passe dans les logs** : + +```bash +docker-compose logs obsigate | grep -A4 "PREMIER" +``` + +``` +============================================================ +PREMIER DÉMARRAGE — Compte admin créé automatiquement + Utilisateur : admin + Mot de passe : xK9mQ3pLr7wN2jT5 +CHANGEZ CE MOT DE PASSE dès la première connexion ! +============================================================ +``` + +### Gestion des utilisateurs via CLI + +```bash +# Créer un utilisateur +docker exec obsigate python backend/create_admin.py create alice MonMotDePasse --role user --vaults Recettes IT + +# Créer un admin avec accès total +docker exec obsigate python backend/create_admin.py create bob SecretPass --role admin --vaults "*" + +# Lister les utilisateurs +docker exec obsigate python backend/create_admin.py list + +# Supprimer un utilisateur +docker exec obsigate python backend/create_admin.py delete alice +``` + +### Interface d'administration + +Lorsqu'un compte **admin** est connecté, une icône 🛡️ apparaît dans le header. Cliquer dessus ouvre le panneau d'administration permettant de : + +- Lister tous les utilisateurs +- Créer / modifier / supprimer des utilisateurs +- Assigner les vaults accessibles par utilisateur +- Activer/désactiver des comptes + +### Contrôle d'accès par vault + +| Valeur vaults | Accès | +|---------------|-------| +| `["*"]` | Toutes les vaults (y compris futures) — défaut admin | +| `["Recettes", "IT"]` | Uniquement ces vaults | +| `[]` | Aucun accès | + +### Variables d'environnement d'auth + +| Variable | Description | Défaut | +|----------|-------------|--------| +| `OBSIGATE_AUTH_ENABLED` | Activer l'authentification | `false` | +| `OBSIGATE_ADMIN_USER` | Nom de l'admin auto-créé | `admin` | +| `OBSIGATE_ADMIN_PASSWORD` | Mot de passe admin (vide = auto-généré) | *(auto)* | +| `OBSIGATE_SECURE_COOKIES` | Cookie `Secure` (HTTPS uniquement) | `false` | + +### Volume pour la persistance + +Les données d'auth (`users.json`, `secret.key`) sont stockées dans `/app/data`. Ajoutez un volume pour les persister : + +```yaml +volumes: + # vaults... + - ./data:/app/data # Persistance des utilisateurs et clé JWT +``` + +--- + ## ➕ Ajouter une nouvelle vault ### Méthode 1 : Édition directe @@ -357,32 +449,35 @@ curl -X POST http://localhost:2020/api/attachments/rescan/MonVault ObsiGate expose une API REST complète : -| Endpoint | Description | Méthode | -|----------|-------------|---------| -| `/api/health` | Health check (status, version, stats) | GET | -| `/api/vaults` | Liste des vaults configurées | GET | -| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET | -| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET | -| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET | -| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET | -| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT | -| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE | -| `/api/search?q=&vault=&tag=` | Recherche fulltext (legacy) | GET | -| `/api/search/advanced?q=&vault=&tag=&limit=&offset=&sort=` | Recherche avancée TF-IDF avec facettes et pagination | GET | -| `/api/suggest?q=&vault=&limit=` | Suggestions de titres de fichiers (autocomplétion) | GET | -| `/api/tags/suggest?q=&vault=&limit=` | Suggestions de tags (autocomplétion) | GET | -| `/api/tags?vault=` | Tags uniques avec compteurs | GET | -| `/api/index/reload` | Force un re-scan des vaults | GET | -| `/api/events` | Flux SSE temps réel (index updates, vault changes) | GET | -| `/api/vaults/add` | Ajouter une vault dynamiquement | POST | -| `/api/vaults/{name}` | Supprimer une vault | DELETE | -| `/api/vaults/status` | Statut détaillé des vaults (fichiers, watching) | GET | -| `/api/image/{vault}?path=` | Servir une image avec MIME type approprié | GET | -| `/api/attachments/rescan/{vault}` | Rescanner les images d'un vault | POST | -| `/api/attachments/stats?vault=` | Statistiques d'images indexées | GET | -| `/api/config` | Lire la configuration | GET | -| `/api/config` | Mettre à jour la configuration | POST | -| `/api/diagnostics` | Statistiques index, mémoire, moteur de recherche | GET | +| Endpoint | Description | Méthode | Auth | +|----------|-------------|---------|------| +| `/api/health` | Health check (status, version, stats) | GET | Non | +| `/api/auth/status` | Statut auth (activé, utilisateurs présents) | GET | Non | +| `/api/auth/login` | Connexion (retourne access token + cookie refresh) | POST | Non | +| `/api/auth/refresh` | Renouveler l'access token via cookie refresh | POST | Cookie | +| `/api/auth/logout` | Déconnexion + révocation refresh token | POST | Oui | +| `/api/auth/me` | Infos utilisateur courant | GET | Oui | +| `/api/auth/change-password` | Changer son mot de passe | POST | Oui | +| `/api/auth/admin/users` | Lister / créer des utilisateurs | GET/POST | Admin | +| `/api/auth/admin/users/{u}` | Modifier / supprimer un utilisateur | PATCH/DELETE | Admin | +| `/api/vaults` | Liste des vaults (filtrée par permissions) | GET | Oui | +| `/api/browse/{vault}?path=` | Navigation dans les dossiers | GET | Oui | +| `/api/file/{vault}?path=` | Contenu rendu d'un fichier | GET | Oui | +| `/api/file/{vault}/raw?path=` | Contenu brut d'un fichier | GET | Oui | +| `/api/file/{vault}/download?path=` | Téléchargement d'un fichier | GET | Oui | +| `/api/file/{vault}/save?path=` | Sauvegarder un fichier | PUT | Oui | +| `/api/file/{vault}?path=` | Supprimer un fichier | DELETE | Oui | +| `/api/search/advanced` | Recherche avancée TF-IDF | GET | Oui | +| `/api/suggest` / `/api/tags/suggest` | Autocomplétion | GET | Oui | +| `/api/tags?vault=` | Tags uniques avec compteurs | GET | Oui | +| `/api/index/reload` | Force un re-scan des vaults | GET | Admin | +| `/api/events` | Flux SSE temps réel | GET | Oui | +| `/api/vaults/add` / `/api/vaults/{name}` | Gestion dynamique des vaults | POST/DELETE | Admin | +| `/api/image/{vault}?path=` | Servir une image | GET | Oui | +| `/api/config` | Lire / écrire la configuration | GET/POST | Oui/Admin | +| `/api/diagnostics` | Statistiques index et mémoire | GET | Admin | + +> Quand `OBSIGATE_AUTH_ENABLED=false`, tous les endpoints sont accessibles sans token. > Tous les endpoints exposent des schémas Pydantic documentés. La doc interactive est disponible sur `/docs` (Swagger UI). @@ -541,11 +636,12 @@ Ces paramètres sont configurables via l'interface (Settings) ou l'API `/api/con ## 🏗️ Stack technique - **Backend** : Python 3.11 + FastAPI 0.110 + Uvicorn +- **Auth** : python-jose (JWT HS256) + argon2-cffi (Argon2id) - **File Watcher** : watchdog 4.x (inotify natif + fallback polling) - **Frontend** : Vanilla JS + HTML + CSS (zéro framework, zéro build) - **Rendu Markdown** : mistune 3.x - **Image Docker** : python:3.11-slim (multi-stage) -- **Base de données** : Aucune (index en mémoire uniquement) +- **Stockage utilisateurs** : JSON local (`data/users.json`) — aucune base de données - **Architecture** : SPA + API REST + SSE --- @@ -598,13 +694,23 @@ ObsiGate/ │ ├── indexer.py # Scan des vaults, index en mémoire, lookup table │ ├── search.py # Moteur de recherche fulltext avec scoring │ ├── watcher.py # Surveillance fichiers (watchdog + debounce) +│ ├── auth/ # Module d'authentification +│ │ ├── password.py # Argon2id hashing +│ │ ├── jwt_handler.py # Tokens JWT + révocation +│ │ ├── user_store.py # CRUD users.json (atomique) +│ │ ├── middleware.py # Dépendances FastAPI auth +│ │ └── router.py # Endpoints /api/auth/* +│ ├── create_admin.py # CLI gestion utilisateurs │ └── requirements.txt ├── frontend/ # Interface web (Vanilla JS, zéro framework) -│ ├── index.html # Page SPA + modales (aide, config, éditeur) -│ ├── app.js # Logique SPA, gestion d'état, API client +│ ├── index.html # Page SPA + écran de login +│ ├── app.js # Logique SPA, AuthManager, AdminPanel │ └── style.css # Styles (CSS variables, thèmes, responsive) +├── data/ # Données persistantes (créé au démarrage) +│ ├── users.json # Utilisateurs (hashés Argon2id) +│ └── secret.key # Clé secrète JWT (512 bits) ├── Dockerfile # Multi-stage, healthcheck, non-root -├── docker-compose.yml # Déploiement avec healthcheck +├── docker-compose.yml # Déploiement avec healthcheck et auth env vars ├── build.sh # Build multi-platform (amd64/arm64/arm/v7/i386) └── CONTRIBUTING.md # Guide de contribution ``` @@ -631,6 +737,34 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les ## 📝 Changelog +### v1.4.0 (2026) + +**Authentification & Contrôle d'accès** +- Système d'authentification JWT (HS256) optionnel — désactivé par défaut, activation via `OBSIGATE_AUTH_ENABLED=true` +- Hachage des mots de passe **Argon2id** (paramètres OWASP 2024 : time_cost=2, mem=64MB) +- Sessions avec **access token (1h)** + **refresh token (7j, HttpOnly cookie)** et révocation +- Contrôle d'accès **par vault** : chaque utilisateur accède uniquement à ses vaults assignées +- Bootstrap automatique du compte admin au premier démarrage (mot de passe généré et affiché dans les logs) +- CLI de gestion des utilisateurs : `python backend/create_admin.py create|list|delete` +- Interface d'administration dans la SPA : création/modification/suppression d'utilisateurs avec sélection de vaults +- Protection brute-force : verrouillage 15 min après 5 tentatives échouées +- Réponses en temps constant sur le login pour éviter l'énumération d'utilisateurs +- Token de rafraîchissement révocable (persisté dans `data/revoked_tokens.json`) +- Clé secrète JWT auto-générée (512 bits) et persistée dans `data/secret.key` + +**Sécurité (headers)** +- `Content-Security-Policy` sur toutes les réponses +- `X-Frame-Options: SAMEORIGIN` +- `X-Content-Type-Options: nosniff` +- `X-XSS-Protection: 1; mode=block` +- `Referrer-Policy: strict-origin-when-cross-origin` + +**Frontend** +- Écran de connexion avec glassmorphism (avant chargement de l'app si auth activée) +- Menu utilisateur dans le header (nom affiché, bouton admin, déconnexion) +- Rafraîchissement silencieux de l'access token (via cookie refresh) +- Compatibilité totale : `OBSIGATE_AUTH_ENABLED=false` → comportement identique à v1.3.x + ### v1.3.0 (2025) **Synchronisation temps réel** @@ -717,4 +851,4 @@ Ce projet est sous licence **MIT** - voir le fichier [LICENSE](LICENSE) pour les --- -*Projet : ObsiGate | Version : 1.2.0 | Dernière mise à jour : 2025* +*Projet : ObsiGate | Version : 1.4.0 | Dernière mise à jour : 2026* diff --git a/backend/requirements.txt b/backend/requirements.txt index 58cd903..94f2171 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -7,3 +7,4 @@ aiofiles==23.2.1 watchdog>=4.0.0 argon2-cffi>=23.1.0 python-jose[cryptography]>=3.3.0 +cryptography>=3.4.0,<42.0.0