fix(import): overwrite mode restores favorite and pinned too
F1 passed when the entry had been deleted first (INSERT path) but not in overwrite mode: that path uses PUT, and PUT deliberately ignores favorite/pinned so a SYNC update can never clobber the local flags — they are device-local by design. A restore, however, must bring them back. The overwrite loop now applies them through the dedicated toggle endpoints, and only when they differ from the local entry. JS-only: PUT keeps ignoring them, so sync behaviour is untouched. Splits the F1 scenario into F1a (delete + reimport, already green) and F1b (overwrite). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+7
-7
@@ -156,7 +156,7 @@ est dirty. Tester avec `askBeforeDelete` **ON puis OFF** (Settings → Security)
|
||||
|
||||
## F — Round-trip export / import (drapeaux & provenance)
|
||||
|
||||
### F1 · favori + épingle survivent au restore
|
||||
### F1 · favori + épingle survivent au restore ✅
|
||||
- **ACTION** : épingler **et** mettre en favori une entrée → *Export vault* → supprimer l'entrée (corbeille + purge) → *Import vault* du fichier.
|
||||
- **ATTENDU** : l'entrée revient **épinglée et favorite**.
|
||||
- **Historique** : `pinned` n'était dans aucun container ; `favorite` y était écrit mais **jamais relu** (ni parser, ni body, ni INSERT) → les deux perdus au restore.
|
||||
@@ -173,7 +173,7 @@ est dirty. Tester avec `askBeforeDelete` **ON puis OFF** (Settings → Security)
|
||||
- **ACTION** : épingler/favoriser une entrée sur le device A → *Sync now* → *Sync now* sur le device B.
|
||||
- **ATTENDU** : sur B, l'entrée **n'est pas** épinglée/favorite. Et un sync ne doit jamais **retirer** un drapeau posé localement sur B (le PUT serveur ne touche pas ces colonnes).
|
||||
|
||||
### F5 · icône custom : provenance préservée
|
||||
### F5 · icône custom : provenance préservée ✅
|
||||
- **ACTION** : upload d'une icône custom → export → supprimer l'entrée → import → puis **Re-fetch all**.
|
||||
- **ATTENDU** : l'icône custom revient **et** survit au Re-fetch all (le flag `icon_custom` a fait le round-trip).
|
||||
|
||||
@@ -214,7 +214,7 @@ Ces flux touchent la dérivation de clé et l'anti-lockout : tester sur un compt
|
||||
**argon2id-v2** (défaut courant). Rebuild **Delphi** requis (fixes serveur) +
|
||||
BuildAssets pour le JS.
|
||||
|
||||
### E1 · reauth — mauvais master pw reste inline (les 4 flux)
|
||||
### E1 · reauth — mauvais master pw reste inline (les 4 flux) ✅
|
||||
- **ACTION** : sur chacun de *Set/Change PIN*, *Enable Quick Unlock*,
|
||||
*Generate recovery code*, *Export* → taper un **mauvais** master pw.
|
||||
- **ATTENDU** : le modal **reste ouvert** avec « Wrong master password. Attempt
|
||||
@@ -222,13 +222,13 @@ BuildAssets pour le JS.
|
||||
moment = action abandonnée, rien fait.
|
||||
- **Historique** : PIN / Quick Unlock / recovery fermaient + toast au 1ᵉʳ essai.
|
||||
|
||||
### E2 · recovery setup sur compte argon2id-v2
|
||||
### E2 · recovery setup sur compte argon2id-v2 ✅
|
||||
- **ACTION** : *Generate recovery code* → bon master pw.
|
||||
- **ATTENDU** : code généré, **pas** de « invalid password ».
|
||||
- **Historique** : `/recovery-key/setup` rejetait tout compte -v2 / argon2id
|
||||
(vérif serveur ne connaissait que pbkdf2 / pbkdf2-sha256).
|
||||
|
||||
### E3 · recovery generate → annuler n'écrit rien
|
||||
### E3 · recovery generate → annuler n'écrit rien ✅
|
||||
- **PRÉ** : un code de recovery **existe déjà**.
|
||||
- **ACTION** : *Regenerate code* → bon master pw → à l'affichage du code,
|
||||
**Cancel** (ou Esc).
|
||||
@@ -236,9 +236,9 @@ BuildAssets pour le JS.
|
||||
code fonctionne toujours au redeem. Refaire avec **I saved it** → nouveau
|
||||
code actif, ancien invalidé.
|
||||
- **Historique** : le setup était POSTé avant l'affichage → annuler écrasait
|
||||
quand même l'ancien code (chemin silencieux vers le lockout).
|
||||
quand même l'ancien code (chemin silencieux vers le lockout).
|
||||
|
||||
### E4 · recovery redeem sur compte argon2id-v2
|
||||
### E4 · recovery redeem sur compte argon2id-v2 ✅
|
||||
- **ACTION** : générer un code, se déconnecter, *Use a recovery code* →
|
||||
username + code.
|
||||
- **ATTENDU** : déverrouillage OK, redirection vers *Change master password*.
|
||||
|
||||
@@ -728,6 +728,19 @@ async function doImport() {
|
||||
// restore the file's icon separately — else overwriting
|
||||
// an entry whose icon was cleared never brings it back.
|
||||
if (src.icon_b64) await saveEntryIcon(local.id, src.icon_b64, !!src.icon_custom);
|
||||
// favorite / pinned are the same story: PUT deliberately
|
||||
// ignores them so a SYNC update can never clobber the local
|
||||
// flags (they're device-local by design). But a restore
|
||||
// must bring them back, so apply them here through their
|
||||
// own toggle endpoints — only when they actually differ.
|
||||
if (!!src.favorite !== !!local.favorite) {
|
||||
await api('/entries/' + local.id + '/favorite',
|
||||
{ method: 'POST', headers: authHeaders() });
|
||||
}
|
||||
if (!!src.pinned !== !!local.pinned) {
|
||||
await api('/entries/' + local.id + '/pin',
|
||||
{ method: 'POST', headers: authHeaders() });
|
||||
}
|
||||
overwritten++;
|
||||
} catch (_) { /* skip the single row on failure */ }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user