From 4364daecef5cd0d494a864fa7e7027c231df032f Mon Sep 17 00:00:00 2001 From: Nguyen Ngo Date: Mon, 18 May 2026 11:48:31 -0400 Subject: [PATCH] 05/18 Enhanced codes and functionalities 2 --- app/routes/auth.py | 2 +- app/routes/vault.py | 12 ++++++------ app/services/auth_service.py | 18 ++++++++++++++++-- app/static/js/vault.js | 29 ++++++++++++++++++++++------- 4 files changed, 45 insertions(+), 16 deletions(-) diff --git a/app/routes/auth.py b/app/routes/auth.py index 730415e..b806c6a 100644 --- a/app/routes/auth.py +++ b/app/routes/auth.py @@ -116,7 +116,7 @@ def login(): # Columns do not exist yet — migration pending. Skip lockout check. db.session.rollback() - if not user or not verify_auth_token(auth_hash, user.master_hash): + if not user or not verify_auth_token(auth_hash, user.master_hash, user=user): if user: try: user.failed_login_count = (user.failed_login_count or 0) + 1 diff --git a/app/routes/vault.py b/app/routes/vault.py index 26a5ebe..f67c6b8 100644 --- a/app/routes/vault.py +++ b/app/routes/vault.py @@ -83,7 +83,7 @@ def create_item(): action='vault_item.create', resource_type='vault_item', resource_id=item.id, - detail=f'Created {item_type} item: "{name}"', + detail=f'Created {item_type} item (id={item.id})', ip_address=client_ip(), ) db.session.commit() @@ -144,7 +144,7 @@ def update_item(item_id): action='vault_item.update', resource_type='vault_item', resource_id=item.id, - detail=f'Updated item: "{item.name}"', + detail=f'Updated {item.item_type} item (id={item.id})', ip_address=client_ip(), ) db.session.commit() @@ -162,16 +162,16 @@ def delete_item(item_id): item = VaultItem.query.filter_by(id=item_id, user_id=g.current_user_id).first() if not item: return jsonify({'error': 'Item not found'}), 404 - item_name = item.name - item_id = item.id + item_id_saved = item.id + item_type_saved = item.item_type db.session.delete(item) db.session.flush() AuditLog.log( user_id=g.current_user_id, action='vault_item.delete', resource_type='vault_item', - resource_id=item_id, - detail=f'Deleted item: "{item_name}"', + resource_id=item_id_saved, + detail=f'Deleted {item_type_saved} item (id={item_id_saved})', ip_address=client_ip(), ) db.session.commit() diff --git a/app/services/auth_service.py b/app/services/auth_service.py index c48ac18..b3ea702 100644 --- a/app/services/auth_service.py +++ b/app/services/auth_service.py @@ -24,10 +24,24 @@ def hash_auth_token(auth_hash: str) -> str: return ph.hash(auth_hash) -def verify_auth_token(auth_hash: str, stored_hash: str) -> bool: +def verify_auth_token(auth_hash: str, stored_hash: str, user=None) -> bool: + """ + Verify auth_hash against stored Argon2id hash. + If user is provided and the stored hash uses outdated parameters, + the hash is transparently upgraded on successful verification. + Caller must commit the session after this returns True. + """ ph = PasswordHasher() try: - return ph.verify(stored_hash, auth_hash) + result = ph.verify(stored_hash, auth_hash) + if result and user is not None and ph.check_needs_rehash(stored_hash): + ph_fresh = PasswordHasher( + time_cost=current_app.config['ARGON2_TIME_COST'], + memory_cost=current_app.config['ARGON2_MEMORY_COST'], + parallelism=current_app.config['ARGON2_PARALLELISM'], + ) + user.master_hash = ph_fresh.hash(auth_hash) + return result except (VerifyMismatchError, VerificationError, InvalidHashError): return False diff --git a/app/static/js/vault.js b/app/static/js/vault.js index c3e51a0..1302f0b 100644 --- a/app/static/js/vault.js +++ b/app/static/js/vault.js @@ -931,13 +931,22 @@ const Vault = (() => { for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; - // Simple CSV split — handles quoted fields containing commas. + // RFC 4180-compliant CSV split — handles quoted fields containing commas + // and embedded double-quotes escaped as "". const cells = []; let cur = "", inQuote = false; - for (const ch of line + ",") { + const src = line + ","; + for (let ci = 0; ci < src.length; ci++) { + const ch = src[ci]; if (ch === '"') { - inQuote = !inQuote; + if (inQuote && src[ci + 1] === '"') { + // Escaped quote inside a quoted field: "" → " + cur += '"'; + ci++; // skip the second quote + } else { + inQuote = !inQuote; + } } else if (ch === "," && !inQuote) { cells.push(cur.trim()); cur = ""; @@ -2856,10 +2865,16 @@ const Vault = (() => { applyCurrentFilter(); return; } - const pool = _activeFilter - ? _activeFilter.type === "itemType" - ? _items.filter((i) => i.item_type === _activeFilter.value) - : _items.filter((i) => i.folder_id === _activeFilter.value) + const pool = !_activeFilter + ? _items + : _activeFilter.type === "itemType" + ? _items.filter((i) => i.item_type === _activeFilter.value) + : _activeFilter.type === "folder" + ? _items.filter((i) => i.folder_id === _activeFilter.value) + : _activeFilter.type === "tag" + ? _items.filter((i) => + (i.plain?.tags || []).includes(_activeFilter.value), + ) : _items; renderItemList( pool.filter(