diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,18 @@ ) endif() + +find_package(Qt5Keychain REQUIRED) +set_package_properties(Qt5Keychain PROPERTIES + DESCRIPTION "Provides support for secure credentials storage" + URL "https://github.com/frankosterfeld/qtkeychain" + TYPE OPTIONAL) +if (Qt5Keychain_FOUND) + set(HAVE_QT5KEYCHAIN 1) +else() + set(HAVE_QT5KEYCHAIN 0) +endif() + include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") diff --git a/autotests/rocketchataccountsettingstest.cpp b/autotests/rocketchataccountsettingstest.cpp --- a/autotests/rocketchataccountsettingstest.cpp +++ b/autotests/rocketchataccountsettingstest.cpp @@ -207,8 +207,6 @@ QVERIFY(chat.userName().isEmpty()); QVERIFY(chat.password().isEmpty()); QVERIFY(chat.serverUrl().isEmpty()); - - chat.loadSettings(); QVERIFY(chat.userId().isEmpty()); QVERIFY(chat.userName().isEmpty()); QVERIFY(chat.authToken().isEmpty()); diff --git a/config-ruqola.h.cmake b/config-ruqola.h.cmake --- a/config-ruqola.h.cmake +++ b/config-ruqola.h.cmake @@ -1 +1,3 @@ #define RUQOLA_VERSION "${RUQOLA_VERSION}" + +#cmakedefine01 HAVE_QT5KEYCHAIN diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,11 +139,15 @@ target_link_libraries(libruqolacore KF5::IconThemes) endif() +if (Qt5Keychain_FOUND) + target_link_libraries(libruqolacore qt5keychain) + target_include_directories(libruqolacore PRIVATE ${QTKEYCHAIN_INCLUDE_DIRS}) +endif() + set_target_properties(libruqolacore PROPERTIES OUTPUT_NAME ruqolacore VERSION ${RUQOLA_LIB_VERSION} SOVERSION ${RUQOLA_LIB_SOVERSION} ) - qt5_add_resources(RuqolaResources qml/qml.qrc) add_executable(ruqola main.cpp ${RuqolaResources}) diff --git a/src/qml/LoginPage.qml b/src/qml/LoginPage.qml --- a/src/qml/LoginPage.qml +++ b/src/qml/LoginPage.qml @@ -34,6 +34,7 @@ serverUrl: rcAccount.serverUrl username: rcAccount.userName originalAccountName: rcAccount.accountName + password: rcAccount.password onAccepted: { //TODO ? //rcAccount.updateAccountSettings(loginTab.accountName, loginTab.password, loginTab.username, loginTab.serverUrl) diff --git a/src/rocketchataccount.h b/src/rocketchataccount.h --- a/src/rocketchataccount.h +++ b/src/rocketchataccount.h @@ -65,7 +65,7 @@ Q_PROPERTY(QString userID READ userID WRITE setUserID NOTIFY userIDChanged) Q_PROPERTY(QString serverUrl READ serverUrl WRITE setServerUrl NOTIFY serverUrlChanged) Q_PROPERTY(QString accountName READ accountName WRITE setAccountName NOTIFY accountNameChanged) - Q_PROPERTY(QString password WRITE setPassword) + Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) Q_PROPERTY(DDPClient::LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) public: explicit RocketChatAccount(const QString &accountName = QString(), QObject *parent = nullptr); @@ -232,15 +232,14 @@ void updateUser(const QJsonObject &object); - void initializeSettings(const QString &accountFileName); - void initializeRoom(const QString &roomId, bool loadInitialHistory = true); Q_SIGNALS: void connectedChanged(); void accountNameChanged(); void userNameChanged(); void userIDChanged(); + void passwordChanged(); void serverUrlChanged(); void loginStatusChanged(); void logoutDone(const QString &accountname); @@ -255,7 +254,7 @@ Q_DISABLE_COPY(RocketChatAccount) void slotNeedToUpdateNotification(); void insertAvatarUrl(const QString &userId, const QString &url); - void loadSettings(); + void loadSettings(const QString &accountFileName); void clearModels(); void fillOauthModel(); void initializeAuthenticationPlugins(); diff --git a/src/rocketchataccount.cpp b/src/rocketchataccount.cpp --- a/src/rocketchataccount.cpp +++ b/src/rocketchataccount.cpp @@ -77,13 +77,10 @@ initializeAuthenticationPlugins(); - //TODO add account name. - initializeSettings(accountFileName); + loadSettings(accountFileName); mRocketChatBackend = new RocketChatBackend(this, this); - loadSettings(); - //After loadSettings mEmojiManager = new EmojiManager(this); mEmojiManager->setServerUrl(mSettings->serverUrl()); @@ -125,14 +122,14 @@ delete mRuqolaLogger; } -void RocketChatAccount::initializeSettings(const QString &accountFileName) +void RocketChatAccount::loadSettings(const QString &accountFileName) { delete mSettings; mSettings = new RocketChatAccountSettings(accountFileName, this); - connect(mSettings, &RocketChatAccountSettings::loginStatusChanged, this, &RocketChatAccount::loginStatusChanged); connect(mSettings, &RocketChatAccountSettings::serverURLChanged, this, &RocketChatAccount::serverUrlChanged); connect(mSettings, &RocketChatAccountSettings::userIDChanged, this, &RocketChatAccount::userIDChanged); connect(mSettings, &RocketChatAccountSettings::userNameChanged, this, &RocketChatAccount::userNameChanged); + connect(mSettings, &RocketChatAccountSettings::passwordChanged, this, &RocketChatAccount::passwordChanged); } void RocketChatAccount::slotNeedToUpdateNotification() @@ -218,11 +215,6 @@ return mRocketChatBackend; } -void RocketChatAccount::loadSettings() -{ - mSettings->loadSettings(); -} - MessageQueue *RocketChatAccount::messageQueue() const { return mMessageQueue; @@ -788,7 +780,7 @@ //Initialize new account room ManagerDataPaths::self()->initializeAccountPath(accountname); qDebug() << "void RocketChatAccount::setAccountName(const QString &servername)"<accountConfigFileName(accountname)); + loadSettings(ManagerDataPaths::self()->accountConfigFileName(accountname)); settings()->setAccountName(accountname); } diff --git a/src/rocketchataccountsettings.h b/src/rocketchataccountsettings.h --- a/src/rocketchataccountsettings.h +++ b/src/rocketchataccountsettings.h @@ -24,6 +24,11 @@ #include #include #include "libruqola_private_export.h" + +namespace QKeychain { + class Job; +} + class QSettings; class LIBRUQOLACORE_EXPORT RocketChatAccountSettings : public QObject { @@ -52,18 +57,19 @@ QString password() const; void setPassword(const QString &password); - void loadSettings(); - Q_SIGNALS: void serverURLChanged(); void userNameChanged(); void userIDChanged(); - void loginStatusChanged(); void accountNameChanged(); + void passwordChanged(); private: Q_DISABLE_COPY(RocketChatAccountSettings) void initializeSettings(const QString &accountFileName); + void slotPasswordRead(QKeychain::Job *job); + void slotPasswordWritten(QKeychain::Job *job); + QString mUserId; QString mAuthToken; QString mServerUrl; diff --git a/src/rocketchataccountsettings.cpp b/src/rocketchataccountsettings.cpp --- a/src/rocketchataccountsettings.cpp +++ b/src/rocketchataccountsettings.cpp @@ -21,10 +21,16 @@ #include "rocketchataccountsettings.h" #include "managerdatapaths.h" #include "ruqola_debug.h" +#include #include #include +#if HAVE_QT5KEYCHAIN +#include +using namespace QKeychain; +#endif + RocketChatAccountSettings::RocketChatAccountSettings(const QString &accountFileName, QObject *parent) : QObject(parent) { @@ -42,6 +48,43 @@ delete mSetting; mSetting = new QSettings(accountFileName, QSettings::IniFormat); qCDebug(RUQOLA_LOG) << "accountFileName "<value(QStringLiteral("serverURL"), QStringLiteral("open.rocket.chat")).toString(); + mUserName = mSetting->value(QStringLiteral("username")).toString(); + mUserId = mSetting->value(QStringLiteral("userID")).toString(); + mAuthToken = mSetting->value(QStringLiteral("authToken")).toString(); + mAccountName = mSetting->value(QStringLiteral("accountName")).toString(); + +#if HAVE_QT5KEYCHAIN + auto readJob = new ReadPasswordJob(QStringLiteral("Ruqola"), this); + connect(readJob, &Job::finished, this, &RocketChatAccountSettings::slotPasswordRead); + readJob->setKey(mAccountName); + readJob->start(); +#endif +} + +void RocketChatAccountSettings::slotPasswordRead(QKeychain::Job *baseJob) +{ +#if HAVE_QT5KEYCHAIN + ReadPasswordJob *job = qobject_cast(baseJob); + Q_ASSERT(job); + if (!job->error()) { + mPassword = job->textData(); + qCDebug(RUQOLA_LOG) << "OK, we have the password now"; + Q_EMIT passwordChanged(); + } +#else + Q_UNUSED(baseJob); +#endif +} + +void RocketChatAccountSettings::slotPasswordWritten(QKeychain::Job *baseJob) +{ +#if HAVE_QT5KEYCHAIN + if (baseJob->error()) { + qCWarning(RUQOLA_LOG) << "Error writing password using QKeychain:" << baseJob->errorString(); + } +#endif } QString RocketChatAccountSettings::userId() const @@ -81,23 +124,24 @@ mPassword.clear(); } -void RocketChatAccountSettings::loadSettings() -{ - mServerUrl = mSetting->value(QStringLiteral("serverURL"), QStringLiteral("open.rocket.chat")).toString(); - mUserName = mSetting->value(QStringLiteral("username")).toString(); - mUserId = mSetting->value(QStringLiteral("userID")).toString(); - mAuthToken = mSetting->value(QStringLiteral("authToken")).toString(); - mAccountName = mSetting->value(QStringLiteral("accountName")).toString(); -} - QString RocketChatAccountSettings::password() const { return mPassword; } void RocketChatAccountSettings::setPassword(const QString &password) { mPassword = password; + +#if HAVE_QT5KEYCHAIN + auto writeJob = new WritePasswordJob(QStringLiteral("Ruqola"), this); + connect(writeJob, &Job::finished, this, &RocketChatAccountSettings::slotPasswordWritten); + writeJob->setKey(mAccountName); + writeJob->setTextData(mPassword); + writeJob->start(); +#endif + + Q_EMIT passwordChanged(); } QString RocketChatAccountSettings::userName() const