From 111d0d2756ed515a983d19429ff6d609bbea0d6b Mon Sep 17 00:00:00 2001 From: handi Date: Sun, 30 Nov 2025 14:55:02 +0100 Subject: [PATCH] Add some new fields for users table --- app/{auth.py => auth__.py} | 0 app/auth_core.py | 50 ++++++---- app/main.py | 90 ++++++++++++------ app/main__.py | 59 ++++++++++++ app/main_authenticator.py | 87 ----------------- app/migrate.py | 11 ++- config/auth.yaml | 4 +- data/app.db | Bin 32768 -> 32768 bytes .../20251129_162900_add_table_sessions.sql | 3 + .../20251130_124700_add_col_email_users.sql | 8 ++ .../20251130_1411_add_col_name_users.sql | 11 +++ 11 files changed, 184 insertions(+), 139 deletions(-) rename app/{auth.py => auth__.py} (100%) create mode 100644 app/main__.py delete mode 100644 app/main_authenticator.py create mode 100644 migrations/20251130_124700_add_col_email_users.sql create mode 100644 migrations/20251130_1411_add_col_name_users.sql diff --git a/app/auth.py b/app/auth__.py similarity index 100% rename from app/auth.py rename to app/auth__.py diff --git a/app/auth_core.py b/app/auth_core.py index 4e9fc5a..7638b66 100644 --- a/app/auth_core.py +++ b/app/auth_core.py @@ -10,31 +10,39 @@ from db import get_conn # DB Initialisierung: `users`-Tabelle # --------------------------------------------------------------------------- -def init_auth_db(): - """ - Legt die users-Tabelle an. - WICHTIG: password_hash wird als TEXT gespeichert, damit sowohl - streamlit-authenticator als auch dein eigener bcrypt-Code kompatibel sind. - """ - with closing(get_conn()) as conn, conn: - conn.execute( - """ - CREATE TABLE IF NOT EXISTS users ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - username TEXT UNIQUE NOT NULL, - email TEXT, - password_hash TEXT NOT NULL, - role TEXT NOT NULL DEFAULT 'user' - ) - """ - ) +# def init_auth_db(): +# """ +# Legt die users-Tabelle an. +# WICHTIG: password_hash wird als TEXT gespeichert, damit sowohl +# streamlit-authenticator als auch dein eigener bcrypt-Code kompatibel sind. +# """ +# with closing(get_conn()) as conn, conn: +# conn.execute( +# """ +# CREATE TABLE IF NOT EXISTS users ( +# id INTEGER PRIMARY KEY AUTOINCREMENT, +# username TEXT UNIQUE NOT NULL, +# email TEXT, +# password_hash TEXT NOT NULL, +# role TEXT NOT NULL DEFAULT 'user' +# ) +# """ +# ) # --------------------------------------------------------------------------- # Benutzer anlegen # --------------------------------------------------------------------------- -def create_user(username: str, password: str, role: str = "user", email: str | None = None) -> bool: +def create_user( + username: str, + password: str, + role: str = "user", + email: str | None = None, + firstname: str | None = None, + lastname: str | None = None +) -> bool: + """ Passwort wird als bcrypt-Hash (TEXT) gespeichert. """ @@ -43,8 +51,8 @@ def create_user(username: str, password: str, role: str = "user", email: str | N try: with closing(get_conn()) as conn, conn: conn.execute( - "INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, ?)", - (username, email, pw_hash, role), + "INSERT INTO users (username, email, password_hash, role, firstname, lastname) VALUES (?, ?, ?, ?, ?, ?)", + (username, email, pw_hash, role, firstname, lastname), ) return True except Exception: diff --git a/app/main.py b/app/main.py index 3240a60..3b6ab32 100644 --- a/app/main.py +++ b/app/main.py @@ -1,58 +1,94 @@ -# app/main.py import streamlit as st - -from auth import ( - init_auth_db, - ensure_logged_in, - authed_header, - current_user, - create_user, # falls du Admin-Funktion brauchst -) +import yaml +from yaml.loader import SafeLoader +import streamlit_authenticator as stauth +from auth_core import load_credentials_from_db, get_role_for_user, create_user +from version import __version__ -def content_for(role: str, username: str): - st.header("Dashboard") +def content_for(username: str, role: str): + st.header("Controlling-Portal") 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_fname = st.text_input("Vorname", key="new_fname") + new_lname = st.text_input("Nachname", key="new_lname") + new_email = st.text_input("E-Mail", key="new_email") 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." - ) + ok = create_user(new_u.strip(), new_p, new_role, new_email, new_fname, new_lname) + st.success("Nutzer angelegt.") if ok else st.error("Username bereits vorhanden oder Fehler.") else: st.warning("Bitte Username und Passwort eingeben.") st.subheader("Dein Bereich") st.write(f"Personalisierter Content für **{username}**.") - # fachliche Inhalte … def main(): st.set_page_config( - page_title="Intranet-Portal", + page_title=f"Co-App Start - V{__version__}", page_icon="🔒", layout="centered", ) - init_auth_db() + # DB-Struktur sicherstellen + # init_auth_db() - # hier wird entweder: - # - st.session_state genutzt, oder - # - ?session_id=... aus URL geprüft, oder - # - Login-Form gezeigt (und Script gestoppt) - ensure_logged_in() + # --- Config laden (Cookie, etc.) --- + with open("config/auth.yaml", "r", encoding="utf-8") as f: + base_config = yaml.load(f, Loader=SafeLoader) - authed_header() - username, role = current_user() - content_for(role, username) + # --- Credentials dynamisch aus DB laden --- + db_creds = load_credentials_from_db() + base_config["credentials"] = db_creds + + authenticator = stauth.Authenticate( + base_config["credentials"], + base_config["cookie"]["name"], + base_config["cookie"]["key"], + base_config["cookie"]["expiry_days"], + #base_config.get("preauthorized", {}), + ) + + #name, auth_status, username = authenticator.login(location="main", key="Login") + # login_result = authenticator.login(location="main", key="Login") + + # if login_result is None: + # st.error("Login-Initialisierung fehlgeschlagen (keine gültigen Credentials?).") + # return + + # name, auth_status, username = login_result + authenticator.login(location="main", key="Login") + + auth_status = st.session_state.get("authentication_status") + name = st.session_state.get("name") + username = st.session_state.get("username") + + + + if auth_status is False: + st.error("Login fehlgeschlagen.") + return + + if auth_status is None: + st.warning("Bitte Benutzername und Passwort eingeben.") + return + + # ---- Ab hier eingeloggt (persistenter Cookie) ---- + role = get_role_for_user(username) + + authenticator.logout("Logout", "sidebar") + st.sidebar.write(f"Angemeldet als **{name}** ({username}, Rolle: {role})") + + content_for(username, role) if __name__ == "__main__": diff --git a/app/main__.py b/app/main__.py new file mode 100644 index 0000000..3240a60 --- /dev/null +++ b/app/main__.py @@ -0,0 +1,59 @@ +# app/main.py +import streamlit as st + +from auth import ( + init_auth_db, + ensure_logged_in, + authed_header, + current_user, + create_user, # falls du Admin-Funktion brauchst +) + + +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}**.") + # fachliche Inhalte … + + +def main(): + st.set_page_config( + page_title="Intranet-Portal", + page_icon="🔒", + layout="centered", + ) + + init_auth_db() + + # hier wird entweder: + # - st.session_state genutzt, oder + # - ?session_id=... aus URL geprüft, oder + # - Login-Form gezeigt (und Script gestoppt) + ensure_logged_in() + + authed_header() + username, role = current_user() + content_for(role, username) + + +if __name__ == "__main__": + main() diff --git a/app/main_authenticator.py b/app/main_authenticator.py deleted file mode 100644 index 323ca19..0000000 --- a/app/main_authenticator.py +++ /dev/null @@ -1,87 +0,0 @@ -# app/main.py -import streamlit as st -import yaml -from yaml.loader import SafeLoader -import streamlit_authenticator as stauth - -from auth_core import ( - init_auth_db, - load_credentials_from_db, - get_role_for_user, - create_user, -) -from version import __version__ - - -def content_for(username: str, role: 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_email = st.text_input("E-Mail", key="new_email") - 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, new_email.strip() or None) - st.success("Nutzer angelegt.") if ok else st.error("Username bereits vorhanden oder Fehler.") - else: - st.warning("Bitte Username und Passwort eingeben.") - - st.subheader("Dein Bereich") - st.write(f"Personalisierter Content für **{username}**.") - - -def main(): - st.set_page_config( - page_title=f"Intranet-Portal v{__version__}", - page_icon="🔒", - layout="centered", - ) - - # DB-Struktur sicherstellen - init_auth_db() - - # --- Config laden (Cookie, etc.) --- - with open("config/auth.yaml", "r", encoding="utf-8") as f: - base_config = yaml.load(f, Loader=SafeLoader) - - # --- Credentials dynamisch aus DB laden --- - db_creds = load_credentials_from_db() - base_config["credentials"] = db_creds - - authenticator = stauth.Authenticate( - base_config["credentials"], - base_config["cookie"]["name"], - base_config["cookie"]["key"], - base_config["cookie"]["expiry_days"], - base_config.get("preauthorized", {}), - ) - - name, auth_status, username = authenticator.login("Login", "main") - - if auth_status is False: - st.error("Login fehlgeschlagen.") - return - - if auth_status is None: - st.warning("Bitte Benutzername und Passwort eingeben.") - return - - # ---- Ab hier eingeloggt (persistenter Cookie) ---- - role = get_role_for_user(username) - - authenticator.logout("Logout", "sidebar") - st.sidebar.write(f"Angemeldet als **{name}** ({username}, Rolle: {role})") - - content_for(username, role) - - -if __name__ == "__main__": - main() diff --git a/app/migrate.py b/app/migrate.py index 03c77f2..20b2f2e 100644 --- a/app/migrate.py +++ b/app/migrate.py @@ -6,7 +6,7 @@ from logging_config import setup_logging import os from contextlib import closing import getpass -from auth import create_user +from auth_core import create_user APP_ENV = os.environ.get("APP_ENV", "dev") logger = setup_logging(APP_ENV) @@ -107,7 +107,14 @@ def create_admin_user(): logger.warning("Passwörter stimmen nicht überein! Abbruch.") return - ok = create_user(ADMIN_USERNAME, pw1, role="admin") + ok = create_user( + username=ADMIN_USERNAME, + password=pw1, + role="admin", + email="admin@co_app", + firstname="co_app", + lastname="admin" + ) if ok: logger.info(f"Admin-Benutzer '{ADMIN_USERNAME}' wurde angelegt.") diff --git a/config/auth.yaml b/config/auth.yaml index cd0733b..df1b230 100644 --- a/config/auth.yaml +++ b/config/auth.yaml @@ -1,7 +1,7 @@ cookie: expiry_days: 7 - key: "please_change_this_cookie_signing_key" - name: "intranet_session" + key: "tHr1FXOYg-xsYBGVu7amRrY8PAdC2gM_zyjPEPPkPgG8tAsLY4QGPZvCvMS9win0D94uawAxsmZjN6O_VlRhUQ" + name: "co_app-session" preauthorized: emails: [] diff --git a/data/app.db b/data/app.db index da176b0e1215578af57b07bb8fc326f5f57f5a74..43962dc62501ea80da47dd3ef96e5c9c7ebd92f2 100644 GIT binary patch delta 721 zcmZo@U}|V!njkI6#=yY90mLxCHc`h|nvFrP_a`s!M+SCoX9j+0{@vWpd>?q{^OW)C zZEQTr#noiM%q}i2&e&o<`8V$uQyqoW+{DZrg_6{Y5{0~cATG_x(NRdtEGjO^OUz9L zOXw)%BqDJ&H<$5LGqUjiVE8|=@%QErd?JiIEc_}A{1^GB^T+V3Y!(y{;jcGiVfHmP zGB7eVG&DAdH#9Lcj89BSiBHbYi3b@RUs{}6R1A{WH8j*UHc&7$wlcP`GBL1ZW)4Qu zXJle-U;x(%b}UY<7EH{6P_0Ij7t1SiBN;pSqP&VU5}ykgYApPP4E#^|_wi5TF9bR| zk>6K^*_V-!n_1MEgOL>BbRhHOefl13%nbZ3{LDbpzw_1CGjTB(i#M{08;Xmg`rOpe z%ET}uF|RmN#VAR|&`2fSxyZdN+bush)6}KJuQ1yy%+1}|KPWgT+oUKwGt9i$!>2gJ zG{ZHqGSSO7JT=(J%Q3|}F(o%M4{VA9nAFWnEzwI!^#GacR+LzuS5?Ibb%ClRnhQW- zZwWR4>H_b`OtWx{Wc?H$-}3D8f~rcxP?ylubg#1XVy`fdvVwdwvto+^udo95Q0LGf tL&w07NW);T0bs%*IX^zJpa4RGMYuLII{24&o=-7 delta 391 zcmZo@U}|V!njkI6&cMLH0mLxCJW$fs*soe2Ln4#83VsG|8Cxod>?q{^OW)C zZ59;x!@aqTr<#$gv4)vlTwI*7IefA&_ZLRZ%^&zg7&(~ur!(+h