feat: move user management to protected config file

This commit is contained in:
2026-07-28 13:46:05 +02:00
parent 2276a124b8
commit 3b54f5ecc0
6 changed files with 368 additions and 86 deletions
+57 -26
View File
@@ -32,31 +32,9 @@ auth = HTTPBasicAuth()
# CONFIGURATION
# ============================================================================
# Benutzer für Beta-Phase (in Produktion aus env-Variablen laden!)
BETA_USERS = {
"beta": {
"password_hash": "pbkdf2:sha256:600000$HNtF3VdZKdmtg8Vw$8f976a99a457c5e924916dc6f735dd631042c8c126af8d4bda9abcd7532d904f"
},
"naue": {
"password_hash": "pbkdf2:sha256:600000$RVPiW2mYXJJIp3d7$0f838b8619d386e2da57e60ae8ccb944bb0f9f63d81ce3d174ec3034b0abcd19"
},
"cniehues": {
"password_hash": "pbkdf2:sha256:600000$iNmhakf34xk26xrz$ae78846461370c52b7f56fad5ac0ae8e33860d6f00075ac78e3a5b8c31d51a3d"
},
"lvollmert": {
"password_hash": "pbkdf2:sha256:600000$8FGop5ymmHpuIqfK$8ba223e20c514cf6bc1297435a2e7e2be81668951297f0e01ad6a931718ad7de"
},
"mtazl": {
"password_hash": "pbkdf2:sha256:600000$H19skoalhWxlLnY5$d30bfeae38470b19900648fd110978b3677607c3a161d663a7e5d5c2167a3710"
},
"controlling": {
"password_hash": "pbkdf2:sha256:600000$ksj86lrKz6nnSpXz$5161e0b5c76bd72d6b2cd04866ef49e69f463f474d7e47075171894bf3366f29"
}
}
# Logging für Auditing
LOG_FILE = "access_log.json"
BUILD_INFO_FILE = "build_info.json"
USER_CONFIG_FILE = os.path.join(app.root_path, "config", "users.json")
DOCS_DIR = os.path.join(app.root_path, "docs")
DEFAULT_DOC_LANGUAGE = "de"
USER_MANUAL_FILENAME = "user_manual.md"
@@ -129,14 +107,67 @@ def inject_build_info():
# AUTHENTICATION
# ============================================================================
class UserConfigError(RuntimeError):
"""Raised when the user configuration is missing or invalid."""
def load_user_hashes(path=USER_CONFIG_FILE):
"""Load user password hashes from config/users.json."""
command_hint = "Run python3 scripts/manage_users.py to initialize or repair it."
if not os.path.exists(path):
raise UserConfigError(
f"User config file is missing: {path}. {command_hint}"
)
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
except json.JSONDecodeError as e:
raise UserConfigError(
f"Invalid JSON in user config file {path}: {e}. {command_hint}"
) from e
except Exception as e:
raise UserConfigError(
f"Could not read user config file {path}: {e}. {command_hint}"
) from e
if not isinstance(data, dict):
raise UserConfigError(
f"Invalid user config file {path}: root element must be an object. "
f"{command_hint}"
)
if not data:
raise UserConfigError(
f"User config file {path} does not contain any users. {command_hint}"
)
users = {}
for username, value in data.items():
if not isinstance(username, str) or not username.strip():
raise UserConfigError(
f"Invalid user config file {path}: usernames must be strings. "
f"{command_hint}"
)
if not isinstance(value, str) or not value.strip():
raise UserConfigError(
f"Invalid user config file {path}: password hash for "
f"user {username!r} must be a string. {command_hint}"
)
users[username.strip()] = value.strip()
return users
USER_PASSWORD_HASHES = load_user_hashes()
@auth.verify_password
def verify_password(username, password):
"""Verify HTTP Basic Auth credentials"""
user_config = BETA_USERS.get(username)
if not user_config:
password_hash = USER_PASSWORD_HASHES.get(username)
if not password_hash:
return None
password_hash = user_config.get("password_hash")
if password_hash and check_password_hash(password_hash, password):
return username
return None