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