From: Nate Graham <nate@kde.org>
Date: Sat, 17 Oct 2020 20:34:05 -0600
Description: Offer to change kwallet password when changing login password
 Right now, when a user changes their login password, their KWallet
 password is not changed. For users whose KWallet password was the same
 as their login password, this causes the two passwords to get out of sync,
 and then the user will be prompted to unlock their wallet the next time
 they log in. If they use their new password, it won't work. This is very
 very frustrating if you don't know what's going on.
 .
 Now, when you change the login password of your own user account, and
 you have a wallet named "kdewallet" (the default one) in your list of
 wallets, you will be prompted to change the password of that wallet to
 match the new login password. This is optional and you don't have to do
 it, and you won't be prompted to do so if you don't have a wallet named
 "kdewallet", which indicates that you have a customized KWallet setup
 and you presumably know what you're doing.
Origin: upstream, https://invent.kde.org/plasma/plasma-desktop/-/commit/07631c727545b56f47f9ddcb5b25a825abbcd3f3

BUG: 389030
FIXED-IN: 5.21
---
 CMakeLists.txt                                |  1 +
 .../contents/ui/ChangeWalletPassword.qml      | 90 +++++++++++++++++++
 .../package/contents/ui/UserDetailsPage.qml   | 12 +++
 kcms/users/src/CMakeLists.txt                 |  1 +
 kcms/users/src/user.cpp                       | 19 +++-
 kcms/users/src/user.h                         |  3 +
 6 files changed, 125 insertions(+), 1 deletion(-)
 create mode 100644 kcms/users/package/contents/ui/ChangeWalletPassword.qml

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a38dc1a4..ac1add857 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,6 +57,7 @@ find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
     Activities
     ActivitiesStats
     Config
+    Wallet
     WidgetsAddons
 )
 
