Skip to content

FEAT(client): Allow not saving passwords#6820

Open
maxer137 wants to merge 2 commits intomumble-voip:masterfrom
maxer137:save-password
Open

FEAT(client): Allow not saving passwords#6820
maxer137 wants to merge 2 commits intomumble-voip:masterfrom
maxer137:save-password

Conversation

@maxer137
Copy link
Contributor

When asked for a password, the user has the option to save or not save the password to the device.
Implements #1087

Checks

@Hartmnt Hartmnt added client feature-request This issue or PR deals with a new feature labels May 24, 2025
@maxer137 maxer137 force-pushed the save-password branch 2 times, most recently from ad1cac3 to 5b6d16b Compare May 26, 2025 16:29
Copy link
Contributor Author

@maxer137 maxer137 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had quite the odd look on my face when it turned out I couldn’t see my comments when checking on mobile.
Seems I’ve been a victim of the GitHub [Pending] UI

@Hartmnt
Copy link
Member

Hartmnt commented Jun 2, 2025

I had quite the odd look on my face when it turned out I couldn’t see my comments when checking on mobile.
Seems I’ve been a victim of the GitHub [Pending] UI

Yeah, I have been there before too.

Copy link
Member

@Hartmnt Hartmnt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I finally had time to compile and try this. While your change does what it advertises, I ran into two usability issues:

  1. If I select the "Suppress certificate and password storage" setting (bSuppressIdentity), it still shows your checkbox (in a checked state for that matter) despite the password not being saved either way. We should hide or disable the checkbox, if that setting is true.

  2. If I connect to a server which is not stored as favorite, it does also not save the password either way. That is not a problem with your code specifically, it looks like it was never intended to store information of non-favorites. See:

    void Database::setPassword(const QString &hostname, unsigned short port, const QString &uname, const QString &pw) {
    QSqlQuery query(db);
    query.prepare(
    QLatin1String("UPDATE `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));
    query.addBindValue(pw);
    query.addBindValue(hostname);
    query.addBindValue(port);
    query.addBindValue(uname);
    execQueryAndLogFailure(query);
    }
    it says "UPDATE" and servers only contain favorites.
    Anyway, we should also hide or disable the checkbox in those cases.

@Hartmnt
Copy link
Member

Hartmnt commented Jun 2, 2025

2. it says "UPDATE" and servers only contain favorites.
Anyway, we should also hide or disable the checkbox in those cases.

Alternatively, we could UPDATE OR INSERT in the second case

@maxer137
Copy link
Contributor Author

maxer137 commented Jun 2, 2025

I'm wondering what is the best way to implement the password prompt.

I could implement a function in MainWindow that, depending on the setting, either prompts the new PasswordDialog screen or simply the password prompt without checkbox.
I could also add this as a functionality of PasswordDialog.

Additionally. I think it's important to explain to the user that the checkbox is missing because they enabled the Suppress Identity setting. Just in case.

@Hartmnt
Copy link
Member

Hartmnt commented Jun 2, 2025

I'm wondering what is the best way to implement the password prompt.

I would suggest adding this as a possible option (or multiple options) for PasswordDialog, now that we have a custom class for that.

Additionally. I think it's important to explain to the user that the checkbox is missing because they enabled the Suppress Identity setting. Just in case.

That's why I lean more towards disabling instead of hiding the checkbox. You could add a label with "disabled by global configuration" or something similar.

@maxer137
Copy link
Contributor Author

maxer137 commented Jun 2, 2025

You could add a label with "disabled by global configuration" or something similar.

I would ensure that we specifically mention which setting disables the checkbox. So that if a user is wondering why the checkbox is disabled, they can specifically target that setting. In the case they turned it on accidentally.

I would suggest adding this as a possible option (or multiple options) for PasswordDialog, now that we have a custom class for that.

Shall I get the fetching of the setting inside PasswordDialog or MainWindow?

@Hartmnt
Copy link
Member

Hartmnt commented Jun 2, 2025

You could add a label with "disabled by global configuration" or something similar.

I would ensure that we specifically mention which setting disables the checkbox. So that if a user is wondering why the checkbox is disabled, they can specifically target that setting. In the case they turned it on accidentally.

I would suggest adding this as a possible option (or multiple options) for PasswordDialog, now that we have a custom class for that.

Shall I get the fetching of the setting inside PasswordDialog or MainWindow?

I think the reasonable thing to do is to fetch the (or later all) settings which may prevent saving the password in the PasswordDialog

@maxer137 maxer137 force-pushed the save-password branch 2 times, most recently from 76113e3 to aa7e322 Compare June 8, 2025 13:05
Copy link
Member

@Hartmnt Hartmnt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, this iteration took a bit longer to review. I still try to look into your merge request whenever I have a bit of time.

dialog.qcbSavePassword->setChecked(save ? *save : false);
} else {
dialog.qcbSavePassword->setEnabled(false);
QString new_tooltip = QObject::tr("Password can't be saved: ") + toString(checkResult.reason);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
QString new_tooltip = QObject::tr("Password can't be saved: ") + toString(checkResult.reason);
QString new_tooltip = QObject::tr("Password can't be saved: %1").arg(toString(checkResult.reason));

That way translators can change the order of the sentence if they need to.

@maxer137 maxer137 requested a review from Hartmnt August 4, 2025 11:12
@maxer137 maxer137 requested a review from Krzmbrzl September 2, 2025 14:57
@Hartmnt
Copy link
Member

Hartmnt commented Sep 9, 2025

Could you please rebase this against master? This branch is missing a fix for a crash from a month ago and I can't test it again 😅

@maxer137
Copy link
Contributor Author

Could you please rebase this against master? This branch is missing a fix for a crash from a month ago and I can't test it again 😅

I am going to have to spend some more time analyzing the new FailedConnectionDialog to understand if this is still valid. It might take a moment to test if the new version still works properly.
@Krzmbrzl do you have any pointers or remarks I need to be careful of with the new dialog box?

@coderabbitai
Copy link

coderabbitai bot commented Sep 13, 2025

Walkthrough

The changes modify certificate and password storage handling across the connection configuration interface. The NetworkConfig.ui text was updated to specify "certificate information" instead of "certificate and password storage." A new "Save settings on this device" checkbox was added to FailedConnectionDialog with accompanying UI layout adjustments. The password persistence logic was revised to depend on whether the server is marked as a favorite and the checkbox is checked, replacing the prior reliance on a global identity suppression setting. The checkbox becomes disabled for non-favorited servers with explanatory messaging.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main feature change: allowing users to not save passwords during login prompts.
Description check ✅ Passed The description clearly explains the feature purpose, references the related issue (#1087), and confirms compliance with commit guidelines via the completed checklist.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4c388b0 and 4164b3f.

📒 Files selected for processing (4)
  • src/mumble/Database.cpp (1 hunks)
  • src/mumble/NetworkConfig.ui (1 hunks)
  • src/mumble/widgets/FailedConnectionDialog.cpp (2 hunks)
  • src/mumble/widgets/FailedConnectionDialog.ui (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: PR (Docs)
  • GitHub Check: PR (Translations)
  • GitHub Check: PR (Windows_x64)
  • GitHub Check: PR (Linux)
  • GitHub Check: PR (macOS)
  • GitHub Check: CodeQL-Build (ubuntu-latest)
  • GitHub Check: build (ubuntu-22.04, shared, 64bit)
  • GitHub Check: build (ubuntu-24.04, shared, 64bit)
  • GitHub Check: freebsd
  • GitHub Check: pr-checks (ubuntu-latest)
🔇 Additional comments (5)
src/mumble/widgets/FailedConnectionDialog.cpp (2)

27-27: LGTM!

The checkbox initialization logic correctly sets the default state based on the inverse of the identity suppression setting, providing good UX by defaulting to enabled when identity is not suppressed.


74-76: LGTM!

The conditional password saving logic correctly implements the new user-controlled behavior, allowing users to explicitly choose whether to save their credentials.

src/mumble/widgets/FailedConnectionDialog.ui (2)

9-9: Minor width increase to accommodate new checkbox.

The dialog width increase from 409 to 424 pixels provides adequate space for the new save password checkbox.


259-265: LGTM!

The new Save Password checkbox is appropriately positioned before the Reconnect button, providing a clear and intuitive user flow for the password saving decision.

src/mumble/Database.cpp (1)

721-729: Add a UNIQUE index on (hostname,port,username) and use proper SQLite UPSERT (ON CONFLICT)

  • Add schema index in Database ctor (alongside other indexes):
execQueryAndLogFailure(query, QLatin1String(
  "CREATE UNIQUE INDEX IF NOT EXISTS `servers_host_port_user` "
  "ON `servers`(`hostname`,`port`,`username`)"));
  • Replace the current UPDATE/INSERT in Database::setPassword (src/mumble/Database.cpp) with a proper INSERT...ON CONFLICT and bind values in column order:
query.prepare(QLatin1String(
  "INSERT INTO `servers` (`hostname`,`port`,`username`,`password`) VALUES (?,?,?,?) "
  "ON CONFLICT(`hostname`,`port`,`username`) DO UPDATE SET `password`=excluded.`password`"));
query.addBindValue(hostname);
query.addBindValue(port);
query.addBindValue(uname);
query.addBindValue(pw);

Quick repo search did not find explicit favorites accessors (getFavorites/FavoriteServer); verify that enabling UPSERT won’t create unnamed rows that the UI would expose as favorites.

Comment on lines 723 to 724
query.prepare(QLatin1String(
"UPDATE OR INSERT `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Invalid SQLite syntax: “UPDATE OR INSERT” is not supported (QSQLITE).

This will fail at runtime. If we keep the “favorites-only in servers” invariant, switch back to UPDATE-only and let the caller decide whether saving is available.

Minimal fix:

-    query.prepare(QLatin1String(
-        "UPDATE OR INSERT `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));
+    query.prepare(QLatin1String(
+        "UPDATE `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));

Optional: detect “no row updated” and surface it (log or change signature to return bool):

     query.addBindValue(uname);
-    execQueryAndLogFailure(query);
+    if (execQueryAndLogFailure(query) && query.numRowsAffected() == 0) {
+        qWarning() << "Database::setPassword: no matching server to update (hostname/port/username).";
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
query.prepare(QLatin1String(
"UPDATE OR INSERT `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));
query.prepare(QLatin1String(
"UPDATE `servers` SET `password` = ? WHERE `hostname` = ? AND `port` = ? AND `username` = ?"));
🤖 Prompt for AI Agents
In src/mumble/Database.cpp around lines 723-724, the SQL uses unsupported
"UPDATE OR INSERT" for QSQLITE; replace it with a plain UPDATE statement so the
call only updates existing rows (preserving the "favorites-only in servers"
invariant) and remove any INSERT behavior; additionally, consider detecting when
no rows were updated (e.g. check query.numRowsAffected() or the execute result)
and either log that fact or change the function signature/return to surface a
bool success so the caller can decide whether to perform an insert.

</property>
<property name="whatsThis">
<string>&lt;b&gt;This will suppress identity information from the client.&lt;/b&gt;&lt;p&gt;The client will not identify itself with a certificate, even if defined, and will not cache passwords for connections. This is primarily a test-option and is not saved.&lt;/p&gt;</string>
<string>&lt;b&gt;This will suppress identity information from the client.&lt;/b&gt;&lt;p&gt;The client will not identify itself with a certificate, even if defined. This is primarily a test-option and is not saved.&lt;/p&gt;</string>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Align wording with new behavior; remove password mention and tighten phrasing.

Tooltip/label should no longer imply that this option affects password saving. Also prefer “test option” (no hyphen).

Apply:

-        <property name="toolTip">
-         <string>Don't send certificate to server and don't save passwords. (Not saved).</string>
-        </property>
+        <property name="toolTip">
+         <string>Do not send a client certificate to the server. This is primarily a test option and is not saved.</string>
+        </property>
@@
-         <string>&lt;b&gt;This will suppress identity information from the client.&lt;/b&gt;&lt;p&gt;The client will not identify itself with a certificate, even if defined. This is primarily a test-option and is not saved.&lt;/p&gt;</string>
+         <string>&lt;b&gt;This suppresses client identity information.&lt;/b&gt;&lt;p&gt;The client will not identify itself with a certificate, even if defined. This is primarily a test option and is not saved.&lt;/p&gt;</string>
@@
-         <string>Suppress certificate information</string>
+         <string>Suppress client certificate</string>

Also applies to: 90-90

🤖 Prompt for AI Agents
In src/mumble/NetworkConfig.ui around lines 87 and 90, the tooltip text still
mentions password saving and uses “test-option”; update the wording to remove
any reference to passwords, tighten the phrasing, and use “test option” (no
hyphen). Replace the current string with a concise message stating that identity
information is suppressed, the client will not present a certificate for
identification, and that this is a test option which is not saved.

@maxer137
Copy link
Contributor Author

I really do not like the coderabbitai service. Is there a way to disable it?
I feel like it greatly increases the clutter when it comes to managing and maintaining the PR.

@Hartmnt
Copy link
Member

Hartmnt commented Sep 14, 2025

Is there a way to disable it?

I don't think so 🥲

I assume you already know, but the current iteration of this PR fails with the SQL error Parameter count mismatch

@Krzmbrzl
Copy link
Member

do you have any pointers or remarks I need to be careful of with the new dialog box?

No - other than the entire handling of failed but potentially "recoverable" connection attempts is now done in the new dialog.

@Krzmbrzl
Copy link
Member

Is there a way to disable it?

It's enabled on purpose and there is no way for users to opt-out from it. It only auto-reviews non-draft PRs once though. While I agree that it currently still creates more comments than strictly necessary, I have found its actual reviews to be relatively good. Far from perfect but it has identified several issues that would have required significant time investments of a human reviewer or which have even been missed entirely.

For instance, the UPDATE OR INSERT SQL syntax that has been used in this PR does not exist according to the official SQLite docs: https://sqlite.org/lang_update.html
I have never seen this syntax either but I assume you have verified it works?

@maxer137
Copy link
Contributor Author

maxer137 commented Jan 27, 2026

Sorry for the long wait. But I have time to work on the PR again.

the UPDATE OR INSERT SQL syntax that has been used in this PR does not exist according to the official SQLite docs: https://sqlite.org/lang_update.html I have never seen this syntax either but I assume you have verified it works?

Oops. I don't remember which SQL-like language I've worked with that did have a similar feature, but SQLite isn't one of them. 😅
This doesn't work; it should be removed.

But I'm a bit at odds at how I should work this feature in.
I have two options right now, and both aren't the prettiest.

The first one is finding out a way to insert passwords anyway, even if the server isn't favorited. The more I think about it now, the less I like this idea. From the UI, it does make it clear when a password is stored. Either the checkbox is checked or it isn't. But I don't know if there are other ways to save a password that aren't behind a checkbox. Plus, then we're storing information about servers that aren't in the favorites already. Seems like we're currently not doing that.

The other one is creating a clear rule when a user can and can't save a server. Then having the checkbox only be available if this condition is met. I feel like this can get convoluted.
Right now there seem to be only two reasons why you wouldn't be able to save a password: SuppressIdentity is enabled or the server isn't in the favorites.
I could create a feature like Database::canStorePassword that checks and returns true or false based on these conditions, but that would introduce an additional database function that needs to be called for each save password attempt.
For this option, I'd also need a way to relay back to the user why the checkbox isn't there or is disabled. Which seems like an additional challenge.

Do you have any ideas or feedback?

…enabled

This change was made so the future commit can optionally save passwords.
@maxer137
Copy link
Contributor Author

maxer137 commented Feb 4, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

This change offers the functionality, but it's hardcoded within the UI FailedConnectionDialog option.
So, if another part of the UI needs to check if they can save password, they'd have to re-implement the checks.

When asked for a password, the user has the option to save or not save the password to the device.
Implements mumble-voip#1087
@maxer137
Copy link
Contributor Author

maxer137 commented Feb 4, 2026

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Hartmnt Hartmnt added this to the 1.7.0 milestone Feb 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

client feature-request This issue or PR deals with a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments