chore(07): integrate LDAP config into YAML and remove JSON files

This commit is contained in:
2026-04-23 12:58:15 +03:00
parent 7d492764d2
commit 88e5d66f36
11 changed files with 91 additions and 115 deletions

View File

@@ -73,9 +73,9 @@ A unified system for inventory management featuring web administration, offline
- **JWT**: Stateless tokens for API auth.
- **LDAP**: Primary source of truth for users in enterprise mode.
- **Password Caching**: Encrypted local cache for offline authentication.
- **CORS**: Restricted origins in production via `inventory.env`.
- **CORS**: Restricted origins in production via `config/backend.yaml`.
---
**Last Updated**: 2026-04-23
**Version**: 1.14.6
**Version**: 1.14.7

View File

@@ -1,5 +1,5 @@
{
"version": "1.14.5",
"lastUpdated": "2026-04-22",
"phase": "Phase 3 Complete - Original Image Storage for Debug"
"version": "1.14.7",
"lastUpdated": "2026-04-23",
"phase": "Phase 7 Complete - Config Consolidation"
}

View File

@@ -9,15 +9,17 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer
from jose import JWTError, jwt
from pydantic import BaseModel
from .config_loader import get_config
# Configuration
SECRET_KEY = os.environ.get("JWT_SECRET_KEY")
if not SECRET_KEY:
config = get_config()
SECRET_KEY = config.get("auth", {}).get("jwt_secret_key")
if not SECRET_KEY or SECRET_KEY == "change_me_in_production":
# Generate fallback key for dev (NOT FOR PRODUCTION)
import secrets
SECRET_KEY = secrets.token_urlsafe(32)
import sys
print(f"[WARNING] JWT_SECRET_KEY not set. Generated ephemeral key: {SECRET_KEY[:20]}...", file=sys.stderr)
print(f"[WARNING] JWT_SECRET_KEY not set or default used. Generated ephemeral key: {SECRET_KEY[:20]}...", file=sys.stderr)
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 480 # 8 hours

View File

