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