Provide a proper dialog for changing the core password
authorManuel Nickschas <sputnick@quassel-irc.org>
Wed, 11 Mar 2015 18:49:27 +0000 (19:49 +0100)
committerManuel Nickschas <sputnick@quassel-irc.org>
Wed, 11 Mar 2015 18:54:17 +0000 (19:54 +0100)
The password change dialog now asks for your old password, and forces
you to confirm the new password by typing it twice, as is customary.
To make this works, we need to add bidirectional communication between
client and core, forcing some more changes to the way the first iteration
of the feature was done.

Note that this commit breaks compatibility with the previous implementation.
If you run a beta1 core or client, you need to upgrade both to make things
work again. Since no real release happened in between, I don't deem it
necessary to provide backwards compat or yet another feature guard.

14 files changed:
src/client/client.cpp
src/client/client.h
src/core/core.cpp
src/core/core.h
src/core/coresession.cpp
src/core/coresession.h
src/core/sessionthread.cpp
src/core/sessionthread.h
src/qtui/CMakeLists.txt
src/qtui/mainwin.cpp
src/qtui/mainwin.h
src/qtui/passwordchangedlg.cpp [new file with mode: 0644]
src/qtui/passwordchangedlg.h [new file with mode: 0644]
src/qtui/ui/passwordchangedlg.ui [new file with mode: 0644]

index d8dbcbf..54a93fb 100644 (file)
@@ -153,6 +153,9 @@ void Client::init()
     p->attachSlot(SIGNAL(networkCreated(NetworkId)), this, SLOT(coreNetworkCreated(NetworkId)));
     p->attachSlot(SIGNAL(networkRemoved(NetworkId)), this, SLOT(coreNetworkRemoved(NetworkId)));
 
+    p->attachSignal(this, SIGNAL(requestPasswordChange(PeerPtr,QString,QString,QString)), SIGNAL(changePassword(PeerPtr,QString,QString,QString)));
+    p->attachSlot(SIGNAL(passwordChanged(PeerPtr,bool)), this, SLOT(corePasswordChanged(PeerPtr,bool)));
+
     //connect(mainUi(), SIGNAL(connectToCore(const QVariantMap &)), this, SLOT(connectToCore(const QVariantMap &)));
     connect(mainUi(), SIGNAL(disconnectFromCore()), this, SLOT(disconnectFromCore()));
     connect(this, SIGNAL(connected()), mainUi(), SLOT(connectedToCore()));
@@ -386,11 +389,6 @@ void Client::setSyncedToCore()
     connect(networkModel(), SIGNAL(requestSetLastSeenMsg(BufferId, MsgId)), bufferSyncer(), SLOT(requestSetLastSeenMsg(BufferId, const MsgId &)));
 
     SignalProxy *p = signalProxy();
-
-    if ((Client::coreFeatures() & Quassel::PasswordChange)) {
-        p->attachSignal(this, SIGNAL(clientChangePassword(QString)));
-    }
-
     p->synchronize(bufferSyncer());
 
     // create a new BufferViewManager
@@ -653,12 +651,20 @@ void Client::markBufferAsRead(BufferId id)
         bufferSyncer()->requestMarkBufferAsRead(id);
 }
 
