fix(android): locking the vault no longer discards the typed settings

Reported as "the token field saves nothing", and it was exact: LockVault
called PushConfig, which pushes the STORED server settings back into the page.
Every lock therefore overwrote a freshly typed token with the old one -
including the three-minute auto-lock, so it happened with no gesture from the
user at all, and the field looked like it forgot on its own.

The user's own sequence isolated it: deleting one character gave a 401, which
proves the typed value does reach the wire; then open, lock, sync gave a 200
again, the lock having restored the old token in between.

PushConfig now runs at startup only. It was pointless in the other four places
anyway: PM.wipe clears the list and the detail, never the configuration fields.

Along the way, and part of the same confusion: the three settings fields had no
labels, only placeholders that vanish once filled - and since the token was
masked this morning it became indistinguishable from the sync password sitting
right below it. They are labelled now, and the log reports both fingerprints so
a value landing in the neighbouring field shows up immediately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-17 18:52:45 +01:00
parent 90e8fabf58
commit 683655415f
2 changed files with 42 additions and 8 deletions
+32 -8
View File
@@ -32,7 +32,7 @@ interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Diagnostics, System.Rtti, System.NetEncoding, System.DateUtils,
System.Generics.Collections, System.IOUtils,
System.Generics.Collections, System.IOUtils, System.Hash,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Platform,
FMX.Controls.Presentation, FMX.StdCtrls, FMX.Memo, FMX.Memo.Types,
FMX.ScrollBox, FMX.Layouts, FMX.Edit, FMX.ListBox, FMX.Objects,
@@ -418,6 +418,14 @@ begin
SetLength(FWebPending, 0);
end;
/// Empreinte courte d'un secret, pour le journal. On veut savoir LEQUEL des
/// jetons est en place, jamais sa valeur : huit caracteres de son SHA-256
/// suffisent a les distinguer et n'en revelent rien.
function Fingerprint(const ASecret: string): string;
begin
if ASecret = '' then Exit('(vide)');
Result := Copy(THashSHA2.GetHashString(ASecret), 1, 8);
end;
/// Chiffre un petit conteneur, le relit, et le laisse sur le disque prive pour
/// qu'une autre implementation le verifie. Iterations volontairement basses :
/// ce qu'on eprouve ici est le CABLAGE - cle, IV, place du tag - pas le cout
@@ -496,8 +504,8 @@ begin
TTimeZone.Local.ToUniversalTime(Now))]),
FSyncPwd, 1000);
Log('PUT ' + LUrl);
Log(' token ' + IntToStr(Length(FToken)) + ' chars, body ' +
IntToStr(Length(LBody)) + ' chars');
Log(Format(' token %s (%d chars), body %d chars',
[Fingerprint(FToken), Length(FToken), Length(LBody)]));
// Sha vide : on cree le fichier, ou on echoue en 409 s'il existe deja -
// ce qui prouverait le controle de concurrence aussi surement.
LRes := LClient.Put(LBody, '', 'write path probe from phone');
@@ -528,6 +536,8 @@ begin
FUrl := LUrl;
if LToken <> '' then FToken := LToken;
end;
Log(Format('config loaded: token %s (%d chars), url %s',
[Fingerprint(FToken), Length(FToken), FUrl]));
WebCall(Format('PM.setConfig(%s, %s, %s, %s)',
[JsArg(FUrl), JsArg(FToken), JsArg(FPwd),
// Replier des champs vides revient a cacher la seule chose a faire.
@@ -560,7 +570,16 @@ end;
procedure TForm8.OnWebNavigate(ASender: TObject; const AUrl: string);
begin
// Le moteur navigue aussi pour charger la page elle-meme.
if AUrl.StartsWith('cmd://') then HandleWebCommand(AUrl.Substring(6));
// L'ACTION SEULE, jamais la requete : elle porte le jeton et le mot de passe.
// Sans cette trace on ne peut pas distinguer « la commande n'arrive pas » de
// « elle arrive et ne fait rien », et les deux se ressemblent de l'exterieur.
if AUrl.StartsWith('cmd://') then
begin
var LDbg := AUrl.Substring(6);
if LDbg.Contains('?') then LDbg := LDbg.Substring(0, LDbg.IndexOf('?')) + '?...';
Log('cmd ' + LDbg);
HandleWebCommand(AUrl.Substring(6));
end;
end;
procedure TForm8.HandleWebCommand(const AUrl: string);
@@ -613,6 +632,11 @@ begin
FUrl := TNetEncoding.URL.Decode(Param('u')).Trim;
FToken := TNetEncoding.URL.Decode(Param('t')).Trim;
FPwd := TNetEncoding.URL.Decode(Param('p'));
// L'empreinte du mot de passe AUSSI : si une saisie destinee au jeton
// atterrit dans le champ voisin - les deux sont masques - c'est celle-ci
// qui bouge, et le jeton reste inchange sans que rien ne l'explique.
Log(Format('config from page: token %s, pwd %s',
[Fingerprint(FToken), Fingerprint(FPwd)]));
end
else if LAction = 'openvault' then
@@ -678,7 +702,10 @@ begin
// Les lignes du detail portent des valeurs EN CLAIR (FRows) : les detruire
// fait partie du verrouillage, pas de l'affichage.
FCurrent := Default(TVaultEntry);
PushConfig;
// PAS DE PushConfig HORS DEMARRAGE. Redescendre les coordonnees du serveur
// ecrase ce que l'utilisateur vient de taper - l'auto-verrouillage le faisait
// donc sans le moindre geste de sa part, et le champ jeton semblait ne rien
// retenir. Verrouiller efface le clair ; la configuration n'a rien a y voir.
ShowUnlock;
end;
@@ -775,7 +802,6 @@ begin
// Un coffre local illisible se remplace, il ne se repare pas.
Log('Local vault corrupt: ' + E.Message);
ClearVault;
PushConfig;
ShowUnlock;
end;
end;
@@ -785,7 +811,6 @@ begin
// ShowUnlock AVANT le message : il repositionne le statut selon ce que
// l'appareil detient, et ecraserait donc l'explication.
FVaultUnreadable := True;
PushConfig;
ShowUnlock;
Log('Local vault unreadable: ' + LastStoreError);
Status('Local vault unreadable - see log');
@@ -841,7 +866,6 @@ begin
FVaultUnreadable := True;
Log('No usable protection on this device - vault kept but unreadable');
Log(' ' + LastBiometricError);
PushConfig;
ShowUnlock;
Status('Screen lock missing - download again to replace the cache');
end;