feat: PIN unlock + table column picker + edit-position chooser + UX

- PIN unlock: device-local 4-12 digit shortcut, DPAPI-wrapped vault
  key. Three modes (state.unlockMode): pw / pin / pw+pin. PIN
  derives a wrap key via PBKDF2(pin, salt, 100k) and unwraps the
  stored vault key (mirrors the Quick Unlock blob shape).
  Anti-brute-force: 5 wrong attempts wipes the blob. Setup gated by
  master-pw reauth so an unattended unlocked laptop can't be
  backdoored. Master pw rotation clears the PIN blob (key drift).
  loadServerSettings post-sync demotes pin/both -> pw when the local
  blob is missing, so a wiped device re-syncs the correct mode up.
  New unit PM.PinUnlock.pas + cmd://pin/{store,get,clear,status}.
- Table column picker: ⚙ in topbar (table view only), checkbox menu
  for Site/Username/Folder/Updated. Site also drives showSiteOnCards
  so the existing "Show site / URL" toggle in Settings stays in
  sync. NAME column auto-widths (180px min, content max, +32px
  right padding) so column hugs the next one without truncating.
- Editor position chooser (Appearance setting): Slide-over right /
  left / Centered modal. Scoped to #slideover + #settingsPanel so
  the click-outside / pointer-events logic doesn't accidentally
  trap the modal-style empty viewport.
- Confirm before discarding unsaved edits: state.confirmOnUnsaved
  setting (default ON), prompts on X / Esc / click-outside / switch-
  to-other-entry. Also gates Lock vault / Sign out actions when the
  editor is dirty; auto-lock and system-lock paths bypass to avoid
  blocking on an unattended machine.
- Open-in-browser button added to the actions cell of the table
  view (was card-only).
- Entry templates pass folder customization + template id through
  duplicate / export / import / auto-backup roundtrips.
- Folder color + icon now persisted across export/import: payload.
  folders carries name/color/icon; import creates missing folders
  additively (existing local customisation kept).
- Bulk move-to-folder, batch add-tag, single add-tag now re-ship
  the full entry payload so partial PUTs don't silently wipe
  TOTP / custom_fields / kind / template.
- FireDAC: switched ftString -> ftMemo for icon_b64 / custom_fields
  / TOTP / template params and replaced .AsString with .Value so a
  large (~200 KB) DeepSeek favicon no longer gets truncated at the
  default ANSI 4000-char cap.
- Unicode filenames: attachment INSERT now uses ftWideString +
  .AsWideString so non-ANSI filenames round-trip instead of being
  mangled to "?".
- HandleSetEntryIcon cap raised 256 KB -> 512 KB chars to accept
  base64 data URIs produced by max-raw favicon fetches.
- promptDialog + askReauth support inline `error` line + retry-
  with-count loops on doExport reauth and auto-backup password
  setup (5 attempts cap before bailing).
- Recently used moved from Tools to Vault section in the sidebar.
- Auth screen passkey button hidden (Delphi backend stubs WebAuthn).
- Sensitive cmd://favicon/refresh-style buttons in Settings now
  stopPropagation so the document-level "close panel" handler
  doesn't dismiss Settings mid-async during DOM reparenting.
- TEST_PLAN.md: +PIN unlock section.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-06-29 04:41:39 +01:00
parent 1f56a03492
commit b00da43ab0
11 changed files with 1417 additions and 71 deletions
+51 -1
View File
@@ -85,7 +85,57 @@ re-vérifier explicitement.
- [✅] "Clear" → vide tout l'historique
- [✅] Dropdown disparaît au click hors searchbar / Escape / blur => un petit probleme > focus search bar > dropdown search affiché > clique à nouveau search (focus non changé) dropdown non affiché
## 12. Regressions générales 🔥
## 12. PIN unlock 🔥
### 12a. Setup
- [✅] Settings → "PIN unlock" section visible (Bridge actif requis)
- [✅] Status "No PIN set." + bouton "Set PIN" visible, "Remove PIN" caché
- [✅] Click "Set PIN" → prompt → tape **3 chiffres** → erreur inline "PIN must be 412 digits" (modal reste ouvert)
- [✅] Tape **abcd** → même erreur (digits only)
- [✅] Tape **123456** → toast "PIN set" → status passe à "PIN is set on this device." → bouton "Change PIN" + "Remove PIN" visibles
- [✅] Vérifie `%LOCALAPPDATA%\PMServer\pin-unlock.bin` existe
### 12b. Mode 'pin' (PIN only)
- [✅] Settings → dropdown "Unlock method" = "PIN only" → toast "Unlock method updated"
- [✅] Lock vault → auth screen affiche **PIN** input (pas master pw) + lien "Use master password instead"
- [✅] Tape bon PIN → unlock instant
- [✅] Lock → tape mauvais PIN → "Wrong PIN. Try again..."
- [✅] Lock → tape mauvais PIN 5 fois → blob auto-supprimé → écran retombe sur master pw → message "Too many wrong PIN attempts. Sign in with your master password."
- [✅] Settings après ça : status "No PIN set." → re-set possible
### 12c. Mode 'both' (master + PIN)
- [✅] Re-set PIN, dropdown = "Master password + PIN"
- [✅] Lock → auth affiche pw + PIN ensemble
- [✅] Tape bon pw + mauvais PIN → unlock initie, puis lockVault avec "Wrong PIN. Try again."
- [✅] Tape bon pw + bon PIN → unlock OK
- [✅] Pw seul (PIN vide) → submit bloqué (HTML5 required)
### 12d. Escape PIN-only
- [✅] Mode 'pin', PIN configuré → auth → click "Use master password instead" → champ pw apparaît, PIN caché → unlock master pw marche
- [✅] Lock après → revient en mode PIN (le choix master était one-shot)
### 12e. Quick Unlock + PIN coexistence
- [✅] Active Quick Unlock + définis PIN en mode 'both'
- [✅] Restart app → Quick Unlock cold-start réussit sans demander PIN (device trusted)
- [✅] Lock manuel → auth demande pw + PIN comme prévu
### 12f. Sensitive actions = master pw obligatoire
- [ ] Mode 'pin', unlocked → Settings → Export encrypted JSON → askReauth demande **master pw** (pas PIN)
- [ ] Idem : Change master password → master pw demandé
- [ ] Idem : Generate recovery code → master pw demandé
### 12g. Master pw rotation invalide le PIN
- [ ] Mode 'pin', PIN configuré → Change master pw → après rotation, status PIN passe à "No PIN set." (blob wipé) + unlockMode tombe à 'pw'
- [ ] Next lock → champ master pw → unlock OK avec nouveau pw
### 12h. Remove PIN
- [✅] Click "Remove PIN" → confirm dialog
- [✅] Confirm → blob deleted, status repasse à "No PIN set.", mode revert à 'pw'
### 12i. Recovery key continue à marcher
- [ ] Avec mode 'pin' actif, ouvre auth → click "Forgot master password? Use a recovery code" → flow recovery normal (le bouton reste visible)
## 13. Regressions générales 🔥
- [✅] Quick unlock encore fonctionnel après update
- [✅] Master password rotation OK + entries déchiffrables après
- [✅] Autofill (Ctrl+Shift+L/P) toujours OK