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