Add initialize admin-account in migration
This commit is contained in:
@@ -71,6 +71,10 @@ APP_ENV = os.environ.get("APP_ENV", "dev")
|
|||||||
Dieses Projekt verwendet SQLite für die interne App-Datenbank.
|
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.
|
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
|
### Ordnerstruktur
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -89,5 +89,6 @@ def authed_header():
|
|||||||
|
|
||||||
st.sidebar.write(f"Angemeldet als **{username}** ({role})")
|
st.sidebar.write(f"Angemeldet als **{username}** ({role})")
|
||||||
if st.sidebar.button("Logout"):
|
if st.sidebar.button("Logout"):
|
||||||
|
st.session_state.pop("app_started_logged", None)
|
||||||
clear_session_user()
|
clear_session_user()
|
||||||
st.rerun()
|
st.rerun()
|
||||||
28
app/auth_core.py
Normal file
28
app/auth_core.py
Normal file
@@ -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
|
||||||
66
app/main.py
66
app/main.py
@@ -1,10 +1,70 @@
|
|||||||
|
import streamlit as st
|
||||||
from version import __version__
|
from version import __version__
|
||||||
from logging_config import setup_logging
|
from logging_config import setup_logging
|
||||||
import os
|
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")
|
APP_ENV = os.environ.get("APP_ENV", "dev")
|
||||||
logger = setup_logging(APP_ENV)
|
logger = setup_logging(APP_ENV)
|
||||||
logger.info(f"Starting app in {APP_ENV} mode - APP-Version {__version__}")
|
|
||||||
|
|
||||||
apply_migrations()
|
|
||||||
|
|
||||||
|
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()
|
||||||
@@ -4,6 +4,9 @@ from pathlib import Path
|
|||||||
import logging
|
import logging
|
||||||
from logging_config import setup_logging
|
from logging_config import setup_logging
|
||||||
import os
|
import os
|
||||||
|
from contextlib import closing
|
||||||
|
import getpass
|
||||||
|
from auth import create_user
|
||||||
|
|
||||||
APP_ENV = os.environ.get("APP_ENV", "dev")
|
APP_ENV = os.environ.get("APP_ENV", "dev")
|
||||||
logger = setup_logging(APP_ENV)
|
logger = setup_logging(APP_ENV)
|
||||||
@@ -17,7 +20,7 @@ BASE_DIR = Path(__file__).resolve().parents[1]
|
|||||||
DB_DIR = BASE_DIR / "data"
|
DB_DIR = BASE_DIR / "data"
|
||||||
DB_PATH = DB_DIR / "app.db"
|
DB_PATH = DB_DIR / "app.db"
|
||||||
MIGRATIONS_DIR = BASE_DIR / "migrations"
|
MIGRATIONS_DIR = BASE_DIR / "migrations"
|
||||||
|
ADMIN_USERNAME = "admin"
|
||||||
|
|
||||||
|
|
||||||
def get_connection() -> sqlite3.Connection:
|
def get_connection() -> sqlite3.Connection:
|
||||||
@@ -74,11 +77,45 @@ def apply_migrations():
|
|||||||
# hier nicht weiter machen
|
# hier nicht weiter machen
|
||||||
raise
|
raise
|
||||||
logger.info(f"Migrationen abgeschlossen. DB: {DB_PATH}")
|
logger.info(f"Migrationen abgeschlossen. DB: {DB_PATH}")
|
||||||
#print(f"Migrationen abgeschlossen. DB: {DB_PATH}")
|
|
||||||
|
create_admin_user()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
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__":
|
if __name__ == "__main__":
|
||||||
apply_migrations()
|
apply_migrations()
|
||||||
BIN
data/app.db
BIN
data/app.db
Binary file not shown.
Reference in New Issue
Block a user