diff --git a/kcms/users/package/contents/ui/ChangeWalletPassword.qml b/kcms/users/package/contents/ui/ChangeWalletPassword.qml
new file mode 100644
index 000000000..1f71fd776
--- /dev/null
+++ b/kcms/users/package/contents/ui/ChangeWalletPassword.qml
@@ -0,0 +1,90 @@
+/*
+    Copyright 2020  Nate Graham <nate@kde.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) version 3, or any
+    later version accepted by the membership of KDE e.V. (or its
+    successor approved by the membership of KDE e.V.), which shall
+    act as a proxy defined in Section 6 of version 3 of the license.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+import QtQuick 2.6
+import QtQuick.Dialogs 1.1
+import QtQuick.Layouts 1.3
+import QtQuick.Controls 2.5 as QQC2
+
+import org.kde.kirigami 2.8 as Kirigami
+
+Kirigami.OverlaySheet {
+    id: walletPasswordRoot
+
+    header: Kirigami.Heading {
+        text: i18n("Change Wallet Password?")
+    }
+
+    ColumnLayout {
+        id: baseLayout
+        Layout.preferredWidth: Kirigami.Units.gridUnit * 27
+        spacing: Kirigami.Units.largeSpacing
+
+        QQC2.Label {
+            Layout.fillWidth: true
+            text: xi18nc("@info", "Now that you have changed your login password, you may also want to change the password on your default KWallet to match it.")
+            wrapMode: Text.Wrap
+        }
+
+        Kirigami.LinkButton {
+            text: i18n("What is KWallet?")
+            onClicked: {
+                whatIsKWalletExplanation.visible = !whatIsKWalletExplanation.visible
+            }
+        }
+
+        QQC2.Label {
+            id: whatIsKWalletExplanation
+            Layout.fillWidth: true
+            visible: false
+            text: i18n("KWallet is a password manager that stores your passwords for wireless networks and other encrypted resources. It is locked with its own password which differs from your login password. If the two passwords match, it can be unlocked at login automatically so you don't have to enter the KWallet password yourself.")
+            wrapMode: Text.Wrap
+        }
+
+        // Not using a QQC2.DialogButtonBox because it can only do horizontal layouts
+        // and we want the buttons to be stacked when the view is really narrow.
+        // TODO: go back to using a DialogButtonBox if this OverlaySheet is ever
+        // parented to the KCM as a whole, not this view in particular
+        GridLayout {
+            readonly property bool narrow: baseLayout.width < Kirigami.Units.gridUnit * 20
+            Layout.alignment: narrow ? Qt.AlignHCenter : Qt.AlignRight
+            rows: narrow ? 2 : 1
+            columns: narrow ? 1 : 2
+
+            QQC2.Button {
+                Layout.alignment: Qt.AlignHCenter
+                text: i18n("Change Wallet Password")
+                icon.name: "lock"
+                onClicked: {
+                    user.changeWalletPassword()
+                    walletPasswordRoot.close()
+                }
+            }
+            QQC2.Button {
+                Layout.alignment: Qt.AlignHCenter
+                text: i18n("Leave Unchanged")
+                icon.name: "dialog-cancel"
+                onClicked: {
+                    walletPasswordRoot.close()
+                }
+            }
+        }
+    }
+}
diff --git a/kcms/users/package/contents/ui/UserDetailsPage.qml b/kcms/users/package/contents/ui/UserDetailsPage.qml
index cf71dd46b..62df63417 100644
--- a/kcms/users/package/contents/ui/UserDetailsPage.qml
+++ b/kcms/users/package/contents/ui/UserDetailsPage.qml
@@ -45,6 +45,16 @@ SimpleKCM {
         }
     }
 
+    Connections {
+        target: user
+        function onPasswordSuccessfullyChanged() {
+            // Prompt to change the wallet password of the logged-in user
+            if (usersDetailPage.user.loggedIn && usersDetailPage.user.usesDefaultWallet()) {
+                changeWalletPassword.open()
+            }
+        }
+    }
+
 
     Connections {
         target: kcm
@@ -464,4 +474,6 @@ SimpleKCM {
     }
 
     ChangePassword { id: changePassword; account: user }
+
+    ChangeWalletPassword { id: changeWalletPassword }
 }
diff --git a/kcms/users/src/CMakeLists.txt b/kcms/users/src/CMakeLists.txt
index bd38a0690..50f98e9d2 100644
--- a/kcms/users/src/CMakeLists.txt
+++ b/kcms/users/src/CMakeLists.txt
@@ -50,6 +50,7 @@ target_link_libraries(kcm_users
     KF5::Declarative
     KF5::I18n
     KF5::QuickAddons
+    KF5::Wallet
     Qt5::DBus
     crypt
 )
diff --git a/kcms/users/src/user.cpp b/kcms/users/src/user.cpp
index f69c1b1f1..9ea2e1ff6 100644
--- a/kcms/users/src/user.cpp
+++ b/kcms/users/src/user.cpp
@@ -27,6 +27,7 @@
 #include <sys/types.h>
 #include <QtConcurrent>
 #include <KLocalizedString>
+#include <KWallet>
 
 User::User(QObject* parent) : QObject(parent) {}
 
@@ -214,7 +215,13 @@ saltPassword(const QString &plain)
 
 void User::setPassword(const QString &password)
 {
-    m_dbusIface->SetPassword(saltPassword(password), QString());
+    // Blocking because we need to wait for the password to be changed before we
+    // can ask the user about also possibly changing their KWallet password
+    auto invocation = m_dbusIface->SetPassword(saltPassword(password), QString());
+    invocation.waitForFinished();
+    if (!invocation.isError()) {
+        emit passwordSuccessfullyChanged();
+    }
 }
 
 QDBusObjectPath User::path() const
@@ -240,6 +247,16 @@ void User::apply()
     job->start();
 }
 
+bool User::usesDefaultWallet()
+{
+    const QStringList wallets = KWallet::Wallet::walletList();
+    return wallets.contains(QStringLiteral("kdewallet"));
+}
+void User::changeWalletPassword()
+{
+    KWallet::Wallet::changePassword(QStringLiteral("kdewallet"), 1);
+}
+
 bool User::loggedIn() const
 {
     return mLoggedIn;
diff --git a/kcms/users/src/user.h b/kcms/users/src/user.h
index 3886e1c7a..a559b3099 100644
--- a/kcms/users/src/user.h
+++ b/kcms/users/src/user.h
@@ -104,6 +104,8 @@ public:
 
 public Q_SLOTS:
     Q_SCRIPTABLE void apply();
+    Q_SCRIPTABLE bool usesDefaultWallet();
+    Q_SCRIPTABLE void changeWalletPassword();
 
 Q_SIGNALS:
 
@@ -116,6 +118,7 @@ Q_SIGNALS:
     void faceValidChanged();
     void administratorChanged();
     void applyError(const QString& errorMessage);
+    void passwordSuccessfullyChanged();
 
 private:
     int mUid = 0;
-- 
GitLab
