Skip to content

Commit

Permalink
Merge pull request #32546 from elpaso/pg-broken-credentials-cache
Browse files Browse the repository at this point in the history
[feature] Ignored credentials temporary cache
  • Loading branch information
elpaso committed Nov 1, 2019
2 parents 17a4a34 + 730c875 commit 7b38ca8
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 61 deletions.
91 changes: 82 additions & 9 deletions src/gui/qgscredentialdialog.cpp
Expand Up @@ -23,7 +23,18 @@
#include "qgsapplication.h"

#include <QPushButton>
#include <QMenu>
#include <QToolButton>
#include <QThread>
#include <QTimer>
#include <QGlobalStatic>

QMutex QgsCredentialDialog::sIgnoredConnectionsCacheMutex;
typedef QSet<QString> IgnoredConnectionsSet;

//! Temporary cache for ignored connections, to avoid GUI freezing by multiple credentials requests to the same connection
Q_GLOBAL_STATIC( IgnoredConnectionsSet, sIgnoredConnectionsCache );


static QString invalidStyle_( const QString &selector = QStringLiteral( "QLineEdit" ) )
{
Expand All @@ -45,7 +56,58 @@ QgsCredentialDialog::QgsCredentialDialog( QWidget *parent, Qt::WindowFlags fl )
connect( this, &QgsCredentialDialog::credentialsRequestedMasterPassword,
this, &QgsCredentialDialog::requestCredentialsMasterPassword,
Qt::BlockingQueuedConnection );
mOkButton = buttonBox->button( QDialogButtonBox::Ok );

// Setup ignore button
mIgnoreButton->setToolTip( tr( "All requests for this connection will be automatically rejected" ) );
QMenu *menu = new QMenu( mIgnoreButton );
QAction *ignoreTemporarily = new QAction( tr( "Ignore for 10 Seconds" ), menu );
ignoreTemporarily->setToolTip( tr( "All requests for this connection will be automatically rejected for 10 seconds" ) );
QAction *ignoreForSession = new QAction( tr( "Ignore for Session" ), menu );
ignoreForSession->setToolTip( tr( "All requests for this connection will be automatically rejected for the duration of the current session" ) );
menu->addAction( ignoreTemporarily );
menu->addAction( ignoreForSession );
connect( ignoreTemporarily, &QAction::triggered, [ = ]
{
mIgnoreMode = IgnoreTemporarily;
//mIgnoreButton->setText( ignoreTemporarily->text() );
mIgnoreButton->setToolTip( ignoreTemporarily->toolTip() );
} );
connect( ignoreForSession, &QAction::triggered, [ = ]
{
mIgnoreMode = IgnoreForSession;
//mIgnoreButton->setText( ignoreForSession->text() );
mIgnoreButton->setToolTip( ignoreForSession->toolTip() );
} );
mIgnoreButton->setText( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->text() : ignoreForSession->text() );
mIgnoreButton->setToolTip( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->toolTip() : ignoreForSession->toolTip() );
mIgnoreButton->setMenu( menu );
mIgnoreButton->setMaximumHeight( mOkButton->sizeHint().height() );

// Connect ok and cancel buttons
connect( mOkButton, &QPushButton::clicked, this, &QgsCredentialDialog::accept );
connect( mCancelButton, &QPushButton::clicked, this, &QgsCredentialDialog::reject );

// Keep a cache of ignored connections, and ignore them for 10 seconds.
connect( mIgnoreButton, &QPushButton::clicked, [ = ]( bool )
{
const QString realm { labelRealm->text() };
{
QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
// Insert the realm in the cache of ignored connections
sIgnoredConnectionsCache->insert( realm );
}
if ( mIgnoreMode == IgnoreTemporarily )
{
QTimer::singleShot( 10000, [ = ]()
{
QgsDebugMsgLevel( QStringLiteral( "Removing ignored connection from cache: %1" ).arg( realm ), 4 );
QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
sIgnoredConnectionsCache->remove( realm );
} );
}
accept( );
} );

leMasterPass->setPlaceholderText( tr( "Required" ) );
chkbxPasswordHelperEnable->setText( tr( "Store/update the master password in your %1" )
.arg( QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
Expand All @@ -71,9 +133,18 @@ bool QgsCredentialDialog::request( const QString &realm, QString &username, QStr
void QgsCredentialDialog::requestCredentials( const QString &realm, QString *username, QString *password, const QString &message, bool *ok )
{
Q_ASSERT( qApp->thread() == thread() && thread() == QThread::currentThread() );
QgsDebugMsg( QStringLiteral( "Entering." ) );
QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );
{
QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
if ( sIgnoredConnectionsCache->contains( realm ) )
{
QgsDebugMsg( QStringLiteral( "Skipping ignored connection: " ) + realm );
*ok = false;
return;
}
}
stackedWidget->setCurrentIndex( 0 );

mIgnoreButton->show();
chkbxPasswordHelperEnable->setChecked( QgsApplication::authManager()->passwordHelperEnabled() );
labelRealm->setText( realm );
leUsername->setText( *username );
Expand All @@ -90,9 +161,9 @@ void QgsCredentialDialog::requestCredentials( const QString &realm, QString *use

QApplication::setOverrideCursor( Qt::ArrowCursor );

QgsDebugMsg( QStringLiteral( "exec()" ) );
QgsDebugMsgLevel( QStringLiteral( "exec()" ), 4 );
*ok = exec() == QDialog::Accepted;
QgsDebugMsg( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ) );
QgsDebugMsgLevel( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ), 4 );

QApplication::restoreOverrideCursor();

Expand All @@ -111,7 +182,7 @@ bool QgsCredentialDialog::requestMasterPassword( QString &password, bool stored
bool ok;
if ( qApp->thread() != QThread::currentThread() )
{
QgsDebugMsg( QStringLiteral( "emitting signal" ) );
QgsDebugMsgLevel( QStringLiteral( "emitting signal" ), 4 );
emit credentialsRequestedMasterPassword( &password, stored, &ok );
}
else
Expand All @@ -123,8 +194,10 @@ bool QgsCredentialDialog::requestMasterPassword( QString &password, bool stored

void QgsCredentialDialog::requestCredentialsMasterPassword( QString *password, bool stored, bool *ok )
{
QgsDebugMsg( QStringLiteral( "Entering." ) );
QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );
stackedWidget->setCurrentIndex( 1 );

mIgnoreButton->hide();
leMasterPass->setFocus();

QString titletxt( stored ? tr( "Enter CURRENT master authentication password" ) : tr( "Set NEW master authentication password" ) );
Expand Down Expand Up @@ -155,9 +228,9 @@ void QgsCredentialDialog::requestCredentialsMasterPassword( QString *password, b
s.setWidth( width() );
resize( s );

QgsDebugMsg( QStringLiteral( "exec()" ) );
QgsDebugMsgLevel( QStringLiteral( "exec()" ), 4 );
*ok = exec() == QDialog::Accepted;
QgsDebugMsg( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ) );
QgsDebugMsgLevel( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ), 4 );

if ( *ok )
{
Expand Down
16 changes: 15 additions & 1 deletion src/gui/qgscredentialdialog.h
Expand Up @@ -63,7 +63,21 @@ class GUI_EXPORT QgsCredentialDialog : public QDialog, public QgsCredentials, pr
bool requestMasterPassword( QString &password SIP_INOUT, bool stored = false ) override;

private:
QPushButton *mOkButton = nullptr;

/**
* The ConnectionsIgnoreMode enum represent the modes a connection can be ignored
*/
enum ConnectionsIgnoreMode
{
IgnoreTemporarily, //!< Ignore for a certain amount of time
IgnoreForSession, //!< Ignore for the whole QGIS session
};

//! mutex for the static ignored connections cache
static QMutex sIgnoredConnectionsCacheMutex;

ConnectionsIgnoreMode mIgnoreMode = ConnectionsIgnoreMode::IgnoreTemporarily;

};

#endif
5 changes: 4 additions & 1 deletion src/providers/postgres/qgspostgresconn.cpp
Expand Up @@ -144,6 +144,7 @@ Oid QgsPostgresResult::PQoidValue()

QMap<QString, QgsPostgresConn *> QgsPostgresConn::sConnectionsRO;
QMap<QString, QgsPostgresConn *> QgsPostgresConn::sConnectionsRW;

const int QgsPostgresConn::GEOM_TYPE_SELECT_LIMIT = 100;

QgsPostgresConn *QgsPostgresConn::connectDb( const QString &conninfo, bool readonly, bool shared, bool transaction )
Expand Down Expand Up @@ -218,6 +219,7 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s
, mTransaction( transaction )
, mLock( QMutex::Recursive )
{

QgsDebugMsg( QStringLiteral( "New PostgreSQL connection for " ) + conninfo );

// expand connectionInfo
Expand Down Expand Up @@ -286,7 +288,9 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s
++i;
bool ok = QgsCredentials::instance()->get( conninfo, username, password, PQerrorMessage() );
if ( !ok )
{
break;
}

PQfinish();

Expand Down Expand Up @@ -360,7 +364,6 @@ QgsPostgresConn::QgsPostgresConn( const QString &conninfo, bool readOnly, bool s
PQexecNR( QStringLiteral( "SET application_name='QGIS'" ) );
}


PQsetNoticeProcessor( mConn, noticeProcessor, nullptr );
}

Expand Down
110 changes: 60 additions & 50 deletions src/ui/qgscredentialdialog.ui
Expand Up @@ -6,28 +6,15 @@
<rect>
<x>0</x>
<y>0</y>
<width>396</width>
<height>289</height>
<width>358</width>
<height>293</height>
</rect>
</property>
<property name="windowTitle">
<string>Enter Credentials</string>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="3" column="0" colspan="2">
<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>
<item row="1" column="0" colspan="2">
<item row="0" column="0">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>1</number>
Expand Down Expand Up @@ -212,6 +199,62 @@ font-style: italic;
</widget>
</widget>
</item>
<item row="1" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="mOkButton">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="mIgnoreButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="toolTip">
<string>All requests for this connection will be automatically rejected for the next 60 seconds</string>
</property>
<property name="text">
<string>Ignore</string>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextOnly</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mCancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
Expand All @@ -229,38 +272,5 @@ font-style: italic;
<tabstop>chkbxEraseAuthDb</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>QgsCredentialDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>QgsCredentialDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
<connections/>
</ui>

0 comments on commit 7b38ca8

Please sign in to comment.