QGIS API Documentation 3.28.14-Firenze (exported)
Loading...
Searching...
No Matches
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 "qgsdatasourceuri.h"
22#include "qgslogger.h"
23#include "qgssettings.h"
24#include "qgsapplication.h"
25
26#include <QPushButton>
27#include <QMenu>
28#include <QToolButton>
29#include <QThread>
30#include <QTimer>
31#include <QGlobalStatic>
32
33QMutex QgsCredentialDialog::sIgnoredConnectionsCacheMutex;
34typedef QSet<QString> IgnoredConnectionsSet;
35
37Q_GLOBAL_STATIC( IgnoredConnectionsSet, sIgnoredConnectionsCache );
38
39
40static QString invalidStyle_( const QString &selector = QStringLiteral( "QLineEdit" ) )
41{
42 return QStringLiteral( "%1{color: rgb(200, 0, 0);}" ).arg( selector );
43}
44
45QgsCredentialDialog::QgsCredentialDialog( QWidget *parent, Qt::WindowFlags fl )
46 : QDialog( parent, fl )
47
48{
49 setupUi( this );
50 connect( leMasterPass, &QgsPasswordLineEdit::textChanged, this, &QgsCredentialDialog::leMasterPass_textChanged );
51 connect( leMasterPassVerify, &QgsPasswordLineEdit::textChanged, this, &QgsCredentialDialog::leMasterPassVerify_textChanged );
52 connect( chkbxEraseAuthDb, &QCheckBox::toggled, this, &QgsCredentialDialog::chkbxEraseAuthDb_toggled );
53 setInstance( this );
55 this, &QgsCredentialDialog::requestCredentials,
56 Qt::BlockingQueuedConnection );
58 this, &QgsCredentialDialog::requestCredentialsMasterPassword,
59 Qt::BlockingQueuedConnection );
60
61 // Setup ignore button
62 mIgnoreButton->setToolTip( tr( "All requests for this connection will be automatically rejected" ) );
63 QMenu *menu = new QMenu( mIgnoreButton );
64 QAction *ignoreTemporarily = new QAction( tr( "Ignore for 10 Seconds" ), menu );
65 ignoreTemporarily->setToolTip( tr( "All requests for this connection will be automatically rejected for 10 seconds" ) );
66 QAction *ignoreForSession = new QAction( tr( "Ignore for Session" ), menu );
67 ignoreForSession->setToolTip( tr( "All requests for this connection will be automatically rejected for the duration of the current session" ) );
68 menu->addAction( ignoreTemporarily );
69 menu->addAction( ignoreForSession );
70 connect( ignoreTemporarily, &QAction::triggered, this, [ = ]
71 {
72 mIgnoreMode = IgnoreTemporarily;
73 mIgnoreButton->setText( ignoreTemporarily->text() );
74 mIgnoreButton->setToolTip( ignoreTemporarily->toolTip() );
75 } );
76 connect( ignoreForSession, &QAction::triggered, this, [ = ]
77 {
78 mIgnoreMode = IgnoreForSession;
79 mIgnoreButton->setText( ignoreForSession->text() );
80 mIgnoreButton->setToolTip( ignoreForSession->toolTip() );
81 } );
82 mIgnoreButton->setText( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->text() : ignoreForSession->text() );
83 mIgnoreButton->setToolTip( mIgnoreMode == IgnoreTemporarily ? ignoreTemporarily->toolTip() : ignoreForSession->toolTip() );
84 mIgnoreButton->setMenu( menu );
85 mIgnoreButton->setMaximumHeight( mOkButton->sizeHint().height() );
86
87 // Connect ok and cancel buttons
88 connect( mOkButton, &QPushButton::clicked, this, &QgsCredentialDialog::accept );
89 connect( mCancelButton, &QPushButton::clicked, this, &QgsCredentialDialog::reject );
90
91 // Keep a cache of ignored connections, and ignore them for 10 seconds.
92 connect( mIgnoreButton, &QPushButton::clicked, this, [ = ]( bool )
93 {
94 const QString realm { mRealm };
95 {
96 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
97 // Insert the realm in the cache of ignored connections
98 sIgnoredConnectionsCache->insert( realm );
99 }
100 if ( mIgnoreMode == IgnoreTemporarily )
101 {
102 QTimer::singleShot( 10000, nullptr, [ = ]()
103 {
104 QgsDebugMsgLevel( QStringLiteral( "Removing ignored connection from cache: %1" ).arg( realm ), 4 );
105 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
106 sIgnoredConnectionsCache->remove( realm );
107 } );
108 }
109 accept( );
110 } );
111
112 leMasterPass->setPlaceholderText( tr( "Required" ) );
113 chkbxPasswordHelperEnable->setText( tr( "Store/update the master password in your %1" )
115 leUsername->setFocus();
116}
117
118bool QgsCredentialDialog::request( const QString &realm, QString &username, QString &password, const QString &message )
119{
120 bool ok;
121 if ( qApp->thread() != QThread::currentThread() )
122 {
123 QgsDebugMsg( QStringLiteral( "emitting signal" ) );
124 emit credentialsRequested( realm, &username, &password, message, &ok );
125 QgsDebugMsg( QStringLiteral( "signal returned %1 (username=%2)" ).arg( ok ? "true" : "false", username ) );
126 }
127 else
128 {
129 requestCredentials( realm, &username, &password, message, &ok );
130 }
131 return ok;
132}
133
134void QgsCredentialDialog::requestCredentials( const QString &realm, QString *username, QString *password, const QString &message, bool *ok )
135{
136 Q_ASSERT( qApp->thread() == thread() && thread() == QThread::currentThread() );
137 QgsDebugMsgLevel( QStringLiteral( "Entering." ), 4 );
138 {
139 const QMutexLocker locker( &sIgnoredConnectionsCacheMutex );
140 if ( sIgnoredConnectionsCache->contains( realm ) )
141 {
142 QgsDebugMsg( QStringLiteral( "Skipping ignored connection: " ) + realm );
143 *ok = false;
144 return;
145 }
146 }
147 stackedWidget->setCurrentIndex( 0 );
148 mIgnoreButton->show();
149 chkbxPasswordHelperEnable->setChecked( QgsApplication::authManager()->passwordHelperEnabled() );
150 labelRealm->setText( QgsDataSourceUri::removePassword( realm ) );
151 mRealm = 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 QString removePassword(const QString &aUri)
Removes the password element from a URI.
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