-void Client::changePassword(QString newPassword) {
+
+void Client::changePassword(const QString &oldPassword, const QString &newPassword) {
     CoreAccount account = currentCoreAccount();
     account.setPassword(newPassword);
     coreAccountModel()->createOrUpdateAccount(account);
-    coreAccountModel()->save();
-    emit clientChangePassword(newPassword);
+    emit instance()->requestPasswordChange(nullptr, account.user(), oldPassword, newPassword);
+}
+
+
+void Client::corePasswordChanged(PeerPtr, bool success)
+{
+    if (success)
+        coreAccountModel()->save();
+    emit passwordChanged(success);
 }
 
 
index 445648b..6d224cf 100644 (file)
@@ -142,6 +142,8 @@ public:
     static void mergeBuffersPermanently(BufferId bufferId1, BufferId bufferId2);
     static void purgeKnownBufferIds();
 
+    static void changePassword(const QString &oldPassword, const QString &newPassword);
+
 #if QT_VERSION < 0x050000
     static void logMessage(QtMsgType type, const char *msg);
 #else
@@ -191,7 +193,9 @@ signals:
      */
     void bufferMarkedAsRead(BufferId id);
 
-    void clientChangePassword(QString password);
+    //! Requests a password change (user name must match the currently logged in user)
+    void requestPasswordChange(PeerPtr peer, const QString &userName, const QString &oldPassword, const QString &newPassword);
+    void passwordChanged(bool success);
 
 public slots:
     void disconnectFromCore();
@@ -202,8 +206,6 @@ public slots:
 
     void markBufferAsRead(BufferId id);
 
-    void changePassword(QString newPassword);
-
 private slots:
     void setSyncedToCore();
     void setDisconnectedFromCore();
@@ -218,6 +220,8 @@ private slots:
     void coreNetworkCreated(NetworkId);
     void coreNetworkRemoved(NetworkId);
 
+    void corePasswordChanged(PeerPtr, bool success);
+
     void requestInitialBacklog();
 
     void sendBufferedUserInput();
index dfd2c95..2049b63 100644 (file)
@@ -638,7 +638,6 @@ SessionThread *Core::sessionForUser(UserId uid, bool restore)
     SessionThread *session = new SessionThread(uid, restore, this);
     _sessions[uid] = session;
     session->start();
-    connect(session, SIGNAL(passwordChangeRequested(UserId, QString)), _storage, SLOT(updateUser(UserId, QString)));
     return session;
 }
 
@@ -819,6 +818,15 @@ void Core::changeUserPass(const QString &username)
 }
 
 
+bool Core::changeUserPassword(UserId userId, const QString &password)
+{
+    if (!isConfigured() || !userId.isValid())
+        return false;
+
+    return instance()->_storage->updateUser(userId, password);
+}
+
+
 AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage)
 {
     if (!storage)
index deef911..e356a0f 100644 (file)
@@ -74,6 +74,15 @@ public:
         return instance()->_storage->validateUser(userName, password);
     }
 
+
+    //! Change a user's password
+    /**
+     * \param userId     The user's ID
+     * \param password   The user's unencrypted new password
+     * \return true, if the password change was successful
+     */
+    static bool changeUserPassword(UserId userId, const QString &password);
+
     //! Store a user setting persistently
     /**
      * \param userId       The users Id
index 1f6345d..5fd5bdc 100644 (file)
@@ -100,7 +100,8 @@ CoreSession::CoreSession(UserId uid, bool restoreState, QObject *parent)
     p->attachSlot(SIGNAL(createNetwork(const NetworkInfo &, const QStringList &)), this, SLOT(createNetwork(const NetworkInfo &, const QStringList &)));
     p->attachSlot(SIGNAL(removeNetwork(NetworkId)), this, SLOT(removeNetwork(NetworkId)));
 
-    p->attachSlot(SIGNAL(clientChangePassword(QString)), this, SLOT(changePassword(QString)));
+    p->attachSlot(SIGNAL(changePassword(PeerPtr,QString,QString,QString)), this, SLOT(changePassword(PeerPtr,QString,QString,QString)));
+    p->attachSignal(this, SIGNAL(passwordChanged(PeerPtr,bool)));
 
     loadSettings();
     initScriptEngine();
@@ -641,7 +642,12 @@ void CoreSession::globalAway(const QString &msg)
     }
 }
 
-void CoreSession::changePassword(QString password)
+void CoreSession::changePassword(PeerPtr peer, const QString &userName, const QString &oldPassword, const QString &newPassword)
 {
-    emit passwordChangeRequested(_user, password);
+    bool success = false;
+    UserId uid = Core::validateUser(userName, oldPassword);
+    if (uid.isValid() && uid == user())
+        success = Core::changeUserPassword(uid, newPassword);
+
+    emit passwordChanged(peer, success);
 }
index 0861b18..a164ffe 100644 (file)
@@ -27,6 +27,7 @@
 #include "corecoreinfo.h"
 #include "corealiasmanager.h"
 #include "coreignorelistmanager.h"
+#include "peer.h"
 #include "protocol.h"
 #include "message.h"
 #include "storage.h"
@@ -45,7 +46,6 @@ class EventManager;
 class EventStringifier;
 class InternalPeer;
 class IrcParser;
-class Peer;
 class MessageEvent;
 class NetworkConnection;
 class RemotePeer;
@@ -127,13 +127,13 @@ public slots:
      */
     void renameBuffer(const NetworkId &networkId, const QString &newName, const QString &oldName);
 
