SmartHeal commited on
Commit
6d516b4
Β·
verified Β·
1 Parent(s): 95e0c4c

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +81 -10
src/streamlit_app.py CHANGED
@@ -7,6 +7,7 @@ from dataclasses import dataclass, asdict
7
  from typing import List, Optional, Dict, Any
8
 
9
  import streamlit as st
 
10
 
11
  # ---- Crypto deps (install: pip install cryptography argon2-cffi==23.1.0) ----
12
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
@@ -263,6 +264,43 @@ def do_lock():
263
 
264
 
265
  # Sidebar: Create / Open / Lock / Export
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  with st.sidebar:
267
  st.title("πŸ” SmartPass")
268
  st.caption("Local, file-based, zero-knowledge vault. No servers.")
@@ -330,15 +368,48 @@ with st.sidebar:
330
 
331
  st.divider()
332
  st.subheader("Export")
333
- if st.session_state.vault is not None:
334
- export_json = json.dumps(st.session_state.vault, separators=(",", ":"))
335
- st.download_button(
336
- label="Download vault JSON",
337
- data=export_json,
338
- file_name="vault.smartpass.json",
339
- mime="application/json",
340
- use_container_width=True,
341
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
  # -------------------------- Main Area --------------------------
344
  st.title(APP_TITLE)
@@ -494,4 +565,4 @@ with st.expander("Security Notes & Tips"):
494
  - **Clipboard caution:** This demo does not auto-copy passwords to avoid OS clipboard risks.
495
  - **KDF tuning:** If your device is slow, reduce memory/iterations in the sidebar's Advanced KDF.
496
  """
497
- )
 
7
  from typing import List, Optional, Dict, Any
8
 
9
  import streamlit as st
10
+ import pandas as pd # for Excel/CSV import
11
 
12
  # ---- Crypto deps (install: pip install cryptography argon2-cffi==23.1.0) ----
13
  from cryptography.hazmat.primitives.ciphers.aead import AESGCM
 
264
 
265
 
266
  # Sidebar: Create / Open / Lock / Export
267
+ # -------------------------- Import helpers --------------------------
268
+
269
+ def _read_tabular(file) -> pd.DataFrame:
270
+ name = (file.name or "").lower()
271
+ if name.endswith(".csv"):
272
+ return pd.read_csv(file)
273
+ # Excel requires openpyxl (installed implicitly by pandas if not, pip install openpyxl)
274
+ return pd.read_excel(file)
275
+
276
+
277
+ def _standardize_columns(df: pd.DataFrame) -> pd.DataFrame:
278
+ # Map common column names to our schema
279
+ aliases = {
280
+ "name": ["name", "title", "site", "account"],
281
+ "username": ["username", "user", "login", "email"],
282
+ "url": ["url", "link", "website"],
283
+ "password": ["password", "pass", "pwd"],
284
+ "note": ["note", "notes", "remark", "remarks"],
285
+ "tags": ["tags", "label", "labels", "category", "categories"],
286
+ "type": ["type", "item_type"],
287
+ }
288
+ colmap: Dict[str, str] = {}
289
+ lower_cols = {c.lower().strip(): c for c in df.columns}
290
+ for target, alias_list in aliases.items():
291
+ for a in alias_list:
292
+ if a in lower_cols:
293
+ colmap[target] = lower_cols[a]
294
+ break
295
+ # Ensure all expected columns exist; create if missing
296
+ for col in ["name", "username", "url", "password", "note", "tags", "type"]:
297
+ if col not in colmap:
298
+ df[col] = ""
299
+ colmap[col] = col
300
+ return df[[colmap[c] for c in ["name", "username", "url", "password", "note", "tags", "type"]]]\
301
+ .rename(columns={colmap[c]: c for c in colmap})
302
+
303
+
304
  with st.sidebar:
305
  st.title("πŸ” SmartPass")
306
  st.caption("Local, file-based, zero-knowledge vault. No servers.")
 
368
 
369
  st.divider()
370
  st.subheader("Export")
371
+ if st.session_state.vault is not None:
372
+ export_json = json.dumps(st.session_state.vault, separators=(",", ":"))
373
+ st.download_button(
374
+ label="Download vault JSON",
375
+ data=export_json,
376
+ file_name="vault.smartpass.json",
377
+ mime="application/json",
378
+ use_container_width=True,
379
+ )
380
+
381
+ st.divider()
382
+ st.subheader("Import from Excel/CSV")
383
+ imp = st.file_uploader("Upload creds file (.xlsx/.xls/.csv)", type=["xlsx","xls","csv"], accept_multiple_files=False, key="importer")
384
+ if imp is not None and st.session_state.unlocked:
385
+ try:
386
+ df = _read_tabular(imp)
387
+ df = _standardize_columns(df)
388
+ # Default type to 'login' if empty
389
+ df['type'] = df['type'].fillna('').astype(str).str.lower().replace({'': 'login'})
390
+ added = 0
391
+ items_local: List[VaultItem] = [VaultItem(**it) for it in st.session_state.vault.get("items", [])]
392
+ for _, row in df.iterrows():
393
+ payload = {
394
+ "type": (row.get('type') or 'login') if (row.get('type') in ['login','note']) else 'login',
395
+ "name": str(row.get('name') or '').strip(),
396
+ "username": str(row.get('username') or '').strip(),
397
+ "url": str(row.get('url') or '').strip(),
398
+ "password": str(row.get('password') or '').strip(),
399
+ "note": str(row.get('note') or '').strip(),
400
+ "tags": [t.strip() for t in str(row.get('tags') or '').split(',') if t.strip()],
401
+ }
402
+ # Skip empty rows
403
+ if not any(payload.values()):
404
+ continue
405
+ items_local.append(encrypt_item(st.session_state.keys["data_key"], payload))
406
+ added += 1
407
+ st.session_state.vault["items"] = [it.to_dict() for it in items_local]
408
+ re_hmac(st.session_state.vault, st.session_state.keys["mac_key"])
409
+ st.success(f"Imported {added} item(s).")
410
+ st.experimental_rerun()
411
+ except Exception as e:
412
+ st.error(f"Import failed: {e}")
413
 
414
  # -------------------------- Main Area --------------------------
415
  st.title(APP_TITLE)
 
565
  - **Clipboard caution:** This demo does not auto-copy passwords to avoid OS clipboard risks.
566
  - **KDF tuning:** If your device is slow, reduce memory/iterations in the sidebar's Advanced KDF.
567
  """
568
+ )