feat(apk): choisir d'ouvrir avec le verrouillage d'ecran, un code ou le mot de passe du coffre

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 14:44:08 +01:00
parent b6ddd8c2fa
commit 799f75d53d
2 changed files with 106 additions and 43 deletions
+62 -40
View File
@@ -395,6 +395,9 @@
<div class="stack">
<button class="btn wide main" id="btnOpen">&#10140;&nbsp; Open vault</button>
<!-- LA SORTIE DE SECOURS DU VERROUILLAGE D'ECRAN : empreinte refusee,
schema oublie - le mot de passe du coffre ouvre toujours. -->
<button class="btn wide" id="btnUsePwd" hidden>Use the vault password</button>
<button class="btn wide" id="btnSync">&#8635;&nbsp; Sync</button>
<!-- UN SEUL BOUTON, ET IL NE MENT PAS SUR SA DESTINATION.
Il y en avait deux, "Settings" et "Log", et le second a fini par
@@ -582,22 +585,21 @@
clair en main : les deux ne pourraient que refuser, et proposer une
action qui refuse toujours vaut moins que ne rien proposer. -->
<div id="secCode" hidden>
<h2>Unlock code</h2>
<h2>Unlock with</h2>
<!-- TROIS FACONS D'OUVRIR, UNE SEULE COPIE LOCALE. Le coffre lui-meme n'est
jamais touche : seul le scellement de la copie rapide change. Le champ
sert a tout - nouveau code, mot de passe du coffre, ou code courant
pour revenir au verrouillage d'ecran (retirer une protection demande
de la connaitre). -->
<div class="in-wrap">
<input class="field-in" id="sPinNew" placeholder="new unlock code or password"
<input class="field-in" id="sPinNew" placeholder="code or password"
type="password" autocomplete="off">
<div class="in-x" data-clear="sPinNew">&#215;</div>
</div>
<button class="btn wide" id="sPinSet">Set this code</button>
<!-- Le champ ci-dessus sert aux deux : le NOUVEAU code pour "Set", le code
COURANT pour "Remove". Retirer une protection demande de la connaitre,
sinon quiconque trouve le telephone ouvert la leve en deux appuis. -->
<button class="btn wide danger" id="sPinClear">Remove the unlock code</button>
<div class="note">To remove the code, type the current one above first.
The code seals the copy kept on this device. It can be
digits or a full password - a longer one is harder to guess, and it is
asked for only when the vault opens. Removing it leaves the vault
readable by anyone holding the phone unlocked.</div>
<button class="btn wide" id="sModeDevice">Screen lock (fingerprint, pattern...)</button>
<button class="btn wide" id="sModeCode">App code (type it above)</button>
<button class="btn wide" id="sModePwd">Vault password (type it above)</button>
<div class="note" id="sModeNote"></div>
</div>
<h2>Diagnostics</h2>
@@ -1648,14 +1650,49 @@ document.getElementById('histBody').addEventListener('click', function (ev) {
document.getElementById('sLog').addEventListener('click', function () {
showLog(document.getElementById('log').hidden);
});
document.getElementById('sPinSet').addEventListener('click', function () {
var v = document.getElementById('sPinNew').value;
if (!v) { toast('Type a code first'); return; }
// DEMANDE EXPLICITE, EFFET IMMEDIAT. Le code ne se depose plus dans un champ
// que l'ouverture suivante appliquerait : Delphi refuse si le coffre n'est
// pas ouvert, ce qui prouve que le code courant est connu.
// FACON D'OUVRIR. Delphi dit laquelle est en place (PM.setUnlockMode) ; chaque
// bouton envoie la commande qui existe deja pour la poser. Revenir au
// verrouillage d'ecran, c'est retirer le code : il faut donc le code courant.
var unlockMode = 'none', canDeviceLock = false, hasContainer = false;
function syncUnlockUi() {
['device', 'code', 'password'].forEach(function (m) {
var id = { device: 'sModeDevice', code: 'sModeCode', password: 'sModePwd' }[m];
document.getElementById(id).classList.toggle('main', unlockMode === m);
});
document.getElementById('sModeDevice').disabled = !canDeviceLock;
document.getElementById('sModeNote').textContent =
{ device: 'Now: screen lock. Changing the screen lock or adding a fingerprint never deletes the vault.',
code: 'Now: app code. To go back to the screen lock, type the current code above.',
password: 'Now: vault password. To go back to the screen lock, type it above.',
none: 'Now: no protection - anyone holding the phone unlocked can open the vault.' }[unlockMode]
+ (canDeviceLock ? '' : ' This phone has no screen lock.');
document.getElementById('btnUsePwd').hidden =
!(authMode === 'open' && unlockMode === 'device' && hasContainer);
}
function modeField() {
var el = document.getElementById('sPinNew'), v = el.value;
el.value = '';
return v;
}
document.getElementById('sModeCode').addEventListener('click', function () {
var v = modeField();
if (!v) { toast('Type the new code above first'); return; }
send('pinset?c=' + encodeURIComponent(v));
document.getElementById('sPinNew').value = '';
});
document.getElementById('sModePwd').addEventListener('click', function () {
var v = modeField();
if (!v) { toast('Type the vault password above first'); return; }
send('pinset?pw=1&c=' + encodeURIComponent(v));
});
document.getElementById('sModeDevice').addEventListener('click', function () {
if (unlockMode === 'device') { toast('Already using the screen lock'); return; }
var v = modeField();
if (!v && unlockMode !== 'none') { toast('Type the current code or password above first'); return; }
send('pinclear?c=' + encodeURIComponent(v));
});
document.getElementById('btnUsePwd').addEventListener('click', function () {
PM.setAuthMode('login');
document.getElementById('pass').focus();
});
document.getElementById('sWipe').addEventListener('click', function (ev) {
// DEUX APPUIS, comme pour retirer le code : un effacement sans retour ne se
@@ -1675,27 +1712,6 @@ document.getElementById('sWipe').addEventListener('click', function (ev) {
send('wipelocal');
closeSettings();
});
document.getElementById('sPinClear').addEventListener('click', function (ev) {
var b = ev.currentTarget;
if (b.getAttribute('data-armed') === null) {
b.setAttribute('data-armed', '1');
b.textContent = 'Really remove - tap again';
setTimeout(function () {
b.removeAttribute('data-armed');
b.textContent = 'Remove the unlock code';
}, 4000);
return;
}
b.removeAttribute('data-armed');
b.textContent = 'Remove the unlock code';
// LE CODE COURANT VOYAGE AVEC LA DEMANDE. Le coffre ouvert ne prouve rien :
// il peut l'etre depuis une heure et rester a l'ecran. Delphi compare a ce
// sous quoi le cache est reellement scelle, et refuse sinon.
send('pinclear?c=' + encodeURIComponent(
document.getElementById('sPinNew').value));
document.getElementById('sPinNew').value = '';
});
function goList() {
closeMenu();
document.body.classList.remove('on-detail');
@@ -1826,6 +1842,12 @@ var PM = {
syncPinbox();
document.getElementById('btnAuth').textContent =
(mode === 'register') ? 'Create vault' : 'Sign in';
syncUnlockUi();
},
setUnlockMode: function (mode, canDevice, container) {
unlockMode = mode; canDeviceLock = canDevice; hasContainer = container;
syncUnlockUi();
},
setPinNeeded: function (on) {
+44 -3
View File
@@ -337,6 +337,7 @@ type
/// le code courant deja connu - c'est ce qui empeche un tiers d'en poser un
/// que le proprietaire ne connaitrait pas.
procedure SetUnlockCode(const ANew: string);
procedure PushUnlockMode;
/// Va chercher l'icone du site de l'entree ouverte, ou l'efface.
procedure SetEntryIcon(AFetch: Boolean);
/// Le depot a bouge, ou ne l'a plus : LE DRAPEAU ET SON TEMOIN ENSEMBLE.
@@ -534,6 +535,7 @@ begin
WebCall('PM.setAuthMode("login")')
else
WebCall('PM.setAuthMode("register")');
PushUnlockMode;
LCanOpen := HasVault and not FVaultUnreadable;
if LCanOpen then
@@ -1701,7 +1703,10 @@ begin
Exit // Touch a deja eu lieu plus haut
else if LAction = 'state' then
FWebView := Param('view')
begin
FWebView := Param('view');
if FWebView = 'settings' then PushUnlockMode;
end
else if LAction = 'open' then
OpenEntry(StrToIntDef(Param('i'), -1))
@@ -1789,7 +1794,16 @@ begin
{$ENDIF}
else if LAction = 'pinset' then
SetUnlockCode(TNetEncoding.URL.Decode(Param('c')))
begin
// MODE MOT DE PASSE = le code EST le mot de passe du coffre. On le
// verifie : sinon une faute de frappe scellerait sous un "mot de passe"
// que l'utilisateur ne retapera jamais a l'identique.
if (Param('pw') = '1') and
((FPlain = '') or (TNetEncoding.URL.Decode(Param('c')) <> CachePwd)) then
WebCall('PM.toast("Wrong vault password")')
else
SetUnlockCode(TNetEncoding.URL.Decode(Param('c')));
end
else if LAction = 'pinclear' then
ClearUnlockCode(TNetEncoding.URL.Decode(Param('c')))
@@ -3070,6 +3084,7 @@ begin
Log('Unlock code set');
WebCall('PM.toast("Unlock code set")');
WebCall('PM.pinChanged()');
PushUnlockMode;
except
on E: Exception do
begin
@@ -3079,6 +3094,28 @@ begin
end;
end;
/// Dit a la page comment le cache est scelle. 'password' = scelle sous un code
/// egal au mot de passe du coffre - connu seulement une fois ouvert (FPinSealed).
procedure TForm8.PushUnlockMode;
var
LMode: string;
begin
if VaultNeedsPin then
begin
if (FPinSealed <> '') and (FPinSealed = CachePwd) then
LMode := 'password'
else
LMode := 'code';
end
else if VaultNeedsAuth then
LMode := 'device'
else
LMode := 'none';
WebCall(Format('PM.setUnlockMode("%s", %s, %s)',
[LMode, LowerCase(BoolToStr(BiometricAvailable, True)),
LowerCase(BoolToStr(HasLocalContainer, True))]));
end;
function TForm8.CachePwd: string;
begin
if HasLocalContainer then Result := FVaultPwd else Result := FSyncPwd;
@@ -3117,7 +3154,10 @@ begin
Exit;
end;
try
SaveVault(FPlain, FETag, CachePwd, BiometricAvailable, False, '',
// Cle NEUVE : le verrouillage d'ecran a pu changer pendant que le code
// protegeait, et une ancienne paire invalidee scellerait un fichier
// impossible a rouvrir.
SaveVault(FPlain, FETag, CachePwd, BiometricAvailable, True, '',
FKey, FKeySalt, FKeyIters, True);
if VaultNeedsPin then
begin
@@ -3130,6 +3170,7 @@ begin
Log('Unlock code removed');
WebCall('PM.toast("Unlock code removed")');
WebCall('PM.pinChanged()');
PushUnlockMode;
ShowUnlock;
except
on E: Exception do