diff --git a/python/core/auto_generated/auth/qgsauthmanager.sip.in b/python/core/auto_generated/auth/qgsauthmanager.sip.in index 4805e289068d..c7969d26b658 100644 --- a/python/core/auto_generated/auth/qgsauthmanager.sip.in +++ b/python/core/auto_generated/auth/qgsauthmanager.sip.in @@ -147,7 +147,7 @@ Check whether supplied password is the same as the one already set bool resetMasterPassword( const QString &newpass, const QString &oldpass, bool keepbackup, QString *backuppath /In,Out/ = 0 ); %Docstring Reset the master password to a new one, then re-encrypt all previous -configs in a new database file, optionally backup curren database +configs in a new database file, optionally backup current database :param newpass: New master password to replace existing :param oldpass: Current master password to replace existing @@ -157,6 +157,7 @@ configs in a new database file, optionally backup curren database + void setScheduledAuthDatabaseEraseRequestEmitted( bool emitted ); %Docstring Re-emit a signal to schedule an optional erase of authentication database. @@ -773,6 +774,7 @@ Store the password manager into the wallet Available in Python bindings since QGIS 3.8.0 %End + static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME; static const QString AUTH_MAN_TAG; diff --git a/src/core/auth/qgsauthmanager.cpp b/src/core/auth/qgsauthmanager.cpp index 1a7c2662abe9..5cb741d95c8a 100644 --- a/src/core/auth/qgsauthmanager.cpp +++ b/src/core/auth/qgsauthmanager.cpp @@ -802,6 +802,17 @@ bool QgsAuthManager::resetMasterPassword( const QString &newpass, const QString return true; } +bool QgsAuthManager::resetMasterPasswordUsingStoredPasswordHelper( const QString &newpass, bool keepbackup, QString *backuppath ) +{ + if ( !verifyStoredPasswordHelperPassword() ) + { + emit passwordHelperMessageOut( tr( "Master password stored in your %1 is not valid" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING ); + return false; + } + + return resetMasterPassword( newpass, passwordHelperRead(), keepbackup, backuppath ); +} + void QgsAuthManager::setScheduledAuthDatabaseErase( bool scheduleErase ) { mScheduledDbErase = scheduleErase; @@ -3070,6 +3081,19 @@ bool QgsAuthManager::passwordHelperSync() return false; } +bool QgsAuthManager::verifyStoredPasswordHelperPassword() +{ + if ( !passwordHelperEnabled() ) + return false; + + const QString currentPass = passwordHelperRead(); + if ( !currentPass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) ) + { + return verifyMasterPassword( currentPass ); + } + return false; +} + ////////////////// Certificate calls - end /////////////////////// diff --git a/src/core/auth/qgsauthmanager.h b/src/core/auth/qgsauthmanager.h index c64b16d4fed9..0ad9e42f6957 100644 --- a/src/core/auth/qgsauthmanager.h +++ b/src/core/auth/qgsauthmanager.h @@ -153,7 +153,7 @@ class CORE_EXPORT QgsAuthManager : public QObject /** * Reset the master password to a new one, then re-encrypt all previous - * configs in a new database file, optionally backup curren database + * configs in a new database file, optionally backup current database * \param newpass New master password to replace existing * \param oldpass Current master password to replace existing * \param keepbackup Whether to keep the generated backup of current database @@ -161,6 +161,21 @@ class CORE_EXPORT QgsAuthManager : public QObject */ bool resetMasterPassword( const QString &newpass, const QString &oldpass, bool keepbackup, QString *backuppath SIP_INOUT = nullptr ); + /** + * Reset the master password to a new one, then re-encrypt all previous + * configs in a new database file, optionally backup current database. + * + * The old password will automatically be retrieved from the password helper. + * + * \param newpass New master password to replace existing + * \param keepbackup Whether to keep the generated backup of current database + * \param backuppath Where the backup is located, if kept + * + * \note Not available in Python bindings + * \since QGIS 3.36 + */ + bool resetMasterPasswordUsingStoredPasswordHelper( const QString &newpass, bool keepbackup, QString *backuppath = nullptr ) SIP_SKIP; + /** * Whether there is a scheduled opitonal erase of authentication database. * \note not available in Python bindings @@ -704,6 +719,15 @@ class CORE_EXPORT QgsAuthManager : public QObject */ bool passwordHelperSync(); + /** + * Verify the password stored in the password helper. + * + * + * \note Not available in Python bindings + * \since QGIS 3.36 + */ + bool verifyStoredPasswordHelperPassword() SIP_SKIP; + //! The display name of the password helper (platform dependent) static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME; diff --git a/src/gui/auth/qgsauthguiutils.cpp b/src/gui/auth/qgsauthguiutils.cpp index 6ea4efb01d6c..edb029cc7782 100644 --- a/src/gui/auth/qgsauthguiutils.cpp +++ b/src/gui/auth/qgsauthguiutils.cpp @@ -213,20 +213,38 @@ void QgsAuthGuiUtils::resetMasterPassword( QgsMessageBar *msgbar, QWidget *pare // get new password via dialog; do current password verification in-dialog QString newpass; QString oldpass; + bool keepbackup = false; QgsMasterPasswordResetDialog dlg( parent ); - if ( !dlg.requestMasterPasswordReset( &newpass, &oldpass, &keepbackup ) ) + QString backuppath; + if ( QgsApplication::authManager()->verifyStoredPasswordHelperPassword() + && ( QgsApplication::authManager()->masterPasswordIsSet() || QgsApplication::authManager()->setMasterPassword( true ) ) ) { - QgsDebugMsgLevel( QStringLiteral( "Master password reset: input canceled by user" ), 2 ); - return; + dlg.useDummyOldPassword(); + if ( !dlg.requestMasterPasswordReset( &newpass, &oldpass, &keepbackup ) ) + { + QgsDebugMsgLevel( QStringLiteral( "Master password reset: input canceled by user" ), 2 ); + return; + } + if ( !QgsApplication::authManager()->resetMasterPasswordUsingStoredPasswordHelper( newpass, keepbackup, &backuppath ) ) + { + msg = QObject::tr( "Master password FAILED to be reset" ); + level = Qgis::MessageLevel::Warning; + } } - - QString backuppath; - if ( !QgsApplication::authManager()->resetMasterPassword( newpass, oldpass, keepbackup, &backuppath ) ) + else { - msg = QObject::tr( "Master password FAILED to be reset" ); - level = Qgis::MessageLevel::Warning; + if ( !dlg.requestMasterPasswordReset( &newpass, &oldpass, &keepbackup ) ) + { + QgsDebugMsgLevel( QStringLiteral( "Master password reset: input canceled by user" ), 2 ); + return; + } + if ( !QgsApplication::authManager()->resetMasterPassword( newpass, oldpass, keepbackup, &backuppath ) ) + { + msg = QObject::tr( "Master password FAILED to be reset" ); + level = Qgis::MessageLevel::Warning; + } } if ( !backuppath.isEmpty() ) diff --git a/src/gui/auth/qgsauthmasterpassresetdialog.cpp b/src/gui/auth/qgsauthmasterpassresetdialog.cpp index da8527716cb2..a287c5ff864b 100644 --- a/src/gui/auth/qgsauthmasterpassresetdialog.cpp +++ b/src/gui/auth/qgsauthmasterpassresetdialog.cpp @@ -43,6 +43,12 @@ QgsMasterPasswordResetDialog::QgsMasterPasswordResetDialog( QWidget *parent ) } } +void QgsMasterPasswordResetDialog::useDummyOldPassword() +{ + leMasterPassCurrent->setText( QStringLiteral( "***************" ) ); + leMasterPassCurrent->setEnabled( false ); +} + bool QgsMasterPasswordResetDialog::requestMasterPasswordReset( QString *newpass, QString *oldpass, bool *keepbackup ) { if ( !QgsApplication::authManager()->isDisabled() ) @@ -81,9 +87,17 @@ void QgsMasterPasswordResetDialog::leMasterPassNew_textChanged( const QString &p void QgsMasterPasswordResetDialog::validatePasswords() { - const QString ss1 = mPassCurOk ? QgsAuthGuiUtils::greenTextStyleSheet( QStringLiteral( "QLineEdit" ) ) - : QgsAuthGuiUtils::redTextStyleSheet( QStringLiteral( "QLineEdit" ) ); - leMasterPassCurrent->setStyleSheet( ss1 ); + if ( leMasterPassCurrent->isEnabled() ) + { + const QString ss1 = mPassCurOk ? QgsAuthGuiUtils::greenTextStyleSheet( QStringLiteral( "QLineEdit" ) ) + : QgsAuthGuiUtils::redTextStyleSheet( QStringLiteral( "QLineEdit" ) ); + leMasterPassCurrent->setStyleSheet( ss1 ); + } + else + { + leMasterPassCurrent->setStyleSheet( QString() ); + } + const QString ss2 = mPassNewOk ? QgsAuthGuiUtils::greenTextStyleSheet( QStringLiteral( "QLineEdit" ) ) : QgsAuthGuiUtils::redTextStyleSheet( QStringLiteral( "QLineEdit" ) ); leMasterPassNew->setStyleSheet( ss2 ); diff --git a/src/gui/auth/qgsauthmasterpassresetdialog.h b/src/gui/auth/qgsauthmasterpassresetdialog.h index 72bb547b3f47..cc4d367b8633 100644 --- a/src/gui/auth/qgsauthmasterpassresetdialog.h +++ b/src/gui/auth/qgsauthmasterpassresetdialog.h @@ -42,6 +42,15 @@ class GUI_EXPORT QgsMasterPasswordResetDialog : public QDialog, private Ui::QgsM public: explicit QgsMasterPasswordResetDialog( QWidget *parent = nullptr ); + /** + * Use an dummy password for the "old" password option, and disable user entry + * of old password. + * + * This can be used when the old password is not required by the user (i.e. it + * will be retrieved from the system password helper). + */ + void useDummyOldPassword(); + bool requestMasterPasswordReset( QString *newpass, QString *oldpass, bool *keepbackup ); private slots: