QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgscredentialdialog.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgscredentialdialog.cpp - description
3 -------------------
4 begin : February 2010
5 copyright : (C) 2010 by Juergen E. Fischer
6 email : jef at norbit dot de
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18#include "qgscredentialdialog.h"
19
20#include "qgsauthmanager.h"
21#include "qgslogger.h"
22#include "qgsapplication.h"
23#include "qgsgui.h"
24
25#include <QPushButton>
26#include <QMenu>
27#include <QToolButton>
28#include <QThread>
29#include <QTimer>
30#include <QGlobalStatic>
31
32QMutex QgsCredentialDialog::sIgnoredConnectionsCacheMutex;
33typedef QSet<QString> IgnoredConnectionsSet;
34
36Q_GLOBAL_STATIC( IgnoredConnectionsSet, sIgnoredConnectionsCache );
37
38
39static QString invalidStyle_( const QString &selector = QStringLiteral( "QLineEdit" ) )
40{
41 return QStringLiteral( "%1{color: rgb(200, 0, 0);}" ).arg( selector );
42}
43
44QgsCredentialDialog::QgsCredentialDialog( QWidget *parent, Qt::WindowFlags fl )
45 : QDialog( parent, fl )
46
47{
48 setupUi( this );
50
51 connect( leMasterPass, &QgsPasswordLineEdit::textChanged, this, &QgsCredentialDialog::leMasterPass_textChanged );
52 connect( leMasterPassVerify, &QgsPasswordLineEdit::textChanged, this, &QgsCredentialDialog::leMasterPassVerify_textChanged );
53 connect( chkbxEraseAuthDb, &QCheckBox::toggled, this, &QgsCredentialDialog::chkbxEraseAuthDb_toggled );
54 setInstance( this );
56 this, &QgsCredentialDialog::requestCredentials,
57 Qt::BlockingQueuedConnection );
59 this, &QgsCredentialDialog::requestCredentialsMasterPassword,
60 Qt::BlockingQueuedConnection );
61
62 // Setup ignore button
63 mIgnoreButton->setToolTip( tr( "All requests for this connection will be automatically rejected" ) );
64 QMenu *menu = new QMenu( mIgnoreButton );
65 QAction *ignoreTemporarily = new QAction( tr( "Ignore for 10 Seconds" ), menu );
66 ignoreTemporarily->setToolTip( tr( "All requests for this connection will be automatically rejected for 10 seconds" ) );
67 QAction *ignoreForSession = new QAction( tr( "Ignore for Session" ), menu );
68 ignoreForSession->setToolTip( tr( "All requests for this connection will be automatically rejected for the duration of the current session" ) );
69 menu->addAction( ignoreTemporarily );
70 menu->addAction( ignoreForSession );
71 connect( ignoreTemporarily, &QAction::triggered, this, [ = ]
72 {
73 mIgnoreMode = IgnoreTemporarily;
74 mIgnoreButton->setText( ignoreTemporarily->text() );
75 mIgnoreButton->setToolTip( ignoreTemporarily->toolTip() );
76 } );
77 connect( ignoreForSession, &QAction::triggered, this, [ = ]
78 {
79 mIgnoreMode = IgnoreForSession;
80 mIgnoreButton->setText( ignoreForSession->text() );
81 mIgnoreButton->setToolTip( ignoreForSession->toolTip() );
82 } );
83 mIgnoreButton->setText( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->text() : ignoreForSession->text() );
84 mIgnoreButton->setToolTip( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->toolTip() : ignoreForSession->toolTip() );
85 mIgnoreButton->setMenu( menu );
86 mIgnoreButton->setMaximumHeight( mOkButton->sizeHint().height() );
87
88 // Connect ok and cancel buttons
89 connect( mOkButton, &QPushButton::clicked, this, &QgsCredentialDialog::accept );
90 connect( mCancelButton, &QPushButton::clicked, this, &QgsCredentialDialog::reject );
91
92 // Keep a cache of ignored connections, and ignore them for 10 seconds.
93 connect( mIgnoreButton, &QPushButton::clicked, this, [ = ]( bool )
94 {
95 const QString realm { labelRealm->text() };
96 {
97 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
98 // Insert the realm in the cache of ignored connections
99 sIgnoredConnectionsCache->insert( realm );
100 }
101 if ( mIgnoreMode == IgnoreTemporarily )
102 {
103 QTimer::singleShot( 10000, nullptr, [ = ]()
104 {
105 QgsDebugMsgLevel( QStringLiteral( "Removing ignored connection from cache: %1" ).arg( realm ), 4 );
106 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
107 sIgnoredConnectionsCache->remove( realm );
108 } );
109 }
110 accept( );
111 } );
112
113 leMasterPass->setPlaceholderText( tr( "Required" ) );
114 chkbxPasswordHelperEnable->setText( tr( "Store/update the master password in your %1" )
116 leUsername->setFocus();
117}
118
119bool QgsCredentialDialog::request( const QString &realm, QString &username, QString &password, const QString &message )
120{
121 bool ok;
122 if ( qApp->thread() != QThread::currentThread() )
123 {
124 QgsDebugMsg( QStringLiteral( "emitting signal" ) );
125 emit credentialsRequested( realm, &username, &password, message, &ok );
126 QgsDebugMsg( QStringLiteral( "signal returned %1 (username=%2)" ).arg( ok ? "true" : "false", username ) );
127 }
128 else
129 {
130 requestCredentials( realm, &username, &password, message, &ok );
131 }
132 return ok;
133}
134
135void QgsCredentialDialog::requestCredentials( const QString &realm, QString *username, QString *password, const QString &message, bool *ok )
136{
137 Q_ASSERT( qApp->thread() == thread() && thread() == QThread::currentThread() );
138 QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );
139 {
140 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
141 if ( sIgnoredConnectionsCache->contains( realm ) )
142 {
143 QgsDebugMsg( QStringLiteral( "Skipping ignored connection: " ) + realm );
144 *ok = false;
145 return;
146 }
147 }
148 stackedWidget->setCurrentIndex( 0 );
149 mIgnoreButton->show();
150 chkbxPasswordHelperEnable->setChecked( QgsApplication::authManager()->passwordHelperEnabled() );
151 labelRealm->setText( realm );
152 leUsername->setText( *username );
153 lePassword->setText( *password );
154 labelMessage->setText( message );
155 labelMessage->setHidden( message.isEmpty() );
156
157 if ( leUsername->text().isEmpty() )
158 leUsername->setFocus();
159 else
160 lePassword->setFocus();
161
162 QWidget *activeWindow = qApp->activeWindow();
163
164 QApplication::setOverrideCursor( Qt::ArrowCursor );
165
166 QgsDebugMsgLevel( QStringLiteral( "exec()" ), 4 );
167 *ok = exec() == QDialog::Accepted;
168 QgsDebugMsgLevel( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ), 4 );
169
170 QApplication::restoreOverrideCursor();
171
172 if ( activeWindow )
173 activeWindow->raise();
174
175 if ( *ok )
176 {
177 *username = leUsername->text();
178 *password = lePassword->text();
179 }
180}
181
182bool QgsCredentialDialog::requestMasterPassword( QString &password, bool stored )
183{
184 bool ok;
185 if ( qApp->thread() != QThread::currentThread() )
186 {
187 QgsDebugMsgLevel( QStringLiteral( "emitting signal" ), 4 );
188 emit credentialsRequestedMasterPassword( &password, stored, &ok );
189 }
190 else
191 {
192 requestCredentialsMasterPassword( &password, stored, &ok );
193 }
194 return ok;
195}
196
197void QgsCredentialDialog::requestCredentialsMasterPassword( QString *password, bool stored, bool *ok )
198{
199 QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );
200 stackedWidget->setCurrentIndex( 1 );
201
202 mIgnoreButton->hide();
203 leMasterPass->setFocus();
204
205 const QString titletxt( stored ? tr( "Enter CURRENT master authentication password" ) : tr( "Set NEW master authentication password" ) );
206 lblPasswordTitle->setText( titletxt );
207
208 chkbxPasswordHelperEnable->setChecked( QgsApplication::authManager()->passwordHelperEnabled() );
209
210 leMasterPassVerify->setVisible( !stored );
211 lblDontForget->setVisible( !stored );
212
213 QApplication::setOverrideCursor( Qt::ArrowCursor );
214
215 grpbxPassAttempts->setVisible( false );
216 int passfailed = 0;
217 while ( true )
218 {
219 mOkButton->setEnabled( false );
220 // TODO: have the number of attempted passwords configurable in auth settings?
221 if ( passfailed >= 3 )
222 {
223 lblSavedForSession->setVisible( false );
224 grpbxPassAttempts->setTitle( tr( "Password attempts: %1" ).arg( passfailed ) );
225 grpbxPassAttempts->setVisible( true );
226 }
227
228 // resize vertically to fit contents
229 QSize s = sizeHint();
230 s.setWidth( width() );
231 resize( s );
232
233 QgsDebugMsgLevel( QStringLiteral( "exec()" ), 4 );
234 *ok = exec() == QDialog::Accepted;
235 QgsDebugMsgLevel( QStringLiteral( "exec(): %1" ).arg( *ok ? "true" : "false" ), 4 );
236
237 if ( *ok )
238 {
239 bool passok = !leMasterPass->text().isEmpty();
240 if ( passok && stored && !chkbxEraseAuthDb->isChecked() )
241 {
242 passok = QgsApplication::authManager()->verifyMasterPassword( leMasterPass->text() );
243 }
244
245 if ( passok && !stored )
246 {
247 passok = ( leMasterPass->text() == leMasterPassVerify->text() );
248 }
249
250 if ( passok || chkbxEraseAuthDb->isChecked() )
251 {
252 if ( stored && chkbxEraseAuthDb->isChecked() )
253 {
255 }
256 else
257 {
258 *password = leMasterPass->text();
259 // Let's store user's preferences to use the password helper
260 if ( chkbxPasswordHelperEnable->isChecked() != QgsApplication::authManager()->passwordHelperEnabled() )
261 {
262 QgsApplication::authManager()->setPasswordHelperEnabled( chkbxPasswordHelperEnable->isChecked() );
263 }
264 }
265 break;
266 }
267 else
268 {
269 if ( stored )
270 ++passfailed;
271
272 leMasterPass->setStyleSheet( invalidStyle_() );
273 if ( leMasterPassVerify->isVisible() )
274 {
275 leMasterPassVerify->setStyleSheet( invalidStyle_() );
276 }
277 }
278 }
279 else
280 {
281 break;
282 }
283
284 if ( passfailed >= 5 )
285 {
286 break;
287 }
288 }
289
290 // don't leave master password in singleton's text field, or the ability to show it
291 leMasterPass->clear();
292 leMasterPassVerify->clear();
293
294 chkbxEraseAuthDb->setChecked( false );
295 lblSavedForSession->setVisible( true );
296
297 // re-enable OK button or non-master-password requests will be blocked
298 // needs to come after leMasterPass->clear() or textChanged auto-slot with disable it again
299 mOkButton->setEnabled( true );
300
301 QApplication::restoreOverrideCursor();
302
303 if ( passfailed >= 5 )
304 {
305 close();
306 }
307}
308
309void QgsCredentialDialog::leMasterPass_textChanged( const QString &pass )
310{
311 leMasterPass->setStyleSheet( QString() );
312 bool passok = !pass.isEmpty(); // regardless of new or comparing existing, empty password disallowed
313 if ( leMasterPassVerify->isVisible() )
314 {
315 leMasterPassVerify->setStyleSheet( QString() );
316 passok = passok && ( leMasterPass->text() == leMasterPassVerify->text() );
317 }
318 mOkButton->setEnabled( passok );
319
320 if ( leMasterPassVerify->isVisible() && !passok )
321 {
322 leMasterPass->setStyleSheet( invalidStyle_() );
323 leMasterPassVerify->setStyleSheet( invalidStyle_() );
324 }
325}
326
327void QgsCredentialDialog::leMasterPassVerify_textChanged( const QString &pass )
328{
329 if ( leMasterPassVerify->isVisible() )
330 {
331 leMasterPass->setStyleSheet( QString() );
332 leMasterPassVerify->setStyleSheet( QString() );
333
334 // empty password disallowed
335 const bool passok = !pass.isEmpty() && ( leMasterPass->text() == leMasterPassVerify->text() );
336 mOkButton->setEnabled( passok );
337 if ( !passok )
338 {
339 leMasterPass->setStyleSheet( invalidStyle_() );
340 leMasterPassVerify->setStyleSheet( invalidStyle_() );
341 }
342 }
343}
344
345void QgsCredentialDialog::chkbxEraseAuthDb_toggled( bool checked )
346{
347 if ( checked )
348 mOkButton->setEnabled( true );
349}
350
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
bool verifyMasterPassword(const QString &compare=QString())
Verify the supplied master password against any existing hash in authentication database.
void setPasswordHelperEnabled(bool enabled)
Password helper enabled setter.
void setScheduledAuthDatabaseErase(bool scheduleErase)
Schedule an optional erase of authentication database, starting when mutex is lockable.
bool passwordHelperEnabled() const
Password helper enabled getter.
static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME
The display name of the password helper (platform dependent)
QgsCredentialDialog(QWidget *parent=nullptr, Qt::WindowFlags fl=QgsGuiUtils::ModalDialogFlags)
QgsCredentialDialog constructor.
bool requestMasterPassword(QString &password, bool stored=false) override
request a master password
bool request(const QString &realm, QString &username, QString &password, const QString &message=QString()) override
request a password
void credentialsRequested(const QString &, QString *, QString *, const QString &, bool *)
void credentialsRequestedMasterPassword(QString *, bool, bool *)
void setInstance(QgsCredentials *instance)
register instance
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Definition: qgsgui.cpp:178
Q_GLOBAL_STATIC(IgnoredConnectionsSet, sIgnoredConnectionsCache)
Temporary cache for ignored connections, to avoid GUI freezing by multiple credentials requests to th...
QSet< QString > IgnoredConnectionsSet
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38