feat(settings): gate debug options behind a confirmation (+ re-auth if secured)
Revealing the DEBUG OPTIONS dropdown now opens a confirmation modal with a warning instead of expanding directly. If the wallet is secured, it also requires re-authentication before the options appear: - a quick-unlock PIN → verified against the vault (Argon2id derive, off-thread, no wallet side effects); - otherwise an encrypted wallet → the passphrase, verified via walletpassphrase. Unsecured wallets just confirm. Collapsing needs no gate; each expand re-gates. App gains debugGateRequiresAuth() + verifyDebugCredential() (cb on the main thread; secrets wiped). Adds a modal-debug-gate sweep surface. Verified 100% + 150%, dark + light. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -144,6 +144,13 @@ struct SettingsPageState {
|
||||
std::set<std::string> debug_categories;
|
||||
bool debug_cats_dirty = false;
|
||||
bool debug_expanded = false;
|
||||
// Debug-options gate: a confirmation + warning (and, when a PIN/passphrase is set, re-auth) is
|
||||
// required before the debug dropdown reveals its options.
|
||||
bool debug_gate_open = false;
|
||||
bool debug_gate_verifying = false;
|
||||
char debug_gate_buf[128] = {0};
|
||||
std::string debug_gate_err;
|
||||
float debug_gate_err_timer = 0.0f;
|
||||
bool effects_expanded = false;
|
||||
bool tools_expanded = false;
|
||||
bool rpc_expanded = false; // Node & Security: reveal the RPC connection fields
|
||||
@@ -2521,7 +2528,15 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1,1,1,0.05f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1,1,1,0.08f));
|
||||
if (ImGui::Button("##DebugToggle", ImVec2(availWidth, ImGui::GetFrameHeight()))) {
|
||||
s_settingsState.debug_expanded = !s_settingsState.debug_expanded;
|
||||
if (s_settingsState.debug_expanded) {
|
||||
s_settingsState.debug_expanded = false; // collapsing needs no gate
|
||||
} else {
|
||||
// Expanding is gated: open the confirmation + (if a PIN/passphrase is set) re-auth.
|
||||
s_settingsState.debug_gate_open = true;
|
||||
s_settingsState.debug_gate_buf[0] = '\0';
|
||||
s_settingsState.debug_gate_err.clear();
|
||||
s_settingsState.debug_gate_verifying = false;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", s_settingsState.debug_expanded ? TR("tt_debug_collapse") : TR("tt_debug_expand"));
|
||||
ImGui::PopStyleColor(3);
|
||||
@@ -2647,6 +2662,76 @@ void RenderSettingsPage(App* app) {
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_restart_daemon"));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Debug-options gate: confirmation + warning (+ re-auth if a PIN/passphrase is set) ----
|
||||
if (s_settingsState.debug_gate_open) {
|
||||
auto& st = s_settingsState;
|
||||
if (st.debug_gate_err_timer > 0.0f) {
|
||||
st.debug_gate_err_timer -= ImGui::GetIO().DeltaTime;
|
||||
if (st.debug_gate_err_timer <= 0.0f) st.debug_gate_err.clear();
|
||||
}
|
||||
const bool needAuth = app->debugGateRequiresAuth();
|
||||
const bool hasPin = app->hasPinVault();
|
||||
const float gdp = Layout::dpiScale();
|
||||
material::OverlayDialogSpec ov;
|
||||
ov.title = TR("debug_gate_title");
|
||||
ov.p_open = &st.debug_gate_open;
|
||||
ov.style = material::OverlayStyle::BlurFloat;
|
||||
ov.cardWidth = 480.0f;
|
||||
ov.idSuffix = "debuggate";
|
||||
if (material::BeginOverlayDialog(ov)) {
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape)) st.debug_gate_open = false;
|
||||
ImGui::TextWrapped("%s", TR("debug_gate_warning"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
bool submit = false;
|
||||
if (needAuth) {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
||||
hasPin ? TR("debug_gate_pin_prompt") : TR("debug_gate_pass_prompt"));
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGuiInputTextFlags f = ImGuiInputTextFlags_Password | ImGuiInputTextFlags_EnterReturnsTrue;
|
||||
if (hasPin) f |= ImGuiInputTextFlags_CharsDecimal; // PIN is numeric
|
||||
if (ImGui::InputText("##debugGateSecret", st.debug_gate_buf, sizeof(st.debug_gate_buf), f))
|
||||
submit = true;
|
||||
if (!st.debug_gate_err.empty()) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
Type().textColored(TypeStyle::Caption, Error(), st.debug_gate_err.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::BeginDisabled(st.debug_gate_verifying);
|
||||
if (TactileButton(TR("debug_gate_confirm"), ImVec2(170.0f * gdp, 0)) || submit) {
|
||||
if (!needAuth) {
|
||||
st.debug_expanded = true;
|
||||
st.debug_gate_open = false;
|
||||
} else if (strlen(st.debug_gate_buf) > 0) {
|
||||
st.debug_gate_verifying = true;
|
||||
std::string secret = st.debug_gate_buf;
|
||||
memset(st.debug_gate_buf, 0, sizeof(st.debug_gate_buf));
|
||||
app->verifyDebugCredential(secret, [](bool ok) {
|
||||
auto& s = s_settingsState;
|
||||
s.debug_gate_verifying = false;
|
||||
if (ok) { s.debug_expanded = true; s.debug_gate_open = false; }
|
||||
else { s.debug_gate_err = TR("debug_gate_incorrect"); s.debug_gate_err_timer = 4.0f; }
|
||||
});
|
||||
if (!secret.empty()) memset(&secret[0], 0, secret.size());
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("cancel"), ImVec2(120.0f * gdp, 0))) {
|
||||
memset(st.debug_gate_buf, 0, sizeof(st.debug_gate_buf));
|
||||
st.debug_gate_open = false;
|
||||
}
|
||||
if (st.debug_gate_verifying) {
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("debug_gate_verifying"));
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Shader-based scroll fade: unbind (restore ImGui's default shader) ---
|
||||
@@ -2923,5 +3008,14 @@ void RenderSettingsPage(App* app) {
|
||||
|
||||
}
|
||||
|
||||
void SweepOpenDebugGate(bool open) {
|
||||
s_settingsState.debug_gate_open = open;
|
||||
if (open) {
|
||||
s_settingsState.debug_gate_buf[0] = '\0';
|
||||
s_settingsState.debug_gate_err.clear();
|
||||
s_settingsState.debug_gate_verifying = false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ui
|
||||
} // namespace dragonx
|
||||
|
||||
Reference in New Issue
Block a user