SmartHeal commited on
Commit
41dd64c
·
verified ·
1 Parent(s): d75db03

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +23 -24
src/streamlit_app.py CHANGED
@@ -17,10 +17,10 @@ st.title("🔐 Simple Password Lookup — 2FA protected")
17
 
18
  # ---------------- Persistence ----------------
19
  PERSIST_DIR = Path("/data") if Path("/data").exists() else Path(".")
20
- PERSIST_FILE = PERSIST_DIR / "creds.xlsx" # saved credentials file (uploaded once)
21
- CONFIG_FILE = PERSIST_DIR / "auth.json" # stores Argon2 password hash + TOTP secret + label
22
 
23
- # ---------------- Globals / helpers ----------------
24
  ph = PasswordHasher()
25
  ALIASES = {
26
  "name": ["name", "title", "site", "account", "platform", "service", "app"],
@@ -53,6 +53,7 @@ def standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
53
  return out[mask].reset_index(drop=True)
54
 
55
  def read_any(file) -> pd.DataFrame:
 
56
  name = (getattr(file, "name", "") or "").lower()
57
  if name.endswith(".csv"):
58
  return pd.read_csv(file)
@@ -78,13 +79,10 @@ if "authed" not in st.session_state:
78
  st.session_state.authed = False
79
  if "failures" not in st.session_state:
80
  st.session_state.failures = 0
81
- # setup state (used only during first-time 2FA setup)
82
- if "setup_secret" not in st.session_state:
83
- st.session_state.setup_secret = None
84
- if "setup_label" not in st.session_state:
85
- st.session_state.setup_label = None
86
- if "setup_pw" not in st.session_state:
87
- st.session_state.setup_pw = None
88
 
89
  # ---------------- Step 1: Upload (only if not saved yet) ----------------
90
  if not PERSIST_FILE.exists():
@@ -110,24 +108,26 @@ if not cfg:
110
 
111
  # Collect label + password; generate secret ONCE and keep it in session
112
  with st.form("setup_form"):
113
- label = st.text_input("Account label (as shown in your authenticator)", value=st.session_state.setup_label or "SimpleLookup")
114
- pw1 = st.text_input("New admin password", type="password", value=st.session_state.setup_pw or "")
115
- pw2 = st.text_input("Confirm password", type="password", value=st.session_state.setup_pw or "")
 
 
 
116
  gen = st.form_submit_button("Generate QR code")
117
  if gen:
118
  if not pw1 or pw1 != pw2:
119
  st.error("Passwords are empty or do not match.")
120
  else:
121
- # generate secret only if not already generated
122
- if not st.session_state.setup_secret:
123
  st.session_state.setup_secret = pyotp.random_base32()
124
  st.session_state.setup_label = label
125
  st.session_state.setup_pw = pw1
126
 
127
  # If secret staged, show QR and verify input
128
- if st.session_state.setup_secret:
129
  secret = st.session_state.setup_secret
130
- label = st.session_state.setup_label or "SimpleLookup"
131
  totp = pyotp.TOTP(secret)
132
  uri = totp.provisioning_uri(name=label, issuer_name="Simple Password Lookup")
133
  st.image(make_qr_png(uri), caption="Scan in Google/Microsoft Authenticator")
@@ -135,16 +135,15 @@ if not cfg:
135
  code = st.text_input("Enter a current 6-digit code to verify", max_chars=6)
136
  if st.button("Verify & Save"):
137
  if totp.verify(code, valid_window=1):
138
- # hash password and save config
139
  hash_pw = ph.hash(st.session_state.setup_pw)
140
  save_config({"password_hash": hash_pw, "totp_secret": secret, "label": label})
141
- # clear setup state
142
- st.session_state.setup_secret = None
143
- st.session_state.setup_label = None
144
- st.session_state.setup_pw = None
145
- st.success("2FA setup complete. Please log in below.")
146
  else:
147
- st.error("Invalid code. Open your authenticator and try a fresh code.")
148
  st.stop()
149
 
150
  # ---------------- Step 3: Login (password + 2FA) ----------------
 
17
 
18
  # ---------------- Persistence ----------------
19
  PERSIST_DIR = Path("/data") if Path("/data").exists() else Path(".")
20
+ PERSIST_FILE = PERSIST_DIR / "creds.xlsx" # saved credentials (uploaded once)
21
+ CONFIG_FILE = PERSIST_DIR / "auth.json" # Argon2 password hash + TOTP secret + label
22
 
23
+ # ---------------- Helpers ----------------
24
  ph = PasswordHasher()
25
  ALIASES = {
26
  "name": ["name", "title", "site", "account", "platform", "service", "app"],
 
53
  return out[mask].reset_index(drop=True)
54
 
55
  def read_any(file) -> pd.DataFrame:
56
+ """Read CSV or Excel from an uploaded file-like object."""
57
  name = (getattr(file, "name", "") or "").lower()
58
  if name.endswith(".csv"):
59
  return pd.read_csv(file)
 
79
  st.session_state.authed = False
80
  if "failures" not in st.session_state:
81
  st.session_state.failures = 0
82
+ # setup state (only used during first-time 2FA setup)
83
+ st.session_state.setdefault("setup_secret", None)
84
+ st.session_state.setdefault("setup_label", None)
85
+ st.session_state.setdefault("setup_pw", None)
 
 
 
86
 
87
  # ---------------- Step 1: Upload (only if not saved yet) ----------------
88
  if not PERSIST_FILE.exists():
 
108
 
109
  # Collect label + password; generate secret ONCE and keep it in session
110
  with st.form("setup_form"):
111
+ label = st.text_input(
112
+ "Account label (as shown in your authenticator)",
113
+ value=st.session_state.get("setup_label") or "SimpleLookup"
114
+ )
115
+ pw1 = st.text_input("New admin password", type="password", value=st.session_state.get("setup_pw") or "")
116
+ pw2 = st.text_input("Confirm password", type="password", value=st.session_state.get("setup_pw") or "")
117
  gen = st.form_submit_button("Generate QR code")
118
  if gen:
119
  if not pw1 or pw1 != pw2:
120
  st.error("Passwords are empty or do not match.")
121
  else:
122
+ if not st.session_state.get("setup_secret"):
 
123
  st.session_state.setup_secret = pyotp.random_base32()
124
  st.session_state.setup_label = label
125
  st.session_state.setup_pw = pw1
126
 
127
  # If secret staged, show QR and verify input
128
+ if st.session_state.get("setup_secret"):
129
  secret = st.session_state.setup_secret
130
+ label = st.session_state.get("setup_label") or "SimpleLookup"
131
  totp = pyotp.TOTP(secret)
132
  uri = totp.provisioning_uri(name=label, issuer_name="Simple Password Lookup")
133
  st.image(make_qr_png(uri), caption="Scan in Google/Microsoft Authenticator")
 
135
  code = st.text_input("Enter a current 6-digit code to verify", max_chars=6)
136
  if st.button("Verify & Save"):
137
  if totp.verify(code, valid_window=1):
 
138
  hash_pw = ph.hash(st.session_state.setup_pw)
139
  save_config({"password_hash": hash_pw, "totp_secret": secret, "label": label})
140
+ # clear transient setup state
141
+ for k in ("setup_secret", "setup_label", "setup_pw"):
142
+ st.session_state.pop(k, None)
143
+ st.success("2FA setup complete. Loading login…")
144
+ st.experimental_rerun() # 🔁 rerun so cfg loads and login form appears
145
  else:
146
+ st.error("Invalid code. Try a fresh code from your authenticator.")
147
  st.stop()
148
 
149
  # ---------------- Step 3: Login (password + 2FA) ----------------