QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsauthmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsauthmanager.cpp
3  ---------------------
4  begin : October 5, 2014
5  copyright : (C) 2014 by Boundless Spatial, Inc. USA
6  author : Larry Shaffer
7  email : lshaffer at boundlessgeo dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgsauthmanager.h"
18 
19 #include <QDir>
20 #include <QEventLoop>
21 #include <QFile>
22 #include <QFileInfo>
23 #include <QMutexLocker>
24 #include <QObject>
25 #include <QSet>
26 #include <QSqlDatabase>
27 #include <QSqlError>
28 #include <QSqlQuery>
29 #include <QTextStream>
30 #include <QTime>
31 #include <QTimer>
32 #include <QVariant>
33 #include <QSqlDriver>
34 #include <QDomElement>
35 #include <QDomDocument>
36 #include <QRegularExpression>
37 
38 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
39 #include <QRandomGenerator>
40 #endif
41 
42 #include <QtCrypto>
43 
44 #ifndef QT_NO_SSL
45 #include <QSslConfiguration>
46 #endif
47 
48 // QtKeyChain library
49 #include "keychain.h"
50 
51 // QGIS includes
52 #include "qgsapplication.h"
53 #include "qgsauthcertutils.h"
54 #include "qgsauthcrypto.h"
55 #include "qgsauthmethod.h"
56 #include "qgsauthmethodmetadata.h"
57 #include "qgsauthmethodregistry.h"
58 #include "qgscredentials.h"
59 #include "qgslogger.h"
60 #include "qgsmessagelog.h"
61 #include "qgssettings.h"
62 #include "qgsruntimeprofiler.h"
63 
64 QgsAuthManager *QgsAuthManager::sInstance = nullptr;
65 
66 const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral( "auth_configs" );
67 const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral( "auth_pass" );
68 const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral( "auth_settings" );
69 const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral( "auth_identities" );
70 const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral( "auth_servers" );
71 const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral( "auth_authorities" );
72 const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral( "auth_trust" );
73 const QString QgsAuthManager::AUTH_MAN_TAG = QObject::tr( "Authentication Manager" );
74 const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
75 
76 
77 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME( "QGIS-Master-Password" );
78 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME( "QGIS" );
79 
80 
81 
82 #if defined(Q_OS_MAC)
83 const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Keychain" );
84 #elif defined(Q_OS_WIN)
85 const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
86 #elif defined(Q_OS_LINUX)
87 const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( QStringLiteral( "Wallet/KeyRing" ) );
88 #else
89 const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
90 #endif
91 
93 {
94  static QMutex sMutex;
95  QMutexLocker locker( &sMutex );
96  if ( !sInstance )
97  {
98  sInstance = new QgsAuthManager( );
99  }
100  return sInstance;
101 }
102 
103 
105 {
106 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
107  mMutex.reset( new QMutex( QMutex::Recursive ) );
108  mMasterPasswordMutex.reset( new QMutex( QMutex::Recursive ) );
109 #else
110  mMutex = std::make_unique<QRecursiveMutex>();
111  mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
112 #endif
113  connect( this, &QgsAuthManager::messageOut,
114  this, &QgsAuthManager::writeToConsole );
115 }
116 
118 {
119  QSqlDatabase authdb;
120  if ( isDisabled() )
121  return authdb;
122 
123  // while everything we use from QSqlDatabase here is thread safe, we need to ensure
124  // that the connection cleanup on thread finalization happens in a predictable order
125  QMutexLocker locker( mMutex.get() );
126 
127  // Sharing the same connection between threads is not allowed.
128  // We use a dedicated connection for each thread requiring access to the database,
129  // using the thread address as connection name.
130  const QString connectionName = QStringLiteral( "authentication.configs:0x%1" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) );
131  QgsDebugMsgLevel( QStringLiteral( "Using auth db connection name: %1 " ).arg( connectionName ), 2 );
132  if ( !QSqlDatabase::contains( connectionName ) )
133  {
134  QgsDebugMsgLevel( QStringLiteral( "No existing connection, creating a new one" ), 2 );
135  authdb = QSqlDatabase::addDatabase( QStringLiteral( "QSQLITE" ), connectionName );
136  authdb.setDatabaseName( authenticationDatabasePath() );
137  // for background threads, remove database when current thread finishes
138  if ( QThread::currentThread() != qApp->thread() )
139  {
140  QgsDebugMsgLevel( QStringLiteral( "Scheduled auth db remove on thread close" ), 2 );
141 
142  // IMPORTANT - we use a direct connection here, because the database removal must happen immediately
143  // when the thread finishes, and we cannot let this get queued on the main thread's event loop (where
144  // QgsAuthManager lives).
145  // Otherwise, the QSqlDatabase's private data's thread gets reset immediately the QThread::finished,
146  // and a subsequent call to QSqlDatabase::database with the same thread address (yep it happens, actually a lot)
147  // triggers a condition in QSqlDatabase which detects the nullptr private thread data and returns an invalid database instead.
148  // QSqlDatabase::removeDatabase is thread safe, so this is ok to do.
149  // Right about now is a good time to re-evaluate your selected career ;)
150  QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName, this ]
151  {
152  QMutexLocker locker( mMutex.get() );
153  QSqlDatabase::removeDatabase( connectionName );
154  mConnectedThreads.remove( QThread::currentThread() );
155  }, Qt::DirectConnection );
156 
157  mConnectedThreads.insert( QThread::currentThread(), connection );
158  }
159  }
160  else
161  {
162  QgsDebugMsgLevel( QStringLiteral( "Reusing existing connection" ), 2 );
163  authdb = QSqlDatabase::database( connectionName );
164  }
165  locker.unlock();
166 
167  if ( !authdb.isOpen() )
168  {
169  if ( !authdb.open() )
170  {
171  QString err = tr( "Opening of authentication db FAILED : %1" ).arg( authdb.lastError().text() );
172  QgsDebugMsg( err );
173  emit messageOut( err, authManTag(), CRITICAL );
174  }
175  }
176 
177  return authdb;
178 }
179 
180 bool QgsAuthManager::init( const QString &pluginPath, const QString &authDatabasePath )
181 {
182  if ( mAuthInit )
183  return true;
184  mAuthInit = true;
185 
186  QgsScopedRuntimeProfile profile( tr( "Initializing authentication manager" ) );
187 
188  QgsDebugMsgLevel( QStringLiteral( "Initializing QCA..." ), 2 );
189  mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
190 
191  QgsDebugMsgLevel( QStringLiteral( "QCA initialized." ), 2 );
192  QCA::scanForPlugins();
193 
194  QgsDebugMsgLevel( QStringLiteral( "QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
195  QStringList capabilities;
196 
197  capabilities = QCA::supportedFeatures();
198  QgsDebugMsgLevel( QStringLiteral( "QCA supports: %1" ).arg( capabilities.join( "," ) ), 2 );
199 
200  // do run-time check for qca-ossl plugin
201  if ( !QCA::isSupported( "cert", QStringLiteral( "qca-ossl" ) ) )
202  {
203  mAuthDisabled = true;
204  mAuthDisabledMessage = tr( "QCA's OpenSSL plugin (qca-ossl) is missing" );
205  return isDisabled();
206  }
207 
208  QgsDebugMsgLevel( QStringLiteral( "Prioritizing qca-ossl over all other QCA providers..." ), 2 );
209  const QCA::ProviderList provds = QCA::providers();
210  QStringList prlist;
211  for ( QCA::Provider *p : provds )
212  {
213  QString pn = p->name();
214  int pr = 0;
215  if ( pn != QLatin1String( "qca-ossl" ) )
216  {
217  pr = QCA::providerPriority( pn ) + 1;
218  }
219  QCA::setProviderPriority( pn, pr );
220  prlist << QStringLiteral( "%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
221  }
222  QgsDebugMsgLevel( QStringLiteral( "QCA provider priorities: %1" ).arg( prlist.join( ", " ) ), 2 );
223 
224  QgsDebugMsgLevel( QStringLiteral( "Populating auth method registry" ), 3 );
226 
227  QStringList methods = authreg->authMethodList();
228 
229  QgsDebugMsgLevel( QStringLiteral( "Authentication methods found: %1" ).arg( methods.join( ", " ) ), 2 );
230 
231  if ( methods.isEmpty() )
232  {
233  mAuthDisabled = true;
234  mAuthDisabledMessage = tr( "No authentication method plugins found" );
235  return isDisabled();
236  }
237 
238  if ( !registerCoreAuthMethods() )
239  {
240  mAuthDisabled = true;
241  mAuthDisabledMessage = tr( "No authentication method plugins could be loaded" );
242  return isDisabled();
243  }
244 
245  mAuthDbPath = QDir::cleanPath( authDatabasePath );
246  QgsDebugMsgLevel( QStringLiteral( "Auth database path: %1" ).arg( authenticationDatabasePath() ), 2 );
247 
248  QFileInfo dbinfo( authenticationDatabasePath() );
249  QFileInfo dbdirinfo( dbinfo.path() );
250  QgsDebugMsgLevel( QStringLiteral( "Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
251 
252  if ( !dbdirinfo.exists() )
253  {
254  QgsDebugMsgLevel( QStringLiteral( "Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
255  if ( !QDir().mkpath( dbdirinfo.filePath() ) )
256  {
257  const char *err = QT_TR_NOOP( "Auth db directory path could not be created" );
258  QgsDebugMsg( err );
259  emit messageOut( tr( err ), authManTag(), CRITICAL );
260  return false;
261  }
262  }
263 
264  if ( dbinfo.exists() )
265  {
266  if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
267  {
268  const char *err = QT_TR_NOOP( "Auth db is not readable or writable by user" );
269  QgsDebugMsg( err );
270  emit messageOut( tr( err ), authManTag(), CRITICAL );
271  return false;
272  }
273  if ( dbinfo.size() > 0 )
274  {
275  QgsDebugMsgLevel( QStringLiteral( "Auth db exists and has data" ), 2 );
276 
277  if ( !createCertTables() )
278  return false;
279 
281 
282 #ifndef QT_NO_SSL
283  initSslCaches();
284 #endif
285 
286  // set the master password from first line of file defined by QGIS_AUTH_PASSWORD_FILE env variable
287  const char *passenv = "QGIS_AUTH_PASSWORD_FILE";
288  if ( getenv( passenv ) && masterPasswordHashInDatabase() )
289  {
290  QString passpath( getenv( passenv ) );
291  // clear the env variable, so it can not be accessed from plugins, etc.
292  // (note: stored QgsApplication::systemEnvVars() skips this env variable as well)
293 #ifdef Q_OS_WIN
294  putenv( passenv );
295 #else
296  unsetenv( passenv );
297 #endif
298  QString masterpass;
299  QFile passfile( passpath );
300  if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
301  {
302  QTextStream passin( &passfile );
303  while ( !passin.atEnd() )
304  {
305  masterpass = passin.readLine();
306  break;
307  }
308  passfile.close();
309  }
310  if ( !masterpass.isEmpty() )
311  {
312  if ( setMasterPassword( masterpass, true ) )
313  {
314  QgsDebugMsgLevel( QStringLiteral( "Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
315  }
316  else
317  {
318  QgsDebugMsg( "QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
319  return false;
320  }
321  }
322  else
323  {
324  QgsDebugMsg( "QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
325  return false;
326  }
327  }
328 
329  return true;
330  }
331  }
332  else
333  {
334  QgsDebugMsgLevel( QStringLiteral( "Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
335 
336  if ( !createConfigTables() )
337  return false;
338 
339  if ( !createCertTables() )
340  return false;
341  }
342 
343 #ifndef QT_NO_SSL
344  initSslCaches();
345 #endif
346 
347  return true;
348 }
349 
350 bool QgsAuthManager::createConfigTables()
351 {
352  QMutexLocker locker( mMutex.get() );
353  // create and open the db
354  if ( !authDbOpen() )
355  {
356  const char *err = QT_TR_NOOP( "Auth db could not be created and opened" );
357  QgsDebugMsg( err );
358  emit messageOut( tr( err ), authManTag(), CRITICAL );
359  return false;
360  }
361 
362  QSqlQuery query( authDatabaseConnection() );
363 
364  // create the tables
365  QString qstr;
366 
367  qstr = QStringLiteral( "CREATE TABLE %1 (\n"
368  " 'salt' TEXT NOT NULL,\n"
369  " 'civ' TEXT NOT NULL\n"
370  ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
371  query.prepare( qstr );
372  if ( !authDbQuery( &query ) )
373  return false;
374  query.clear();
375 
376  qstr = QStringLiteral( "CREATE TABLE %1 (\n"
377  " 'id' TEXT NOT NULL,\n"
378  " 'name' TEXT NOT NULL,\n"
379  " 'uri' TEXT,\n"
380  " 'type' TEXT NOT NULL,\n"
381  " 'version' INTEGER NOT NULL\n"
382  ", 'config' TEXT NOT NULL);" ).arg( authDatabaseConfigTable() );
383  query.prepare( qstr );
384  if ( !authDbQuery( &query ) )
385  return false;
386  query.clear();
387 
388  qstr = QStringLiteral( "CREATE UNIQUE INDEX 'id_index' on %1 (id ASC);" ).arg( authDatabaseConfigTable() );
389  query.prepare( qstr );
390  if ( !authDbQuery( &query ) )
391  return false;
392  query.clear();
393 
394  qstr = QStringLiteral( "CREATE INDEX 'uri_index' on %1 (uri ASC);" ).arg( authDatabaseConfigTable() );
395  query.prepare( qstr );
396  if ( !authDbQuery( &query ) )
397  return false;
398  query.clear();
399 
400  return true;
401 }
402 
403 bool QgsAuthManager::createCertTables()
404 {
405  QMutexLocker locker( mMutex.get() );
406  // NOTE: these tables were added later, so IF NOT EXISTS is used
407  QgsDebugMsgLevel( QStringLiteral( "Creating cert tables in auth db" ), 2 );
408 
409  QSqlQuery query( authDatabaseConnection() );
410 
411  // create the tables
412  QString qstr;
413 
414  qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
415  " 'setting' TEXT NOT NULL\n"
416  ", 'value' TEXT);" ).arg( authDbSettingsTable() );
417  query.prepare( qstr );
418  if ( !authDbQuery( &query ) )
419  return false;
420  query.clear();
421 
422 
423  qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
424  " 'id' TEXT NOT NULL,\n"
425  " 'key' TEXT NOT NULL\n"
426  ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
427  query.prepare( qstr );
428  if ( !authDbQuery( &query ) )
429  return false;
430  query.clear();
431 
432  qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
433  query.prepare( qstr );
434  if ( !authDbQuery( &query ) )
435  return false;
436  query.clear();
437 
438 
439  qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
440  " 'id' TEXT NOT NULL,\n"
441  " 'host' TEXT NOT NULL,\n"
442  " 'cert' TEXT\n"
443  ", 'config' TEXT NOT NULL);" ).arg( authDatabaseServersTable() );
444  query.prepare( qstr );
445  if ( !authDbQuery( &query ) )
446  return false;
447  query.clear();
448 
449  qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg( authDatabaseServersTable() );
450  query.prepare( qstr );
451  if ( !authDbQuery( &query ) )
452  return false;
453  query.clear();
454 
455 
456  qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
457  " 'id' TEXT NOT NULL\n"
458  ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
459  query.prepare( qstr );
460  if ( !authDbQuery( &query ) )
461  return false;
462  query.clear();
463 
464  qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
465  query.prepare( qstr );
466  if ( !authDbQuery( &query ) )
467  return false;
468  query.clear();
469 
470  qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
471  " 'id' TEXT NOT NULL\n"
472  ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
473  query.prepare( qstr );
474  if ( !authDbQuery( &query ) )
475  return false;
476  query.clear();
477 
478  qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
479  query.prepare( qstr );
480  if ( !authDbQuery( &query ) )
481  return false;
482  query.clear();
483 
484  return true;
485 }
486 
488 {
489  if ( mAuthDisabled )
490  {
491  QgsDebugMsg( QStringLiteral( "Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
492  }
493  return mAuthDisabled;
494 }
495 
496 const QString QgsAuthManager::disabledMessage() const
497 {
498  return tr( "Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
499 }
500 
502 {
503  QMutexLocker locker( mMasterPasswordMutex.get() );
504  if ( isDisabled() )
505  return false;
506 
507  if ( mScheduledDbErase )
508  return false;
509 
510  if ( mMasterPass.isEmpty() )
511  {
512  QgsDebugMsgLevel( QStringLiteral( "Master password is not yet set by user" ), 2 );
513  if ( !masterPasswordInput() )
514  {
515  QgsDebugMsgLevel( QStringLiteral( "Master password input canceled by user" ), 2 );
516  return false;
517  }
518  }
519  else
520  {
521  QgsDebugMsgLevel( QStringLiteral( "Master password is set" ), 2 );
522  if ( !verify )
523  return true;
524  }
525 
526  if ( !verifyMasterPassword() )
527  return false;
528 
529  QgsDebugMsgLevel( QStringLiteral( "Master password is set and verified" ), 2 );
530  return true;
531 }
532 
533 bool QgsAuthManager::setMasterPassword( const QString &pass, bool verify )
534 {
535  QMutexLocker locker( mMutex.get() );
536  if ( isDisabled() )
537  return false;
538 
539  if ( mScheduledDbErase )
540  return false;
541 
542  // since this is generally for automation, we don't care if passed-in is same as existing
543  QString prevpass = QString( mMasterPass );
544  mMasterPass = pass;
545  if ( verify && !verifyMasterPassword() )
546  {
547  mMasterPass = prevpass;
548  const char *err = QT_TR_NOOP( "Master password set: FAILED to verify, reset to previous" );
549  QgsDebugMsg( err );
550  emit messageOut( tr( err ), authManTag(), WARNING );
551  return false;
552  }
553 
554  QgsDebugMsgLevel( QStringLiteral( "Master password set: SUCCESS%1" ).arg( verify ? " and verified" : "" ), 2 );
555  return true;
556 }
557 
558 bool QgsAuthManager::verifyMasterPassword( const QString &compare )
559 {
560  if ( isDisabled() )
561  return false;
562 
563  int rows = 0;
564  if ( !masterPasswordRowsInDb( &rows ) )
565  {
566  const char *err = QT_TR_NOOP( "Master password: FAILED to access database" );
567  QgsDebugMsg( err );
568  emit messageOut( tr( err ), authManTag(), CRITICAL );
569 
571  return false;
572  }
573 
574  QgsDebugMsgLevel( QStringLiteral( "Master password: %1 rows in database" ).arg( rows ), 2 );
575 
576  if ( rows > 1 )
577  {
578  const char *err = QT_TR_NOOP( "Master password: FAILED to find just one master password record in database" );
579  QgsDebugMsg( err );
580  emit messageOut( tr( err ), authManTag(), WARNING );
581 
583  return false;
584  }
585  else if ( rows == 1 )
586  {
587  if ( !masterPasswordCheckAgainstDb( compare ) )
588  {
589  if ( compare.isNull() ) // don't complain when comparing, since it could be an incomplete comparison string
590  {
591  const char *err = QT_TR_NOOP( "Master password: FAILED to verify against hash in database" );
592  QgsDebugMsg( err );
593  emit messageOut( tr( err ), authManTag(), WARNING );
594 
596 
597  emit masterPasswordVerified( false );
598  }
599  ++mPassTries;
600  if ( mPassTries >= 5 )
601  {
602  mAuthDisabled = true;
603  const char *err = QT_TR_NOOP( "Master password: failed 5 times authentication system DISABLED" );
604  QgsDebugMsg( err );
605  emit messageOut( tr( err ), authManTag(), WARNING );
606  }
607  return false;
608  }
609  else
610  {
611  QgsDebugMsgLevel( QStringLiteral( "Master password: verified against hash in database" ), 2 );
612  if ( compare.isNull() )
613  emit masterPasswordVerified( true );
614  }
615  }
616  else if ( compare.isNull() ) // compares should never be stored
617  {
618  if ( !masterPasswordStoreInDb() )
619  {
620  const char *err = QT_TR_NOOP( "Master password: hash FAILED to be stored in database" );
621  QgsDebugMsg( err );
622  emit messageOut( tr( err ), authManTag(), CRITICAL );
623 
625  return false;
626  }
627  else
628  {
629  QgsDebugMsgLevel( QStringLiteral( "Master password: hash stored in database" ), 2 );
630  }
631  // double-check storing
632  if ( !masterPasswordCheckAgainstDb() )
633  {
634  const char *err = QT_TR_NOOP( "Master password: FAILED to verify against hash in database" );
635  QgsDebugMsg( err );
636  emit messageOut( tr( err ), authManTag(), WARNING );
637 
639  emit masterPasswordVerified( false );
640  return false;
641  }
642  else
643  {
644  QgsDebugMsgLevel( QStringLiteral( "Master password: verified against hash in database" ), 2 );
645  emit masterPasswordVerified( true );
646  }
647  }
648 
649  return true;
650 }
651 
653 {
654  return !mMasterPass.isEmpty();
655 }
656 
657 bool QgsAuthManager::masterPasswordSame( const QString &pass ) const
658 {
659  return mMasterPass == pass;
660 }
661 
662 bool QgsAuthManager::resetMasterPassword( const QString &newpass, const QString &oldpass,
663  bool keepbackup, QString *backuppath )
664 {
665  if ( isDisabled() )
666  return false;
667 
668  // verify caller knows the current master password
669  // this means that the user will have had to already set the master password as well
670  if ( !masterPasswordSame( oldpass ) )
671  return false;
672 
673  QString dbbackup;
674  if ( !backupAuthenticationDatabase( &dbbackup ) )
675  return false;
676 
677  QgsDebugMsgLevel( QStringLiteral( "Master password reset: backed up current database" ), 2 );
678 
679  // create new database and connection
681 
682  // store current password and civ
683  QString prevpass = QString( mMasterPass );
684  QString prevciv = QString( masterPasswordCiv() );
685 
686  // on ANY FAILURE from this point, reinstate previous password and database
687  bool ok = true;
688 
689  // clear password hash table (also clears mMasterPass)
690  if ( ok && !masterPasswordClearDb() )
691  {
692  ok = false;
693  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not clear current password from database" );
694  QgsDebugMsg( err );
695  emit messageOut( tr( err ), authManTag(), WARNING );
696  }
697  if ( ok )
698  {
699  QgsDebugMsgLevel( QStringLiteral( "Master password reset: cleared current password from database" ), 2 );
700  }
701 
702  // mMasterPass empty, set new password (don't verify, since not stored yet)
703  setMasterPassword( newpass, false );
704 
705  // store new password hash
706  if ( ok && !masterPasswordStoreInDb() )
707  {
708  ok = false;
709  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not store new password in database" );
710  QgsDebugMsg( err );
711  emit messageOut( tr( err ), authManTag(), WARNING );
712  }
713  if ( ok )
714  {
715  QgsDebugMsgLevel( QStringLiteral( "Master password reset: stored new password in database" ), 2 );
716  }
717 
718  // verify it stored password properly
719  if ( ok && !verifyMasterPassword() )
720  {
721  ok = false;
722  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not verify new password in database" );
723  QgsDebugMsg( err );
724  emit messageOut( tr( err ), authManTag(), WARNING );
725  }
726 
727  // re-encrypt everything with new password
728  if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
729  {
730  ok = false;
731  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt configs in database" );
732  QgsDebugMsg( err );
733  emit messageOut( tr( err ), authManTag(), WARNING );
734  }
735  if ( ok )
736  {
737  QgsDebugMsgLevel( QStringLiteral( "Master password reset: re-encrypted configs in database" ), 2 );
738  }
739 
740  // verify it all worked
741  if ( ok && !verifyPasswordCanDecryptConfigs() )
742  {
743  ok = false;
744  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
745  QgsDebugMsg( err );
746  emit messageOut( tr( err ), authManTag(), WARNING );
747  }
748 
749  if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
750  {
751  ok = false;
752  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt settings in database" );
753  QgsDebugMsg( err );
754  emit messageOut( tr( err ), authManTag(), WARNING );
755  }
756 
757  if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
758  {
759  ok = false;
760  const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt identities in database" );
761  QgsDebugMsg( err );
762  emit messageOut( tr( err ), authManTag(), WARNING );
763  }
764 
765  // something went wrong, reinstate previous password and database
766  if ( !ok )
767  {
768  // backup database of failed attempt, for inspection
769  authDatabaseConnection().close();
770  QString errdbbackup( dbbackup );
771  errdbbackup.replace( QLatin1String( ".db" ), QLatin1String( "_ERROR.db" ) );
772  QFile::rename( authenticationDatabasePath(), errdbbackup );
773  QgsDebugMsg( QStringLiteral( "Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
774 
775  // reinstate previous database and password
776  QFile::rename( dbbackup, authenticationDatabasePath() );
777  mMasterPass = prevpass;
779  QgsDebugMsg( QStringLiteral( "Master password reset FAILED: reinstated previous password and database" ) );
780 
781  // assign error db backup
782  if ( backuppath )
783  *backuppath = errdbbackup;
784 
785  return false;
786  }
787 
788 
789  if ( !keepbackup && !QFile::remove( dbbackup ) )
790  {
791  const char *err = QT_TR_NOOP( "Master password reset: could not remove old database backup" );
792  QgsDebugMsg( err );
793  emit messageOut( tr( err ), authManTag(), WARNING );
794  // a non-blocking error, continue
795  }
796 
797  if ( keepbackup )
798  {
799  QgsDebugMsgLevel( QStringLiteral( "Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
800  if ( backuppath )
801  *backuppath = dbbackup;
802  }
803 
804  QgsDebugMsgLevel( QStringLiteral( "Master password reset: SUCCESS" ), 2 );
805  emit authDatabaseChanged();
806  return true;
807 }
808 
810 {
811  mScheduledDbErase = scheduleErase;
812  // any call (start or stop) should reset these
813  mScheduledDbEraseRequestEmitted = false;
814  mScheduledDbEraseRequestCount = 0;
815 
816  if ( scheduleErase )
817  {
818  if ( !mScheduledDbEraseTimer )
819  {
820  mScheduledDbEraseTimer = new QTimer( this );
821  connect( mScheduledDbEraseTimer, &QTimer::timeout, this, &QgsAuthManager::tryToStartDbErase );
822  mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
823  }
824  else if ( !mScheduledDbEraseTimer->isActive() )
825  {
826  mScheduledDbEraseTimer->start();
827  }
828  }
829  else
830  {
831  if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
832  mScheduledDbEraseTimer->stop();
833  }
834 }
835 
837 {
838  if ( isDisabled() )
839  return false;
840 
841  qDeleteAll( mAuthMethods );
842  mAuthMethods.clear();
843  const QStringList methods = QgsAuthMethodRegistry::instance()->authMethodList();
844  for ( const auto &authMethodKey : methods )
845  {
846  mAuthMethods.insert( authMethodKey, QgsAuthMethodRegistry::instance()->createAuthMethod( authMethodKey ) );
847  }
848 
849  return !mAuthMethods.isEmpty();
850 }
851 
852 const QString QgsAuthManager::uniqueConfigId() const
853 {
854  QStringList configids = configIds();
855  QString id;
856  int len = 7;
857  // sleep just a bit to make sure the current time has changed
858  QEventLoop loop;
859  QTimer::singleShot( 3, &loop, &QEventLoop::quit );
860  loop.exec();
861 
862 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
863  uint seed = static_cast< uint >( QTime::currentTime().msec() );
864  qsrand( seed );
865 #endif
866 
867  while ( true )
868  {
869  id.clear();
870  for ( int i = 0; i < len; i++ )
871  {
872 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
873  switch ( qrand() % 2 )
874 #else
875  switch ( QRandomGenerator::system()->generate() % 2 )
876 #endif
877  {
878  case 0:
879 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
880  id += ( '0' + qrand() % 10 );
881 #else
882  id += static_cast<char>( '0' + QRandomGenerator::system()->generate() % 10 );
883 #endif
884  break;
885  case 1:
886 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
887  id += ( 'a' + qrand() % 26 );
888 #else
889  id += static_cast<char>( 'a' + QRandomGenerator::system()->generate() % 26 );
890 #endif
891  break;
892  }
893  }
894  if ( !configids.contains( id ) )
895  {
896  break;
897  }
898  }
899  QgsDebugMsgLevel( QStringLiteral( "Generated unique ID: %1" ).arg( id ), 2 );
900  return id;
901 }
902 
903 bool QgsAuthManager::configIdUnique( const QString &id ) const
904 {
905  if ( isDisabled() )
906  return false;
907 
908  if ( id.isEmpty() )
909  {
910  const char *err = QT_TR_NOOP( "Config ID is empty" );
911  QgsDebugMsg( err );
912  emit messageOut( tr( err ), authManTag(), WARNING );
913  return false;
914  }
915  QStringList configids = configIds();
916  return !configids.contains( id );
917 }
918 
919 bool QgsAuthManager::hasConfigId( const QString &txt ) const
920 {
921  const thread_local QRegularExpression authCfgRegExp( AUTH_CFG_REGEX );
922  return txt.indexOf( authCfgRegExp ) != -1;
923 }
924 
926 {
927  QMutexLocker locker( mMutex.get() );
928  QStringList providerAuthMethodsKeys;
929  if ( !dataprovider.isEmpty() )
930  {
931  providerAuthMethodsKeys = authMethodsKeys( dataprovider.toLower() );
932  }
933 
934  QgsAuthMethodConfigsMap baseConfigs;
935 
936  if ( isDisabled() )
937  return baseConfigs;
938 
939  QSqlQuery query( authDatabaseConnection() );
940  query.prepare( QStringLiteral( "SELECT id, name, uri, type, version FROM %1" ).arg( authDatabaseConfigTable() ) );
941 
942  if ( !authDbQuery( &query ) )
943  {
944  return baseConfigs;
945  }
946 
947  if ( query.isActive() && query.isSelect() )
948  {
949  while ( query.next() )
950  {
951  QString authcfg = query.value( 0 ).toString();
952  QgsAuthMethodConfig config;
953  config.setId( authcfg );
954  config.setName( query.value( 1 ).toString() );
955  config.setUri( query.value( 2 ).toString() );
956  config.setMethod( query.value( 3 ).toString() );
957  config.setVersion( query.value( 4 ).toInt() );
958 
959  if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.method() ) )
960  {
961  continue;
962  }
963 
964  baseConfigs.insert( authcfg, config );
965  }
966  }
967  return baseConfigs;
968 }
969 
971 {
972  QMutexLocker locker( mMutex.get() );
973  if ( isDisabled() )
974  return;
975 
976  QSqlQuery query( authDatabaseConnection() );
977  query.prepare( QStringLiteral( "SELECT id, type FROM %1" ).arg( authDatabaseConfigTable() ) );
978 
979  if ( !authDbQuery( &query ) )
980  {
981  return;
982  }
983 
984  if ( query.isActive() )
985  {
986  QgsDebugMsgLevel( QStringLiteral( "Syncing existing auth config and their auth methods" ), 2 );
987  mConfigAuthMethods.clear();
988  QStringList cfgmethods;
989  while ( query.next() )
990  {
991  mConfigAuthMethods.insert( query.value( 0 ).toString(),
992  query.value( 1 ).toString() );
993  cfgmethods << QStringLiteral( "%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
994  }
995  QgsDebugMsgLevel( QStringLiteral( "Stored auth config/methods:\n%1" ).arg( cfgmethods.join( ", " ) ), 2 );
996  }
997 }
998 
1000 {
1001  if ( isDisabled() )
1002  return nullptr;
1003 
1004  if ( !mConfigAuthMethods.contains( authcfg ) )
1005  {
1006  QgsDebugMsg( QStringLiteral( "No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1007  return nullptr;
1008  }
1009 
1010  QString authMethodKey = mConfigAuthMethods.value( authcfg );
1011 
1012  return authMethod( authMethodKey );
1013 }
1014 
1015 QString QgsAuthManager::configAuthMethodKey( const QString &authcfg ) const
1016 {
1017  if ( isDisabled() )
1018  return QString();
1019 
1020  return mConfigAuthMethods.value( authcfg, QString() );
1021 }
1022 
1023 
1024 QStringList QgsAuthManager::authMethodsKeys( const QString &dataprovider )
1025 {
1026  return authMethodsMap( dataprovider.toLower() ).keys();
1027 }
1028 
1029 QgsAuthMethod *QgsAuthManager::authMethod( const QString &authMethodKey )
1030 {
1031  if ( !mAuthMethods.contains( authMethodKey ) )
1032  {
1033  QgsDebugMsg( QStringLiteral( "No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1034  return nullptr;
1035  }
1036 
1037  return mAuthMethods.value( authMethodKey );
1038 }
1039 
1040 const QgsAuthMethodMetadata *QgsAuthManager::authMethodMetadata( const QString &authMethodKey )
1041 {
1042  if ( !mAuthMethods.contains( authMethodKey ) )
1043  {
1044  QgsDebugMsg( QStringLiteral( "No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1045  return nullptr;
1046  }
1047 
1048  return QgsAuthMethodRegistry::instance()->authMethodMetadata( authMethodKey );
1049 }
1050 
1051 
1053 {
1054  if ( dataprovider.isEmpty() )
1055  {
1056  return mAuthMethods;
1057  }
1058 
1059  QgsAuthMethodsMap filteredmap;
1060  QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1061  while ( i != mAuthMethods.constEnd() )
1062  {
1063  if ( i.value()
1064  && ( i.value()->supportedDataProviders().contains( QStringLiteral( "all" ) )
1065  || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1066  {
1067  filteredmap.insert( i.key(), i.value() );
1068  }
1069  ++i;
1070  }
1071  return filteredmap;
1072 }
1073 
1074 #ifdef HAVE_GUI
1075 QWidget *QgsAuthManager::authMethodEditWidget( const QString &authMethodKey, QWidget *parent )
1076 {
1077  QgsAuthMethod *method = authMethod( authMethodKey );
1078  if ( method )
1079  return method->editWidget( parent );
1080  else
1081  return nullptr;
1082 }
1083 #endif
1084 
1085 QgsAuthMethod::Expansions QgsAuthManager::supportedAuthMethodExpansions( const QString &authcfg )
1086 {
1087  if ( isDisabled() )
1088  return QgsAuthMethod::Expansions();
1089 
1090  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1091  if ( authmethod )
1092  {
1093  return authmethod->supportedExpansions();
1094  }
1095  return QgsAuthMethod::Expansions();
1096 }
1097 
1099 {
1100  QMutexLocker locker( mMutex.get() );
1101  if ( !setMasterPassword( true ) )
1102  return false;
1103 
1104  // don't need to validate id, since it has not be defined yet
1105  if ( !mconfig.isValid() )
1106  {
1107  const char *err = QT_TR_NOOP( "Store config: FAILED because config is invalid" );
1108  QgsDebugMsg( err );
1109  emit messageOut( tr( err ), authManTag(), WARNING );
1110  return false;
1111  }
1112 
1113  QString uid = mconfig.id();
1114  bool passedinID = !uid.isEmpty();
1115  if ( uid.isEmpty() )
1116  {
1117  uid = uniqueConfigId();
1118  }
1119  else if ( configIds().contains( uid ) )
1120  {
1121  if ( !overwrite )
1122  {
1123  const char *err = QT_TR_NOOP( "Store config: FAILED because pre-defined config ID %1 is not unique" );
1124  QgsDebugMsg( err );
1125  emit messageOut( tr( err ), authManTag(), WARNING );
1126  return false;
1127  }
1128  locker.unlock();
1130  locker.relock();
1131  }
1132 
1133  QString configstring = mconfig.configString();
1134  if ( configstring.isEmpty() )
1135  {
1136  const char *err = QT_TR_NOOP( "Store config: FAILED because config string is empty" );
1137  QgsDebugMsg( err );
1138  emit messageOut( tr( err ), authManTag(), WARNING );
1139  return false;
1140  }
1141 #if( 0 )
1142  QgsDebugMsg( QStringLiteral( "authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1143  QgsDebugMsg( QStringLiteral( "name: %1" ).arg( config.name() ) );
1144  QgsDebugMsg( QStringLiteral( "uri: %1" ).arg( config.uri() ) );
1145  QgsDebugMsg( QStringLiteral( "type: %1" ).arg( config.method() ) );
1146  QgsDebugMsg( QStringLiteral( "version: %1" ).arg( config.version() ) );
1147  QgsDebugMsg( QStringLiteral( "config: %1" ).arg( configstring ) ); // DO NOT LEAVE THIS LINE UNCOMMENTED !
1148 #endif
1149 
1150  QSqlQuery query( authDatabaseConnection() );
1151  query.prepare( QStringLiteral( "INSERT INTO %1 (id, name, uri, type, version, config) "
1152  "VALUES (:id, :name, :uri, :type, :version, :config)" ).arg( authDatabaseConfigTable() ) );
1153 
1154  query.bindValue( QStringLiteral( ":id" ), uid );
1155  query.bindValue( QStringLiteral( ":name" ), mconfig.name() );
1156  query.bindValue( QStringLiteral( ":uri" ), mconfig.uri() );
1157  query.bindValue( QStringLiteral( ":type" ), mconfig.method() );
1158  query.bindValue( QStringLiteral( ":version" ), mconfig.version() );
1159  query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1160 
1161  if ( !authDbStartTransaction() )
1162  return false;
1163 
1164  if ( !authDbQuery( &query ) )
1165  return false;
1166 
1167  if ( !authDbCommit() )
1168  return false;
1169 
1170 // passed-in config should now be like as if it was just loaded from db
1171  if ( !passedinID )
1172  mconfig.setId( uid );
1173 
1175 
1176  QgsDebugMsgLevel( QStringLiteral( "Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1177  return true;
1178 }
1179 
1181 {
1182  QMutexLocker locker( mMutex.get() );
1183  if ( !setMasterPassword( true ) )
1184  return false;
1185 
1186  // validate id
1187  if ( !config.isValid( true ) )
1188  {
1189  const char *err = QT_TR_NOOP( "Update config: FAILED because config is invalid" );
1190  QgsDebugMsg( err );
1191  emit messageOut( tr( err ), authManTag(), WARNING );
1192  return false;
1193  }
1194 
1195  QString configstring = config.configString();
1196  if ( configstring.isEmpty() )
1197  {
1198  const char *err = QT_TR_NOOP( "Update config: FAILED because config is empty" );
1199  QgsDebugMsg( err );
1200  emit messageOut( tr( err ), authManTag(), WARNING );
1201  return false;
1202  }
1203 
1204 #if( 0 )
1205  QgsDebugMsg( QStringLiteral( "authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1206  QgsDebugMsg( QStringLiteral( "id: %1" ).arg( config.id() ) );
1207  QgsDebugMsg( QStringLiteral( "name: %1" ).arg( config.name() ) );
1208  QgsDebugMsg( QStringLiteral( "uri: %1" ).arg( config.uri() ) );
1209  QgsDebugMsg( QStringLiteral( "type: %1" ).arg( config.method() ) );
1210  QgsDebugMsg( QStringLiteral( "version: %1" ).arg( config.version() ) );
1211  QgsDebugMsg( QStringLiteral( "config: %1" ).arg( configstring ) ); // DO NOT LEAVE THIS LINE UNCOMMENTED !
1212 #endif
1213 
1214  QSqlQuery query( authDatabaseConnection() );
1215  if ( !query.prepare( QStringLiteral( "UPDATE %1 "
1216  "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1217  "WHERE id = :id" ).arg( authDatabaseConfigTable() ) ) )
1218  {
1219  const char *err = QT_TR_NOOP( "Update config: FAILED to prepare query" );
1220  QgsDebugMsg( err );
1221  emit messageOut( tr( err ), authManTag(), WARNING );
1222  return false;
1223  }
1224 
1225  query.bindValue( QStringLiteral( ":id" ), config.id() );
1226  query.bindValue( QStringLiteral( ":name" ), config.name() );
1227  query.bindValue( QStringLiteral( ":uri" ), config.uri() );
1228  query.bindValue( QStringLiteral( ":type" ), config.method() );
1229  query.bindValue( QStringLiteral( ":version" ), config.version() );
1230  query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1231 
1232  if ( !authDbStartTransaction() )
1233  return false;
1234 
1235  if ( !authDbQuery( &query ) )
1236  return false;
1237 
1238  if ( !authDbCommit() )
1239  return false;
1240 
1241  // should come before updating auth methods, in case user switched auth methods in config
1242  clearCachedConfig( config.id() );
1243 
1245 
1246  QgsDebugMsgLevel( QStringLiteral( "Update config SUCCESS for authcfg: %1" ).arg( config.id() ), 2 );
1247 
1248  return true;
1249 }
1250 
1251 bool QgsAuthManager::loadAuthenticationConfig( const QString &authcfg, QgsAuthMethodConfig &mconfig, bool full )
1252 {
1253  if ( isDisabled() )
1254  return false;
1255 
1256  if ( full && !setMasterPassword( true ) )
1257  return false;
1258 
1259  QMutexLocker locker( mMutex.get() );
1260 
1261  QSqlQuery query( authDatabaseConnection() );
1262  if ( full )
1263  {
1264  query.prepare( QStringLiteral( "SELECT id, name, uri, type, version, config FROM %1 "
1265  "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1266  }
1267  else
1268  {
1269  query.prepare( QStringLiteral( "SELECT id, name, uri, type, version FROM %1 "
1270  "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1271  }
1272 
1273  query.bindValue( QStringLiteral( ":id" ), authcfg );
1274 
1275  if ( !authDbQuery( &query ) )
1276  {
1277  return false;
1278  }
1279 
1280  if ( query.isActive() && query.isSelect() )
1281  {
1282  if ( query.first() )
1283  {
1284  mconfig.setId( query.value( 0 ).toString() );
1285  mconfig.setName( query.value( 1 ).toString() );
1286  mconfig.setUri( query.value( 2 ).toString() );
1287  mconfig.setMethod( query.value( 3 ).toString() );
1288  mconfig.setVersion( query.value( 4 ).toInt() );
1289 
1290  if ( full )
1291  {
1292  mconfig.loadConfigString( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 5 ).toString() ) );
1293  }
1294 
1295  QString authMethodKey = configAuthMethodKey( authcfg );
1296  QgsAuthMethod *authmethod = authMethod( authMethodKey );
1297  if ( authmethod )
1298  {
1299  authmethod->updateMethodConfig( mconfig );
1300  }
1301  else
1302  {
1303  QgsDebugMsg( QStringLiteral( "Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1304  }
1305 
1306  QgsDebugMsgLevel( QStringLiteral( "Load %1 config SUCCESS for authcfg: %2" ).arg( full ? "full" : "base", authcfg ), 2 );
1307  return true;
1308  }
1309  if ( query.next() )
1310  {
1311  QgsDebugMsg( QStringLiteral( "Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1312  emit messageOut( tr( "Authentication database contains duplicate configuration IDs" ), authManTag(), WARNING );
1313  }
1314  }
1315 
1316  return false;
1317 }
1318 
1319 bool QgsAuthManager::removeAuthenticationConfig( const QString &authcfg )
1320 {
1321  QMutexLocker locker( mMutex.get() );
1322  if ( isDisabled() )
1323  return false;
1324 
1325  if ( authcfg.isEmpty() )
1326  return false;
1327 
1328  QSqlQuery query( authDatabaseConnection() );
1329 
1330  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1331 
1332  query.bindValue( QStringLiteral( ":id" ), authcfg );
1333 
1334  if ( !authDbStartTransaction() )
1335  return false;
1336 
1337  if ( !authDbQuery( &query ) )
1338  return false;
1339 
1340  if ( !authDbCommit() )
1341  return false;
1342 
1343  clearCachedConfig( authcfg );
1344 
1346 
1347  QgsDebugMsgLevel( QStringLiteral( "REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1348 
1349  return true;
1350 }
1351 
1352 bool QgsAuthManager::exportAuthenticationConfigsToXml( const QString &filename, const QStringList &authcfgs, const QString &password )
1353 {
1354  if ( filename.isEmpty() )
1355  return false;
1356 
1357  QDomDocument document( QStringLiteral( "qgis_authentication" ) );
1358  QDomElement root = document.createElement( QStringLiteral( "qgis_authentication" ) );
1359  document.appendChild( root );
1360 
1361  QString civ;
1362  if ( !password.isEmpty() )
1363  {
1364  QString salt;
1365  QString hash;
1366  QgsAuthCrypto::passwordKeyHash( password, &salt, &hash, &civ );
1367  root.setAttribute( QStringLiteral( "salt" ), salt );
1368  root.setAttribute( QStringLiteral( "hash" ), hash );
1369  root.setAttribute( QStringLiteral( "civ" ), civ );
1370  }
1371 
1372  QDomElement configurations = document.createElement( QStringLiteral( "configurations" ) );
1373  for ( const QString &authcfg : authcfgs )
1374  {
1375  QgsAuthMethodConfig authMethodConfig;
1376 
1377  bool ok = loadAuthenticationConfig( authcfg, authMethodConfig, true );
1378  if ( ok )
1379  {
1380  authMethodConfig.writeXml( configurations, document );
1381  }
1382  }
1383  if ( !password.isEmpty() )
1384  {
1385  QString configurationsString;
1386  QTextStream ts( &configurationsString );
1387 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1388  ts.setCodec( "UTF-8" );
1389 #endif
1390  configurations.save( ts, 2 );
1391  root.appendChild( document.createTextNode( QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
1392  }
1393  else
1394  {
1395  root.appendChild( configurations );
1396  }
1397 
1398  QFile file( filename );
1399  if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
1400  return false;
1401 
1402  QTextStream ts( &file );
1403 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1404  ts.setCodec( "UTF-8" );
1405 #endif
1406  document.save( ts, 2 );
1407  file.close();
1408  return true;
1409 }
1410 
1411 bool QgsAuthManager::importAuthenticationConfigsFromXml( const QString &filename, const QString &password, bool overwrite )
1412 {
1413  QFile file( filename );
1414  if ( !file.open( QFile::ReadOnly ) )
1415  {
1416  return false;
1417  }
1418 
1419  QDomDocument document( QStringLiteral( "qgis_authentication" ) );
1420  if ( !document.setContent( &file ) )
1421  {
1422  file.close();
1423  return false;
1424  }
1425  file.close();
1426 
1427  QDomElement root = document.documentElement();
1428  if ( root.tagName() != QLatin1String( "qgis_authentication" ) )
1429  {
1430  return false;
1431  }
1432 
1433  QDomElement configurations;
1434  if ( root.hasAttribute( QStringLiteral( "salt" ) ) )
1435  {
1436  QString salt = root.attribute( QStringLiteral( "salt" ) );
1437  QString hash = root.attribute( QStringLiteral( "hash" ) );
1438  QString civ = root.attribute( QStringLiteral( "civ" ) );
1439  if ( !QgsAuthCrypto::verifyPasswordKeyHash( password, salt, hash ) )
1440  return false;
1441 
1442  document.setContent( QgsAuthCrypto::decrypt( password, civ, root.text() ) );
1443  configurations = document.firstChild().toElement();
1444  }
1445  else
1446  {
1447  configurations = root.firstChildElement( QStringLiteral( "configurations" ) );
1448  }
1449 
1450  QDomElement configuration = configurations.firstChildElement();
1451  while ( !configuration.isNull() )
1452  {
1453  QgsAuthMethodConfig authMethodConfig;
1454  authMethodConfig.readXml( configuration );
1455  storeAuthenticationConfig( authMethodConfig, overwrite );
1456 
1457  configuration = configuration.nextSiblingElement();
1458  }
1459  return true;
1460 }
1461 
1463 {
1464  QMutexLocker locker( mMutex.get() );
1465  if ( isDisabled() )
1466  return false;
1467 
1468  QSqlQuery query( authDatabaseConnection() );
1469  query.prepare( QStringLiteral( "DELETE FROM %1" ).arg( authDatabaseConfigTable() ) );
1470  bool res = authDbTransactionQuery( &query );
1471 
1472  if ( res )
1473  {
1476  }
1477 
1478  QgsDebugMsgLevel( QStringLiteral( "Remove configs from database: %1" ).arg( res ? "SUCCEEDED" : "FAILED" ), 2 );
1479 
1480  return res;
1481 }
1482 
1484 {
1485  QMutexLocker locker( mMutex.get() );
1486  if ( !QFile::exists( authenticationDatabasePath() ) )
1487  {
1488  const char *err = QT_TR_NOOP( "No authentication database found" );
1489  QgsDebugMsg( err );
1490  emit messageOut( tr( err ), authManTag(), WARNING );
1491  return false;
1492  }
1493 
1494  // close any connection to current db
1495  QSqlDatabase authConn = authDatabaseConnection();
1496  if ( authConn.isValid() && authConn.isOpen() )
1497  authConn.close();
1498 
1499  // duplicate current db file to 'qgis-auth_YYYY-MM-DD-HHMMSS.db' backup
1500  QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral( "yyyy-MM-dd-hhmmss" ) ) );
1501  QString dbbackup( authenticationDatabasePath() );
1502  dbbackup.replace( QLatin1String( ".db" ), QStringLiteral( "_%1.db" ).arg( datestamp ) );
1503 
1504  if ( !QFile::copy( authenticationDatabasePath(), dbbackup ) )
1505  {
1506  const char *err = QT_TR_NOOP( "Could not back up authentication database" );
1507  QgsDebugMsg( err );
1508  emit messageOut( tr( err ), authManTag(), WARNING );
1509  return false;
1510  }
1511 
1512  if ( backuppath )
1513  *backuppath = dbbackup;
1514 
1515  QgsDebugMsgLevel( QStringLiteral( "Backed up auth database at %1" ).arg( dbbackup ), 2 );
1516  return true;
1517 }
1518 
1519 bool QgsAuthManager::eraseAuthenticationDatabase( bool backup, QString *backuppath )
1520 {
1521  QMutexLocker locker( mMutex.get() );
1522  if ( isDisabled() )
1523  return false;
1524 
1525  QString dbbackup;
1526  if ( backup && !backupAuthenticationDatabase( &dbbackup ) )
1527  {
1528  return false;
1529  }
1530 
1531  if ( backuppath && !dbbackup.isEmpty() )
1532  *backuppath = dbbackup;
1533 
1534  QFileInfo dbinfo( authenticationDatabasePath() );
1535  if ( dbinfo.exists() )
1536  {
1537  if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1538  {
1539  const char *err = QT_TR_NOOP( "Auth db is not readable or writable by user" );
1540  QgsDebugMsg( err );
1541  emit messageOut( tr( err ), authManTag(), CRITICAL );
1542  return false;
1543  }
1544  }
1545  else
1546  {
1547  const char *err = QT_TR_NOOP( "No authentication database found" );
1548  QgsDebugMsg( err );
1549  emit messageOut( tr( err ), authManTag(), WARNING );
1550  return false;
1551  }
1552 
1553  if ( !QFile::remove( authenticationDatabasePath() ) )
1554  {
1555  const char *err = QT_TR_NOOP( "Authentication database could not be deleted" );
1556  QgsDebugMsg( err );
1557  emit messageOut( tr( err ), authManTag(), WARNING );
1558  return false;
1559  }
1560 
1561  mMasterPass = QString();
1562 
1563  QgsDebugMsgLevel( QStringLiteral( "Creating Auth db through QSqlDatabase initial connection" ), 2 );
1564 
1565  QSqlDatabase authConn = authDatabaseConnection();
1566  if ( !authConn.isValid() || !authConn.isOpen() )
1567  {
1568  const char *err = QT_TR_NOOP( "Authentication database could not be initialized" );
1569  QgsDebugMsg( err );
1570  emit messageOut( tr( err ), authManTag(), WARNING );
1571  return false;
1572  }
1573 
1574  if ( !createConfigTables() )
1575  {
1576  const char *err = QT_TR_NOOP( "FAILED to create auth database config tables" );
1577  QgsDebugMsg( err );
1578  emit messageOut( tr( err ), authManTag(), WARNING );
1579  return false;
1580  }
1581 
1582  if ( !createCertTables() )
1583  {
1584  const char *err = QT_TR_NOOP( "FAILED to create auth database cert tables" );
1585  QgsDebugMsg( err );
1586  emit messageOut( tr( err ), authManTag(), WARNING );
1587  return false;
1588  }
1589 
1592  initSslCaches();
1593 
1594  emit authDatabaseChanged();
1595 
1596  return true;
1597 }
1598 
1599 bool QgsAuthManager::updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
1600  const QString &dataprovider )
1601 {
1602  if ( isDisabled() )
1603  return false;
1604 
1605  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1606  if ( authmethod )
1607  {
1608  if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkRequest ) )
1609  {
1610  QgsDebugMsg( QStringLiteral( "Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1611  return true;
1612  }
1613 
1614  if ( !authmethod->updateNetworkRequest( request, authcfg, dataprovider.toLower() ) )
1615  {
1616  authmethod->clearCachedConfig( authcfg );
1617  return false;
1618  }
1619  return true;
1620  }
1621  return false;
1622 }
1623 
1624 bool QgsAuthManager::updateNetworkReply( QNetworkReply *reply, const QString &authcfg,
1625  const QString &dataprovider )
1626 {
1627  if ( isDisabled() )
1628  return false;
1629 
1630  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1631  if ( authmethod )
1632  {
1633  if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkReply ) )
1634  {
1635  QgsDebugMsg( QStringLiteral( "Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1636  return true;
1637  }
1638 
1639  if ( !authmethod->updateNetworkReply( reply, authcfg, dataprovider.toLower() ) )
1640  {
1641  authmethod->clearCachedConfig( authcfg );
1642  return false;
1643  }
1644  return true;
1645  }
1646 
1647  return false;
1648 }
1649 
1650 bool QgsAuthManager::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
1651  const QString &dataprovider )
1652 {
1653  if ( isDisabled() )
1654  return false;
1655 
1656  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1657  if ( authmethod )
1658  {
1659  if ( !( authmethod->supportedExpansions() & QgsAuthMethod::DataSourceUri ) )
1660  {
1661  QgsDebugMsg( QStringLiteral( "Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1662  return true;
1663  }
1664 
1665  if ( !authmethod->updateDataSourceUriItems( connectionItems, authcfg, dataprovider.toLower() ) )
1666  {
1667  authmethod->clearCachedConfig( authcfg );
1668  return false;
1669  }
1670  return true;
1671  }
1672 
1673  return false;
1674 }
1675 
1676 bool QgsAuthManager::updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider )
1677 {
1678  if ( isDisabled() )
1679  return false;
1680 
1681  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1682  if ( authmethod )
1683  {
1684  if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkProxy ) )
1685  {
1686  QgsDebugMsg( QStringLiteral( "Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1687  return true;
1688  }
1689 
1690  if ( !authmethod->updateNetworkProxy( proxy, authcfg, dataprovider.toLower() ) )
1691  {
1692  authmethod->clearCachedConfig( authcfg );
1693  return false;
1694  }
1695  QgsDebugMsgLevel( QStringLiteral( "Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1696  return true;
1697  }
1698 
1699  return false;
1700 }
1701 
1702 bool QgsAuthManager::storeAuthSetting( const QString &key, const QVariant &value, bool encrypt )
1703 {
1704  QMutexLocker locker( mMutex.get() );
1705  if ( key.isEmpty() )
1706  return false;
1707 
1708  QString storeval( value.toString() );
1709  if ( encrypt )
1710  {
1711  if ( !setMasterPassword( true ) )
1712  {
1713  return false;
1714  }
1715  else
1716  {
1717  storeval = QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), value.toString() );
1718  }
1719  }
1720 
1721  removeAuthSetting( key );
1722 
1723  QSqlQuery query( authDatabaseConnection() );
1724  query.prepare( QStringLiteral( "INSERT INTO %1 (setting, value) "
1725  "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1726 
1727  query.bindValue( QStringLiteral( ":setting" ), key );
1728  query.bindValue( QStringLiteral( ":value" ), storeval );
1729 
1730  if ( !authDbStartTransaction() )
1731  return false;
1732 
1733  if ( !authDbQuery( &query ) )
1734  return false;
1735 
1736  if ( !authDbCommit() )
1737  return false;
1738 
1739  QgsDebugMsgLevel( QStringLiteral( "Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1740  return true;
1741 }
1742 
1743 QVariant QgsAuthManager::authSetting( const QString &key, const QVariant &defaultValue, bool decrypt )
1744 {
1745  QMutexLocker locker( mMutex.get() );
1746  if ( key.isEmpty() )
1747  return QVariant();
1748 
1749  if ( decrypt && !setMasterPassword( true ) )
1750  return QVariant();
1751 
1752  QVariant value = defaultValue;
1753  QSqlQuery query( authDatabaseConnection() );
1754  query.prepare( QStringLiteral( "SELECT value FROM %1 "
1755  "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1756 
1757  query.bindValue( QStringLiteral( ":setting" ), key );
1758 
1759  if ( !authDbQuery( &query ) )
1760  return QVariant();
1761 
1762  if ( query.isActive() && query.isSelect() )
1763  {
1764  if ( query.first() )
1765  {
1766  if ( decrypt )
1767  {
1768  value = QVariant( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1769  }
1770  else
1771  {
1772  value = query.value( 0 );
1773  }
1774  QgsDebugMsgLevel( QStringLiteral( "Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1775  }
1776  if ( query.next() )
1777  {
1778  QgsDebugMsg( QStringLiteral( "Select contains more than one for setting key: %1" ).arg( key ) );
1779  emit messageOut( tr( "Authentication database contains duplicate settings" ), authManTag(), WARNING );
1780  return QVariant();
1781  }
1782  }
1783  return value;
1784 }
1785 
1786 bool QgsAuthManager::existsAuthSetting( const QString &key )
1787 {
1788  QMutexLocker locker( mMutex.get() );
1789  if ( key.isEmpty() )
1790  return false;
1791 
1792  QSqlQuery query( authDatabaseConnection() );
1793  query.prepare( QStringLiteral( "SELECT value FROM %1 "
1794  "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1795 
1796  query.bindValue( QStringLiteral( ":setting" ), key );
1797 
1798  if ( !authDbQuery( &query ) )
1799  return false;
1800 
1801  bool res = false;
1802  if ( query.isActive() && query.isSelect() )
1803  {
1804  if ( query.first() )
1805  {
1806  QgsDebugMsgLevel( QStringLiteral( "Authentication setting exists for key: %1" ).arg( key ), 2 );
1807  res = true;
1808  }
1809  if ( query.next() )
1810  {
1811  QgsDebugMsg( QStringLiteral( "Select contains more than one for setting key: %1" ).arg( key ) );
1812  emit messageOut( tr( "Authentication database contains duplicate settings" ), authManTag(), WARNING );
1813  return false;
1814  }
1815  }
1816  return res;
1817 }
1818 
1819 bool QgsAuthManager::removeAuthSetting( const QString &key )
1820 {
1821  QMutexLocker locker( mMutex.get() );
1822  if ( key.isEmpty() )
1823  return false;
1824 
1825  QSqlQuery query( authDatabaseConnection() );
1826 
1827  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1828 
1829  query.bindValue( QStringLiteral( ":setting" ), key );
1830 
1831  if ( !authDbStartTransaction() )
1832  return false;
1833 
1834  if ( !authDbQuery( &query ) )
1835  return false;
1836 
1837  if ( !authDbCommit() )
1838  return false;
1839 
1840  QgsDebugMsgLevel( QStringLiteral( "REMOVED setting for key: %1" ).arg( key ), 2 );
1841 
1842  return true;
1843 }
1844 
1845 
1846 #ifndef QT_NO_SSL
1847 
1849 
1851 {
1852  QgsScopedRuntimeProfile profile( "Initialize SSL cache" );
1853 
1854  QMutexLocker locker( mMutex.get() );
1855  bool res = true;
1856  res = res && rebuildCaCertsCache();
1857  res = res && rebuildCertTrustCache();
1858  res = res && rebuildTrustedCaCertsCache();
1859  res = res && rebuildIgnoredSslErrorCache();
1860  mCustomConfigByHostCache.clear();
1861  mHasCheckedIfCustomConfigByHostExists = false;
1862 
1863  if ( !res )
1864  QgsDebugMsg( QStringLiteral( "Init of SSL caches FAILED" ) );
1865  return res;
1866 }
1867 
1868 bool QgsAuthManager::storeCertIdentity( const QSslCertificate &cert, const QSslKey &key )
1869 {
1870  QMutexLocker locker( mMutex.get() );
1871  if ( cert.isNull() )
1872  {
1873  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
1874  return false;
1875  }
1876  if ( key.isNull() )
1877  {
1878  QgsDebugMsg( QStringLiteral( "Passed private key is null" ) );
1879  return false;
1880  }
1881 
1882  if ( !setMasterPassword( true ) )
1883  return false;
1884 
1885  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1886  removeCertIdentity( id );
1887 
1888  QString certpem( cert.toPem() );
1889  QString keypem( QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), key.toPem() ) );
1890 
1891  QSqlQuery query( authDatabaseConnection() );
1892  query.prepare( QStringLiteral( "INSERT INTO %1 (id, key, cert) "
1893  "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1894 
1895  query.bindValue( QStringLiteral( ":id" ), id );
1896  query.bindValue( QStringLiteral( ":key" ), keypem );
1897  query.bindValue( QStringLiteral( ":cert" ), certpem );
1898 
1899  if ( !authDbStartTransaction() )
1900  return false;
1901 
1902  if ( !authDbQuery( &query ) )
1903  return false;
1904 
1905  if ( !authDbCommit() )
1906  return false;
1907 
1908  QgsDebugMsgLevel( QStringLiteral( "Store certificate identity SUCCESS for id: %1" ).arg( id ), 2 );
1909  return true;
1910 }
1911 
1912 const QSslCertificate QgsAuthManager::certIdentity( const QString &id )
1913 {
1914  QMutexLocker locker( mMutex.get() );
1915  QSslCertificate emptycert;
1916  QSslCertificate cert;
1917  if ( id.isEmpty() )
1918  return emptycert;
1919 
1920  QSqlQuery query( authDatabaseConnection() );
1921  query.prepare( QStringLiteral( "SELECT cert FROM %1 "
1922  "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1923 
1924  query.bindValue( QStringLiteral( ":id" ), id );
1925 
1926  if ( !authDbQuery( &query ) )
1927  return emptycert;
1928 
1929  if ( query.isActive() && query.isSelect() )
1930  {
1931  if ( query.first() )
1932  {
1933  cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1934  QgsDebugMsgLevel( QStringLiteral( "Certificate identity retrieved for id: %1" ).arg( id ), 2 );
1935  }
1936  if ( query.next() )
1937  {
1938  QgsDebugMsg( QStringLiteral( "Select contains more than one certificate identity for id: %1" ).arg( id ) );
1939  emit messageOut( tr( "Authentication database contains duplicate certificate identity" ), authManTag(), WARNING );
1940  return emptycert;
1941  }
1942  }
1943  return cert;
1944 }
1945 
1946 const QPair<QSslCertificate, QSslKey> QgsAuthManager::certIdentityBundle( const QString &id )
1947 {
1948  QMutexLocker locker( mMutex.get() );
1949  QPair<QSslCertificate, QSslKey> bundle;
1950  if ( id.isEmpty() )
1951  return bundle;
1952 
1953  if ( !setMasterPassword( true ) )
1954  return bundle;
1955 
1956  QSqlQuery query( authDatabaseConnection() );
1957  query.prepare( QStringLiteral( "SELECT key, cert FROM %1 "
1958  "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1959 
1960  query.bindValue( QStringLiteral( ":id" ), id );
1961 
1962  if ( !authDbQuery( &query ) )
1963  return bundle;
1964 
1965  if ( query.isActive() && query.isSelect() )
1966  {
1967  QSslCertificate cert;
1968  QSslKey key;
1969  if ( query.first() )
1970  {
1971  key = QSslKey( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1972  QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1973  if ( key.isNull() )
1974  {
1975  const char *err = QT_TR_NOOP( "Retrieve certificate identity bundle: FAILED to create private key" );
1976  QgsDebugMsg( err );
1977  emit messageOut( tr( err ), authManTag(), WARNING );
1978  return bundle;
1979  }
1980  cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1981  if ( cert.isNull() )
1982  {
1983  const char *err = QT_TR_NOOP( "Retrieve certificate identity bundle: FAILED to create certificate" );
1984  QgsDebugMsg( err );
1985  emit messageOut( tr( err ), authManTag(), WARNING );
1986  return bundle;
1987  }
1988  QgsDebugMsgLevel( QStringLiteral( "Certificate identity bundle retrieved for id: %1" ).arg( id ), 2 );
1989  }
1990  if ( query.next() )
1991  {
1992  QgsDebugMsg( QStringLiteral( "Select contains more than one certificate identity for id: %1" ).arg( id ) );
1993  emit messageOut( tr( "Authentication database contains duplicate certificate identity" ), authManTag(), WARNING );
1994  return bundle;
1995  }
1996  bundle = qMakePair( cert, key );
1997  }
1998  return bundle;
1999 }
2000 
2001 const QStringList QgsAuthManager::certIdentityBundleToPem( const QString &id )
2002 {
2003  QMutexLocker locker( mMutex.get() );
2004  QPair<QSslCertificate, QSslKey> bundle( certIdentityBundle( id ) );
2005  if ( QgsAuthCertUtils::certIsViable( bundle.first ) && !bundle.second.isNull() )
2006  {
2007  return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
2008  }
2009  return QStringList();
2010 }
2011 
2012 const QList<QSslCertificate> QgsAuthManager::certIdentities()
2013 {
2014  QMutexLocker locker( mMutex.get() );
2015  QList<QSslCertificate> certs;
2016 
2017  QSqlQuery query( authDatabaseConnection() );
2018  query.prepare( QStringLiteral( "SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2019 
2020  if ( !authDbQuery( &query ) )
2021  return certs;
2022 
2023  if ( query.isActive() && query.isSelect() )
2024  {
2025  while ( query.next() )
2026  {
2027  certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2028  }
2029  }
2030 
2031  return certs;
2032 }
2033 
2035 {
2036  QMutexLocker locker( mMutex.get() );
2037  QStringList identityids = QStringList();
2038 
2039  if ( isDisabled() )
2040  return identityids;
2041 
2042  QSqlQuery query( authDatabaseConnection() );
2043  query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2044 
2045  if ( !authDbQuery( &query ) )
2046  {
2047  return identityids;
2048  }
2049 
2050  if ( query.isActive() )
2051  {
2052  while ( query.next() )
2053  {
2054  identityids << query.value( 0 ).toString();
2055  }
2056  }
2057  return identityids;
2058 }
2059 
2060 bool QgsAuthManager::existsCertIdentity( const QString &id )
2061 {
2062  QMutexLocker locker( mMutex.get() );
2063  if ( id.isEmpty() )
2064  return false;
2065 
2066  QSqlQuery query( authDatabaseConnection() );
2067  query.prepare( QStringLiteral( "SELECT cert FROM %1 "
2068  "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2069 
2070  query.bindValue( QStringLiteral( ":id" ), id );
2071 
2072  if ( !authDbQuery( &query ) )
2073  return false;
2074 
2075  bool res = false;
2076  if ( query.isActive() && query.isSelect() )
2077  {
2078  if ( query.first() )
2079  {
2080  QgsDebugMsgLevel( QStringLiteral( "Certificate bundle exists for id: %1" ).arg( id ), 2 );
2081  res = true;
2082  }
2083  if ( query.next() )
2084  {
2085  QgsDebugMsg( QStringLiteral( "Select contains more than one certificate bundle for id: %1" ).arg( id ) );
2086  emit messageOut( tr( "Authentication database contains duplicate certificate bundles" ), authManTag(), WARNING );
2087  return false;
2088  }
2089  }
2090  return res;
2091 }
2092 
2093 bool QgsAuthManager::removeCertIdentity( const QString &id )
2094 {
2095  QMutexLocker locker( mMutex.get() );
2096  if ( id.isEmpty() )
2097  {
2098  QgsDebugMsg( QStringLiteral( "Passed bundle ID is empty" ) );
2099  return false;
2100  }
2101 
2102  QSqlQuery query( authDatabaseConnection() );
2103 
2104  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2105 
2106  query.bindValue( QStringLiteral( ":id" ), id );
2107 
2108  if ( !authDbStartTransaction() )
2109  return false;
2110 
2111  if ( !authDbQuery( &query ) )
2112  return false;
2113 
2114  if ( !authDbCommit() )
2115  return false;
2116 
2117  QgsDebugMsgLevel( QStringLiteral( "REMOVED certificate identity for id: %1" ).arg( id ), 2 );
2118  return true;
2119 }
2120 
2122 {
2123  QMutexLocker locker( mMutex.get() );
2124  if ( config.isNull() )
2125  {
2126  QgsDebugMsg( QStringLiteral( "Passed config is null" ) );
2127  return false;
2128  }
2129 
2130  QSslCertificate cert( config.sslCertificate() );
2131 
2132  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2133  removeSslCertCustomConfig( id, config.sslHostPort().trimmed() );
2134 
2135  QString certpem( cert.toPem() );
2136 
2137  QSqlQuery query( authDatabaseConnection() );
2138  query.prepare( QStringLiteral( "INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2139  "VALUES (:id, :host, :cert, :config)" ).arg( authDatabaseServersTable() ) );
2140 
2141  query.bindValue( QStringLiteral( ":id" ), id );
2142  query.bindValue( QStringLiteral( ":host" ), config.sslHostPort().trimmed() );
2143  query.bindValue( QStringLiteral( ":cert" ), certpem );
2144  query.bindValue( QStringLiteral( ":config" ), config.configString() );
2145 
2146  if ( !authDbStartTransaction() )
2147  return false;
2148 
2149  if ( !authDbQuery( &query ) )
2150  return false;
2151 
2152  if ( !authDbCommit() )
2153  return false;
2154 
2155  QgsDebugMsgLevel( QStringLiteral( "Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2156  .arg( config.sslHostPort().trimmed(), id ), 2 );
2157 
2159  mHasCheckedIfCustomConfigByHostExists = false;
2160  mCustomConfigByHostCache.clear();
2161 
2162  return true;
2163 }
2164 
2165 const QgsAuthConfigSslServer QgsAuthManager::sslCertCustomConfig( const QString &id, const QString &hostport )
2166 {
2167  QMutexLocker locker( mMutex.get() );
2168  QgsAuthConfigSslServer config;
2169 
2170  if ( id.isEmpty() || hostport.isEmpty() )
2171  {
2172  QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2173  return config;
2174  }
2175 
2176  QSqlQuery query( authDatabaseConnection() );
2177  query.prepare( QStringLiteral( "SELECT id, host, cert, config FROM %1 "
2178  "WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2179 
2180  query.bindValue( QStringLiteral( ":id" ), id );
2181  query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2182 
2183  if ( !authDbQuery( &query ) )
2184  return config;
2185 
2186  if ( query.isActive() && query.isSelect() )
2187  {
2188  if ( query.first() )
2189  {
2190  config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2191  config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2192  config.loadConfigString( query.value( 3 ).toString() );
2193  QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2194  }
2195  if ( query.next() )
2196  {
2197  QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ) );
2198  emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2199  .arg( hostport, id ), authManTag(), WARNING );
2200  QgsAuthConfigSslServer emptyconfig;
2201  return emptyconfig;
2202  }
2203  }
2204  return config;
2205 }
2206 
2208 {
2209  QgsAuthConfigSslServer config;
2210  if ( hostport.isEmpty() )
2211  {
2212  return config;
2213  }
2214 
2215  QMutexLocker locker( mMutex.get() );
2216  if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2217  return config;
2218  if ( mCustomConfigByHostCache.contains( hostport ) )
2219  return mCustomConfigByHostCache.value( hostport );
2220 
2221  QSqlQuery query( authDatabaseConnection() );
2222 
2223  // first run -- see if we have ANY custom config by host. If not, we can skip all future checks for any host
2224  if ( !mHasCheckedIfCustomConfigByHostExists )
2225  {
2226  mHasCheckedIfCustomConfigByHostExists = true;
2227  query.prepare( QString( "SELECT count(*) FROM %1" ).arg( authDatabaseServersTable() ) );
2228  if ( !authDbQuery( &query ) )
2229  {
2230  mHasCustomConfigByHost = false;
2231  return config;
2232  }
2233  if ( query.isActive() && query.isSelect() && query.first() )
2234  {
2235  mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2236  if ( !mHasCustomConfigByHost )
2237  return config;
2238  }
2239  else
2240  {
2241  mHasCustomConfigByHost = false;
2242  return config;
2243  }
2244  }
2245 
2246  query.prepare( QString( "SELECT id, host, cert, config FROM %1 "
2247  "WHERE host = :host" ).arg( authDatabaseServersTable() ) );
2248 
2249  query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2250 
2251  if ( !authDbQuery( &query ) )
2252  {
2253  mCustomConfigByHostCache.insert( hostport, config );
2254  return config;
2255  }
2256 
2257  if ( query.isActive() && query.isSelect() )
2258  {
2259  if ( query.first() )
2260  {
2261  config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2262  config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2263  config.loadConfigString( query.value( 3 ).toString() );
2264  QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2265  }
2266  if ( query.next() )
2267  {
2268  QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2269  emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2270  .arg( hostport ), authManTag(), WARNING );
2271  QgsAuthConfigSslServer emptyconfig;
2272  mCustomConfigByHostCache.insert( hostport, emptyconfig );
2273  return emptyconfig;
2274  }
2275  }
2276 
2277  mCustomConfigByHostCache.insert( hostport, config );
2278  return config;
2279 }
2280 
2281 const QList<QgsAuthConfigSslServer> QgsAuthManager::sslCertCustomConfigs()
2282 {
2283  QMutexLocker locker( mMutex.get() );
2284  QList<QgsAuthConfigSslServer> configs;
2285 
2286  QSqlQuery query( authDatabaseConnection() );
2287  query.prepare( QStringLiteral( "SELECT id, host, cert, config FROM %1" ).arg( authDatabaseServersTable() ) );
2288 
2289  if ( !authDbQuery( &query ) )
2290  return configs;
2291 
2292  if ( query.isActive() && query.isSelect() )
2293  {
2294  while ( query.next() )
2295  {
2296  QgsAuthConfigSslServer config;
2297  config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2298  config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2299  config.loadConfigString( query.value( 3 ).toString() );
2300 
2301  configs.append( config );
2302  }
2303  }
2304 
2305  return configs;
2306 }
2307 
2308 bool QgsAuthManager::existsSslCertCustomConfig( const QString &id, const QString &hostport )
2309 {
2310  QMutexLocker locker( mMutex.get() );
2311  if ( id.isEmpty() || hostport.isEmpty() )
2312  {
2313  QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2314  return false;
2315  }
2316 
2317  QSqlQuery query( authDatabaseConnection() );
2318  query.prepare( QStringLiteral( "SELECT cert FROM %1 "
2319  "WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2320 
2321  query.bindValue( QStringLiteral( ":id" ), id );
2322  query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2323 
2324  if ( !authDbQuery( &query ) )
2325  return false;
2326 
2327  bool res = false;
2328  if ( query.isActive() && query.isSelect() )
2329  {
2330  if ( query.first() )
2331  {
2332  QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2333  res = true;
2334  }
2335  if ( query.next() )
2336  {
2337  QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ) );
2338  emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2339  .arg( hostport, id ), authManTag(), WARNING );
2340  return false;
2341  }
2342  }
2343  return res;
2344 }
2345 
2346 bool QgsAuthManager::removeSslCertCustomConfig( const QString &id, const QString &hostport )
2347 {
2348  QMutexLocker locker( mMutex.get() );
2349  if ( id.isEmpty() || hostport.isEmpty() )
2350  {
2351  QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2352  return false;
2353  }
2354 
2355  mHasCheckedIfCustomConfigByHostExists = false;
2356  mCustomConfigByHostCache.clear();
2357 
2358  QSqlQuery query( authDatabaseConnection() );
2359 
2360  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2361 
2362  query.bindValue( QStringLiteral( ":id" ), id );
2363  query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2364 
2365  if ( !authDbStartTransaction() )
2366  return false;
2367 
2368  if ( !authDbQuery( &query ) )
2369  return false;
2370 
2371  if ( !authDbCommit() )
2372  return false;
2373 
2374  QString shahostport( QStringLiteral( "%1:%2" ).arg( id, hostport ) );
2375  if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2376  {
2377  mIgnoredSslErrorsCache.remove( shahostport );
2378  }
2379 
2380  QgsDebugMsgLevel( QStringLiteral( "REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2382  return true;
2383 }
2384 
2386 {
2387  QMutexLocker locker( mMutex.get() );
2388  if ( !mIgnoredSslErrorsCache.isEmpty() )
2389  {
2390  QgsDebugMsg( QStringLiteral( "Ignored SSL errors cache items:" ) );
2391  QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2392  while ( i != mIgnoredSslErrorsCache.constEnd() )
2393  {
2394  QStringList errs;
2395  for ( auto err : i.value() )
2396  {
2397  errs << QgsAuthCertUtils::sslErrorEnumString( err );
2398  }
2399  QgsDebugMsg( QStringLiteral( "%1 = %2" ).arg( i.key(), errs.join( ", " ) ) );
2400  ++i;
2401  }
2402  }
2403  else
2404  {
2405  QgsDebugMsgLevel( QStringLiteral( "Ignored SSL errors cache EMPTY" ), 2 );
2406  }
2407 }
2408 
2410 {
2411  QMutexLocker locker( mMutex.get() );
2412  if ( config.isNull() )
2413  {
2414  QgsDebugMsg( QStringLiteral( "Passed config is null" ) );
2415  return false;
2416  }
2417 
2418  QString shahostport( QStringLiteral( "%1:%2" )
2419  .arg( QgsAuthCertUtils::shaHexForCert( config.sslCertificate() ).trimmed(),
2420  config.sslHostPort().trimmed() ) );
2421  if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2422  {
2423  mIgnoredSslErrorsCache.remove( shahostport );
2424  }
2425  QList<QSslError::SslError> errenums( config.sslIgnoredErrorEnums() );
2426  if ( !errenums.isEmpty() )
2427  {
2428  mIgnoredSslErrorsCache.insert( shahostport, qgis::listToSet( errenums ) );
2429  QgsDebugMsgLevel( QStringLiteral( "Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2431  return true;
2432  }
2433 
2434  QgsDebugMsgLevel( QStringLiteral( "No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2435  return true;
2436 }
2437 
2438 bool QgsAuthManager::updateIgnoredSslErrorsCache( const QString &shahostport, const QList<QSslError> &errors )
2439 {
2440  QMutexLocker locker( mMutex.get() );
2441  const thread_local QRegularExpression rx( QRegularExpression::anchoredPattern( "\\S+:\\S+:\\d+" ) );
2442  if ( !rx.match( shahostport ).hasMatch() )
2443  {
2444  QgsDebugMsg( "Passed shahostport does not match \\S+:\\S+:\\d+, "
2445  "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2446  return false;
2447  }
2448 
2449  if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2450  {
2451  mIgnoredSslErrorsCache.remove( shahostport );
2452  }
2453 
2454  if ( errors.isEmpty() )
2455  {
2456  QgsDebugMsg( QStringLiteral( "Passed errors list empty" ) );
2457  return false;
2458  }
2459 
2460  QSet<QSslError::SslError> errs;
2461  for ( const auto &error : errors )
2462  {
2463  if ( error.error() == QSslError::NoError )
2464  continue;
2465 
2466  errs.insert( error.error() );
2467  }
2468 
2469  if ( errs.isEmpty() )
2470  {
2471  QgsDebugMsg( QStringLiteral( "Passed errors list does not contain errors" ) );
2472  return false;
2473  }
2474 
2475  mIgnoredSslErrorsCache.insert( shahostport, errs );
2476 
2477  QgsDebugMsgLevel( QStringLiteral( "Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2479  return true;
2480 }
2481 
2483 {
2484  QMutexLocker locker( mMutex.get() );
2485  QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2486  QHash<QString, QSet<QSslError::SslError> > nextcache;
2487 
2488  QSqlQuery query( authDatabaseConnection() );
2489  query.prepare( QStringLiteral( "SELECT id, host, config FROM %1" ).arg( authDatabaseServersTable() ) );
2490 
2491  if ( !authDbQuery( &query ) )
2492  {
2493  QgsDebugMsg( QStringLiteral( "Rebuild of ignored SSL errors cache FAILED" ) );
2494  return false;
2495  }
2496 
2497  if ( query.isActive() && query.isSelect() )
2498  {
2499  while ( query.next() )
2500  {
2501  QString shahostport( QStringLiteral( "%1:%2" )
2502  .arg( query.value( 0 ).toString().trimmed(),
2503  query.value( 1 ).toString().trimmed() ) );
2504  QgsAuthConfigSslServer config;
2505  config.loadConfigString( query.value( 2 ).toString() );
2506  QList<QSslError::SslError> errenums( config.sslIgnoredErrorEnums() );
2507  if ( !errenums.isEmpty() )
2508  {
2509  nextcache.insert( shahostport, qgis::listToSet( errenums ) );
2510  }
2511  if ( prevcache.contains( shahostport ) )
2512  {
2513  prevcache.remove( shahostport );
2514  }
2515  }
2516  }
2517 
2518  if ( !prevcache.isEmpty() )
2519  {
2520  // preserve any existing per-session ignored errors for hosts
2521  QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2522  while ( i != prevcache.constEnd() )
2523  {
2524  nextcache.insert( i.key(), i.value() );
2525  ++i;
2526  }
2527  }
2528 
2529  if ( nextcache != mIgnoredSslErrorsCache )
2530  {
2531  mIgnoredSslErrorsCache.clear();
2532  mIgnoredSslErrorsCache = nextcache;
2533  QgsDebugMsgLevel( QStringLiteral( "Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2535  return true;
2536  }
2537 
2538  QgsDebugMsgLevel( QStringLiteral( "Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2540  return true;
2541 }
2542 
2543 
2544 bool QgsAuthManager::storeCertAuthorities( const QList<QSslCertificate> &certs )
2545 {
2546  QMutexLocker locker( mMutex.get() );
2547  if ( certs.isEmpty() )
2548  {
2549  QgsDebugMsg( QStringLiteral( "Passed certificate list has no certs" ) );
2550  return false;
2551  }
2552 
2553  for ( const auto &cert : certs )
2554  {
2555  if ( !storeCertAuthority( cert ) )
2556  return false;
2557  }
2558  return true;
2559 }
2560 
2561 bool QgsAuthManager::storeCertAuthority( const QSslCertificate &cert )
2562 {
2563  QMutexLocker locker( mMutex.get() );
2564  // don't refuse !cert.isValid() (actually just expired) CAs,
2565  // as user may want to ignore that SSL connection error
2566  if ( cert.isNull() )
2567  {
2568  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2569  return false;
2570  }
2571 
2572  removeCertAuthority( cert );
2573 
2574  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2575  QString pem( cert.toPem() );
2576 
2577  QSqlQuery query( authDatabaseConnection() );
2578  query.prepare( QStringLiteral( "INSERT INTO %1 (id, cert) "
2579  "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2580 
2581  query.bindValue( QStringLiteral( ":id" ), id );
2582  query.bindValue( QStringLiteral( ":cert" ), pem );
2583 
2584  if ( !authDbStartTransaction() )
2585  return false;
2586 
2587  if ( !authDbQuery( &query ) )
2588  return false;
2589 
2590  if ( !authDbCommit() )
2591  return false;
2592 
2593  QgsDebugMsgLevel( QStringLiteral( "Store certificate authority SUCCESS for id: %1" ).arg( id ), 2 );
2594  return true;
2595 }
2596 
2597 const QSslCertificate QgsAuthManager::certAuthority( const QString &id )
2598 {
2599  QMutexLocker locker( mMutex.get() );
2600  QSslCertificate emptycert;
2601  QSslCertificate cert;
2602  if ( id.isEmpty() )
2603  return emptycert;
2604 
2605  QSqlQuery query( authDatabaseConnection() );
2606  query.prepare( QStringLiteral( "SELECT cert FROM %1 "
2607  "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2608 
2609  query.bindValue( QStringLiteral( ":id" ), id );
2610 
2611  if ( !authDbQuery( &query ) )
2612  return emptycert;
2613 
2614  if ( query.isActive() && query.isSelect() )
2615  {
2616  if ( query.first() )
2617  {
2618  cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2619  QgsDebugMsgLevel( QStringLiteral( "Certificate authority retrieved for id: %1" ).arg( id ), 2 );
2620  }
2621  if ( query.next() )
2622  {
2623  QgsDebugMsg( QStringLiteral( "Select contains more than one certificate authority for id: %1" ).arg( id ) );
2624  emit messageOut( tr( "Authentication database contains duplicate certificate authorities" ), authManTag(), WARNING );
2625  return emptycert;
2626  }
2627  }
2628  return cert;
2629 }
2630 
2631 bool QgsAuthManager::existsCertAuthority( const QSslCertificate &cert )
2632 {
2633  QMutexLocker locker( mMutex.get() );
2634  if ( cert.isNull() )
2635  {
2636  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2637  return false;
2638  }
2639 
2640  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2641 
2642  QSqlQuery query( authDatabaseConnection() );
2643  query.prepare( QStringLiteral( "SELECT value FROM %1 "
2644  "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2645 
2646  query.bindValue( QStringLiteral( ":id" ), id );
2647 
2648  if ( !authDbQuery( &query ) )
2649  return false;
2650 
2651  bool res = false;
2652  if ( query.isActive() && query.isSelect() )
2653  {
2654  if ( query.first() )
2655  {
2656  QgsDebugMsgLevel( QStringLiteral( "Certificate authority exists for id: %1" ).arg( id ), 2 );
2657  res = true;
2658  }
2659  if ( query.next() )
2660  {
2661  QgsDebugMsg( QStringLiteral( "Select contains more than one certificate authority for id: %1" ).arg( id ) );
2662  emit messageOut( tr( "Authentication database contains duplicate certificate authorities" ), authManTag(), WARNING );
2663  return false;
2664  }
2665  }
2666  return res;
2667 }
2668 
2669 bool QgsAuthManager::removeCertAuthority( const QSslCertificate &cert )
2670 {
2671  QMutexLocker locker( mMutex.get() );
2672  if ( cert.isNull() )
2673  {
2674  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2675  return false;
2676  }
2677 
2678  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2679 
2680  QSqlQuery query( authDatabaseConnection() );
2681 
2682  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2683 
2684  query.bindValue( QStringLiteral( ":id" ), id );
2685 
2686  if ( !authDbStartTransaction() )
2687  return false;
2688 
2689  if ( !authDbQuery( &query ) )
2690  return false;
2691 
2692  if ( !authDbCommit() )
2693  return false;
2694 
2695  QgsDebugMsgLevel( QStringLiteral( "REMOVED authority for id: %1" ).arg( id ), 2 );
2696  return true;
2697 }
2698 
2699 const QList<QSslCertificate> QgsAuthManager::systemRootCAs()
2700 {
2701  return QSslConfiguration::systemCaCertificates();
2702 }
2703 
2704 const QList<QSslCertificate> QgsAuthManager::extraFileCAs()
2705 {
2706  QMutexLocker locker( mMutex.get() );
2707  QList<QSslCertificate> certs;
2708  QList<QSslCertificate> filecerts;
2709  QVariant cafileval = QgsAuthManager::instance()->authSetting( QStringLiteral( "cafile" ) );
2710  if ( cafileval.isNull() )
2711  return certs;
2712 
2713  QVariant allowinvalid = QgsAuthManager::instance()->authSetting( QStringLiteral( "cafileallowinvalid" ), QVariant( false ) );
2714  if ( allowinvalid.isNull() )
2715  return certs;
2716 
2717  QString cafile( cafileval.toString() );
2718  if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2719  {
2720  filecerts = QgsAuthCertUtils::certsFromFile( cafile );
2721  }
2722  // only CAs or certs capable of signing other certs are allowed
2723  for ( const auto &cert : std::as_const( filecerts ) )
2724  {
2725  if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2726  || cert.isNull()
2727  || cert.expiryDate() <= QDateTime::currentDateTime()
2728  || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2729  {
2730  continue;
2731  }
2732 
2734  {
2735  certs << cert;
2736  }
2737  }
2738  return certs;
2739 }
2740 
2741 const QList<QSslCertificate> QgsAuthManager::databaseCAs()
2742 {
2743  QMutexLocker locker( mMutex.get() );
2744  QList<QSslCertificate> certs;
2745 
2746  QSqlQuery query( authDatabaseConnection() );
2747  query.prepare( QStringLiteral( "SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2748 
2749  if ( !authDbQuery( &query ) )
2750  return certs;
2751 
2752  if ( query.isActive() && query.isSelect() )
2753  {
2754  while ( query.next() )
2755  {
2756  certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2757  }
2758  }
2759 
2760  return certs;
2761 }
2762 
2763 const QMap<QString, QSslCertificate> QgsAuthManager::mappedDatabaseCAs()
2764 {
2765  QMutexLocker locker( mMutex.get() );
2767 }
2768 
2770 {
2771  QMutexLocker locker( mMutex.get() );
2772  mCaCertsCache.clear();
2773  // in reverse order of precedence, with regards to duplicates, so QMap inserts overwrite
2774  insertCaCertInCache( QgsAuthCertUtils::SystemRoot, systemRootCAs() );
2775  insertCaCertInCache( QgsAuthCertUtils::FromFile, extraFileCAs() );
2776  insertCaCertInCache( QgsAuthCertUtils::InDatabase, databaseCAs() );
2777 
2778  bool res = !mCaCertsCache.isEmpty(); // should at least contain system root CAs
2779  if ( !res )
2780  QgsDebugMsg( QStringLiteral( "Rebuild of CA certs cache FAILED" ) );
2781  return res;
2782 }
2783 
2785 {
2786  QMutexLocker locker( mMutex.get() );
2787  if ( cert.isNull() )
2788  {
2789  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2790  return false;
2791  }
2792 
2793  removeCertTrustPolicy( cert );
2794 
2795  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2796 
2797  if ( policy == QgsAuthCertUtils::DefaultTrust )
2798  {
2799  QgsDebugMsg( QStringLiteral( "Passed policy was default, all cert records in database were removed for id: %1" ).arg( id ) );
2800  return true;
2801  }
2802 
2803  QSqlQuery query( authDatabaseConnection() );
2804  query.prepare( QStringLiteral( "INSERT INTO %1 (id, policy) "
2805  "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2806 
2807  query.bindValue( QStringLiteral( ":id" ), id );
2808  query.bindValue( QStringLiteral( ":policy" ), static_cast< int >( policy ) );
2809 
2810  if ( !authDbStartTransaction() )
2811  return false;
2812 
2813  if ( !authDbQuery( &query ) )
2814  return false;
2815 
2816  if ( !authDbCommit() )
2817  return false;
2818 
2819  QgsDebugMsgLevel( QStringLiteral( "Store certificate trust policy SUCCESS for id: %1" ).arg( id ), 2 );
2820  return true;
2821 }
2822 
2824 {
2825  QMutexLocker locker( mMutex.get() );
2826  if ( cert.isNull() )
2827  {
2828  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2830  }
2831 
2832  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2833 
2834  QSqlQuery query( authDatabaseConnection() );
2835  query.prepare( QStringLiteral( "SELECT policy FROM %1 "
2836  "WHERE id = :id" ).arg( authDbTrustTable() ) );
2837 
2838  query.bindValue( QStringLiteral( ":id" ), id );
2839 
2840  if ( !authDbQuery( &query ) )
2842 
2844  if ( query.isActive() && query.isSelect() )
2845  {
2846  if ( query.first() )
2847  {
2848  policy = static_cast< QgsAuthCertUtils::CertTrustPolicy >( query.value( 0 ).toInt() );
2849  QgsDebugMsgLevel( QStringLiteral( "Authentication cert trust policy retrieved for id: %1" ).arg( id ), 2 );
2850  }
2851  if ( query.next() )
2852  {
2853  QgsDebugMsg( QStringLiteral( "Select contains more than one cert trust policy for id: %1" ).arg( id ) );
2854  emit messageOut( tr( "Authentication database contains duplicate cert trust policies" ), authManTag(), WARNING );
2856  }
2857  }
2858  return policy;
2859 }
2860 
2861 bool QgsAuthManager::removeCertTrustPolicies( const QList<QSslCertificate> &certs )
2862 {
2863  QMutexLocker locker( mMutex.get() );
2864  if ( certs.empty() )
2865  {
2866  QgsDebugMsg( QStringLiteral( "Passed certificate list has no certs" ) );
2867  return false;
2868  }
2869 
2870  for ( const auto &cert : certs )
2871  {
2872  if ( !removeCertTrustPolicy( cert ) )
2873  return false;
2874  }
2875  return true;
2876 }
2877 
2878 bool QgsAuthManager::removeCertTrustPolicy( const QSslCertificate &cert )
2879 {
2880  QMutexLocker locker( mMutex.get() );
2881  if ( cert.isNull() )
2882  {
2883  QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2884  return false;
2885  }
2886 
2887  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2888 
2889  QSqlQuery query( authDatabaseConnection() );
2890 
2891  query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2892 
2893  query.bindValue( QStringLiteral( ":id" ), id );
2894 
2895  if ( !authDbStartTransaction() )
2896  return false;
2897 
2898  if ( !authDbQuery( &query ) )
2899  return false;
2900 
2901  if ( !authDbCommit() )
2902  return false;
2903 
2904  QgsDebugMsgLevel( QStringLiteral( "REMOVED cert trust policy for id: %1" ).arg( id ), 2 );
2905 
2906  return true;
2907 }
2908 
2910 {
2911  QMutexLocker locker( mMutex.get() );
2912  if ( cert.isNull() )
2913  {
2915  }
2916 
2917  QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2918  const QStringList &trustedids = mCertTrustCache.value( QgsAuthCertUtils::Trusted );
2919  const QStringList &untrustedids = mCertTrustCache.value( QgsAuthCertUtils::Untrusted );
2920 
2922  if ( trustedids.contains( id ) )
2923  {
2924  policy = QgsAuthCertUtils::Trusted;
2925  }
2926  else if ( untrustedids.contains( id ) )
2927  {
2928  policy = QgsAuthCertUtils::Untrusted;
2929  }
2930  return policy;
2931 }
2932 
2934 {
2935 
2936  if ( policy == QgsAuthCertUtils::DefaultTrust )
2937  {
2938  // set default trust policy to Trusted by removing setting
2939  return removeAuthSetting( QStringLiteral( "certdefaulttrust" ) );
2940  }
2941  return storeAuthSetting( QStringLiteral( "certdefaulttrust" ), static_cast< int >( policy ) );
2942 }
2943 
2945 {
2946  QMutexLocker locker( mMutex.get() );
2947  QVariant policy( authSetting( QStringLiteral( "certdefaulttrust" ) ) );
2948  if ( policy.isNull() )
2949  {
2951  }
2952  return static_cast< QgsAuthCertUtils::CertTrustPolicy >( policy.toInt() );
2953 }
2954 
2956 {
2957  QMutexLocker locker( mMutex.get() );
2958  mCertTrustCache.clear();
2959 
2960  QSqlQuery query( authDatabaseConnection() );
2961  query.prepare( QStringLiteral( "SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2962 
2963  if ( !authDbQuery( &query ) )
2964  {
2965  QgsDebugMsg( QStringLiteral( "Rebuild of cert trust policy cache FAILED" ) );
2966  return false;
2967  }
2968 
2969  if ( query.isActive() && query.isSelect() )
2970  {
2971  while ( query.next() )
2972  {
2973  QString id = query.value( 0 ).toString();
2974  QgsAuthCertUtils::CertTrustPolicy policy = static_cast< QgsAuthCertUtils::CertTrustPolicy >( query.value( 1 ).toInt() );
2975 
2976  QStringList ids;
2977  if ( mCertTrustCache.contains( policy ) )
2978  {
2979  ids = mCertTrustCache.value( policy );
2980  }
2981  mCertTrustCache.insert( policy, ids << id );
2982  }
2983  }
2984 
2985  QgsDebugMsgLevel( QStringLiteral( "Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2986  return true;
2987 }
2988 
2989 const QList<QSslCertificate> QgsAuthManager::trustedCaCerts( bool includeinvalid )
2990 {
2991  QMutexLocker locker( mMutex.get() );
2993  QStringList trustedids = mCertTrustCache.value( QgsAuthCertUtils::Trusted );
2994  QStringList untrustedids = mCertTrustCache.value( QgsAuthCertUtils::Untrusted );
2995  const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2996 
2997  QList<QSslCertificate> trustedcerts;
2998  for ( int i = 0; i < certpairs.size(); ++i )
2999  {
3000  QSslCertificate cert( certpairs.at( i ).second );
3001  QString certid( QgsAuthCertUtils::shaHexForCert( cert ) );
3002  if ( trustedids.contains( certid ) )
3003  {
3004  // trusted certs are always added regardless of their validity
3005  trustedcerts.append( cert );
3006  }
3007  else if ( defaultpolicy == QgsAuthCertUtils::Trusted && !untrustedids.contains( certid ) )
3008  {
3009  if ( !includeinvalid && !QgsAuthCertUtils::certIsViable( cert ) )
3010  continue;
3011  trustedcerts.append( cert );
3012  }
3013  }
3014 
3015  // update application default SSL config for new requests
3016  QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
3017  sslconfig.setCaCertificates( trustedcerts );
3018  QSslConfiguration::setDefaultConfiguration( sslconfig );
3019 
3020  return trustedcerts;
3021 }
3022 
3023 const QList<QSslCertificate> QgsAuthManager::untrustedCaCerts( QList<QSslCertificate> trustedCAs )
3024 {
3025  QMutexLocker locker( mMutex.get() );
3026  if ( trustedCAs.isEmpty() )
3027  {
3028  if ( mTrustedCaCertsCache.isEmpty() )
3029  {
3031  }
3032  trustedCAs = trustedCaCertsCache();
3033  }
3034 
3035  const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3036 
3037  QList<QSslCertificate> untrustedCAs;
3038  for ( int i = 0; i < certpairs.size(); ++i )
3039  {
3040  QSslCertificate cert( certpairs.at( i ).second );
3041  if ( !trustedCAs.contains( cert ) )
3042  {
3043  untrustedCAs.append( cert );
3044  }
3045  }
3046  return untrustedCAs;
3047 }
3048 
3050 {
3051  QMutexLocker locker( mMutex.get() );
3052  mTrustedCaCertsCache = trustedCaCerts();
3053  QgsDebugMsgLevel( QStringLiteral( "Rebuilt trusted cert authorities cache" ), 2 );
3054  // TODO: add some error trapping for the operation
3055  return true;
3056 }
3057 
3059 {
3060  QMutexLocker locker( mMutex.get() );
3062 }
3063 
3065 {
3066  QMutexLocker locker( mMutex.get() );
3067  if ( masterPasswordIsSet() )
3068  {
3069  return passwordHelperWrite( mMasterPass );
3070  }
3071  return false;
3072 }
3073 
3074 
3076 
3077 #endif
3078 
3080 {
3081  if ( isDisabled() )
3082  return;
3083 
3084  const QStringList ids = configIds();
3085  for ( const auto &authcfg : ids )
3086  {
3087  clearCachedConfig( authcfg );
3088  }
3089 }
3090 
3091 void QgsAuthManager::clearCachedConfig( const QString &authcfg )
3092 {
3093  if ( isDisabled() )
3094  return;
3095 
3096  QgsAuthMethod *authmethod = configAuthMethod( authcfg );
3097  if ( authmethod )
3098  {
3099  authmethod->clearCachedConfig( authcfg );
3100  }
3101 }
3102 
3103 void QgsAuthManager::writeToConsole( const QString &message,
3104  const QString &tag,
3106 {
3107  Q_UNUSED( tag )
3108 
3109  // only output WARNING and CRITICAL messages
3110  if ( level == QgsAuthManager::INFO )
3111  return;
3112 
3113  QString msg;
3114  switch ( level )
3115  {
3117  msg += QLatin1String( "WARNING: " );
3118  break;
3120  msg += QLatin1String( "ERROR: " );
3121  break;
3122  default:
3123  break;
3124  }
3125  msg += message;
3126 
3127  QTextStream out( stdout, QIODevice::WriteOnly );
3128 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
3129  out << msg << endl;
3130 #else
3131  out << msg << Qt::endl;
3132 #endif
3133 }
3134 
3135 void QgsAuthManager::tryToStartDbErase()
3136 {
3137  ++mScheduledDbEraseRequestCount;
3138  // wait a total of 90 seconds for GUI availiability or user interaction, then cancel schedule
3139  int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3140  if ( mScheduledDbEraseRequestCount >= trycutoff )
3141  {
3143  QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3144  return;
3145  }
3146  else
3147  {
3148  QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest attempt (%1 of %2)" )
3149  .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3150  }
3151 
3152  if ( scheduledAuthDatabaseErase() && !mScheduledDbEraseRequestEmitted && mMutex->tryLock() )
3153  {
3154  // see note in header about this signal's use
3155  mScheduledDbEraseRequestEmitted = true;
3157 
3158  mMutex->unlock();
3159 
3160  QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emitted" ), 2 );
3161  return;
3162  }
3163  QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emit skipped" ), 2 );
3164 }
3165 
3166 
3168 {
3169  QMutexLocker locker( mMutex.get() );
3170  QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3171  while ( iterator.hasNext() )
3172  {
3173  iterator.next();
3174  QThread::disconnect( iterator.value() );
3175  }
3176  locker.unlock();
3177 
3178  if ( !isDisabled() )
3179  {
3181  qDeleteAll( mAuthMethods );
3182 
3183  QSqlDatabase authConn = authDatabaseConnection();
3184  if ( authConn.isValid() && authConn.isOpen() )
3185  authConn.close();
3186  }
3187  delete mScheduledDbEraseTimer;
3188  mScheduledDbEraseTimer = nullptr;
3189  QSqlDatabase::removeDatabase( QStringLiteral( "authentication.configs" ) );
3190 }
3191 
3192 
3193 QString QgsAuthManager::passwordHelperName() const
3194 {
3195  return tr( "Password Helper" );
3196 }
3197 
3198 
3199 void QgsAuthManager::passwordHelperLog( const QString &msg ) const
3200 {
3202  {
3203  QgsMessageLog::logMessage( msg, passwordHelperName() );
3204  }
3205 }
3206 
3208 {
3209  passwordHelperLog( tr( "Opening %1 for DELETE…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3210  bool result;
3211  QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3212  QgsSettings settings;
3213  job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3214  job.setAutoDelete( false );
3215  job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3216  QEventLoop loop;
3217  connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3218  job.start();
3219  loop.exec();
3220  if ( job.error() )
3221  {
3222  mPasswordHelperErrorCode = job.error();
3223  mPasswordHelperErrorMessage = tr( "Delete password failed: %1." ).arg( job.errorString() );
3224  // Signals used in the tests to exit main application loop
3225  emit passwordHelperFailure();
3226  result = false;
3227  }
3228  else
3229  {
3230  // Signals used in the tests to exit main application loop
3231  emit passwordHelperSuccess();
3232  result = true;
3233  }
3234  passwordHelperProcessError();
3235  return result;
3236 }
3237 
3238 QString QgsAuthManager::passwordHelperRead()
3239 {
3240  // Retrieve it!
3241  QString password;
3242  passwordHelperLog( tr( "Opening %1 for READ…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3243  QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3244  QgsSettings settings;
3245  job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3246  job.setAutoDelete( false );
3247  job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3248  QEventLoop loop;
3249  connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3250  job.start();
3251  loop.exec();
3252  if ( job.error() )
3253  {
3254  mPasswordHelperErrorCode = job.error();
3255  mPasswordHelperErrorMessage = tr( "Retrieving password from your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
3256  // Signals used in the tests to exit main application loop
3257  emit passwordHelperFailure();
3258  }
3259  else
3260  {
3261  password = job.textData();
3262  // Password is there but it is empty, treat it like if it was not found
3263  if ( password.isEmpty() )
3264  {
3265  mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3266  mPasswordHelperErrorMessage = tr( "Empty password retrieved from your %1." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME );
3267  // Signals used in the tests to exit main application loop
3268  emit passwordHelperFailure();
3269  }
3270  else
3271  {
3272  // Signals used in the tests to exit main application loop
3273  emit passwordHelperSuccess();
3274  }
3275  }
3276  passwordHelperProcessError();
3277  return password;
3278 }
3279 
3280 bool QgsAuthManager::passwordHelperWrite( const QString &password )
3281 {
3282  Q_ASSERT( !password.isEmpty() );
3283  bool result;
3284  passwordHelperLog( tr( "Opening %1 for WRITE…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3285  QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3286  QgsSettings settings;
3287  job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3288  job.setAutoDelete( false );
3289  job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3290  job.setTextData( password );
3291  QEventLoop loop;
3292  connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3293  job.start();
3294  loop.exec();
3295  if ( job.error() )
3296  {
3297  mPasswordHelperErrorCode = job.error();
3298  mPasswordHelperErrorMessage = tr( "Storing password in your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
3299  // Signals used in the tests to exit main application loop
3300  emit passwordHelperFailure();
3301  result = false;
3302  }
3303  else
3304  {
3305  passwordHelperClearErrors();
3306  // Signals used in the tests to exit main application loop
3307  emit passwordHelperSuccess();
3308  result = true;
3309  }
3310  passwordHelperProcessError();
3311  return result;
3312 }
3313 
3315 {
3316  // Does the user want to store the password in the wallet?
3317  QgsSettings settings;
3318  return settings.value( QStringLiteral( "use_password_helper" ), true, QgsSettings::Section::Auth ).toBool();
3319 }
3320 
3322 {
3323  QgsSettings settings;
3324  settings.setValue( QStringLiteral( "use_password_helper" ), enabled, QgsSettings::Section::Auth );
3325  emit messageOut( enabled ? tr( "Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3327  tr( "Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3329 }
3330 
3332 {
3333  // Does the user want to store the password in the wallet?
3334  QgsSettings settings;
3335  return settings.value( QStringLiteral( "password_helper_logging" ), false, QgsSettings::Section::Auth ).toBool();
3336 }
3337 
3339 {
3340  QgsSettings settings;
3341  settings.setValue( QStringLiteral( "password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3342 }
3343 
3344 void QgsAuthManager::passwordHelperClearErrors()
3345 {
3346  mPasswordHelperErrorCode = QKeychain::NoError;
3347  mPasswordHelperErrorMessage.clear();
3348 }
3349 
3350 void QgsAuthManager::passwordHelperProcessError()
3351 {
3352  if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3353  mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3354  mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3355  mPasswordHelperErrorCode == QKeychain::NotImplemented )
3356  {
3357  // If the error is permanent or the user denied access to the wallet
3358  // we also want to disable the wallet system to prevent annoying
3359  // notification on each subsequent access.
3360  setPasswordHelperEnabled( false );
3361  mPasswordHelperErrorMessage = tr( "There was an error and integration with your %1 system has been disabled. "
3362  "You can re-enable it at any time through the \"Utilities\" menu "
3363  "in the Authentication pane of the options dialog. %2" )
3364  .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage );
3365  }
3366  if ( mPasswordHelperErrorCode != QKeychain::NoError )
3367  {
3368  // We've got an error from the wallet
3369  passwordHelperLog( tr( "Error in %1: %2" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage ) );
3370  emit passwordHelperMessageOut( mPasswordHelperErrorMessage, authManTag(), CRITICAL );
3371  }
3372  passwordHelperClearErrors();
3373 }
3374 
3375 
3376 bool QgsAuthManager::masterPasswordInput()
3377 {
3378  if ( isDisabled() )
3379  return false;
3380 
3381  QString pass;
3382  bool storedPasswordIsValid = false;
3383  bool ok = false;
3384 
3385  // Read the password from the wallet
3386  if ( passwordHelperEnabled() )
3387  {
3388  pass = passwordHelperRead();
3389  if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3390  {
3391  // Let's check the password!
3392  if ( verifyMasterPassword( pass ) )
3393  {
3394  ok = true;
3395  storedPasswordIsValid = true;
3396  emit passwordHelperMessageOut( tr( "Master password has been successfully read from your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
3397  }
3398  else
3399  {
3400  emit passwordHelperMessageOut( tr( "Master password stored in your %1 is not valid" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
3401  }
3402  }
3403  }
3404 
3405  if ( ! ok )
3406  {
3407  pass.clear();
3409  }
3410 
3411  if ( ok && !pass.isEmpty() && mMasterPass != pass )
3412  {
3413  mMasterPass = pass;
3414  if ( passwordHelperEnabled() && ! storedPasswordIsValid )
3415  {
3416  if ( passwordHelperWrite( pass ) )
3417  {
3418  emit passwordHelperMessageOut( tr( "Master password has been successfully written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
3419  }
3420  else
3421  {
3422  emit passwordHelperMessageOut( tr( "Master password could not be written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
3423  }
3424  }
3425  return true;
3426  }
3427  return false;
3428 }
3429 
3430 bool QgsAuthManager::masterPasswordRowsInDb( int *rows ) const
3431 {
3432  if ( isDisabled() )
3433  return false;
3434 
3435  QSqlQuery query( authDatabaseConnection() );
3436  query.prepare( QStringLiteral( "SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3437 
3438  bool ok = authDbQuery( &query );
3439  if ( query.first() )
3440  {
3441  *rows = query.value( 0 ).toInt();
3442  }
3443 
3444  return ok;
3445 }
3446 
3448 {
3449  if ( isDisabled() )
3450  return false;
3451 
3452  int rows = 0;
3453  if ( !masterPasswordRowsInDb( &rows ) )
3454  {
3455  const char *err = QT_TR_NOOP( "Master password: FAILED to access database" );
3456  QgsDebugMsg( err );
3457  emit messageOut( tr( err ), authManTag(), CRITICAL );
3458 
3459  return false;
3460  }
3461  return ( rows == 1 );
3462 }
3463 
3464 bool QgsAuthManager::masterPasswordCheckAgainstDb( const QString &compare ) const
3465 {
3466  if ( isDisabled() )
3467  return false;
3468 
3469  // first verify there is only one row in auth db (uses first found)
3470 
3471  QSqlQuery query( authDatabaseConnection() );
3472  query.prepare( QStringLiteral( "SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3473  if ( !authDbQuery( &query ) )
3474  return false;
3475 
3476  if ( !query.first() )
3477  return false;
3478 
3479  QString salt = query.value( 0 ).toString();
3480  QString hash = query.value( 1 ).toString();
3481 
3482  return QgsAuthCrypto::verifyPasswordKeyHash( compare.isNull() ? mMasterPass : compare, salt, hash );
3483 }
3484 
3485 bool QgsAuthManager::masterPasswordStoreInDb() const
3486 {
3487  if ( isDisabled() )
3488  return false;
3489 
3490  QString salt, hash, civ;
3491  QgsAuthCrypto::passwordKeyHash( mMasterPass, &salt, &hash, &civ );
3492 
3493  QSqlQuery query( authDatabaseConnection() );
3494  query.prepare( QStringLiteral( "INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3495 
3496  query.bindValue( QStringLiteral( ":salt" ), salt );
3497  query.bindValue( QStringLiteral( ":hash" ), hash );
3498  query.bindValue( QStringLiteral( ":civ" ), civ );
3499 
3500  if ( !authDbStartTransaction() )
3501  return false;
3502 
3503  if ( !authDbQuery( &query ) )
3504  return false;
3505 
3506  if ( !authDbCommit() )
3507  return false;
3508 
3509  return true;
3510 }
3511 
3512 bool QgsAuthManager::masterPasswordClearDb()
3513 {
3514  if ( isDisabled() )
3515  return false;
3516 
3517  QSqlQuery query( authDatabaseConnection() );
3518  query.prepare( QStringLiteral( "DELETE FROM %1" ).arg( authDbPassTable() ) );
3519  bool res = authDbTransactionQuery( &query );
3520  if ( res )
3522  return res;
3523 }
3524 
3525 const QString QgsAuthManager::masterPasswordCiv() const
3526 {
3527  if ( isDisabled() )
3528  return QString();
3529 
3530  QSqlQuery query( authDatabaseConnection() );
3531  query.prepare( QStringLiteral( "SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3532  if ( !authDbQuery( &query ) )
3533  return QString();
3534 
3535  if ( !query.first() )
3536  return QString();
3537 
3538  return query.value( 0 ).toString();
3539 }
3540 
3541 QStringList QgsAuthManager::configIds() const
3542 {
3543  QStringList configids = QStringList();
3544 
3545  if ( isDisabled() )
3546  return configids;
3547 
3548  QSqlQuery query( authDatabaseConnection() );
3549  query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( authDatabaseConfigTable() ) );
3550 
3551  if ( !authDbQuery( &query ) )
3552  {
3553  return configids;
3554  }
3555 
3556  if ( query.isActive() )
3557  {
3558  while ( query.next() )
3559  {
3560  configids << query.value( 0 ).toString();
3561  }
3562  }
3563  return configids;
3564 }
3565 
3566 bool QgsAuthManager::verifyPasswordCanDecryptConfigs() const
3567 {
3568  if ( isDisabled() )
3569  return false;
3570 
3571  // no need to check for setMasterPassword, since this is private and it will be set
3572 
3573  QSqlQuery query( authDatabaseConnection() );
3574 
3575  query.prepare( QStringLiteral( "SELECT id, config FROM %1" ).arg( authDatabaseConfigTable() ) );
3576 
3577  if ( !authDbQuery( &query ) )
3578  return false;
3579 
3580  if ( !query.isActive() || !query.isSelect() )
3581  {
3582  QgsDebugMsg( QStringLiteral( "Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3583  return false;
3584  }
3585 
3586  int checked = 0;
3587  while ( query.next() )
3588  {
3589  ++checked;
3590  QString configstring( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3591  if ( configstring.isEmpty() )
3592  {
3593  QgsDebugMsg( QStringLiteral( "Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3594  .arg( query.value( 0 ).toString() ) );
3595  return false;
3596  }
3597  }
3598 
3599  QgsDebugMsgLevel( QStringLiteral( "Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3600  return true;
3601 }
3602 
3603 bool QgsAuthManager::reencryptAllAuthenticationConfigs( const QString &prevpass, const QString &prevciv )
3604 {
3605  if ( isDisabled() )
3606  return false;
3607 
3608  bool res = true;
3609  const QStringList ids = configIds();
3610  for ( const auto &configid : ids )
3611  {
3612  res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3613  }
3614  return res;
3615 }
3616 
3617 bool QgsAuthManager::reencryptAuthenticationConfig( const QString &authcfg, const QString &prevpass, const QString &prevciv )
3618 {
3619  if ( isDisabled() )
3620  return false;
3621 
3622  // no need to check for setMasterPassword, since this is private and it will be set
3623 
3624  QSqlQuery query( authDatabaseConnection() );
3625 
3626  query.prepare( QStringLiteral( "SELECT config FROM %1 "
3627  "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
3628 
3629  query.bindValue( QStringLiteral( ":id" ), authcfg );
3630 
3631  if ( !authDbQuery( &query ) )
3632  return false;
3633 
3634  if ( !query.isActive() || !query.isSelect() )
3635  {
3636  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3637  return false;
3638  }
3639 
3640  if ( query.first() )
3641  {
3642  QString configstring( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3643 
3644  if ( query.next() )
3645  {
3646  QgsDebugMsg( QStringLiteral( "Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3647  emit messageOut( tr( "Authentication database contains duplicate configuration IDs" ), authManTag(), WARNING );
3648  return false;
3649  }
3650 
3651  query.clear();
3652 
3653  query.prepare( QStringLiteral( "UPDATE %1 "
3654  "SET config = :config "
3655  "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
3656 
3657  query.bindValue( QStringLiteral( ":id" ), authcfg );
3658  query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3659 
3660  if ( !authDbStartTransaction() )
3661  return false;
3662 
3663  if ( !authDbQuery( &query ) )
3664  return false;
3665 
3666  if ( !authDbCommit() )
3667  return false;
3668 
3669  QgsDebugMsgLevel( QStringLiteral( "Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3670  return true;
3671  }
3672  else
3673  {
3674  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3675  return false;
3676  }
3677 }
3678 
3679 bool QgsAuthManager::reencryptAllAuthenticationSettings( const QString &prevpass, const QString &prevciv )
3680 {
3681  // TODO: start remove (when function is actually used)
3682  Q_UNUSED( prevpass )
3683  Q_UNUSED( prevciv )
3684  return true;
3685  // end remove
3686 
3687 #if 0
3688  if ( isDisabled() )
3689  return false;
3690 
3692  // When adding settings that require encryption, add to list //
3694 
3695  QStringList encryptedsettings;
3696  encryptedsettings << "";
3697 
3698  for ( const auto & sett, std::as_const( encryptedsettings ) )
3699  {
3700  if ( sett.isEmpty() || !existsAuthSetting( sett ) )
3701  continue;
3702 
3703  // no need to check for setMasterPassword, since this is private and it will be set
3704 
3705  QSqlQuery query( authDbConnection() );
3706 
3707  query.prepare( QStringLiteral( "SELECT value FROM %1 "
3708  "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3709 
3710  query.bindValue( ":setting", sett );
3711 
3712  if ( !authDbQuery( &query ) )
3713  return false;
3714 
3715  if ( !query.isActive() || !query.isSelect() )
3716  {
3717  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3718  return false;
3719  }
3720 
3721  if ( query.first() )
3722  {
3723  QString settvalue( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3724 
3725  query.clear();
3726 
3727  query.prepare( QStringLiteral( "UPDATE %1 "
3728  "SET value = :value "
3729  "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3730 
3731  query.bindValue( ":setting", sett );
3732  query.bindValue( ":value", QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), settvalue ) );
3733 
3734  if ( !authDbStartTransaction() )
3735  return false;
3736 
3737  if ( !authDbQuery( &query ) )
3738  return false;
3739 
3740  if ( !authDbCommit() )
3741  return false;
3742 
3743  QgsDebugMsg( QStringLiteral( "Reencrypt SUCCESS for setting: %2" ).arg( sett ) );
3744  return true;
3745  }
3746  else
3747  {
3748  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3749  return false;
3750  }
3751 
3752  if ( query.next() )
3753  {
3754  QgsDebugMsg( QStringLiteral( "Select contains more than one for setting: %1" ).arg( sett ) );
3755  emit messageOut( tr( "Authentication database contains duplicate setting keys" ), authManTag(), WARNING );
3756  }
3757 
3758  return false;
3759  }
3760 
3761  return true;
3762 #endif
3763 }
3764 
3765 bool QgsAuthManager::reencryptAllAuthenticationIdentities( const QString &prevpass, const QString &prevciv )
3766 {
3767  if ( isDisabled() )
3768  return false;
3769 
3770  bool res = true;
3771  const QStringList ids = certIdentityIds();
3772  for ( const auto &identid : ids )
3773  {
3774  res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3775  }
3776  return res;
3777 }
3778 
3779 bool QgsAuthManager::reencryptAuthenticationIdentity(
3780  const QString &identid,
3781  const QString &prevpass,
3782  const QString &prevciv )
3783 {
3784  if ( isDisabled() )
3785  return false;
3786 
3787  // no need to check for setMasterPassword, since this is private and it will be set
3788 
3789  QSqlQuery query( authDatabaseConnection() );
3790 
3791  query.prepare( QStringLiteral( "SELECT key FROM %1 "
3792  "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3793 
3794  query.bindValue( QStringLiteral( ":id" ), identid );
3795 
3796  if ( !authDbQuery( &query ) )
3797  return false;
3798 
3799  if ( !query.isActive() || !query.isSelect() )
3800  {
3801  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3802  return false;
3803  }
3804 
3805  if ( query.first() )
3806  {
3807  QString keystring( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3808 
3809  if ( query.next() )
3810  {
3811  QgsDebugMsg( QStringLiteral( "Select contains more than one for identity id: %1" ).arg( identid ) );
3812  emit messageOut( tr( "Authentication database contains duplicate identity IDs" ), authManTag(), WARNING );
3813  return false;
3814  }
3815 
3816  query.clear();
3817 
3818  query.prepare( QStringLiteral( "UPDATE %1 "
3819  "SET key = :key "
3820  "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3821 
3822  query.bindValue( QStringLiteral( ":id" ), identid );
3823  query.bindValue( QStringLiteral( ":key" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
3824 
3825  if ( !authDbStartTransaction() )
3826  return false;
3827 
3828  if ( !authDbQuery( &query ) )
3829  return false;
3830 
3831  if ( !authDbCommit() )
3832  return false;
3833 
3834  QgsDebugMsgLevel( QStringLiteral( "Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
3835  return true;
3836  }
3837  else
3838  {
3839  QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3840  return false;
3841  }
3842 }
3843 
3844 bool QgsAuthManager::authDbOpen() const
3845 {
3846  if ( isDisabled() )
3847  return false;
3848 
3849  QSqlDatabase authdb = authDatabaseConnection();
3850  if ( !authdb.isOpen() )
3851  {
3852  if ( !authdb.open() )
3853  {
3854  QgsDebugMsg( QStringLiteral( "Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3856  authdb.lastError().driverText(),
3857  authdb.lastError().databaseText() ) );
3858  emit messageOut( tr( "Unable to establish authentication database connection" ), authManTag(), CRITICAL );
3859  return false;
3860  }
3861  }
3862  return true;
3863 }
3864 
3865 bool QgsAuthManager::authDbQuery( QSqlQuery *query ) const
3866 {
3867  if ( isDisabled() )
3868  return false;
3869 
3870  query->setForwardOnly( true );
3871  if ( !query->exec() )
3872  {
3873  const char *err = QT_TR_NOOP( "Auth db query exec() FAILED" );
3874  QgsDebugMsg( err );
3875  emit messageOut( tr( err ), authManTag(), WARNING );
3876  return false;
3877  }
3878 
3879  if ( query->lastError().isValid() )
3880  {
3881  QgsDebugMsg( QStringLiteral( "Auth db query FAILED: %1\nError: %2" )
3882  .arg( query->executedQuery(),
3883  query->lastError().text() ) );
3884  emit messageOut( tr( "Auth db query FAILED" ), authManTag(), WARNING );
3885  return false;
3886  }
3887 
3888  return true;
3889 }
3890 
3891 bool QgsAuthManager::authDbStartTransaction() const
3892 {
3893  if ( isDisabled() )
3894  return false;
3895 
3896  if ( !authDatabaseConnection().transaction() )
3897  {
3898  const char *err = QT_TR_NOOP( "Auth db FAILED to start transaction" );
3899  QgsDebugMsg( err );
3900  emit messageOut( tr( err ), authManTag(), WARNING );
3901  return false;
3902  }
3903 
3904  return true;
3905 }
3906 
3907 bool QgsAuthManager::authDbCommit() const
3908 {
3909  if ( isDisabled() )
3910  return false;
3911 
3912  if ( !authDatabaseConnection().commit() )
3913  {
3914  const char *err = QT_TR_NOOP( "Auth db FAILED to rollback changes" );
3915  QgsDebugMsg( err );
3916  emit messageOut( tr( err ), authManTag(), WARNING );
3917  ( void )authDatabaseConnection().rollback();
3918  return false;
3919  }
3920 
3921  return true;
3922 }
3923 
3924 bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query ) const
3925 {
3926  if ( isDisabled() )
3927  return false;
3928 
3929  if ( !authDatabaseConnection().transaction() )
3930  {
3931  const char *err = QT_TR_NOOP( "Auth db FAILED to start transaction" );
3932  QgsDebugMsg( err );
3933  emit messageOut( tr( err ), authManTag(), WARNING );
3934  return false;
3935  }
3936 
3937  bool ok = authDbQuery( query );
3938 
3939  if ( ok && !authDatabaseConnection().commit() )
3940  {
3941  const char *err = QT_TR_NOOP( "Auth db FAILED to rollback changes" );
3942  QgsDebugMsg( err );
3943  emit messageOut( tr( err ), authManTag(), WARNING );
3944  ( void )authDatabaseConnection().rollback();
3945  return false;
3946  }
3947 
3948  return ok;
3949 }
3950 
3951 void QgsAuthManager::insertCaCertInCache( QgsAuthCertUtils::CaCertSource source, const QList<QSslCertificate> &certs )
3952 {
3953  for ( const auto &cert : certs )
3954  {
3955  mCaCertsCache.insert( QgsAuthCertUtils::shaHexForCert( cert ),
3956  QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate>( source, cert ) );
3957  }
3958 }
3959 
QgsAuthMethod::NetworkReply
@ NetworkReply
Definition: qgsauthmethod.h:60
QgsCredentials::instance
static QgsCredentials * instance()
retrieves instance
Definition: qgscredentials.cpp:34
QgsAuthManager::verifyMasterPassword
bool verifyMasterPassword(const QString &compare=QString())
Verify the supplied master password against any existing hash in authentication database.
Definition: qgsauthmanager.cpp:558
QgsAuthManager::clearAllCachedConfigs
void clearAllCachedConfigs()
Clear all authentication configs from authentication method caches.
Definition: qgsauthmanager.cpp:3079
QgsAuthMethodMetadata
Holds data auth method key, description, and associated shared library file information.
Definition: qgsauthmethodmetadata.h:43
QgsAuthConfigSslServer::setSslHostPort
void setSslHostPort(const QString &hostport)
Sets server host:port string.
Definition: qgsauthconfig.h:406
QgsAuthManager::storeAuthenticationConfig
bool storeAuthenticationConfig(QgsAuthMethodConfig &mconfig, bool overwrite=false)
Store an authentication config in the database.
Definition: qgsauthmanager.cpp:1098
QgsAuthManager::authMethodsKeys
QStringList authMethodsKeys(const QString &dataprovider=QString())
Gets keys of supported authentication methods.
Definition: qgsauthmanager.cpp:1024
QgsAuthMethodConfig::name
const QString name() const
Gets name of configuration.
Definition: qgsauthconfig.h:83
QgsAuthCertUtils::CaCertSource
CaCertSource
Type of CA certificate source.
Definition: qgsauthcertutils.h:44
qgsauthcertutils.h
qgsruntimeprofiler.h
QgsAuthMethod
Abstract base class for authentication method plugins.
Definition: qgsauthmethod.h:42
QgsAuthManager::removeAuthSetting
bool removeAuthSetting(const QString &key)
Remove an authentication setting.
Definition: qgsauthmanager.cpp:1819
QgsAuthManager::storeCertAuthority
bool storeCertAuthority(const QSslCertificate &cert)
Store a certificate authority.
Definition: qgsauthmanager.cpp:2561
QgsAuthConfigSslServer::sslHostPort
const QString sslHostPort() const
Server host:port string.
Definition: qgsauthconfig.h:404
QgsAuthManager::messageOut
void messageOut(const QString &message, const QString &tag=QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level=QgsAuthManager::INFO) const
Custom logging signal to relay to console output and QgsMessageLog.
QgsAuthManager::sslCertCustomConfigs
const QList< QgsAuthConfigSslServer > sslCertCustomConfigs()
sslCertCustomConfigs get SSL certificate custom configs
Definition: qgsauthmanager.cpp:2281
QgsAuthCrypto::decrypt
static const QString decrypt(const QString &pass, const QString &cipheriv, const QString &text)
Decrypt data using master password.
Definition: qgsauthcrypto.cpp:53
QgsAuthManager::rebuildCaCertsCache
bool rebuildCaCertsCache()
Rebuild certificate authority cache.
Definition: qgsauthmanager.cpp:2769
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsAuthManager::authMethod
QgsAuthMethod * authMethod(const QString &authMethodKey)
Gets authentication method from the config/provider cache via its key.
Definition: qgsauthmanager.cpp:1029
QgsAuthManager::storeCertAuthorities
bool storeCertAuthorities(const QList< QSslCertificate > &certs)
Store multiple certificate authorities.
Definition: qgsauthmanager.cpp:2544
QgsAuthMethod::updateNetworkRequest
virtual bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Update a network request with authentication components.
Definition: qgsauthmethod.h:113
QgsAuthManager::passwordHelperSuccess
void passwordHelperSuccess()
Signals emitted on password helper success, mainly used in the tests to exit main application loop.
QgsAuthManager::loadAuthenticationConfig
bool loadAuthenticationConfig(const QString &authcfg, QgsAuthMethodConfig &mconfig, bool full=false)
Load an authentication config from the database into subclass.
Definition: qgsauthmanager.cpp:1251
QgsAuthMethodConfig::setUri
void setUri(const QString &uri)
Definition: qgsauthconfig.h:89
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsAuthMethod::updateMethodConfig
virtual void updateMethodConfig(QgsAuthMethodConfig &mconfig)=0
Update an authentication configuration in place.
qgsauthmanager.h
QgsAuthManager::instance
static QgsAuthManager * instance()
Enforce singleton pattern.
Definition: qgsauthmanager.cpp:92
QgsAuthManager::rebuildIgnoredSslErrorCache
bool rebuildIgnoredSslErrorCache()
Rebuild ignoredSSL error cache.
Definition: qgsauthmanager.cpp:2482
QgsAuthManager::configAuthMethodKey
QString configAuthMethodKey(const QString &authcfg) const
Gets key of authentication method associated with config ID.
Definition: qgsauthmanager.cpp:1015
QgsAuthManager::certIdentities
const QList< QSslCertificate > certIdentities()
certIdentities get certificate identities
Definition: qgsauthmanager.cpp:2012
QgsAuthManager::setPasswordHelperLoggingEnabled
void setPasswordHelperLoggingEnabled(bool enabled)
Password helper logging enabled setter.
Definition: qgsauthmanager.cpp:3338
QgsAuthManager::trustedCaCerts
const QList< QSslCertificate > trustedCaCerts(bool includeinvalid=false)
trustedCaCerts get list of all trusted CA certificates
Definition: qgsauthmanager.cpp:2989
QgsAuthManager::storeSslCertCustomConfig
bool storeSslCertCustomConfig(const QgsAuthConfigSslServer &config)
Store an SSL certificate custom config.
Definition: qgsauthmanager.cpp:2121
QgsAuthMethodRegistry
A registry / canonical manager of authentication methods.
Definition: qgsauthmethodregistry.h:47
QgsAuthMethodConfig::setMethod
void setMethod(const QString &method)
Definition: qgsauthconfig.h:93
QgsAuthManager::passwordHelperSync
bool passwordHelperSync()
Store the password manager into the wallet.
Definition: qgsauthmanager.cpp:3064
QgsAuthManager::supportedAuthMethodExpansions
QgsAuthMethod::Expansions supportedAuthMethodExpansions(const QString &authcfg)
Gets supported authentication method expansion(s), e.g.
Definition: qgsauthmanager.cpp:1085
QgsAuthMethodsMap
QHash< QString, QgsAuthMethod * > QgsAuthMethodsMap
Definition: qgsauthmethod.h:217
QgsAuthManager::configAuthMethod
QgsAuthMethod * configAuthMethod(const QString &authcfg)
Gets authentication method from the config/provider cache.
Definition: qgsauthmanager.cpp:999
QgsAuthManager::importAuthenticationConfigsFromXml
bool importAuthenticationConfigsFromXml(const QString &filename, const QString &password=QString(), bool overwrite=false)
Import authentication configurations from an XML file.
Definition: qgsauthmanager.cpp:1411
QgsAuthManager::authenticationDatabasePath
const QString authenticationDatabasePath() const
The standard authentication database file in ~/.qgis3/ or defined location.
Definition: qgsauthmanager.h:116
QgsAuthManager::removeSslCertCustomConfig
bool removeSslCertCustomConfig(const QString &id, const QString &hostport)
Remove an SSL certificate custom config.
Definition: qgsauthmanager.cpp:2346
QgsAuthManager::AUTH_MAN_TAG
static const QString AUTH_MAN_TAG
The display name of the Authentication Manager.
Definition: qgsauthmanager.h:715
QgsAuthManager::setDefaultCertTrustPolicy
bool setDefaultCertTrustPolicy(QgsAuthCertUtils::CertTrustPolicy policy)
Sets the default certificate trust policy preferred by user.
Definition: qgsauthmanager.cpp:2933
QgsAuthManager::exportAuthenticationConfigsToXml
bool exportAuthenticationConfigsToXml(const QString &filename, const QStringList &authcfgs, const QString &password=QString())
Export authentication configurations to an XML file.
Definition: qgsauthmanager.cpp:1352
QgsAuthManager::systemRootCAs
const QList< QSslCertificate > systemRootCAs()
systemRootCAs get root system certificate authorities
Definition: qgsauthmanager.cpp:2699
QgsAuthManager::defaultCertTrustPolicy
QgsAuthCertUtils::CertTrustPolicy defaultCertTrustPolicy()
Gets the default certificate trust policy preferred by user.
Definition: qgsauthmanager.cpp:2944
QgsAuthManager::databaseCAs
const QList< QSslCertificate > databaseCAs()
databaseCAs get database-stored certificate authorities
Definition: qgsauthmanager.cpp:2741
QgsAuthMethod::updateNetworkProxy
virtual bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Update proxy settings with authentication components.
Definition: qgsauthmethod.h:164
QgsAuthManager::hasConfigId
bool hasConfigId(const QString &txt) const
Returns whether a string includes an authcfg ID token.
Definition: qgsauthmanager.cpp:919
QgsAuthCertUtils::CertTrustPolicy
CertTrustPolicy
Type of certificate trust policy.
Definition: qgsauthcertutils.h:53
QgsAuthMethodConfig::readXml
bool readXml(const QDomElement &element)
from a DOM element.
Definition: qgsauthconfig.cpp:182
QgsAuthManager::updateNetworkReply
bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkReply with an authentication config (used to skip known SSL errors,...
Definition: qgsauthmanager.cpp:1624
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsAuthManager::authDatabaseConfigTable
const QString authDatabaseConfigTable() const
Name of the authentication database table that stores configs.
Definition: qgsauthmanager.h:100
QgsAuthManager::uniqueConfigId
const QString uniqueConfigId() const
Gets a unique generated 7-character string to assign to as config id.
Definition: qgsauthmanager.cpp:852
qgsauthmethod.h
QgsAuthManager::extraFileCAs
const QList< QSslCertificate > extraFileCAs()
extraFileCAs extra file-based certificate authorities
Definition: qgsauthmanager.cpp:2704
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsAuthConfigSslServer::setSslCertificate
void setSslCertificate(const QSslCertificate &cert)
Sets server certificate object.
Definition: qgsauthconfig.h:401
QgsAuthMethodConfig::loadConfigString
void loadConfigString(const QString &configstr)
Load existing extended configuration.
Definition: qgsauthconfig.cpp:88
QgsAuthManager::passwordHelperEnabled
bool passwordHelperEnabled() const
Password helper enabled getter.
Definition: qgsauthmanager.cpp:3314
QgsAuthManager::backupAuthenticationDatabase
bool backupAuthenticationDatabase(QString *backuppath=nullptr)
Close connection to current authentication database and back it up.
Definition: qgsauthmanager.cpp:1483
QgsAuthManager::trustedCaCertsPemText
const QByteArray trustedCaCertsPemText()
trustedCaCertsPemText get concatenated string of all trusted CA certificates
Definition: qgsauthmanager.cpp:3058
QgsAuthManager::setScheduledAuthDatabaseErase
void setScheduledAuthDatabaseErase(bool scheduleErase)
Schedule an optional erase of authentication database, starting when mutex is lockable.
Definition: qgsauthmanager.cpp:809
QgsAuthManager::existsCertIdentity
bool existsCertIdentity(const QString &id)
Check if a certificate identity exists.
Definition: qgsauthmanager.cpp:2060
QgsAuthManager::existsSslCertCustomConfig
bool existsSslCertCustomConfig(const QString &id, const QString &hostport)
Check if SSL certificate custom config exists.
Definition: qgsauthmanager.cpp:2308
QgsAuthManager::certIdentityBundleToPem
const QStringList certIdentityBundleToPem(const QString &id)
certIdentityBundleToPem get a certificate identity bundle by id (sha hash) returned as PEM text
Definition: qgsauthmanager.cpp:2001
QgsCredentials::getMasterPassword
bool getMasterPassword(QString &password, bool stored=false)
Definition: qgscredentials.cpp:77
QgsAuthCertUtils::shaHexForCert
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
Definition: qgsauthcertutils.cpp:748
QgsAuthManager::disabledMessage
const QString disabledMessage() const
Standard message for when QCA's qca-ossl plugin is missing and system is disabled.
Definition: qgsauthmanager.cpp:496
QgsAuthManager::removeCertTrustPolicy
bool removeCertTrustPolicy(const QSslCertificate &cert)
Remove a certificate authority.
Definition: qgsauthmanager.cpp:2878
QgsAuthManager::CRITICAL
@ CRITICAL
Definition: qgsauthmanager.h:80
qgsauthmethodmetadata.h
QgsAuthManager::updateIgnoredSslErrorsCacheFromConfig
bool updateIgnoredSslErrorsCacheFromConfig(const QgsAuthConfigSslServer &config)
Update ignored SSL error cache with possible ignored SSL errors, using server config.
Definition: qgsauthmanager.cpp:2409
QgsAuthMethod::updateDataSourceUriItems
virtual bool updateDataSourceUriItems(QStringList &connectionItems, const QString &authcfg, const QString &dataprovider=QString())
Update data source connection items with authentication components.
Definition: qgsauthmethod.h:147
qgsapplication.h
QgsAuthManager::passwordHelperLoggingEnabled
bool passwordHelperLoggingEnabled() const
Password helper logging enabled getter.
Definition: qgsauthmanager.cpp:3331
QgsAuthManager::rebuildTrustedCaCertsCache
bool rebuildTrustedCaCertsCache()
Rebuild trusted certificate authorities cache.
Definition: qgsauthmanager.cpp:3049
QgsAuthMethod::updateNetworkReply
virtual bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Update a network reply with authentication components.
Definition: qgsauthmethod.h:130
QgsAuthMethodConfigsMap
QHash< QString, QgsAuthMethodConfig > QgsAuthMethodConfigsMap
Definition: qgsauthconfig.h:200
QgsAuthManager::masterPasswordVerified
void masterPasswordVerified(bool verified)
Emitted when a password has been verify (or not)
QgsAuthMethod::DataSourceUri
@ DataSourceUri
Definition: qgsauthmethod.h:61
QgsAuthManager::certificateTrustPolicy
QgsAuthCertUtils::CertTrustPolicy certificateTrustPolicy(const QSslCertificate &cert)
certificateTrustPolicy get trust policy for a particular certificate cert
Definition: qgsauthmanager.cpp:2909
QgsAuthManager::resetMasterPassword
bool resetMasterPassword(const QString &newpass, const QString &oldpass, bool keepbackup, QString *backuppath=nullptr)
Reset the master password to a new one, then re-encrypt all previous configs in a new database file,...
Definition: qgsauthmanager.cpp:662
QgsAuthManager::updateAuthenticationConfig
bool updateAuthenticationConfig(const QgsAuthMethodConfig &config)
Update an authentication config in the database.
Definition: qgsauthmanager.cpp:1180
QgsAuthMethod::NetworkRequest
@ NetworkRequest
Definition: qgsauthmethod.h:59
QgsAuthManager::eraseAuthenticationDatabase
bool eraseAuthenticationDatabase(bool backup, QString *backuppath=nullptr)
Erase all rows from all tables in authentication database.
Definition: qgsauthmanager.cpp:1519
QgsAuthMethodConfig::setVersion
void setVersion(int version)
Sets version of the configuration.
Definition: qgsauthconfig.h:98
QgsAuthManager::authMethodsMap
QgsAuthMethodsMap authMethodsMap(const QString &dataprovider=QString())
Gets available authentication methods mapped to their key.
Definition: qgsauthmanager.cpp:1052
QgsAuthConfigSslServer::sslIgnoredErrorEnums
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
Definition: qgsauthconfig.h:416
QgsAuthManager::untrustedCaCerts
const QList< QSslCertificate > untrustedCaCerts(QList< QSslCertificate > trustedCAs=QList< QSslCertificate >())
untrustedCaCerts get list of untrusted certificate authorities
Definition: qgsauthmanager.cpp:3023
QgsAuthMethodConfig::writeXml
bool writeXml(QDomElement &parentElement, QDomDocument &document)
Stores the configuration in a DOM.
Definition: qgsauthconfig.cpp:160
qgscredentials.h
QgsAuthManager
Singleton offering an interface to manage the authentication configuration database and to utilize co...
Definition: qgsauthmanager.h:69
QgsAuthManager::MessageLevel
MessageLevel
Message log level (mirrors that of QgsMessageLog, so it can also output there)
Definition: qgsauthmanager.h:76
QgsAuthMethodConfig::version
int version() const
Gets version of the configuration.
Definition: qgsauthconfig.h:96
QgsAuthCertUtils::NoPolicy
@ NoPolicy
Definition: qgsauthcertutils.h:58
QgsAuthCertUtils::FromFile
@ FromFile
Definition: qgsauthcertutils.h:47
QgsAuthMethod::NetworkProxy
@ NetworkProxy
Definition: qgsauthmethod.h:63
qgsauthmethodregistry.h
QgsAuthManager::authManTag
QString authManTag() const
Simple text tag describing authentication system for message logs.
Definition: qgsauthmanager.h:199
QgsAuthManager::WARNING
@ WARNING
Definition: qgsauthmanager.h:79
QgsAuthManager::authDatabaseEraseRequested
void authDatabaseEraseRequested()
Emitted when a user has indicated they may want to erase the authentication db.
QgsAuthMethodConfig::method
QString method() const
Textual key of the associated authentication method.
Definition: qgsauthconfig.h:92
QgsAuthManager::sslCertCustomConfigByHost
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port)
Definition: qgsauthmanager.cpp:2207
QgsAuthManager::authSetting
QVariant authSetting(const QString &key, const QVariant &defaultValue=QVariant(), bool decrypt=false)
authSetting get an authentication setting (retrieved as string and returned as QVariant( QString ))
Definition: qgsauthmanager.cpp:1743
QgsMessageLog::logMessage
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Definition: qgsmessagelog.cpp:27
QgsAuthManager::passwordHelperMessageOut
void passwordHelperMessageOut(const QString &message, const QString &tag=QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level=QgsAuthManager::INFO)
Custom logging signal to inform the user about master password <-> password manager interactions.
QgsAuthMethodRegistry::authMethodList
QStringList authMethodList() const
Returns list of available auth methods by their keys.
Definition: qgsauthmethodregistry.cpp:313
QgsAuthManager::initSslCaches
bool initSslCaches()
Initialize various SSL authentication caches.
Definition: qgsauthmanager.cpp:1850
QgsAuthManager::clearCachedConfig
void clearCachedConfig(const QString &authcfg)
Clear an authentication config from its associated authentication method cache.
Definition: qgsauthmanager.cpp:3091
QgsAuthManager::passwordHelperFailure
void passwordHelperFailure()
Signals emitted on password helper failure, mainly used in the tests to exit main application loop.
QgsAuthManager::authDatabaseConnection
QSqlDatabase authDatabaseConnection() const
Sets up the application instance of the authentication database connection.
Definition: qgsauthmanager.cpp:117
QgsAuthManager::updateDataSourceUriItems
bool updateDataSourceUriItems(QStringList &connectionItems, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QgsDataSourceUri with an authentication config.
Definition: qgsauthmanager.cpp:1650
QgsAuthManager::existsAuthSetting
bool existsAuthSetting(const QString &key)
Check if an authentication setting exists.
Definition: qgsauthmanager.cpp:1786
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsAuthManager::passwordHelperDelete
bool passwordHelperDelete()
Delete master password from wallet.
Definition: qgsauthmanager.cpp:3207
QgsAuthCertUtils::SystemRoot
@ SystemRoot
Definition: qgsauthcertutils.h:46
QgsAuthManager::trustedCaCertsCache
const QList< QSslCertificate > trustedCaCertsCache()
trustedCaCertsCache cache of trusted certificate authorities, ready for network connections
Definition: qgsauthmanager.h:658
QgsAuthCertUtils::DefaultTrust
@ DefaultTrust
Definition: qgsauthcertutils.h:55
QgsAuthManager::QgsAuthManager
QgsAuthManager()
Definition: qgsauthmanager.cpp:104
QgsAuthCertUtils::certsToPemText
static QByteArray certsToPemText(const QList< QSslCertificate > &certs)
certsToPemText dump a list of QSslCertificates to PEM text
Definition: qgsauthcertutils.cpp:564
QgsAuthManager::existsCertAuthority
bool existsCertAuthority(const QSslCertificate &cert)
Check if a certificate authority exists.
Definition: qgsauthmanager.cpp:2631
QgsAuthManager::certIdentityBundle
const QPair< QSslCertificate, QSslKey > certIdentityBundle(const QString &id)
Gets a certificate identity bundle by id (sha hash).
Definition: qgsauthmanager.cpp:1946
QgsAuthCrypto::passwordKeyHash
static void passwordKeyHash(const QString &pass, QString *salt, QString *hash, QString *cipheriv=nullptr)
Generate SHA256 hash for master password, with iterations and salt.
Definition: qgsauthcrypto.cpp:68
QgsAuthMethod::clearCachedConfig
virtual void clearCachedConfig(const QString &authcfg)=0
Clear any cached configuration.
QgsAuthManager::scheduledAuthDatabaseErase
bool scheduledAuthDatabaseErase()
Whether there is a scheduled opitonal erase of authentication database.
Definition: qgsauthmanager.h:172
QgsAuthManager::storeCertTrustPolicy
bool storeCertTrustPolicy(const QSslCertificate &cert, QgsAuthCertUtils::CertTrustPolicy policy)
Store user trust value for a certificate.
Definition: qgsauthmanager.cpp:2784
QgsAuthCertUtils::Trusted
@ Trusted
Definition: qgsauthcertutils.h:56
QgsAuthManager::certIdentity
const QSslCertificate certIdentity(const QString &id)
certIdentity get a certificate identity by id (sha hash)
Definition: qgsauthmanager.cpp:1912
QgsAuthCertUtils::certificateIsAuthorityOrIssuer
static bool certificateIsAuthorityOrIssuer(const QSslCertificate &cert)
Gets whether a certificate is an Authority or can at least sign other certificates.
Definition: qgsauthcertutils.cpp:1026
QgsAuthManager::setMasterPassword
bool setMasterPassword(bool verify=false)
Main call to initially set or continually check master password is set.
Definition: qgsauthmanager.cpp:501
QgsAuthMethod::supportedExpansions
QgsAuthMethod::Expansions supportedExpansions() const
Flags that represent the update points (where authentication configurations are expanded) supported b...
Definition: qgsauthmethod.h:97
QgsAuthManager::removeAuthenticationConfig
bool removeAuthenticationConfig(const QString &authcfg)
Remove an authentication config in the database.
Definition: qgsauthmanager.cpp:1319
QgsAuthManager::authMethodMetadata
const QgsAuthMethodMetadata * authMethodMetadata(const QString &authMethodKey)
Gets authentication method metadata via its key.
Definition: qgsauthmanager.cpp:1040
QgsAuthManager::updateIgnoredSslErrorsCache
bool updateIgnoredSslErrorsCache(const QString &shahostport, const QList< QSslError > &errors)
Update ignored SSL error cache with possible ignored SSL errors, using sha:host:port key.
Definition: qgsauthmanager.cpp:2438
QgsAuthManager::removeCertAuthority
bool removeCertAuthority(const QSslCertificate &cert)
Remove a certificate authority.
Definition: qgsauthmanager.cpp:2669
QgsAuthManager::storeCertIdentity
bool storeCertIdentity(const QSslCertificate &cert, const QSslKey &key)
Store a certificate identity.
Definition: qgsauthmanager.cpp:1868
QgsAuthManager::authDatabaseChanged
void authDatabaseChanged()
Emitted when the authentication db is significantly changed, e.g. large record removal,...
QgsAuthMethodRegistry::instance
static QgsAuthMethodRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
Definition: qgsauthmethodregistry.cpp:47
QgsAuthManager::isDisabled
bool isDisabled() const
Whether QCA has the qca-ossl plugin, which a base run-time requirement.
Definition: qgsauthmanager.cpp:487
QgsAuthCertUtils::certsFromFile
static QList< QSslCertificate > certsFromFile(const QString &certspath)
Returns a list of concatenated certs from a PEM or DER formatted file.
Definition: qgsauthcertutils.cpp:130
QgsAuthConfigSslServer::configString
const QString configString() const
Configuration as a concatenated string.
Definition: qgsauthconfig.cpp:384
QgsAuthMethodConfig::id
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:78
QgsAuthManager::updateNetworkRequest
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
Definition: qgsauthmanager.cpp:1599
QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME
static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME
The display name of the password helper (platform dependent)
Definition: qgsauthmanager.h:712
QgsScopedRuntimeProfile
Scoped object for logging of the runtime for a single operation or group of operations.
Definition: qgsruntimeprofiler.h:327
QgsAuthManager::~QgsAuthManager
~QgsAuthManager() override
Definition: qgsauthmanager.cpp:3167
qgssettings.h
QgsAuthCertUtils::mapDigestToCerts
static QMap< QString, QSslCertificate > mapDigestToCerts(const QList< QSslCertificate > &certs)
Map certificate sha1 to certificate as simple cache.
Definition: qgsauthcertutils.cpp:60
QgsAuthConfigSslServer::isNull
bool isNull() const
Whether configuration is null (missing components)
Definition: qgsauthconfig.cpp:430
QgsAuthManager::mappedDatabaseCAs
const QMap< QString, QSslCertificate > mappedDatabaseCAs()
mappedDatabaseCAs get sha1-mapped database-stored certificate authorities
Definition: qgsauthmanager.cpp:2763
QgsAuthManager::rebuildCertTrustCache
bool rebuildCertTrustCache()
Rebuild certificate authority cache.
Definition: qgsauthmanager.cpp:2955
QgsAuthMethodConfig::configString
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
Definition: qgsauthconfig.cpp:76
QgsAuthConfigSslServer
Configuration container for SSL server connection exceptions or overrides.
Definition: qgsauthconfig.h:392
QgsAuthMethodRegistry::authMethodMetadata
const QgsAuthMethodMetadata * authMethodMetadata(const QString &authMethodKey) const
Returns metadata of the auth method or nullptr if not found.
Definition: qgsauthmethodregistry.cpp:296
QgsAuthMethodConfig::isValid
bool isValid(bool validateid=false) const
Whether the configuration is valid.
Definition: qgsauthconfig.cpp:65
QgsAuthManager::certAuthority
const QSslCertificate certAuthority(const QString &id)
Gets a certificate authority by id (sha hash)
Definition: qgsauthmanager.cpp:2597
QgsAuthCertUtils::sslErrorEnumString
static QString sslErrorEnumString(QSslError::SslError errenum)
Gets short strings describing an SSL error.
Definition: qgsauthcertutils.cpp:1158
QgsAuthManager::masterPasswordHashInDatabase
bool masterPasswordHashInDatabase() const
Verify a password hash existing in authentication database.
Definition: qgsauthmanager.cpp:3447
QgsAuthManager::removeAllAuthenticationConfigs
bool removeAllAuthenticationConfigs()
Clear all authentication configs from table in database and from provider caches.
Definition: qgsauthmanager.cpp:1462
QgsAuthMethodConfig::setId
void setId(const QString &id)
Sets auth config ID.
Definition: qgsauthconfig.h:80
QgsAuthCrypto::encrypt
static const QString encrypt(const QString &pass, const QString &cipheriv, const QString &text)
Encrypt data using master password.
Definition: qgsauthcrypto.cpp:45
QgsAuthManager::configIdUnique
bool configIdUnique(const QString &id) const
Verify if provided authentication id is unique.
Definition: qgsauthmanager.cpp:903
QgsAuthManager::setPasswordHelperEnabled
void setPasswordHelperEnabled(bool enabled)
Password helper enabled setter.
Definition: qgsauthmanager.cpp:3321
QgsAuthManager::configIds
QStringList configIds() const
Gets list of authentication ids from database.
Definition: qgsauthmanager.cpp:3541
qgslogger.h
QgsAuthManager::certIdentityIds
QStringList certIdentityIds() const
certIdentityIds get list of certificate identity ids from database
Definition: qgsauthmanager.cpp:2034
QgsAuthManager::init
bool init(const QString &pluginPath=QString(), const QString &authDatabasePath=QString())
init initialize QCA, prioritize qca-ossl plugin and optionally set up the authentication database
Definition: qgsauthmanager.cpp:180
QgsAuthManager::masterPasswordIsSet
bool masterPasswordIsSet() const
Whether master password has be input and verified, i.e. authentication database is accessible.
Definition: qgsauthmanager.cpp:652
QgsAuthManager::INFO
@ INFO
Definition: qgsauthmanager.h:78
qgsauthcrypto.h
QgsAuthMethodConfig::setName
void setName(const QString &name)
Sets name of configuration.
Definition: qgsauthconfig.h:85
QgsAuthCertUtils::certIsViable
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
Definition: qgsauthcertutils.cpp:1305
QgsAuthManager::authDatabaseServersTable
const QString authDatabaseServersTable() const
Name of the authentication database table that stores server exceptions/configs.
Definition: qgsauthmanager.h:103
QgsAuthManager::storeAuthSetting
bool storeAuthSetting(const QString &key, const QVariant &value, bool encrypt=false)
Store an authentication setting (stored as string via QVariant( value ).toString() )
Definition: qgsauthmanager.cpp:1702
QgsAuthManager::certTrustPolicy
QgsAuthCertUtils::CertTrustPolicy certTrustPolicy(const QSslCertificate &cert)
certTrustPolicy get whether certificate cert is trusted by user
Definition: qgsauthmanager.cpp:2823
QgsAuthCertUtils::InDatabase
@ InDatabase
Definition: qgsauthcertutils.h:48
QgsAuthManager::removeCertIdentity
bool removeCertIdentity(const QString &id)
Remove a certificate identity.
Definition: qgsauthmanager.cpp:2093
QgsAuthManager::clearMasterPassword
void clearMasterPassword()
Clear supplied master password.
Definition: qgsauthmanager.h:150
QgsAuthManager::sslCertCustomConfig
const QgsAuthConfigSslServer sslCertCustomConfig(const QString &id, const QString &hostport)
sslCertCustomConfig get an SSL certificate custom config by id (sha hash) and hostport (host:port)
Definition: qgsauthmanager.cpp:2165
QgsAuthManager::removeCertTrustPolicies
bool removeCertTrustPolicies(const QList< QSslCertificate > &certs)
Remove a group certificate authorities.
Definition: qgsauthmanager.cpp:2861
QgsAuthManager::dumpIgnoredSslErrorsCache_
void dumpIgnoredSslErrorsCache_()
Utility function to dump the cache for debug purposes.
Definition: qgsauthmanager.cpp:2385
QgsAuthConfigSslServer::sslCertificate
const QSslCertificate sslCertificate() const
Server certificate object.
Definition: qgsauthconfig.h:399
QgsAuthManager::masterPasswordSame
bool masterPasswordSame(const QString &pass) const
Check whether supplied password is the same as the one already set.
Definition: qgsauthmanager.cpp:657
QgsAuthManager::updateConfigAuthMethods
void updateConfigAuthMethods()
Sync the confg/authentication method cache with what is in database.
Definition: qgsauthmanager.cpp:970
QgsAuthMethodConfig
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:41
QgsAuthConfigSslServer::loadConfigString
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
Definition: qgsauthconfig.cpp:403
QgsAuthManager::registerCoreAuthMethods
bool registerCoreAuthMethods()
Instantiate and register existing C++ core authentication methods from plugins.
Definition: qgsauthmanager.cpp:836
QgsAuthManager::availableAuthMethodConfigs
QgsAuthMethodConfigsMap availableAuthMethodConfigs(const QString &dataprovider=QString())
Gets mapping of authentication config ids and their base configs (not decrypted data)
Definition: qgsauthmanager.cpp:925
QgsAuthCrypto::verifyPasswordKeyHash
static bool verifyPasswordKeyHash(const QString &pass, const QString &salt, const QString &hash, QString *hashderived=nullptr)
Verify existing master password hash to a re-generated one.
Definition: qgsauthcrypto.cpp:92
qgsmessagelog.h
QgsAuthCertUtils::Untrusted
@ Untrusted
Definition: qgsauthcertutils.h:57
QgsAuthManager::updateNetworkProxy
bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkProxy with an authentication config.
Definition: qgsauthmanager.cpp:1676
QgsAuthMethodConfig::uri
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:88