23 #include <QMutexLocker>
26 #include <QSqlDatabase>
29 #include <QTextStream>
34 #include <QDomElement>
35 #include <QDomDocument>
37 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
38 #include <QRandomGenerator>
44 #include <QSslConfiguration>
60 #include "qgssettings.h"
65 const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral(
"auth_configs" );
66 const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral(
"auth_pass" );
67 const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral(
"auth_settings" );
68 const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral(
"auth_identities" );
69 const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral(
"auth_servers" );
70 const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral(
"auth_authorities" );
71 const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral(
"auth_trust" );
73 const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral(
"authcfg=([a-z]|[A-Z]|[0-9]){7}" );
76 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME(
"QGIS-Master-Password" );
77 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME(
"QGIS" );
83 #elif defined(Q_OS_WIN)
85 #elif defined(Q_OS_LINUX)
94 QMutexLocker locker( &sMutex );
105 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
106 mMutex.reset(
new QMutex( QMutex::Recursive ) );
107 mMasterPasswordMutex.reset(
new QMutex( QMutex::Recursive ) );
109 mMutex = std::make_unique<QRecursiveMutex>();
110 mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
113 this, &QgsAuthManager::writeToConsole );
124 QMutexLocker locker( mMutex.get() );
129 const QString connectionName = QStringLiteral(
"authentication.configs:0x%1" ).arg(
reinterpret_cast<quintptr
>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) );
130 QgsDebugMsgLevel( QStringLiteral(
"Using auth db connection name: %1 " ).arg( connectionName ), 2 );
131 if ( !QSqlDatabase::contains( connectionName ) )
133 QgsDebugMsgLevel( QStringLiteral(
"No existing connection, creating a new one" ), 2 );
134 authdb = QSqlDatabase::addDatabase( QStringLiteral(
"QSQLITE" ), connectionName );
137 if ( QThread::currentThread() != qApp->thread() )
139 QgsDebugMsgLevel( QStringLiteral(
"Scheduled auth db remove on thread close" ), 2 );
149 QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName,
this ]
151 QMutexLocker locker( mMutex.get() );
152 QSqlDatabase::removeDatabase( connectionName );
153 mConnectedThreads.remove( QThread::currentThread() );
154 }, Qt::DirectConnection );
156 mConnectedThreads.insert( QThread::currentThread(), connection );
162 authdb = QSqlDatabase::database( connectionName );
166 if ( !authdb.isOpen() )
168 if ( !authdb.open() )
170 const char *err = QT_TR_NOOP(
"Opening of authentication db FAILED" );
188 mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
191 QCA::scanForPlugins();
193 QgsDebugMsgLevel( QStringLiteral(
"QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
194 QStringList capabilities;
196 capabilities = QCA::supportedFeatures();
197 QgsDebugMsgLevel( QStringLiteral(
"QCA supports: %1" ).arg( capabilities.join(
"," ) ), 2 );
200 if ( !QCA::isSupported(
"cert", QStringLiteral(
"qca-ossl" ) ) )
202 mAuthDisabled =
true;
203 mAuthDisabledMessage = tr(
"QCA's OpenSSL plugin (qca-ossl) is missing" );
207 QgsDebugMsgLevel( QStringLiteral(
"Prioritizing qca-ossl over all other QCA providers..." ), 2 );
208 const QCA::ProviderList provds = QCA::providers();
210 for ( QCA::Provider *p : provds )
212 QString pn = p->name();
214 if ( pn != QLatin1String(
"qca-ossl" ) )
216 pr = QCA::providerPriority( pn ) + 1;
218 QCA::setProviderPriority( pn, pr );
219 prlist << QStringLiteral(
"%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
221 QgsDebugMsgLevel( QStringLiteral(
"QCA provider priorities: %1" ).arg( prlist.join(
", " ) ), 2 );
228 QgsDebugMsgLevel( QStringLiteral(
"Authentication methods found: %1" ).arg( methods.join(
", " ) ), 2 );
230 if ( methods.isEmpty() )
232 mAuthDisabled =
true;
233 mAuthDisabledMessage = tr(
"No authentication method plugins found" );
239 mAuthDisabled =
true;
240 mAuthDisabledMessage = tr(
"No authentication method plugins could be loaded" );
244 mAuthDbPath = QDir::cleanPath( authDatabasePath );
248 QFileInfo dbdirinfo( dbinfo.path() );
249 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
251 if ( !dbdirinfo.exists() )
253 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
254 if ( !QDir().mkpath( dbdirinfo.filePath() ) )
256 const char *err = QT_TR_NOOP(
"Auth db directory path could not be created" );
263 if ( dbinfo.exists() )
265 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
267 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
272 if ( dbinfo.size() > 0 )
276 if ( !createCertTables() )
286 const char *passenv =
"QGIS_AUTH_PASSWORD_FILE";
289 QString passpath( getenv( passenv ) );
298 QFile passfile( passpath );
299 if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
301 QTextStream passin( &passfile );
302 while ( !passin.atEnd() )
304 masterpass = passin.readLine();
309 if ( !masterpass.isEmpty() )
313 QgsDebugMsgLevel( QStringLiteral(
"Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
317 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
323 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
333 QgsDebugMsgLevel( QStringLiteral(
"Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
335 if ( !createConfigTables() )
338 if ( !createCertTables() )
349 bool QgsAuthManager::createConfigTables()
351 QMutexLocker locker( mMutex.get() );
355 const char *err = QT_TR_NOOP(
"Auth db could not be created and opened" );
366 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
367 " 'salt' TEXT NOT NULL,\n"
368 " 'civ' TEXT NOT NULL\n"
369 ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
370 query.prepare( qstr );
371 if ( !authDbQuery( &query ) )
375 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
376 " 'id' TEXT NOT NULL,\n"
377 " 'name' TEXT NOT NULL,\n"
379 " 'type' TEXT NOT NULL,\n"
380 " 'version' INTEGER NOT NULL\n"
382 query.prepare( qstr );
383 if ( !authDbQuery( &query ) )
388 query.prepare( qstr );
389 if ( !authDbQuery( &query ) )
394 query.prepare( qstr );
395 if ( !authDbQuery( &query ) )
402 bool QgsAuthManager::createCertTables()
404 QMutexLocker locker( mMutex.get() );
413 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
414 " 'setting' TEXT NOT NULL\n"
415 ", 'value' TEXT);" ).arg( authDbSettingsTable() );
416 query.prepare( qstr );
417 if ( !authDbQuery( &query ) )
422 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
423 " 'id' TEXT NOT NULL,\n"
424 " 'key' TEXT NOT NULL\n"
425 ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
426 query.prepare( qstr );
427 if ( !authDbQuery( &query ) )
431 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
432 query.prepare( qstr );
433 if ( !authDbQuery( &query ) )
438 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
439 " 'id' TEXT NOT NULL,\n"
440 " 'host' TEXT NOT NULL,\n"
443 query.prepare( qstr );
444 if ( !authDbQuery( &query ) )
448 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg(
authDatabaseServersTable() );
449 query.prepare( qstr );
450 if ( !authDbQuery( &query ) )
455 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
456 " 'id' TEXT NOT NULL\n"
457 ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
458 query.prepare( qstr );
459 if ( !authDbQuery( &query ) )
463 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
464 query.prepare( qstr );
465 if ( !authDbQuery( &query ) )
469 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
470 " 'id' TEXT NOT NULL\n"
471 ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
472 query.prepare( qstr );
473 if ( !authDbQuery( &query ) )
477 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
478 query.prepare( qstr );
479 if ( !authDbQuery( &query ) )
490 QgsDebugMsg( QStringLiteral(
"Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
492 return mAuthDisabled;
497 return tr(
"Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
502 QMutexLocker locker( mMasterPasswordMutex.get() );
506 if ( mScheduledDbErase )
509 if ( mMasterPass.isEmpty() )
511 QgsDebugMsgLevel( QStringLiteral(
"Master password is not yet set by user" ), 2 );
512 if ( !masterPasswordInput() )
514 QgsDebugMsgLevel( QStringLiteral(
"Master password input canceled by user" ), 2 );
528 QgsDebugMsgLevel( QStringLiteral(
"Master password is set and verified" ), 2 );
534 QMutexLocker locker( mMutex.get() );
538 if ( mScheduledDbErase )
542 QString prevpass = QString( mMasterPass );
546 mMasterPass = prevpass;
547 const char *err = QT_TR_NOOP(
"Master password set: FAILED to verify, reset to previous" );
553 QgsDebugMsgLevel( QStringLiteral(
"Master password set: SUCCESS%1" ).arg( verify ?
" and verified" :
"" ), 2 );
563 if ( !masterPasswordRowsInDb( &rows ) )
565 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
573 QgsDebugMsgLevel( QStringLiteral(
"Master password: %1 rows in database" ).arg( rows ), 2 );
577 const char *err = QT_TR_NOOP(
"Master password: FAILED to find just one master password record in database" );
584 else if ( rows == 1 )
586 if ( !masterPasswordCheckAgainstDb( compare ) )
588 if ( compare.isNull() )
590 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
599 if ( mPassTries >= 5 )
601 mAuthDisabled =
true;
602 const char *err = QT_TR_NOOP(
"Master password: failed 5 times authentication system DISABLED" );
610 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
611 if ( compare.isNull() )
615 else if ( compare.isNull() )
617 if ( !masterPasswordStoreInDb() )
619 const char *err = QT_TR_NOOP(
"Master password: hash FAILED to be stored in database" );
628 QgsDebugMsgLevel( QStringLiteral(
"Master password: hash stored in database" ), 2 );
631 if ( !masterPasswordCheckAgainstDb() )
633 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
643 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
653 return !mMasterPass.isEmpty();
658 return mMasterPass == pass;
662 bool keepbackup, QString *backuppath )
676 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up current database" ), 2 );
682 QString prevpass = QString( mMasterPass );
683 QString prevciv = QString( masterPasswordCiv() );
689 if ( ok && !masterPasswordClearDb() )
692 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not clear current password from database" );
698 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: cleared current password from database" ), 2 );
705 if ( ok && !masterPasswordStoreInDb() )
708 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not store new password in database" );
714 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: stored new password in database" ), 2 );
721 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify new password in database" );
727 if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
730 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt configs in database" );
736 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: re-encrypted configs in database" ), 2 );
740 if ( ok && !verifyPasswordCanDecryptConfigs() )
743 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
748 if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
751 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt settings in database" );
756 if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
759 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt identities in database" );
769 QString errdbbackup( dbbackup );
770 errdbbackup.replace( QLatin1String(
".db" ), QLatin1String(
"_ERROR.db" ) );
772 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
776 mMasterPass = prevpass;
778 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: reinstated previous password and database" ) );
782 *backuppath = errdbbackup;
788 if ( !keepbackup && !QFile::remove( dbbackup ) )
790 const char *err = QT_TR_NOOP(
"Master password reset: could not remove old database backup" );
798 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
800 *backuppath = dbbackup;
810 mScheduledDbErase = scheduleErase;
812 mScheduledDbEraseRequestEmitted =
false;
813 mScheduledDbEraseRequestCount = 0;
817 if ( !mScheduledDbEraseTimer )
819 mScheduledDbEraseTimer =
new QTimer(
this );
820 connect( mScheduledDbEraseTimer, &QTimer::timeout,
this, &QgsAuthManager::tryToStartDbErase );
821 mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
823 else if ( !mScheduledDbEraseTimer->isActive() )
825 mScheduledDbEraseTimer->start();
830 if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
831 mScheduledDbEraseTimer->stop();
840 qDeleteAll( mAuthMethods );
841 mAuthMethods.clear();
843 for (
const auto &authMethodKey : methods )
848 return !mAuthMethods.isEmpty();
858 QTimer::singleShot( 3, &loop, &QEventLoop::quit );
861 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
862 uint seed =
static_cast< uint
>( QTime::currentTime().msec() );
869 for (
int i = 0; i < len; i++ )
871 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
872 switch ( qrand() % 2 )
874 switch ( QRandomGenerator::system()->generate() % 2 )
878 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
879 id += (
'0' + qrand() % 10 );
881 id +=
static_cast<char>(
'0' + QRandomGenerator::system()->generate() % 10 );
885 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
886 id += (
'a' + qrand() % 26 );
888 id +=
static_cast<char>(
'a' + QRandomGenerator::system()->generate() % 26 );
893 if ( !configids.contains(
id ) )
898 QgsDebugMsgLevel( QStringLiteral(
"Generated unique ID: %1" ).arg(
id ), 2 );
909 const char *err = QT_TR_NOOP(
"Config ID is empty" );
915 return !configids.contains(
id );
920 QRegExp rx( AUTH_CFG_REGEX );
921 return rx.indexIn( txt ) != -1;
926 QMutexLocker locker( mMutex.get() );
927 QStringList providerAuthMethodsKeys;
928 if ( !dataprovider.isEmpty() )
939 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1" ).arg(
authDatabaseConfigTable() ) );
941 if ( !authDbQuery( &query ) )
946 if ( query.isActive() && query.isSelect() )
948 while ( query.next() )
950 QString authcfg = query.value( 0 ).toString();
952 config.
setId( authcfg );
953 config.
setName( query.value( 1 ).toString() );
954 config.
setUri( query.value( 2 ).toString() );
955 config.
setMethod( query.value( 3 ).toString() );
956 config.
setVersion( query.value( 4 ).toInt() );
958 if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.
method() ) )
963 baseConfigs.insert( authcfg, config );
971 QMutexLocker locker( mMutex.get() );
978 if ( !authDbQuery( &query ) )
983 if ( query.isActive() )
985 QgsDebugMsgLevel( QStringLiteral(
"Syncing existing auth config and their auth methods" ), 2 );
986 mConfigAuthMethods.clear();
987 QStringList cfgmethods;
988 while ( query.next() )
990 mConfigAuthMethods.insert( query.value( 0 ).toString(),
991 query.value( 1 ).toString() );
992 cfgmethods << QStringLiteral(
"%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
994 QgsDebugMsgLevel( QStringLiteral(
"Stored auth config/methods:\n%1" ).arg( cfgmethods.join(
", " ) ), 2 );
1003 if ( !mConfigAuthMethods.contains( authcfg ) )
1005 QgsDebugMsg( QStringLiteral(
"No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1009 QString authMethodKey = mConfigAuthMethods.value( authcfg );
1019 return mConfigAuthMethods.value( authcfg, QString() );
1030 if ( !mAuthMethods.contains( authMethodKey ) )
1032 QgsDebugMsg( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1036 return mAuthMethods.value( authMethodKey );
1041 if ( dataprovider.isEmpty() )
1043 return mAuthMethods;
1047 QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1048 while ( i != mAuthMethods.constEnd() )
1051 && ( i.value()->supportedDataProviders().contains( QStringLiteral(
"all" ) )
1052 || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1054 filteredmap.insert( i.key(), i.value() );
1069 return QgsAuthMethod::Expansions();
1076 return QgsAuthMethod::Expansions();
1081 QMutexLocker locker( mMutex.get() );
1088 const char *err = QT_TR_NOOP(
"Store config: FAILED because config is invalid" );
1094 QString uid = mconfig.
id();
1095 bool passedinID = !uid.isEmpty();
1096 if ( uid.isEmpty() )
1104 const char *err = QT_TR_NOOP(
"Store config: FAILED because pre-defined config ID %1 is not unique" );
1115 if ( configstring.isEmpty() )
1117 const char *err = QT_TR_NOOP(
"Store config: FAILED because config string is empty" );
1123 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1124 QgsDebugMsg( QStringLiteral(
"name: %1" ).arg( config.name() ) );
1125 QgsDebugMsg( QStringLiteral(
"uri: %1" ).arg( config.uri() ) );
1126 QgsDebugMsg( QStringLiteral(
"type: %1" ).arg( config.method() ) );
1127 QgsDebugMsg( QStringLiteral(
"version: %1" ).arg( config.version() ) );
1128 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1132 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, name, uri, type, version, config) "
1135 query.bindValue( QStringLiteral(
":id" ), uid );
1136 query.bindValue( QStringLiteral(
":name" ), mconfig.
name() );
1137 query.bindValue( QStringLiteral(
":uri" ), mconfig.
uri() );
1138 query.bindValue( QStringLiteral(
":type" ), mconfig.
method() );
1139 query.bindValue( QStringLiteral(
":version" ), mconfig.
version() );
1140 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1142 if ( !authDbStartTransaction() )
1145 if ( !authDbQuery( &query ) )
1148 if ( !authDbCommit() )
1153 mconfig.
setId( uid );
1157 QgsDebugMsgLevel( QStringLiteral(
"Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1163 QMutexLocker locker( mMutex.get() );
1168 if ( !config.
isValid(
true ) )
1170 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is invalid" );
1177 if ( configstring.isEmpty() )
1179 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is empty" );
1186 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1187 QgsDebugMsg( QStringLiteral(
"id: %1" ).arg( config.
id() ) );
1192 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1196 if ( !query.prepare( QStringLiteral(
"UPDATE %1 "
1197 "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1200 const char *err = QT_TR_NOOP(
"Update config: FAILED to prepare query" );
1206 query.bindValue( QStringLiteral(
":id" ), config.
id() );
1207 query.bindValue( QStringLiteral(
":name" ), config.
name() );
1208 query.bindValue( QStringLiteral(
":uri" ), config.
uri() );
1209 query.bindValue( QStringLiteral(
":type" ), config.
method() );
1210 query.bindValue( QStringLiteral(
":version" ), config.
version() );
1211 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1213 if ( !authDbStartTransaction() )
1216 if ( !authDbQuery( &query ) )
1219 if ( !authDbCommit() )
1227 QgsDebugMsgLevel( QStringLiteral(
"Update config SUCCESS for authcfg: %1" ).arg( config.
id() ), 2 );
1240 QMutexLocker locker( mMutex.get() );
1245 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version, config FROM %1 "
1250 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1 "
1254 query.bindValue( QStringLiteral(
":id" ), authcfg );
1256 if ( !authDbQuery( &query ) )
1261 if ( query.isActive() && query.isSelect() )
1263 if ( query.first() )
1265 mconfig.
setId( query.value( 0 ).toString() );
1266 mconfig.
setName( query.value( 1 ).toString() );
1267 mconfig.
setUri( query.value( 2 ).toString() );
1268 mconfig.
setMethod( query.value( 3 ).toString() );
1269 mconfig.
setVersion( query.value( 4 ).toInt() );
1284 QgsDebugMsg( QStringLiteral(
"Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1287 QgsDebugMsgLevel( QStringLiteral(
"Load %1 config SUCCESS for authcfg: %2" ).arg( full ?
"full" :
"base", authcfg ), 2 );
1292 QgsDebugMsg( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1302 QMutexLocker locker( mMutex.get() );
1306 if ( authcfg.isEmpty() )
1313 query.bindValue( QStringLiteral(
":id" ), authcfg );
1315 if ( !authDbStartTransaction() )
1318 if ( !authDbQuery( &query ) )
1321 if ( !authDbCommit() )
1328 QgsDebugMsgLevel( QStringLiteral(
"REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1335 if ( filename.isEmpty() )
1338 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1339 QDomElement root = document.createElement( QStringLiteral(
"qgis_authentication" ) );
1340 document.appendChild( root );
1343 if ( !password.isEmpty() )
1348 root.setAttribute( QStringLiteral(
"salt" ), salt );
1349 root.setAttribute( QStringLiteral(
"hash" ), hash );
1350 root.setAttribute( QStringLiteral(
"civ" ), civ );
1353 QDomElement configurations = document.createElement( QStringLiteral(
"configurations" ) );
1354 for (
const QString &authcfg : authcfgs )
1361 authMethodConfig.
writeXml( configurations, document );
1364 if ( !password.isEmpty() )
1366 QString configurationsString;
1367 QTextStream ts( &configurationsString );
1368 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1369 ts.setCodec(
"UTF-8" );
1371 configurations.save( ts, 2 );
1372 root.appendChild( document.createTextNode(
QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
1376 root.appendChild( configurations );
1379 QFile file( filename );
1380 if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
1383 QTextStream ts( &file );
1384 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1385 ts.setCodec(
"UTF-8" );
1387 document.save( ts, 2 );
1394 QFile file( filename );
1395 if ( !file.open( QFile::ReadOnly ) )
1400 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1401 if ( !document.setContent( &file ) )
1408 QDomElement root = document.documentElement();
1409 if ( root.tagName() != QLatin1String(
"qgis_authentication" ) )
1414 QDomElement configurations;
1415 if ( root.hasAttribute( QStringLiteral(
"salt" ) ) )
1417 QString salt = root.attribute( QStringLiteral(
"salt" ) );
1418 QString hash = root.attribute( QStringLiteral(
"hash" ) );
1419 QString civ = root.attribute( QStringLiteral(
"civ" ) );
1424 configurations = document.firstChild().toElement();
1428 configurations = root.firstChildElement( QStringLiteral(
"configurations" ) );
1431 QDomElement configuration = configurations.firstChildElement();
1432 while ( !configuration.isNull() )
1435 authMethodConfig.
readXml( configuration );
1438 configuration = configuration.nextSiblingElement();
1445 QMutexLocker locker( mMutex.get() );
1451 bool res = authDbTransactionQuery( &query );
1459 QgsDebugMsgLevel( QStringLiteral(
"Remove configs from database: %1" ).arg( res ?
"SUCCEEDED" :
"FAILED" ), 2 );
1466 QMutexLocker locker( mMutex.get() );
1469 const char *err = QT_TR_NOOP(
"No authentication database found" );
1477 if ( authConn.isValid() && authConn.isOpen() )
1481 QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral(
"yyyy-MM-dd-hhmmss" ) ) );
1483 dbbackup.replace( QLatin1String(
".db" ), QStringLiteral(
"_%1.db" ).arg( datestamp ) );
1487 const char *err = QT_TR_NOOP(
"Could not back up authentication database" );
1494 *backuppath = dbbackup;
1496 QgsDebugMsgLevel( QStringLiteral(
"Backed up auth database at %1" ).arg( dbbackup ), 2 );
1502 QMutexLocker locker( mMutex.get() );
1512 if ( backuppath && !dbbackup.isEmpty() )
1513 *backuppath = dbbackup;
1516 if ( dbinfo.exists() )
1518 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1520 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
1528 const char *err = QT_TR_NOOP(
"No authentication database found" );
1536 const char *err = QT_TR_NOOP(
"Authentication database could not be deleted" );
1542 mMasterPass = QString();
1544 QgsDebugMsgLevel( QStringLiteral(
"Creating Auth db through QSqlDatabase initial connection" ), 2 );
1547 if ( !authConn.isValid() || !authConn.isOpen() )
1549 const char *err = QT_TR_NOOP(
"Authentication database could not be initialized" );
1555 if ( !createConfigTables() )
1557 const char *err = QT_TR_NOOP(
"FAILED to create auth database config tables" );
1563 if ( !createCertTables() )
1565 const char *err = QT_TR_NOOP(
"FAILED to create auth database cert tables" );
1581 const QString &dataprovider )
1591 QgsDebugMsg( QStringLiteral(
"Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1606 const QString &dataprovider )
1616 QgsDebugMsg( QStringLiteral(
"Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1632 const QString &dataprovider )
1642 QgsDebugMsg( QStringLiteral(
"Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1667 QgsDebugMsg( QStringLiteral(
"Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1676 QgsDebugMsgLevel( QStringLiteral(
"Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1685 QMutexLocker locker( mMutex.get() );
1686 if ( key.isEmpty() )
1689 QString storeval( value.toString() );
1705 query.prepare( QStringLiteral(
"INSERT INTO %1 (setting, value) "
1706 "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1708 query.bindValue( QStringLiteral(
":setting" ), key );
1709 query.bindValue( QStringLiteral(
":value" ), storeval );
1711 if ( !authDbStartTransaction() )
1714 if ( !authDbQuery( &query ) )
1717 if ( !authDbCommit() )
1720 QgsDebugMsgLevel( QStringLiteral(
"Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1726 QMutexLocker locker( mMutex.get() );
1727 if ( key.isEmpty() )
1733 QVariant value = defaultValue;
1735 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1736 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1738 query.bindValue( QStringLiteral(
":setting" ), key );
1740 if ( !authDbQuery( &query ) )
1743 if ( query.isActive() && query.isSelect() )
1745 if ( query.first() )
1749 value = QVariant(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1753 value = query.value( 0 );
1755 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1759 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1769 QMutexLocker locker( mMutex.get() );
1770 if ( key.isEmpty() )
1774 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1775 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1777 query.bindValue( QStringLiteral(
":setting" ), key );
1779 if ( !authDbQuery( &query ) )
1783 if ( query.isActive() && query.isSelect() )
1785 if ( query.first() )
1787 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting exists for key: %1" ).arg( key ), 2 );
1792 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1802 QMutexLocker locker( mMutex.get() );
1803 if ( key.isEmpty() )
1808 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1810 query.bindValue( QStringLiteral(
":setting" ), key );
1812 if ( !authDbStartTransaction() )
1815 if ( !authDbQuery( &query ) )
1818 if ( !authDbCommit() )
1821 QgsDebugMsgLevel( QStringLiteral(
"REMOVED setting for key: %1" ).arg( key ), 2 );
1835 QMutexLocker locker( mMutex.get() );
1841 mCustomConfigByHostCache.clear();
1842 mHasCheckedIfCustomConfigByHostExists =
false;
1845 QgsDebugMsg( QStringLiteral(
"Init of SSL caches FAILED" ) );
1851 QMutexLocker locker( mMutex.get() );
1852 if ( cert.isNull() )
1854 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
1859 QgsDebugMsg( QStringLiteral(
"Passed private key is null" ) );
1869 QString certpem( cert.toPem() );
1873 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, key, cert) "
1874 "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1876 query.bindValue( QStringLiteral(
":id" ),
id );
1877 query.bindValue( QStringLiteral(
":key" ), keypem );
1878 query.bindValue( QStringLiteral(
":cert" ), certpem );
1880 if ( !authDbStartTransaction() )
1883 if ( !authDbQuery( &query ) )
1886 if ( !authDbCommit() )
1889 QgsDebugMsgLevel( QStringLiteral(
"Store certificate identity SUCCESS for id: %1" ).arg(
id ), 2 );
1895 QMutexLocker locker( mMutex.get() );
1896 QSslCertificate emptycert;
1897 QSslCertificate cert;
1902 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
1903 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1905 query.bindValue( QStringLiteral(
":id" ),
id );
1907 if ( !authDbQuery( &query ) )
1910 if ( query.isActive() && query.isSelect() )
1912 if ( query.first() )
1914 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1915 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity retrieved for id: %1" ).arg(
id ), 2 );
1919 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1929 QMutexLocker locker( mMutex.get() );
1930 QPair<QSslCertificate, QSslKey> bundle;
1938 query.prepare( QStringLiteral(
"SELECT key, cert FROM %1 "
1939 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1941 query.bindValue( QStringLiteral(
":id" ),
id );
1943 if ( !authDbQuery( &query ) )
1946 if ( query.isActive() && query.isSelect() )
1948 QSslCertificate cert;
1950 if ( query.first() )
1952 key = QSslKey(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1953 QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1956 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create private key" );
1961 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1962 if ( cert.isNull() )
1964 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create certificate" );
1969 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity bundle retrieved for id: %1" ).arg(
id ), 2 );
1973 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1977 bundle = qMakePair( cert, key );
1984 QMutexLocker locker( mMutex.get() );
1988 return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
1990 return QStringList();
1995 QMutexLocker locker( mMutex.get() );
1996 QList<QSslCertificate> certs;
1999 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2001 if ( !authDbQuery( &query ) )
2004 if ( query.isActive() && query.isSelect() )
2006 while ( query.next() )
2008 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2017 QMutexLocker locker( mMutex.get() );
2018 QStringList identityids = QStringList();
2024 query.prepare( QStringLiteral(
"SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2026 if ( !authDbQuery( &query ) )
2031 if ( query.isActive() )
2033 while ( query.next() )
2035 identityids << query.value( 0 ).toString();
2043 QMutexLocker locker( mMutex.get() );
2048 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2049 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2051 query.bindValue( QStringLiteral(
":id" ),
id );
2053 if ( !authDbQuery( &query ) )
2057 if ( query.isActive() && query.isSelect() )
2059 if ( query.first() )
2061 QgsDebugMsgLevel( QStringLiteral(
"Certificate bundle exists for id: %1" ).arg(
id ), 2 );
2066 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate bundle for id: %1" ).arg(
id ) );
2076 QMutexLocker locker( mMutex.get() );
2079 QgsDebugMsg( QStringLiteral(
"Passed bundle ID is empty" ) );
2085 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2087 query.bindValue( QStringLiteral(
":id" ),
id );
2089 if ( !authDbStartTransaction() )
2092 if ( !authDbQuery( &query ) )
2095 if ( !authDbCommit() )
2098 QgsDebugMsgLevel( QStringLiteral(
"REMOVED certificate identity for id: %1" ).arg(
id ), 2 );
2104 QMutexLocker locker( mMutex.get() );
2107 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2116 QString certpem( cert.toPem() );
2119 query.prepare( QStringLiteral(
"INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2122 query.bindValue( QStringLiteral(
":id" ),
id );
2123 query.bindValue( QStringLiteral(
":host" ), config.
sslHostPort().trimmed() );
2124 query.bindValue( QStringLiteral(
":cert" ), certpem );
2125 query.bindValue( QStringLiteral(
":config" ), config.
configString() );
2127 if ( !authDbStartTransaction() )
2130 if ( !authDbQuery( &query ) )
2133 if ( !authDbCommit() )
2136 QgsDebugMsgLevel( QStringLiteral(
"Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2140 mHasCheckedIfCustomConfigByHostExists =
false;
2141 mCustomConfigByHostCache.clear();
2148 QMutexLocker locker( mMutex.get() );
2151 if (
id.isEmpty() || hostport.isEmpty() )
2153 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2158 query.prepare( QStringLiteral(
"SELECT id, host, cert, config FROM %1 "
2161 query.bindValue( QStringLiteral(
":id" ),
id );
2162 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2164 if ( !authDbQuery( &query ) )
2167 if ( query.isActive() && query.isSelect() )
2169 if ( query.first() )
2171 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2174 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2178 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2179 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2191 if ( hostport.isEmpty() )
2196 QMutexLocker locker( mMutex.get() );
2197 if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2199 if ( mCustomConfigByHostCache.contains( hostport ) )
2200 return mCustomConfigByHostCache.value( hostport );
2205 if ( !mHasCheckedIfCustomConfigByHostExists )
2207 mHasCheckedIfCustomConfigByHostExists =
true;
2209 if ( !authDbQuery( &query ) )
2211 mHasCustomConfigByHost =
false;
2214 if ( query.isActive() && query.isSelect() && query.first() )
2216 mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2217 if ( !mHasCustomConfigByHost )
2222 mHasCustomConfigByHost =
false;
2227 query.prepare( QString(
"SELECT id, host, cert, config FROM %1 "
2230 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2232 if ( !authDbQuery( &query ) )
2234 mCustomConfigByHostCache.insert( hostport, config );
2238 if ( query.isActive() && query.isSelect() )
2240 if ( query.first() )
2242 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2245 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2249 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2250 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2253 mCustomConfigByHostCache.insert( hostport, emptyconfig );
2258 mCustomConfigByHostCache.insert( hostport, config );
2264 QMutexLocker locker( mMutex.get() );
2265 QList<QgsAuthConfigSslServer> configs;
2270 if ( !authDbQuery( &query ) )
2273 if ( query.isActive() && query.isSelect() )
2275 while ( query.next() )
2278 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2282 configs.append( config );
2291 QMutexLocker locker( mMutex.get() );
2292 if (
id.isEmpty() || hostport.isEmpty() )
2294 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2299 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2302 query.bindValue( QStringLiteral(
":id" ),
id );
2303 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2305 if ( !authDbQuery( &query ) )
2309 if ( query.isActive() && query.isSelect() )
2311 if ( query.first() )
2313 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2318 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2319 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2329 QMutexLocker locker( mMutex.get() );
2330 if (
id.isEmpty() || hostport.isEmpty() )
2332 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2336 mHasCheckedIfCustomConfigByHostExists =
false;
2337 mCustomConfigByHostCache.clear();
2341 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id AND host = :host" ).arg(
authDatabaseServersTable() ) );
2343 query.bindValue( QStringLiteral(
":id" ),
id );
2344 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2346 if ( !authDbStartTransaction() )
2349 if ( !authDbQuery( &query ) )
2352 if ( !authDbCommit() )
2355 QString shahostport( QStringLiteral(
"%1:%2" ).arg(
id, hostport ) );
2356 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2358 mIgnoredSslErrorsCache.remove( shahostport );
2361 QgsDebugMsgLevel( QStringLiteral(
"REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2368 QMutexLocker locker( mMutex.get() );
2369 if ( !mIgnoredSslErrorsCache.isEmpty() )
2371 QgsDebugMsg( QStringLiteral(
"Ignored SSL errors cache items:" ) );
2372 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2373 while ( i != mIgnoredSslErrorsCache.constEnd() )
2376 for (
auto err : i.value() )
2380 QgsDebugMsg( QStringLiteral(
"%1 = %2" ).arg( i.key(), errs.join(
", " ) ) );
2392 QMutexLocker locker( mMutex.get() );
2395 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2399 QString shahostport( QStringLiteral(
"%1:%2" )
2402 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2404 mIgnoredSslErrorsCache.remove( shahostport );
2407 if ( !errenums.isEmpty() )
2409 mIgnoredSslErrorsCache.insert( shahostport, qgis::listToSet( errenums ) );
2410 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2415 QgsDebugMsgLevel( QStringLiteral(
"No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2421 QMutexLocker locker( mMutex.get() );
2422 QRegExp rx(
"\\S+:\\S+:\\d+" );
2423 if ( !rx.exactMatch( shahostport ) )
2425 QgsDebugMsg(
"Passed shahostport does not match \\S+:\\S+:\\d+, "
2426 "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2430 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2432 mIgnoredSslErrorsCache.remove( shahostport );
2435 if ( errors.isEmpty() )
2437 QgsDebugMsg( QStringLiteral(
"Passed errors list empty" ) );
2441 QSet<QSslError::SslError> errs;
2442 for (
const auto &error : errors )
2444 if ( error.error() == QSslError::NoError )
2447 errs.insert( error.error() );
2450 if ( errs.isEmpty() )
2452 QgsDebugMsg( QStringLiteral(
"Passed errors list does not contain errors" ) );
2456 mIgnoredSslErrorsCache.insert( shahostport, errs );
2458 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2465 QMutexLocker locker( mMutex.get() );
2466 QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2467 QHash<QString, QSet<QSslError::SslError> > nextcache;
2472 if ( !authDbQuery( &query ) )
2474 QgsDebugMsg( QStringLiteral(
"Rebuild of ignored SSL errors cache FAILED" ) );
2478 if ( query.isActive() && query.isSelect() )
2480 while ( query.next() )
2482 QString shahostport( QStringLiteral(
"%1:%2" )
2483 .arg( query.value( 0 ).toString().trimmed(),
2484 query.value( 1 ).toString().trimmed() ) );
2488 if ( !errenums.isEmpty() )
2490 nextcache.insert( shahostport, qgis::listToSet( errenums ) );
2492 if ( prevcache.contains( shahostport ) )
2494 prevcache.remove( shahostport );
2499 if ( !prevcache.isEmpty() )
2502 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2503 while ( i != prevcache.constEnd() )
2505 nextcache.insert( i.key(), i.value() );
2510 if ( nextcache != mIgnoredSslErrorsCache )
2512 mIgnoredSslErrorsCache.clear();
2513 mIgnoredSslErrorsCache = nextcache;
2514 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2519 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2527 QMutexLocker locker( mMutex.get() );
2528 if ( certs.isEmpty() )
2530 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2534 for (
const auto &cert : certs )
2544 QMutexLocker locker( mMutex.get() );
2547 if ( cert.isNull() )
2549 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2556 QString pem( cert.toPem() );
2559 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, cert) "
2560 "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2562 query.bindValue( QStringLiteral(
":id" ),
id );
2563 query.bindValue( QStringLiteral(
":cert" ), pem );
2565 if ( !authDbStartTransaction() )
2568 if ( !authDbQuery( &query ) )
2571 if ( !authDbCommit() )
2574 QgsDebugMsgLevel( QStringLiteral(
"Store certificate authority SUCCESS for id: %1" ).arg(
id ), 2 );
2580 QMutexLocker locker( mMutex.get() );
2581 QSslCertificate emptycert;
2582 QSslCertificate cert;
2587 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2588 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2590 query.bindValue( QStringLiteral(
":id" ),
id );
2592 if ( !authDbQuery( &query ) )
2595 if ( query.isActive() && query.isSelect() )
2597 if ( query.first() )
2599 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2600 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority retrieved for id: %1" ).arg(
id ), 2 );
2604 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2614 QMutexLocker locker( mMutex.get() );
2615 if ( cert.isNull() )
2617 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2624 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
2625 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2627 query.bindValue( QStringLiteral(
":id" ),
id );
2629 if ( !authDbQuery( &query ) )
2633 if ( query.isActive() && query.isSelect() )
2635 if ( query.first() )
2637 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority exists for id: %1" ).arg(
id ), 2 );
2642 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2652 QMutexLocker locker( mMutex.get() );
2653 if ( cert.isNull() )
2655 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2663 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2665 query.bindValue( QStringLiteral(
":id" ),
id );
2667 if ( !authDbStartTransaction() )
2670 if ( !authDbQuery( &query ) )
2673 if ( !authDbCommit() )
2676 QgsDebugMsgLevel( QStringLiteral(
"REMOVED authority for id: %1" ).arg(
id ), 2 );
2682 return QSslConfiguration::systemCaCertificates();
2687 QMutexLocker locker( mMutex.get() );
2688 QList<QSslCertificate> certs;
2689 QList<QSslCertificate> filecerts;
2691 if ( cafileval.isNull() )
2695 if ( allowinvalid.isNull() )
2698 QString cafile( cafileval.toString() );
2699 if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2704 for (
const auto &cert : std::as_const( filecerts ) )
2706 if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2708 || cert.expiryDate() <= QDateTime::currentDateTime()
2709 || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2724 QMutexLocker locker( mMutex.get() );
2725 QList<QSslCertificate> certs;
2728 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2730 if ( !authDbQuery( &query ) )
2733 if ( query.isActive() && query.isSelect() )
2735 while ( query.next() )
2737 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2746 QMutexLocker locker( mMutex.get() );
2752 QMutexLocker locker( mMutex.get() );
2753 mCaCertsCache.clear();
2759 bool res = !mCaCertsCache.isEmpty();
2761 QgsDebugMsg( QStringLiteral(
"Rebuild of CA certs cache FAILED" ) );
2767 QMutexLocker locker( mMutex.get() );
2768 if ( cert.isNull() )
2770 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2780 QgsDebugMsg( QStringLiteral(
"Passed policy was default, all cert records in database were removed for id: %1" ).arg(
id ) );
2785 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, policy) "
2786 "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2788 query.bindValue( QStringLiteral(
":id" ),
id );
2789 query.bindValue( QStringLiteral(
":policy" ),
static_cast< int >( policy ) );
2791 if ( !authDbStartTransaction() )
2794 if ( !authDbQuery( &query ) )
2797 if ( !authDbCommit() )
2800 QgsDebugMsgLevel( QStringLiteral(
"Store certificate trust policy SUCCESS for id: %1" ).arg(
id ), 2 );
2806 QMutexLocker locker( mMutex.get() );
2807 if ( cert.isNull() )
2809 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2816 query.prepare( QStringLiteral(
"SELECT policy FROM %1 "
2817 "WHERE id = :id" ).arg( authDbTrustTable() ) );
2819 query.bindValue( QStringLiteral(
":id" ),
id );
2821 if ( !authDbQuery( &query ) )
2825 if ( query.isActive() && query.isSelect() )
2827 if ( query.first() )
2830 QgsDebugMsgLevel( QStringLiteral(
"Authentication cert trust policy retrieved for id: %1" ).arg(
id ), 2 );
2834 QgsDebugMsg( QStringLiteral(
"Select contains more than one cert trust policy for id: %1" ).arg(
id ) );
2844 QMutexLocker locker( mMutex.get() );
2845 if ( certs.empty() )
2847 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2851 for (
const auto &cert : certs )
2861 QMutexLocker locker( mMutex.get() );
2862 if ( cert.isNull() )
2864 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2872 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2874 query.bindValue( QStringLiteral(
":id" ),
id );
2876 if ( !authDbStartTransaction() )
2879 if ( !authDbQuery( &query ) )
2882 if ( !authDbCommit() )
2885 QgsDebugMsgLevel( QStringLiteral(
"REMOVED cert trust policy for id: %1" ).arg(
id ), 2 );
2892 QMutexLocker locker( mMutex.get() );
2893 if ( cert.isNull() )
2903 if ( trustedids.contains(
id ) )
2907 else if ( untrustedids.contains(
id ) )
2922 return storeAuthSetting( QStringLiteral(
"certdefaulttrust" ),
static_cast< int >( policy ) );
2927 QMutexLocker locker( mMutex.get() );
2928 QVariant policy(
authSetting( QStringLiteral(
"certdefaulttrust" ) ) );
2929 if ( policy.isNull() )
2938 QMutexLocker locker( mMutex.get() );
2939 mCertTrustCache.clear();
2942 query.prepare( QStringLiteral(
"SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2944 if ( !authDbQuery( &query ) )
2946 QgsDebugMsg( QStringLiteral(
"Rebuild of cert trust policy cache FAILED" ) );
2950 if ( query.isActive() && query.isSelect() )
2952 while ( query.next() )
2954 QString
id = query.value( 0 ).toString();
2958 if ( mCertTrustCache.contains( policy ) )
2960 ids = mCertTrustCache.value( policy );
2962 mCertTrustCache.insert( policy, ids <<
id );
2966 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2972 QMutexLocker locker( mMutex.get() );
2976 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2978 QList<QSslCertificate> trustedcerts;
2979 for (
int i = 0; i < certpairs.size(); ++i )
2981 QSslCertificate cert( certpairs.at( i ).second );
2983 if ( trustedids.contains( certid ) )
2986 trustedcerts.append( cert );
2992 trustedcerts.append( cert );
2997 QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
2998 sslconfig.setCaCertificates( trustedcerts );
2999 QSslConfiguration::setDefaultConfiguration( sslconfig );
3001 return trustedcerts;
3006 QMutexLocker locker( mMutex.get() );
3007 if ( trustedCAs.isEmpty() )
3009 if ( mTrustedCaCertsCache.isEmpty() )
3016 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3018 QList<QSslCertificate> untrustedCAs;
3019 for (
int i = 0; i < certpairs.size(); ++i )
3021 QSslCertificate cert( certpairs.at( i ).second );
3022 if ( !trustedCAs.contains( cert ) )
3024 untrustedCAs.append( cert );
3027 return untrustedCAs;
3032 QMutexLocker locker( mMutex.get() );
3034 QgsDebugMsgLevel( QStringLiteral(
"Rebuilt trusted cert authorities cache" ), 2 );
3041 QMutexLocker locker( mMutex.get() );
3047 QMutexLocker locker( mMutex.get() );
3050 return passwordHelperWrite( mMasterPass );
3066 for (
const auto &authcfg : ids )
3084 void QgsAuthManager::writeToConsole(
const QString &message,
3098 msg += QLatin1String(
"WARNING: " );
3101 msg += QLatin1String(
"ERROR: " );
3108 QTextStream out( stdout, QIODevice::WriteOnly );
3109 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
3112 out << msg << Qt::endl;
3116 void QgsAuthManager::tryToStartDbErase()
3118 ++mScheduledDbEraseRequestCount;
3120 int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3121 if ( mScheduledDbEraseRequestCount >= trycutoff )
3124 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3129 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest attempt (%1 of %2)" )
3130 .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3136 mScheduledDbEraseRequestEmitted =
true;
3141 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitted" ), 2 );
3144 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emit skipped" ), 2 );
3150 QMutexLocker locker( mMutex.get() );
3151 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3152 while ( iterator.hasNext() )
3155 iterator.key()->disconnect( iterator.value() );
3162 qDeleteAll( mAuthMethods );
3165 if ( authConn.isValid() && authConn.isOpen() )
3168 delete mScheduledDbEraseTimer;
3169 mScheduledDbEraseTimer =
nullptr;
3170 QSqlDatabase::removeDatabase( QStringLiteral(
"authentication.configs" ) );
3174 QString QgsAuthManager::passwordHelperName()
const
3176 return tr(
"Password Helper" );
3180 void QgsAuthManager::passwordHelperLog(
const QString &msg )
const
3192 QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3193 QgsSettings settings;
3194 job.setInsecureFallback( settings.value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3195 job.setAutoDelete(
false );
3196 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3198 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3203 mPasswordHelperErrorCode = job.error();
3204 mPasswordHelperErrorMessage = tr(
"Delete password failed: %1." ).arg( job.errorString() );
3215 passwordHelperProcessError();
3219 QString QgsAuthManager::passwordHelperRead()
3224 QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3225 QgsSettings settings;
3226 job.setInsecureFallback( settings.value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3227 job.setAutoDelete(
false );
3228 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3230 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3235 mPasswordHelperErrorCode = job.error();
3242 password = job.textData();
3244 if ( password.isEmpty() )
3246 mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3257 passwordHelperProcessError();
3261 bool QgsAuthManager::passwordHelperWrite(
const QString &password )
3263 Q_ASSERT( !password.isEmpty() );
3266 QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3267 QgsSettings settings;
3268 job.setInsecureFallback( settings.value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3269 job.setAutoDelete(
false );
3270 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3271 job.setTextData( password );
3273 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3278 mPasswordHelperErrorCode = job.error();
3286 passwordHelperClearErrors();
3291 passwordHelperProcessError();
3298 QgsSettings settings;
3299 return settings.value( QStringLiteral(
"use_password_helper" ),
true, QgsSettings::Section::Auth ).toBool();
3304 QgsSettings settings;
3305 settings.setValue( QStringLiteral(
"use_password_helper" ), enabled, QgsSettings::Section::Auth );
3306 emit
messageOut( enabled ? tr(
"Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3308 tr(
"Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3315 QgsSettings settings;
3316 return settings.value( QStringLiteral(
"password_helper_logging" ),
false, QgsSettings::Section::Auth ).toBool();
3321 QgsSettings settings;
3322 settings.setValue( QStringLiteral(
"password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3325 void QgsAuthManager::passwordHelperClearErrors()
3327 mPasswordHelperErrorCode = QKeychain::NoError;
3328 mPasswordHelperErrorMessage.clear();
3331 void QgsAuthManager::passwordHelperProcessError()
3333 if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3334 mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3335 mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3336 mPasswordHelperErrorCode == QKeychain::NotImplemented )
3342 mPasswordHelperErrorMessage = tr(
"There was an error and integration with your %1 system has been disabled. "
3343 "You can re-enable it at any time through the \"Utilities\" menu "
3344 "in the Authentication pane of the options dialog. %2" )
3347 if ( mPasswordHelperErrorCode != QKeychain::NoError )
3353 passwordHelperClearErrors();
3357 bool QgsAuthManager::masterPasswordInput()
3363 bool storedPasswordIsValid =
false;
3369 pass = passwordHelperRead();
3370 if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3376 storedPasswordIsValid =
true;
3392 if ( ok && !pass.isEmpty() && mMasterPass != pass )
3397 if ( passwordHelperWrite( pass ) )
3411 bool QgsAuthManager::masterPasswordRowsInDb(
int *rows )
const
3417 query.prepare( QStringLiteral(
"SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3419 bool ok = authDbQuery( &query );
3420 if ( query.first() )
3422 *rows = query.value( 0 ).toInt();
3434 if ( !masterPasswordRowsInDb( &rows ) )
3436 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
3442 return ( rows == 1 );
3445 bool QgsAuthManager::masterPasswordCheckAgainstDb(
const QString &compare )
const
3453 query.prepare( QStringLiteral(
"SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3454 if ( !authDbQuery( &query ) )
3457 if ( !query.first() )
3460 QString salt = query.value( 0 ).toString();
3461 QString hash = query.value( 1 ).toString();
3466 bool QgsAuthManager::masterPasswordStoreInDb()
const
3471 QString salt, hash, civ;
3475 query.prepare( QStringLiteral(
"INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3477 query.bindValue( QStringLiteral(
":salt" ), salt );
3478 query.bindValue( QStringLiteral(
":hash" ), hash );
3479 query.bindValue( QStringLiteral(
":civ" ), civ );
3481 if ( !authDbStartTransaction() )
3484 if ( !authDbQuery( &query ) )
3487 if ( !authDbCommit() )
3493 bool QgsAuthManager::masterPasswordClearDb()
3499 query.prepare( QStringLiteral(
"DELETE FROM %1" ).arg( authDbPassTable() ) );
3500 bool res = authDbTransactionQuery( &query );
3506 const QString QgsAuthManager::masterPasswordCiv()
const
3512 query.prepare( QStringLiteral(
"SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3513 if ( !authDbQuery( &query ) )
3516 if ( !query.first() )
3519 return query.value( 0 ).toString();
3524 QStringList configids = QStringList();
3532 if ( !authDbQuery( &query ) )
3537 if ( query.isActive() )
3539 while ( query.next() )
3541 configids << query.value( 0 ).toString();
3547 bool QgsAuthManager::verifyPasswordCanDecryptConfigs()
const
3558 if ( !authDbQuery( &query ) )
3561 if ( !query.isActive() || !query.isSelect() )
3563 QgsDebugMsg( QStringLiteral(
"Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3568 while ( query.next() )
3571 QString configstring(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3572 if ( configstring.isEmpty() )
3574 QgsDebugMsg( QStringLiteral(
"Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3575 .arg( query.value( 0 ).toString() ) );
3580 QgsDebugMsgLevel( QStringLiteral(
"Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3584 bool QgsAuthManager::reencryptAllAuthenticationConfigs(
const QString &prevpass,
const QString &prevciv )
3591 for (
const auto &configid : ids )
3593 res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3598 bool QgsAuthManager::reencryptAuthenticationConfig(
const QString &authcfg,
const QString &prevpass,
const QString &prevciv )
3607 query.prepare( QStringLiteral(
"SELECT config FROM %1 "
3610 query.bindValue( QStringLiteral(
":id" ), authcfg );
3612 if ( !authDbQuery( &query ) )
3615 if ( !query.isActive() || !query.isSelect() )
3617 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3621 if ( query.first() )
3627 QgsDebugMsg( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3634 query.prepare( QStringLiteral(
"UPDATE %1 "
3635 "SET config = :config "
3638 query.bindValue( QStringLiteral(
":id" ), authcfg );
3639 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3641 if ( !authDbStartTransaction() )
3644 if ( !authDbQuery( &query ) )
3647 if ( !authDbCommit() )
3650 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3655 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3660 bool QgsAuthManager::reencryptAllAuthenticationSettings(
const QString &prevpass,
const QString &prevciv )
3663 Q_UNUSED( prevpass )
3676 QStringList encryptedsettings;
3677 encryptedsettings <<
"";
3679 for (
const auto & sett, std::as_const( encryptedsettings ) )
3686 QSqlQuery query( authDbConnection() );
3688 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
3689 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3691 query.bindValue(
":setting", sett );
3693 if ( !authDbQuery( &query ) )
3696 if ( !query.isActive() || !query.isSelect() )
3698 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3702 if ( query.first() )
3708 query.prepare( QStringLiteral(
"UPDATE %1 "
3709 "SET value = :value "
3710 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3712 query.bindValue(
":setting", sett );
3715 if ( !authDbStartTransaction() )
3718 if ( !authDbQuery( &query ) )
3721 if ( !authDbCommit() )
3724 QgsDebugMsg( QStringLiteral(
"Reencrypt SUCCESS for setting: %2" ).arg( sett ) );
3729 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3735 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting: %1" ).arg( sett ) );
3746 bool QgsAuthManager::reencryptAllAuthenticationIdentities(
const QString &prevpass,
const QString &prevciv )
3753 for (
const auto &identid : ids )
3755 res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3760 bool QgsAuthManager::reencryptAuthenticationIdentity(
3761 const QString &identid,
3762 const QString &prevpass,
3763 const QString &prevciv )
3772 query.prepare( QStringLiteral(
"SELECT key FROM %1 "
3773 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3775 query.bindValue( QStringLiteral(
":id" ), identid );
3777 if ( !authDbQuery( &query ) )
3780 if ( !query.isActive() || !query.isSelect() )
3782 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3786 if ( query.first() )
3792 QgsDebugMsg( QStringLiteral(
"Select contains more than one for identity id: %1" ).arg( identid ) );
3799 query.prepare( QStringLiteral(
"UPDATE %1 "
3801 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3803 query.bindValue( QStringLiteral(
":id" ), identid );
3804 query.bindValue( QStringLiteral(
":key" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
3806 if ( !authDbStartTransaction() )
3809 if ( !authDbQuery( &query ) )
3812 if ( !authDbCommit() )
3815 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
3820 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3825 bool QgsAuthManager::authDbOpen()
const
3831 if ( !authdb.isOpen() )
3833 if ( !authdb.open() )
3835 QgsDebugMsg( QStringLiteral(
"Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3837 authdb.lastError().driverText(),
3838 authdb.lastError().databaseText() ) );
3846 bool QgsAuthManager::authDbQuery( QSqlQuery *query )
const
3851 query->setForwardOnly(
true );
3852 if ( !query->exec() )
3854 const char *err = QT_TR_NOOP(
"Auth db query exec() FAILED" );
3860 if ( query->lastError().isValid() )
3862 QgsDebugMsg( QStringLiteral(
"Auth db query FAILED: %1\nError: %2" )
3863 .arg( query->executedQuery(),
3864 query->lastError().text() ) );
3872 bool QgsAuthManager::authDbStartTransaction()
const
3879 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3888 bool QgsAuthManager::authDbCommit()
const
3895 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3905 bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query )
const
3912 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3918 bool ok = authDbQuery( query );
3922 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3934 for (
const auto &cert : certs )
3937 QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate>( source, cert ) );
static QString sslErrorEnumString(QSslError::SslError errenum)
Gets short strings describing an SSL error.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
CertTrustPolicy
Type of certificate trust policy.
static QMap< QString, QSslCertificate > mapDigestToCerts(const QList< QSslCertificate > &certs)
Map certificate sha1 to certificate as simple cache.
static QByteArray certsToPemText(const QList< QSslCertificate > &certs)
certsToPemText dump a list of QSslCertificates to PEM text
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QList< QSslCertificate > certsFromFile(const QString &certspath)
Returns a list of concatenated certs from a PEM or DER formatted file.
static bool certificateIsAuthorityOrIssuer(const QSslCertificate &cert)
Gets whether a certificate is an Authority or can at least sign other certificates.
CaCertSource
Type of CA certificate source.
Configuration container for SSL server connection exceptions or overrides.
void setSslCertificate(const QSslCertificate &cert)
Sets server certificate object.
void setSslHostPort(const QString &hostport)
Sets server host:port string.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
bool isNull() const
Whether configuration is null (missing components)
const QSslCertificate sslCertificate() const
Server certificate object.
const QString sslHostPort() const
Server host:port string.
const QString configString() const
Configuration as a concatenated string.
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
static void passwordKeyHash(const QString &pass, QString *salt, QString *hash, QString *cipheriv=nullptr)
Generate SHA256 hash for master password, with iterations and salt.
static const QString encrypt(const QString &pass, const QString &cipheriv, const QString &text)
Encrypt data using master password.
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.
static const QString decrypt(const QString &pass, const QString &cipheriv, const QString &text)
Decrypt data using master password.
Singleton offering an interface to manage the authentication configuration database and to utilize co...
bool storeAuthSetting(const QString &key, const QVariant &value, bool encrypt=false)
Store an authentication setting (stored as string via QVariant( value ).toString() )
const QString authDatabaseServersTable() const
Name of the authentication database table that stores server exceptions/configs.
bool setDefaultCertTrustPolicy(QgsAuthCertUtils::CertTrustPolicy policy)
Sets the default certificate trust policy preferred by user.
void clearAllCachedConfigs()
Clear all authentication configs from authentication method caches.
const QSslCertificate certIdentity(const QString &id)
certIdentity get a certificate identity by id (sha hash)
const QStringList certIdentityBundleToPem(const QString &id)
certIdentityBundleToPem get a certificate identity bundle by id (sha hash) returned as PEM text
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 verifyMasterPassword(const QString &compare=QString())
Verify the supplied master password against any existing hash in authentication database.
bool updateIgnoredSslErrorsCacheFromConfig(const QgsAuthConfigSslServer &config)
Update ignored SSL error cache with possible ignored SSL errors, using server config.
MessageLevel
Message log level (mirrors that of QgsMessageLog, so it can also output there)
const QString disabledMessage() const
Standard message for when QCA's qca-ossl plugin is missing and system is disabled.
QgsAuthMethod * configAuthMethod(const QString &authcfg)
Gets authentication method from the config/provider cache.
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 storeCertIdentity(const QSslCertificate &cert, const QSslKey &key)
Store a certificate identity.
QgsAuthMethodsMap authMethodsMap(const QString &dataprovider=QString())
Gets available authentication methods mapped to their key.
QWidget * authMethodEditWidget(const QString &authMethodKey, QWidget *parent)
Gets authentication method edit widget via its key.
bool rebuildIgnoredSslErrorCache()
Rebuild ignoredSSL error cache.
bool initSslCaches()
Initialize various SSL authentication caches.
bool masterPasswordSame(const QString &pass) const
Check whether supplied password is the same as the one already set.
const QList< QSslCertificate > extraFileCAs()
extraFileCAs extra file-based certificate authorities
bool removeAuthSetting(const QString &key)
Remove an authentication setting.
bool storeCertTrustPolicy(const QSslCertificate &cert, QgsAuthCertUtils::CertTrustPolicy policy)
Store user trust value for a certificate.
bool rebuildCaCertsCache()
Rebuild certificate authority cache.
bool scheduledAuthDatabaseErase()
Whether there is a scheduled opitonal erase of authentication database.
bool eraseAuthenticationDatabase(bool backup, QString *backuppath=nullptr)
Erase all rows from all tables in authentication database.
bool exportAuthenticationConfigsToXml(const QString &filename, const QStringList &authcfgs, const QString &password=QString())
Export authentication configurations to an XML file.
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 authDatabaseChanged()
Emitted when the authentication db is significantly changed, e.g. large record removal,...
void setPasswordHelperEnabled(bool enabled)
Password helper enabled setter.
void setScheduledAuthDatabaseErase(bool scheduleErase)
Schedule an optional erase of authentication database, starting when mutex is lockable.
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.
const QList< QgsAuthConfigSslServer > sslCertCustomConfigs()
sslCertCustomConfigs get SSL certificate custom configs
const QList< QSslCertificate > untrustedCaCerts(QList< QSslCertificate > trustedCAs=QList< QSslCertificate >())
untrustedCaCerts get list of untrusted certificate authorities
const QString uniqueConfigId() const
Gets a unique generated 7-character string to assign to as config id.
const QPair< QSslCertificate, QSslKey > certIdentityBundle(const QString &id)
Gets a certificate identity bundle by id (sha hash).
bool isDisabled() const
Whether QCA has the qca-ossl plugin, which a base run-time requirement.
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 ))
static const QString AUTH_MAN_TAG
The display name of the Authentication Manager.
QgsAuthCertUtils::CertTrustPolicy defaultCertTrustPolicy()
Gets the default certificate trust policy preferred by user.
const QByteArray trustedCaCertsPemText()
trustedCaCertsPemText get concatenated string of all trusted CA certificates
bool removeAllAuthenticationConfigs()
Clear all authentication configs from table in database and from provider caches.
QgsAuthCertUtils::CertTrustPolicy certificateTrustPolicy(const QSslCertificate &cert)
certificateTrustPolicy get trust policy for a particular certificate cert
bool rebuildCertTrustCache()
Rebuild certificate authority cache.
const QString authenticationDatabasePath() const
The standard authentication database file in ~/.qgis3/ or defined location.
const QList< QSslCertificate > systemRootCAs()
systemRootCAs get root system certificate authorities
bool removeCertAuthority(const QSslCertificate &cert)
Remove a certificate authority.
const QList< QSslCertificate > trustedCaCerts(bool includeinvalid=false)
trustedCaCerts get list of all trusted CA certificates
bool existsCertAuthority(const QSslCertificate &cert)
Check if a certificate authority exists.
const QMap< QString, QSslCertificate > mappedDatabaseCAs()
mappedDatabaseCAs get sha1-mapped database-stored certificate authorities
bool importAuthenticationConfigsFromXml(const QString &filename, const QString &password=QString(), bool overwrite=false)
Import authentication configurations from an XML file.
bool configIdUnique(const QString &id) const
Verify if provided authentication id is unique.
QStringList configIds() const
Gets list of authentication ids from database.
QString authManTag() const
Simple text tag describing authentication system for message logs.
bool loadAuthenticationConfig(const QString &authcfg, QgsAuthMethodConfig &mconfig, bool full=false)
Load an authentication config from the database into subclass.
QgsAuthCertUtils::CertTrustPolicy certTrustPolicy(const QSslCertificate &cert)
certTrustPolicy get whether certificate cert is trusted by user
bool masterPasswordHashInDatabase() const
Verify a password hash existing in authentication database.
bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkProxy with an authentication config.
const QSslCertificate certAuthority(const QString &id)
Gets a certificate authority by id (sha hash)
void passwordHelperSuccess()
Signals emitted on password helper success, mainly used in the tests to exit main application loop.
bool registerCoreAuthMethods()
Instantiate and register existing C++ core authentication methods from plugins.
bool passwordHelperDelete()
Delete master password from wallet.
~QgsAuthManager() override
void dumpIgnoredSslErrorsCache_()
Utility function to dump the cache for debug purposes.
const QList< QSslCertificate > databaseCAs()
databaseCAs get database-stored certificate authorities
bool backupAuthenticationDatabase(QString *backuppath=nullptr)
Close connection to current authentication database and back it up.
void authDatabaseEraseRequested()
Emitted when a user has indicated they may want to erase the authentication db.
void passwordHelperFailure()
Signals emitted on password helper failure, mainly used in the tests to exit main application loop.
bool existsSslCertCustomConfig(const QString &id, const QString &hostport)
Check if SSL certificate custom config exists.
bool existsAuthSetting(const QString &key)
Check if an authentication setting exists.
void clearCachedConfig(const QString &authcfg)
Clear an authentication config from its associated authentication method cache.
void clearMasterPassword()
Clear supplied master password.
bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QNetworkRequest with an authentication config.
const QList< QSslCertificate > certIdentities()
certIdentities get certificate identities
bool storeCertAuthority(const QSslCertificate &cert)
Store a certificate authority.
QStringList certIdentityIds() const
certIdentityIds get list of certificate identity ids from database
bool removeCertTrustPolicies(const QList< QSslCertificate > &certs)
Remove a group certificate authorities.
QgsAuthMethod * authMethod(const QString &authMethodKey)
Gets authentication method from the config/provider cache via its key.
bool updateDataSourceUriItems(QStringList &connectionItems, const QString &authcfg, const QString &dataprovider=QString())
Provider call to update a QgsDataSourceUri with an authentication config.
static QgsAuthManager * instance()
Enforce singleton pattern.
QSqlDatabase authDatabaseConnection() const
Sets up the application instance of the authentication database connection.
void updateConfigAuthMethods()
Sync the confg/authentication method cache with what is in database.
bool storeSslCertCustomConfig(const QgsAuthConfigSslServer &config)
Store an SSL certificate custom config.
void setPasswordHelperLoggingEnabled(bool enabled)
Password helper logging enabled setter.
const QgsAuthConfigSslServer sslCertCustomConfigByHost(const QString &hostport)
sslCertCustomConfigByHost get an SSL certificate custom config by hostport (host:port)
bool updateAuthenticationConfig(const QgsAuthMethodConfig &config)
Update an authentication config in the database.
const QString authDatabaseConfigTable() const
Name of the authentication database table that stores configs.
bool existsCertIdentity(const QString &id)
Check if a certificate identity exists.
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,...
QStringList authMethodsKeys(const QString &dataprovider=QString())
Gets keys of supported authentication methods.
bool passwordHelperSync()
Store the password manager into the wallet.
bool masterPasswordIsSet() const
Whether master password has be input and verified, i.e. authentication database is accessible.
bool passwordHelperEnabled() const
Password helper enabled getter.
bool passwordHelperLoggingEnabled() const
Password helper logging enabled getter.
void masterPasswordVerified(bool verified)
Emitted when a password has been verify (or not)
bool setMasterPassword(bool verify=false)
Main call to initially set or continually check master password is set.
bool storeCertAuthorities(const QList< QSslCertificate > &certs)
Store multiple certificate authorities.
bool removeSslCertCustomConfig(const QString &id, const QString &hostport)
Remove an SSL certificate custom config.
static const QString AUTH_PASSWORD_HELPER_DISPLAY_NAME
The display name of the password helper (platform dependent)
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,...
bool rebuildTrustedCaCertsCache()
Rebuild trusted certificate authorities cache.
bool removeAuthenticationConfig(const QString &authcfg)
Remove an authentication config in the database.
bool removeCertTrustPolicy(const QSslCertificate &cert)
Remove a certificate authority.
bool hasConfigId(const QString &txt) const
Returns whether a string includes an authcfg ID token.
QgsAuthMethod::Expansions supportedAuthMethodExpansions(const QString &authcfg)
Gets supported authentication method expansion(s), e.g.
const QgsAuthConfigSslServer sslCertCustomConfig(const QString &id, const QString &hostport)
sslCertCustomConfig get an SSL certificate custom config by id (sha hash) and hostport (host:port)
QgsAuthMethodConfigsMap availableAuthMethodConfigs(const QString &dataprovider=QString())
Gets mapping of authentication config ids and their base configs (not decrypted data)
bool storeAuthenticationConfig(QgsAuthMethodConfig &mconfig, bool overwrite=false)
Store an authentication config in the database.
bool removeCertIdentity(const QString &id)
Remove a certificate identity.
QString configAuthMethodKey(const QString &authcfg) const
Gets key of authentication method associated with config ID.
const QList< QSslCertificate > trustedCaCertsCache()
trustedCaCertsCache cache of trusted certificate authorities, ready for network connections
Configuration storage class for authentication method configurations.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
QString method() const
Textual key of the associated authentication method.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
void setName(const QString &name)
Sets name of configuration.
void setVersion(int version)
Sets version of the configuration.
bool readXml(const QDomElement &element)
from a DOM element.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
const QString name() const
Gets name of configuration.
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
void loadConfigString(const QString &configstr)
Load existing extended configuration.
bool writeXml(QDomElement &parentElement, QDomDocument &document)
Stores the configuration in a DOM.
int version() const
Gets version of the configuration.
void setMethod(const QString &method)
void setUri(const QString &uri)
void setId(const QString &id)
Sets auth config ID.
A registry / canonical manager of authentication methods.
static QgsAuthMethodRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QWidget * editWidget(const QString &authMethodKey, QWidget *parent=nullptr)
Returns the GUI edit widget associated with the auth method.
QStringList authMethodList() const
Returns list of available auth methods by their keys.
Abstract base class for authentication method plugins.
virtual bool updateNetworkProxy(QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider=QString())
Update proxy settings with authentication components.
virtual bool updateNetworkRequest(QNetworkRequest &request, const QString &authcfg, const QString &dataprovider=QString())
Update a network request with authentication components.
QgsAuthMethod::Expansions supportedExpansions() const
Flags that represent the update points (where authentication configurations are expanded) supported b...
virtual void clearCachedConfig(const QString &authcfg)=0
Clear any cached configuration.
virtual void updateMethodConfig(QgsAuthMethodConfig &mconfig)=0
Update an authentication configuration in place.
virtual bool updateNetworkReply(QNetworkReply *reply, const QString &authcfg, const QString &dataprovider=QString())
Update a network reply with authentication components.
virtual bool updateDataSourceUriItems(QStringList &connectionItems, const QString &authcfg, const QString &dataprovider=QString())
Update data source connection items with authentication components.
static QgsCredentials * instance()
retrieves instance
bool getMasterPassword(QString &password, bool stored=false)
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Scoped object for logging of the runtime for a single operation or group of operations.
QHash< QString, QgsAuthMethodConfig > QgsAuthMethodConfigsMap
QHash< QString, QgsAuthMethod * > QgsAuthMethodsMap
#define QgsDebugMsgLevel(str, level)