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