v0.2.6-pre.010-fix.001

This commit is contained in:
2026-08-21 14:53:50 +02:00
parent 4dcdbfaa55
commit 6dcd278459
13 changed files with 280 additions and 45 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/frontend/ts/main.ts
// version: 10
// version: 11
import { Modal } from "bootstrap";
import DataTable from "datatables.net-bs5";
@@ -234,12 +234,13 @@ function updateOwnerMetadataActions(): void {
function updateSecurityRotationActions(): void {
const ownerOpen = activeSessionState === "owner_open" && authorizedWalletProjection?.capability === "owner";
const viewOpen = activeSessionState === "view_open" && authorizedWalletProjection?.capability === "view";
document.querySelectorAll<HTMLInputElement | HTMLButtonElement>("[data-owner-rotation-control]").forEach(control => {
control.disabled = !ownerOpen;
});
const viewControls = document.querySelectorAll<HTMLInputElement | HTMLButtonElement>("[data-view-rotation-control]");
viewControls.forEach(control => {
control.disabled = !ownerOpen || !activeViewEnabled;
control.disabled = (!ownerOpen && !viewOpen) || !activeViewEnabled;
});
}
@@ -442,8 +443,8 @@ function renderAuthorizedWallet(wallet: WalletAuthorizedDto): void {
setText(
"#rotationStatus",
wallet.capability === "owner"
? "OWNER ouvert. Une rotation modifie le fichier Wallet uniquement ; les secrets Config restent inchangés."
: "Session OWNER requise pour effectuer une rotation de credential.",
? "OWNER ouvert. Rotation OWNER et administration VIEW disponibles ; les secrets Config restent inchangés."
: "VIEW ouvert. Self-rotation VIEW disponible ; OWNER reste inaccessible.",
);
updateSessionActions();
frontendDebug("main", "Authorized Wallet session rendered", {
@@ -561,6 +562,25 @@ async function recoverOwnerStateConflict(walletId: string): Promise<void> {
}
}
async function recoverRotationStateConflict(walletId: string, capability: UnlockCapability): Promise<void> {
clearAuthorizedProjection();
clearBalanceProjection();
const request: WalletSelectionRequestDto = { walletId };
try {
const wallet = await invokeKsp<LockedWalletDto>("main", "select_wallet", { request });
renderLockedWallet(wallet);
setText("#rotationStatus", `Conflit détat : rotation ${capability.toUpperCase()} interrompue, wallet reverrouillé.`);
setText("#unlockStatus", `Conflit détat détecté : réautoriser ${capability.toUpperCase()} explicitement avant une nouvelle rotation.`);
await loadWalletInventory("list_wallets", false);
activateView("security", "user");
frontendWarn("main", "Wallet state conflict forced credential reauthorization", { capability, walletId });
} catch {
clearSelectedWallet();
setText("#rotationStatus", "Conflit détat détecté ; la réinspection du wallet a échoué.");
frontendWarn("main", "Wallet credential rotation state conflict recovery failed", { capability, walletId });
}
}
async function runOwnerMetadataMutation(
command: "add_wallet_note" | "delete_wallet_note" | "update_wallet_alias" | "update_wallet_note",
args: Record<string, unknown>,
@@ -963,9 +983,12 @@ async function unlockConfigured(capability: UnlockCapability): Promise<void> {
}
async function rotateWalletPassword(capability: UnlockCapability): Promise<void> {
if (activeSessionState !== "owner_open" || !activeWalletId || authorizedWalletProjection?.capability !== "owner") {
const sessionCapability = authorizedWalletProjection?.capability;
const ownerAuthorized = activeSessionState === "owner_open" && sessionCapability === "owner";
const viewAuthorized = activeSessionState === "view_open" && sessionCapability === "view";
if (!activeWalletId || (capability === "owner" && !ownerAuthorized) || (capability === "view" && !ownerAuthorized && !viewAuthorized)) {
clearRotationSensitiveInputs();
setText("#rotationStatus", "Session OWNER requise.");
setText("#rotationStatus", capability === "owner" ? "Session OWNER requise." : "Session VIEW ou OWNER requise.");
return;
}
if (capability === "view" && !activeViewEnabled) {
@@ -1012,13 +1035,13 @@ async function rotateWalletPassword(capability: UnlockCapability): Promise<void>
} catch (caughtError) {
clearRotationSensitiveInputs();
if (isWalletStateConflict(caughtError)) {
await recoverOwnerStateConflict(walletId);
await recoverRotationStateConflict(walletId, sessionCapability === "view" ? "view" : "owner");
return;
}
if (activeWalletId === walletId) {
activeSessionState = "owner_open";
setText("#currentWalletState", "OWNER open");
setText("#rotationStatus", "Rotation refusée ; la session OWNER reste ouverte.");
if (activeWalletId === walletId && sessionCapability) {
activeSessionState = sessionCapability === "view" ? "view_open" : "owner_open";
setText("#currentWalletState", `${sessionCapability.toUpperCase()} open`);
setText("#rotationStatus", `Rotation refusée ; la session ${sessionCapability.toUpperCase()} reste ouverte.`);
updateSessionActions();
}
frontendWarn("main", "Wallet credential rotation failed", { capability, walletId });