@@ -59,8 +59,20 @@ def _apply_env_overrides(config):
"CLAUDE_API_KEY": (("ai", "claude_api_key"), str),
"BACKEND_AUTH_JWT_SECRET_KEY": (("auth", "jwt_secret_key"), str),
"JWT_SECRET_KEY": (("auth", "jwt_secret_key"), str),
"BACKEND_AUTH_LDAP_ENABLED": (("auth", "ldap_enabled"), _to_bool),
"LDAP_ENABLED": (("auth", "ldap_enabled"), _to_bool),
"BACKEND_AUTH_LDAP_SERVER": (("auth", "ldap_server"), str),
"LDAP_SERVER": (("auth", "ldap_server"), str),
"BACKEND_AUTH_LDAP_BASE_DN": (("auth", "ldap_base_dn"), str),
"LDAP_BASE_DN": (("auth", "ldap_base_dn"), str),
"BACKEND_AUTH_LDAP_USER_TEMPLATE": (("auth", "ldap_user_template"), str),
"LDAP_USER_TEMPLATE": (("auth", "ldap_user_template"), str),
"BACKEND_AUTH_LDAP_GROUPS_DN": (("auth", "ldap_groups_dn"), str),
"LDAP_GROUPS_DN": (("auth", "ldap_groups_dn"), str),
"BACKEND_AUTH_LDAP_USE_TLS": (("auth", "ldap_use_tls"), _to_bool),
"LDAP_USE_TLS": (("auth", "ldap_use_tls"), _to_bool),
"BACKEND_AUTH_LDAP_IGNORE_CERT": (("auth", "ldap_ignore_cert"), _to_bool),
"LDAP_IGNORE_CERT": (("auth", "ldap_ignore_cert"), _to_bool),
"BACKEND_AUTH_PASSWORD_CACHE_PATH": (("auth", "password_cache_path"), str),
"BACKEND_LOGGING_LOG_LEVEL": (("logging", "log_level"), str),
"LOG_LEVEL": (("logging", "log_level"), str),
@@ -130,8 +142,17 @@ def load_config() -> dict:
},
"auth": {
"jwt_secret_key": "change_me_in_production",
"ldap_enabled": False,
"ldap_server": "",
"ldap_base_dn": "",
"ldap_user_template": "uid={username},ou=people,dc=ldap,dc=lan",
"ldap_groups_dn": "ou=groups",
"ldap_use_tls": True,
"ldap_ignore_cert": False,
"ldap_role_mappings": [
{"group": "inventory_admins", "role": "admin"},
{"group": "inventory_users", "role": "user"}
],
"password_cache_path": "data/.passwords"
},
"logging": {

View File

@@ -14,6 +14,7 @@ import os
import socket
import subprocess
from .. import models, schemas, database, auth
from ..config_loader import get_config
from ..logger import log
router = APIRouter(prefix="/users", tags=["auth"])
@@ -22,20 +23,18 @@ pwd_context = CryptContext(schemes=["pbkdf2_sha256"], deprecated="auto")
def get_ldap_config():
# Priority 1: Check in DATA_DIR (for Docker production)
config_path = os.path.join(database.DATA_DIR, "config", "ldap_config.json")
if os.path.exists(config_path):
with open(config_path, "r") as f:
return json.load(f)
# Priority 2: Fallback to source-relative config (for local dev)
root_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
source_config_path = os.path.join(root_dir, "config", "ldap_config.json")
if os.path.exists(source_config_path):
with open(source_config_path, "r") as f:
return json.load(f)
return {"ldap_enabled": False}
config = get_config()
auth_config = config.get("auth", {})
return {
"ldap_enabled": auth_config.get("ldap_enabled", False),
"server_uri": auth_config.get("ldap_server"),
"base_dn": auth_config.get("ldap_base_dn"),
"user_template": auth_config.get("ldap_user_template"),
"groups_dn": auth_config.get("ldap_groups_dn"),
"use_tls": auth_config.get("ldap_use_tls", True),
"ignore_cert": auth_config.get("ldap_ignore_cert", False),
"role_mappings": auth_config.get("ldap_role_mappings", [])
}
def authenticate_ldap(username, password):

View File

@@ -1,19 +0,0 @@
{
"ldap_enabled": true,
"server_uri": "ldaps://192.168.84.107:6360",
"base_dn": "dc=ldap,dc=lan",
"user_template": "uid={username},ou=people,dc=ldap,dc=lan",
"groups_dn": "ou=groups",
"use_tls": true,
"role_mappings": [
{
"group": "inventory_admins",
"role": "admin"
},
{
"group": "inventory_users",
"role": "user"
}
],
"ignore_cert": true
}

View File

@@ -1,18 +0,0 @@
{
"ldap_enabled": false,
"server_uri": "ldap://192.168.1.100:389",
"use_tls": false,
"ignore_cert": false,
"base_dn": "dc=example,dc=com",
"user_template": "uid={username},ou=users,dc=example,dc=com",
"role_mappings": [
{
"group": "cn=inventory_admins,ou=groups,dc=example,dc=com",
"role": "admin"
},
{
"group": "cn=inventory_users,ou=groups,dc=example,dc=com",
"role": "user"
}
]
}

View File

@@ -32,13 +32,20 @@ A unified inventory management system that eliminates manual data entry through
- **Search & Filtering**: Advanced modal-based search.
- **Export/Reports**: CSV/Excel exports for compliance.
### Phase 6: Deployment & Scale (CURRENT)
### Phase 6: Deployment & Scale (COMPLETED ✓)
- **Containerization**: Full Docker support with Caddy proxy.
- **Automation**: Single-command `deploy.sh`.
- **Automation**: Single-command `deploy.sh` (now `deploy.py`).
- **Cleanup**: Consolidate documentation, remove obsolete planning artifacts.
- **Performance**: Scale testing for 10K+ items and 5+ concurrent users.
### Phase 7: Hardening & Release (PLANNED)
### Phase 7: Config Consolidation (COMPLETED ✓)
- **Centralization**: All config in `config/` folder (YAML format).
- **Automation**: Bash scripts converted to Python (`scripts/`).
- **Structure**: Clear separation of `backend.yaml`, `frontend.yaml`, `network.yaml`, `docker.yaml`, `secrets.yaml`.
- **D-06 Load Order**: Env Vars > YAML > Defaults.
- **Deprecation**: `inventory.env` completely removed.
### Phase 8: Hardening & Release (PLANNED)
- Stability monitoring, final UX refinements, production-ready runbook.
---

View File

@@ -2,68 +2,53 @@
**Active AI:** Claude Haiku 4.5 (Claude Code)
**Last Updated:** 2026-04-23
**Current Version:** v1.14.6
**Status**: 🟢 PHASE 7 PLANNED
**Current Version:** v1.14.7
**Status**: PHASE 7 COMPLETE
---
## SESSION 40 EXECUTION — Phase 7 Config Consolidation Planning
## SESSION 41 EXECUTION — Phase 7 Execution & Consolidation
### Work Completed This Session
**1. Phase 7 Planning (Config Consolidation):**
-Executed `/gsd-plan-phase 7 --skip-research`
-Replanned Phase 7 from scratch (existing 18-task .env plan was misaligned with CONTEXT decisions)
-Created 4 new comprehensive plans aligned with YAML + Python decisions:
- **Plan 07-01**: Create config/ folder structure with YAML files + examples + documentation
- **Plan 07-02**: Refactor backend config_loader.py for YAML parsing + deprecate inventory.env
- **Plan 07-03**: Convert 4 bash deployment scripts to Python (deploy.py, run_standalone.py, install_service.py, export_prod.py)
- **Plan 07-04**: Update Docker/Compose, Dockerfile, .gitignore, documentation
- ✅ All plans verified and passed gsd-plan-checker (17 tasks, 31 files, all decisions covered)
**1. Phase 7 Execution (Config Consolidation):**
-Wave 1: Created `config/` structure, 5 schema examples, 4 actual config files, and 155-line `config/README.md`.
-Wave 2 (Backend): Refactored `backend/config_loader.py` for YAML and D-06 load order. Updated `config_manager.py`, `main.py`, and `entrypoint.sh`.
-Wave 2 (Scripts): Converted `deploy.sh`, `run_standalone.sh`, `install_service.sh`, and `export_prod.sh` to secure Python scripts with YAML parsing.
- ✅ Wave 3 (Docker & Docs): Updated `docker-compose.yml` (volume mounts), `Dockerfile`, `.gitignore`, `DEPLOYMENT.md`, and `README.md`.
- ✅ Post-Wave: Refactored legacy `load_dotenv()` in `ai_vision.py`, `check_models.py`, `gemini.py`, and `claude.py`.
- ✅ Cleanup: Deleted all deprecated bash scripts and legacy `.env` files. Updated `scripts/save_version.py` for new infrastructure.
**2. Phase 7 Decisions Implemented:**
-**D-01**: YAML format standardization (backend.yaml, frontend.yaml, network.yaml, docker.yaml)
-**D-02**: Secrets management (config/secrets.yaml, git-ignored, .example files)
-**D-03**: Example files tracked in git (*.yaml.example)
-**D-04**: Complete inventory.env deprecation (no fallback)
-**D-05**: Bash → Python script conversion for deployment
-**D-06**: Backend config load order: env vars > YAML > defaults
-**D-07**: Docker Compose updated for config/ structure
-**D-08**: Comprehensive documentation (DEPLOYMENT.md, README.md, config/README.md)
**3. Plan Structure:**
- **Wave 1**: Foundation (config/ structure creation)
- **Wave 2**: Backend refactoring + Python scripts (parallel execution)
- **Wave 3**: Docker integration + final documentation (depends on Waves 1-2)
**2. Versioning & State Sync:**
-Incremented version to `v1.14.7` across all SSOT files (`VERSION.json`, `frontend/VERSION.json`, `PROJECT_ARCHITECTURE.md`, `dev_docs/PLAN.md`).
-Updated roadmap in `dev_docs/PLAN.md` marking Phase 7 as COMPLETED.
-Verified all files pass syntax checks (Python/Bash).
### Phase 7 Artifact Status
- **Plans**: `.planning/phases/07-config-consolidation/07-{01,02,03,04}-PLAN.md` ✓ Created
- **Context**: `.planning/phases/07-config-consolidation/07-CONTEXT.md`Exists (locked decisions)
- **Verification**: All 4 plans passed gsd-plan-checker ✓
- **Plans**: `.planning/phases/07-config-consolidation/07-01-PLAN.md` ✓ COMPLETED
- **Plans**: `.planning/phases/07-config-consolidation/07-02-PLAN.md`COMPLETED
- **Plans**: `.planning/phases/07-config-consolidation/07-03-PLAN.md` ✓ COMPLETED
- **Plans**: `.planning/phases/07-config-consolidation/07-04-PLAN.md` ✓ COMPLETED
- **Summaries**: `.planning/phases/07-config-consolidation/07-01,02,03,04-SUMMARY.md` ✓ Created
- **Context**: `.planning/phases/07-config-consolidation/07-CONTEXT.md` ✓ Archived
---
## NEXT STEPS
1. **Execute Phase 7**:
```bash
/gsd-execute-phase 7
```
- Wave 1: Create config/ folder with YAML files and examples
- Wave 2 (parallel): Refactor backend config loader + create Python deployment scripts
- Wave 3: Update Docker, documentation, and .gitignore
1. **Start Phase 8 (Hardening & Release)**:
- Perform end-to-end testing of the new Python deployment scripts.
- Verify Docker deployment with `python3 scripts/deploy.py production`.
- Perform final UX refinements and accessibility audits.
- Prepare production-ready runbook for v1.15.0 stable release.
2. **After Phase 7 Completion**:
- All configuration consolidated in config/ folder (YAML format)
- inventory.env deprecated and removed
- Deployment scripts converted to Python
- Docker and standalone deployments fully working with new structure
- Comprehensive documentation in place
2. **Stability Monitoring**:
- Check logs for any configuration parsing warnings.
- Ensure all environment variable overrides work as expected (D-06).
3. **Future Phases**:
- Phase 8+ (as defined in ROADMAP)
- Consider advanced config features (hot-reload, KMS encryption, config versioning) for future phases
3. **Cleanup**:
- Archive Phase 7 planning artifacts to `dev_docs/ARCHIVE_LOGS.md` or similar.
---
✓ Phase 7 planned and ready for execution.
✓ Phase 7 fully implemented, verified, and cleaned up. Ready for the next phase.

View File

@@ -1,6 +1,6 @@
{
"version": "1.13.1",
"last_build": "2026-04-23-1256",
"codename": "PhotoUI",
"version": "1.14.7",
"last_build": "2026-04-23-1400",
"codename": "ConfigCore",
"commit": "d9dffdc3"
}

View File

@@ -83,10 +83,9 @@ def main():
# 5. Create Production Bundle
print("📦 Generating production bundle...")
run_command(['./export_prod.sh'])
run_command(['python3', 'scripts/export_prod.py'])
print(f"Successfully saved version {new_version}, created branch {branch_name}, updated 'master', and generated production ZIP.")
print("Verification: check for the .zip file in the root.")
print(f"Successfully saved version {new_version}, created branch {branch_name}, updated 'master', and generated production archive in backups/.")
if __name__ == "__main__":
main()