+    void changePassword(PeerPtr peer, const QString &userName, const QString &oldPassword, const QString &newPassword);
+
     QHash<QString, QString> persistentChannels(NetworkId) const;
 
     //! Marks us away (or unaway) on all networks
     void globalAway(const QString &msg = QString());
 
-    void changePassword(QString password);
-
 signals:
     void initialized();
     void sessionState(const Protocol::SessionState &sessionState);
@@ -160,7 +160,7 @@ signals:
     void networkRemoved(NetworkId);
     void networkDisconnected(NetworkId);
 
-    void passwordChangeRequested(UserId user, QString password);
+    void passwordChanged(PeerPtr peer, bool success);
 
 protected:
     virtual void customEvent(QEvent *event);
index 1439bfc..c2f9b5a 100644 (file)
@@ -121,7 +121,6 @@ void SessionThread::addInternalClientToSession(InternalPeer *internalPeer)
 void SessionThread::run()
 {
     _session = new CoreSession(user(), _restoreState);
-    connect(_session, SIGNAL(passwordChangeRequested(UserId, QString)), SIGNAL(passwordChangeRequested(UserId, QString)));
     connect(this, SIGNAL(addRemoteClient(RemotePeer*)), _session, SLOT(addClient(RemotePeer*)));
     connect(this, SIGNAL(addInternalClient(InternalPeer*)), _session, SLOT(addClient(InternalPeer*)));
     connect(_session, SIGNAL(sessionState(Protocol::SessionState)), Core::instance(), SIGNAL(sessionState(Protocol::SessionState)));
index 9a4a1b6..fee6e64 100644 (file)
@@ -57,8 +57,6 @@ signals:
     void addRemoteClient(RemotePeer *peer);
     void addInternalClient(InternalPeer *peer);
 
-    void passwordChangeRequested(UserId user, QString newPassword);
-
 private:
     CoreSession *_session;
     UserId _user;
index f17cd45..be9a14b 100644 (file)
@@ -34,6 +34,7 @@ set(SOURCES
     markerlineitem.cpp
     msgprocessorstatuswidget.cpp
     nicklistwidget.cpp
+    passwordchangedlg.cpp
     qtui.cpp
     qtuiapplication.cpp
     qtuimessageprocessor.cpp
@@ -71,6 +72,7 @@ set(FORMS
     inputwidget.ui
     msgprocessorstatuswidget.ui
     nicklistwidget.ui
+    passwordchangedlg.ui
     settingsdlg.ui
     settingspagedlg.ui
     simplenetworkeditor.ui
index 84c3159..8f022ca 100644 (file)
@@ -87,6 +87,7 @@
 #include "legacysystemtray.h"
 #include "msgprocessorstatuswidget.h"
 #include "nicklistwidget.h"
+#include "passwordchangedlg.h"
 #include "qtuiapplication.h"
 #include "qtuimessageprocessor.h"
 #include "qtuisettings.h"
@@ -197,8 +198,6 @@ void MainWin::init()
     connect(Client::coreConnection(), SIGNAL(handleSslErrors(const QSslSocket *, bool *, bool *)), SLOT(handleSslErrors(const QSslSocket *, bool *, bool *)));
 #endif
 
-    connect(this, SIGNAL(changePassword(QString)), Client::instance(), SLOT(changePassword(QString)));
-
     // Setup Dock Areas
     setDockNestingEnabled(true);
     setCorner(Qt::TopLeftCorner, Qt::LeftDockWidgetArea);
@@ -363,7 +362,7 @@ void MainWin::setupActions()
     coll->addAction("DisconnectCore", new Action(QIcon::fromTheme("network-disconnect"), tr("&Disconnect from Core"), coll,
             Client::instance(), SLOT(disconnectFromCore())));
     coll->addAction("ChangePassword", new Action(QIcon::fromTheme("dialog-password"), tr("Change &Password..."), coll,
-            this, SLOT(showChangePasswordDialog())));
+            this, SLOT(showPasswordChangeDlg())));
     coll->addAction("CoreInfo", new Action(QIcon::fromTheme("help-about"), tr("Core &Info..."), coll,
             this, SLOT(showCoreInfoDlg())));
     coll->addAction("ConfigureNetworks", new Action(QIcon::fromTheme("configure"), tr("Configure &Networks..."), coll,
@@ -738,14 +737,11 @@ void MainWin::changeActiveBufferView(int bufferViewId)
 }
 
 
-void MainWin::showChangePasswordDialog()
+void MainWin::showPasswordChangeDlg()
 {
     if((Client::coreFeatures() & Quassel::PasswordChange)) {
-        bool ok;
-        QString newPassword = QInputDialog::getText(this, tr("Set Core Password"), tr("New password for your Quassel Core:"), QLineEdit::Password, QString(), &ok);
-        if (ok && !newPassword.isEmpty()) {
-            emit changePassword(newPassword);
-        }
+        PasswordChangeDlg dlg(this);
+        dlg.exec();
     }
     else {
         QMessageBox box(QMessageBox::Warning, tr("Feature Not Supported"),
index 4444426..900354b 100644 (file)
@@ -123,6 +123,7 @@ private slots:
     void showNotificationsDlg();
     void showIgnoreList(QString newRule = QString());
     void showShortcutsDlg();
+    void showPasswordChangeDlg();
     void showNewTransferDlg(const ClientTransfer *transfer);
     void onFullScreenToggled();
 
@@ -164,12 +165,9 @@ private slots:
     void changeActiveBufferView(bool backwards);
     void changeActiveBufferView(int bufferViewId);
 
-    void showChangePasswordDialog();
-
 signals:
     void connectToCore(const QVariantMap &connInfo);
     void disconnectFromCore();
-    void changePassword(QString newPassword);
 
 private:
 #ifdef HAVE_KDE
diff --git a/src/qtui/passwordchangedlg.cpp b/src/qtui/passwordchangedlg.cpp
new file mode 100644 (file)
index 0000000..7009adf
--- /dev/null
@@ -0,0 +1,75 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#include "passwordchangedlg.h"
+
+#include <QMessageBox>
+#include <QPushButton>
+
+#include "client.h"
+
+PasswordChangeDlg::PasswordChangeDlg(QWidget *parent) : QDialog(parent)
+{
+    ui.setupUi(this);
+
+    CoreAccount account = Client::currentCoreAccount();
+    ui.infoLabel->setText(tr("This changes the password for your username <b>%1</b> "
+                             "on the Quassel Core running at <b>%2</b>.")
+                          .arg(account.user(), account.hostName()));
+
+    connect(ui.oldPasswordEdit, SIGNAL(textChanged(QString)), SLOT(inputChanged()));
+    connect(ui.newPasswordEdit, SIGNAL(textChanged(QString)), SLOT(inputChanged()));
+    connect(ui.confirmPasswordEdit, SIGNAL(textChanged(QString)), SLOT(inputChanged()));
+    connect(ui.buttonBox, SIGNAL(accepted()), SLOT(changePassword()));
+
+    connect(Client::instance(), SIGNAL(passwordChanged(bool)), SLOT(passwordChanged(bool)));
+
+    ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+}
+
+
+void PasswordChangeDlg::inputChanged()
+{
+    bool ok = !ui.oldPasswordEdit->text().isEmpty() && !ui.newPasswordEdit->text().isEmpty()
+              && ui.newPasswordEdit->text() == ui.confirmPasswordEdit->text();
+    ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(ok);
+}
+
+
+void PasswordChangeDlg::changePassword()
+{
+    ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+    Client::changePassword(ui.oldPasswordEdit->text(), ui.newPasswordEdit->text());
+}
+
+
+void PasswordChangeDlg::passwordChanged(bool success)
+{
+    if (!success) {
+        QMessageBox box(QMessageBox::Warning, tr("Password Not Changed"),
+                        tr("<b>Password change failed<b>"),
+                        QMessageBox::Ok, this);
+        box.setInformativeText(tr("The core reported an error when trying to change your password. Make sure you entered your old password correctly!"));
+        box.exec();
+    }
+    else {
+        accept();
+    }
+}
diff --git a/src/qtui/passwordchangedlg.h b/src/qtui/passwordchangedlg.h
new file mode 100644 (file)
index 0000000..36bfc63
--- /dev/null
@@ -0,0 +1,42 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2015 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) version 3.                                           *
+ *                                                                         *
+ *   This program 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 General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#pragma once
+
+#include <QDialog>
+
+#include "ui_passwordchangedlg.h"
+
+class PasswordChangeDlg : public QDialog
+{
+    Q_OBJECT
+
+public:
+    PasswordChangeDlg(QWidget *parent = nullptr);
+
+private slots:
+    void inputChanged();
+    void changePassword();
+    void passwordChanged(bool success);
+
+private:
+    Ui::PasswordChangeDlg ui;
+    QString _newPassword;
+};
diff --git a/src/qtui/ui/passwordchangedlg.ui b/src/qtui/ui/passwordchangedlg.ui
new file mode 100644 (file)
index 0000000..cf68c1f
--- /dev/null
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordChangeDlg</class>
+ <widget class="QDialog" name="PasswordChangeDlg">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>455</width>
+    <height>173</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Change Password</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout">
+     <item>
+      <widget class="QLabel" name="infoLabel">
+       <property name="text">
+        <string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This changes the password for your username &lt;span style=&quot; font-weight:600;&quot;&gt;John&lt;/span&gt; on the Quassel Core running at &lt;span style=&quot; font-weight:600;&quot;&gt;example.invalid&lt;/span&gt;.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+       </property>
+       <property name="wordWrap">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QFormLayout" name="formLayout">
+       <item row="0" column="0">
+        <widget class="QLabel" name="oldPasswordLabel">
+         <property name="text">
+          <string>Old password:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="oldPasswordEdit">
+         <property name="echoMode">
+          <enum>QLineEdit::Password</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="newPasswordLabel">
+         <property name="text">
+          <string>New Password:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QLineEdit" name="newPasswordEdit">
+         <property name="echoMode">
+          <enum>QLineEdit::Password</enum>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLabel" name="confirmPasswordLabel">
+         <property name="text">
+          <string>Confirm password:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QLineEdit" name="confirmPasswordEdit">
+         <property name="echoMode">
+          <enum>QLineEdit::Password</enum>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <spacer name="verticalSpacer">
+       <property name="orientation">
+        <enum>Qt::Vertical</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>20</width>
+         <height>40</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QDialogButtonBox" name="buttonBox">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="standardButtons">
+        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>PasswordChangeDlg</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>152</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>229</x>
+     <y>-14</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>