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