From 06e532293115a3db50d114b3fc3a823d2b321720 Mon Sep 17 00:00:00 2001 From: hansi Date: Sat, 29 Nov 2025 16:27:34 +0100 Subject: [PATCH] Add initialize admin-account in migration --- README.md | 4 +++ app/auth.py | 1 + app/auth_core.py | 28 ++++++++++++++++++++ app/main.py | 66 ++++++++++++++++++++++++++++++++++++++++++++--- app/migrate.py | 41 +++++++++++++++++++++++++++-- data/app.db | Bin 24576 -> 24576 bytes 6 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 app/auth_core.py diff --git a/README.md b/README.md index 0b79f56..6be8bfe 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,10 @@ APP_ENV = os.environ.get("APP_ENV", "dev") Dieses Projekt verwendet SQLite für die interne App-Datenbank. Das Schema wird vollständig über SQL-Migrationsdateien verwaltet, die automatisch in der richtigen Reihenfolge ausgeführt werden. +**WICHTIG:** + +Bei der ersten Initialisierung muss ein admin-Passwort vergeben werden. Beim erstmaligen Anmelden an der App muss man sich dann als "adim" mit dem vergebenen Passwort anmelden. In der App können dann User angelegt. + ### Ordnerstruktur ```bash diff --git a/app/auth.py b/app/auth.py index 55f7562..ff57039 100644 --- a/app/auth.py +++ b/app/auth.py @@ -89,5 +89,6 @@ def authed_header(): st.sidebar.write(f"Angemeldet als **{username}** ({role})") if st.sidebar.button("Logout"): + st.session_state.pop("app_started_logged", None) clear_session_user() st.rerun() \ No newline at end of file diff --git a/app/auth_core.py b/app/auth_core.py new file mode 100644 index 0000000..ca5e678 --- /dev/null +++ b/app/auth_core.py @@ -0,0 +1,28 @@ +from contextlib import closing +import bcrypt + +from app.db import get_conn + +def verify_user(username: str, password: str): + """Prüft Username/Passwort gegen die users-Tabelle.""" + with closing(get_conn()) as conn: + row = conn.execute( + "SELECT password_hash, role FROM users WHERE username = ?", + (username,), + ).fetchone() + if not row: + return False, None + stored_hash, role = row + + # stored_hash ist BLOB -> bytes + ok = bcrypt.checkpw(password.encode("utf-8"), stored_hash) + return (ok, role) if ok else (False, None) + +def get_role_for_user(username: str) -> str | None: + """Liest nur die Rolle aus der DB (z.B. wenn du später OIDC nimmst).""" + with closing(get_conn()) as conn: + row = conn.execute( + "SELECT role FROM users WHERE username = ?", + (username,), + ).fetchone() + return row[0] if row else None \ No newline at end of file diff --git a/app/main.py b/app/main.py index 58d55d1..af80c29 100644 --- a/app/main.py +++ b/app/main.py @@ -1,10 +1,70 @@ +import streamlit as st from version import __version__ from logging_config import setup_logging import os -from migrate import apply_migrations +from auth import login_view, authed_header, current_user, create_user, clear_session_user APP_ENV = os.environ.get("APP_ENV", "dev") logger = setup_logging(APP_ENV) -logger.info(f"Starting app in {APP_ENV} mode - APP-Version {__version__}") -apply_migrations() \ No newline at end of file + + +def content_for(role: str, username: str): + st.header("Dashboard") + st.info(f"Willkommen, {username}!") + + if role == "admin": + st.subheader("Admin-Bereich") + st.write("Nur Admins sehen das hier.") + with st.expander("Neuen Nutzer anlegen"): + new_u = st.text_input("Neuer Username", key="new_u") + new_p = st.text_input("Neues Passwort", type="password", key="new_p") + new_role = st.selectbox("Rolle", ["user", "admin"], key="new_role") + if st.button("Anlegen"): + if new_u and new_p: + ok = create_user(new_u.strip(), new_p, new_role) + st.success("Nutzer angelegt.") if ok else st.error( + "Username bereits vorhanden." + ) + else: + st.warning("Bitte Username und Passwort eingeben.") + + st.subheader("Dein Bereich") + st.write(f"Personalisierter Content für **{username}**.") + # hier: weitere Seite(n), DB-Zugriffe etc. + +def main(): + if "app_started_logged" not in st.session_state: + logger.info(f"Starting app in {APP_ENV} mode - APP-Version {__version__}") + st.session_state["app_started_logged"] = True + # logger.info(f"User Starting app in {APP_ENV} mode - APP-Version {__version__}") + st.set_page_config( + page_title="Intranet-Portal", + page_icon="🔒", + layout="centered", + ) + # Falls kein Login: Login-View anzeigen und raus + if not st.session_state.get("auth"): + login_view() + return + + # Ab hier sind wir eingeloggt + authed_header() + username, role = current_user() + content_for(role, username) + + st.write(st.session_state) + + if st.sidebar.button("set state"): + st.session_state["app_started_logged"] = True + #clear_session_user() + st.write(st.session_state) + #st.rerun() + if st.sidebar.button("delete state"): + st.session_state.pop("app_started_logged", None) + #clear_session_user() + st.write(st.session_state) + #st.rerun() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/app/migrate.py b/app/migrate.py index 04247c6..03c77f2 100644 --- a/app/migrate.py +++ b/app/migrate.py @@ -4,6 +4,9 @@ from pathlib import Path import logging from logging_config import setup_logging import os +from contextlib import closing +import getpass +from auth import create_user APP_ENV = os.environ.get("APP_ENV", "dev") logger = setup_logging(APP_ENV) @@ -17,7 +20,7 @@ BASE_DIR = Path(__file__).resolve().parents[1] DB_DIR = BASE_DIR / "data" DB_PATH = DB_DIR / "app.db" MIGRATIONS_DIR = BASE_DIR / "migrations" - +ADMIN_USERNAME = "admin" def get_connection() -> sqlite3.Connection: @@ -74,11 +77,45 @@ def apply_migrations(): # hier nicht weiter machen raise logger.info(f"Migrationen abgeschlossen. DB: {DB_PATH}") - #print(f"Migrationen abgeschlossen. DB: {DB_PATH}") + + create_admin_user() finally: conn.close() +def admin_exists() -> bool: + """Prüft, ob der Admin-User bereits existiert.""" + with closing(get_connection()) as conn: + row = conn.execute( + "SELECT 1 FROM users WHERE username = ?", + (ADMIN_USERNAME,), + ).fetchone() + return row is not None + + +def create_admin_user(): + if admin_exists(): + logger.info("Adminkonto existiert bereits! Kein initiales Konto angelegt.") + return + logger.info("Adminkonto wird angelegt ...") + + pw1 = getpass.getpass("Passwort: ") + pw2 = getpass.getpass("Passwort wiederholen: ") + + if pw1 != pw2: + logger.warning("Passwörter stimmen nicht überein! Abbruch.") + return + + ok = create_user(ADMIN_USERNAME, pw1, role="admin") + + if ok: + logger.info(f"Admin-Benutzer '{ADMIN_USERNAME}' wurde angelegt.") + else: + # Sollte eigentlich nicht passieren, weil wir vorher geprüft haben, + # aber falls z.B. Parallelzugriff o.Ä. + logger.info(f"Admin-Benutzer '{ADMIN_USERNAME}' konnte nicht angelegt werden.") + + if __name__ == "__main__": apply_migrations() \ No newline at end of file diff --git a/data/app.db b/data/app.db index 1a9750e1bf6a5c4f497eed55e8540016a552d13f..8e32fc2df264bdb9c40b2a29587d986f738f0cdd 100644 GIT binary patch delta 310 zcmZoTz}Rqrae_1>^F$eEM&^wP)8x4<6%3863{0(zjW#K;2yig*+c5CY< z1%7T}W*M1aE4UaK7?}9qGVp)rf4f=G;32;(C$l&s*!fHx%;KD2MZ7>ojQn32_`d>` SyyoZTWCW@yElw>eW&!}}mQ&LJ delta 75 zcmZoTz}Rqrae_1><3t%}M#hZ^)8x4<6pW0lj0~*|Og1U72rw}SY!(c7!as3>7#kM@ K5Fog`C|m&Gf)d^U