From 5fceae9a45d7cf0dcbc51d175de73aac4608198d Mon Sep 17 00:00:00 2001 From: hansi Date: Sat, 22 Nov 2025 14:54:34 +0100 Subject: [PATCH] initial commit --- login.py | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ users.db | Bin 0 -> 16384 bytes 2 files changed, 110 insertions(+) create mode 100644 login.py create mode 100644 users.db diff --git a/login.py b/login.py new file mode 100644 index 0000000..eaa8797 --- /dev/null +++ b/login.py @@ -0,0 +1,110 @@ +import streamlit as st +import sqlite3 +import bcrypt +from contextlib import closing + +DB_PATH = "users.db" + +# ---------- DB helpers ---------- +def get_conn(): + return sqlite3.connect(DB_PATH, check_same_thread=False) + +def init_db(): + 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, + password_hash BLOB NOT NULL, + role TEXT NOT NULL DEFAULT 'user' + ) + """) + +def create_user(username: str, password: str, role: str = "user") -> bool: + pw_hash = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) + try: + with closing(get_conn()) as conn, conn: + conn.execute( + "INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)", + (username, pw_hash, role), + ) + return True + except sqlite3.IntegrityError: + return False + +def verify_user(username: str, password: str): + 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 + ok = bcrypt.checkpw(password.encode("utf-8"), stored_hash) + return ok, role if ok else (False, None) + +# ---------- UI helpers ---------- +def login_view(): + st.title("Intranet Login") + with st.form("login"): + u = st.text_input("Username") + p = st.text_input("Passwort", type="password") + submitted = st.form_submit_button("Anmelden") + if submitted: + ok, role = verify_user(u.strip(), p) + if ok: + st.session_state["auth"] = True + st.session_state["username"] = u.strip() + st.session_state["role"] = role + st.success("Erfolgreich angemeldet.") + st.rerun() + else: + st.error("Login fehlgeschlagen.") + +def authed_header(): + st.sidebar.write(f"Angemeldet als **{st.session_state['username']}** ({st.session_state['role']})") + if st.sidebar.button("Logout"): + for k in ["auth", "username", "role"]: + st.session_state.pop(k, None) + st.rerun() + +def content_for(role: str, username: str): + # Beispiel: rollen- und nutzerbezogene Inhalte + 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 würdest du z. B. Daten aus einer Fach-DB nutzerbezogen filtern/anzeigen. + +# ---------- App start ---------- +def main(): + st.set_page_config(page_title="Intranet-Portal", page_icon="🔒", layout="centered") + init_db() + + # Optional: Initialer Admin (einmalig manuell auskommentieren, starten, dann wieder aus) + #create_user("admin", "change_me", role="admin") + + if not st.session_state.get("auth"): + login_view() + else: + authed_header() + content_for(st.session_state["role"], st.session_state["username"]) + +if __name__ == "__main__": + main() diff --git a/users.db b/users.db new file mode 100644 index 0000000000000000000000000000000000000000..feec23460efbbe080a4f8c40490171cf3c02dd1b GIT binary patch literal 16384 zcmeI(J#X4T7zgmPjcACJxJ%{r2BZWfN^5AFER|}f4p1BOW|NQw8SK;!m^TASQ+0^? zjru+EQ94%b*s}{mh}xlB+y5iq9cS(y?4P{cAvfzrF!SieXc{^*S|E=J$C1~R5<&!4 zQmm|kV;|Q0tM7i*3gqBsvL${e!uAKUeJ9?r1_1#GKmY;|fB*y_009U<00RG)Kut(X zdwX1a=5z;MG#RjUXeaU}b8pyN@1~9$nqq0xQdC2uYh!nZPU!8knYr7K+#^YnxKBTG z&LeLcB^7^fesYTLZql^~Tv|3Qt*A9KhjW*S_HdSn^S~gjRie_5-4`#*L;wzw* zc4krAEZ1#~npKOMwqZORK6aw$YBY5_z7zRWHLB`w22V$WW!_an`bIlZY{Q~E@$c^3 zyP8~%PfM>Pjs!#3yIv|e^Vulrcaq!g$Vn5g?Kk4bZHpCN^oaPGRR{<`00Izz00bZa z0SG_<0uX=z1pZt==hH;GO+E3Qp&MlL-K?C?s$JV{s;$Fue$dbB#k$^zi)IM-uMm1-mpZfmd zvR1Xv7h$3LtkEhJ#&zBDFG^RkY#z!_&-<0p^(0BK$?l35zYy^os}K-?00bZa0SG_< z0uX=z1Rwwb2>ks58A0NbJqUh7kTS{oKmPxp6TcF6ARqt%2tWV=5P$##AOHafKmY;| Q_?HAS9NP#;-UHx&03_wI_y7O^ literal 0 HcmV?d00001