24#include <QMutexLocker>
27#include <QSqlDatabase>
36#include <QDomDocument>
37#include <QRegularExpression>
39#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
40#include <QRandomGenerator>
46#include <QSslConfiguration>
67const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral(
"auth_configs" );
68const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral(
"auth_pass" );
69const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral(
"auth_settings" );
70const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral(
"auth_identities" );
71const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral(
"auth_servers" );
72const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral(
"auth_authorities" );
73const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral(
"auth_trust" );
75const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral(
"authcfg=([a-z]|[A-Z]|[0-9]){7}" );
78const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME(
"QGIS-Master-Password" );
79const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME(
"QGIS" );
85#elif defined(Q_OS_WIN)
87#elif defined(Q_OS_LINUX)
96 QMutexLocker locker( &sMutex );
107 mMutex = std::make_unique<QRecursiveMutex>();
108 mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
110 this, &QgsAuthManager::writeToConsole );
121 QMutexLocker locker( mMutex.get() );
126 const QString connectionName = QStringLiteral(
"authentication.configs:0x%1" ).arg(
reinterpret_cast<quintptr
>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) );
127 QgsDebugMsgLevel( QStringLiteral(
"Using auth db connection name: %1 " ).arg( connectionName ), 2 );
128 if ( !QSqlDatabase::contains( connectionName ) )
130 QgsDebugMsgLevel( QStringLiteral(
"No existing connection, creating a new one" ), 2 );
131 authdb = QSqlDatabase::addDatabase( QStringLiteral(
"QSQLITE" ), connectionName );
134 if ( QThread::currentThread() != qApp->thread() )
136 QgsDebugMsgLevel( QStringLiteral(
"Scheduled auth db remove on thread close" ), 2 );
146 QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName,
this ]
148 QMutexLocker locker( mMutex.get() );
149 QSqlDatabase::removeDatabase( connectionName );
150 mConnectedThreads.remove( QThread::currentThread() );
151 }, Qt::DirectConnection );
153 mConnectedThreads.insert( QThread::currentThread(), connection );
159 authdb = QSqlDatabase::database( connectionName );
163 if ( !authdb.isOpen() )
165 if ( !authdb.open() )
167 QString err = tr(
"Opening of authentication db FAILED : %1" ).arg( authdb.lastError().text() );
185 mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
188 QCA::scanForPlugins();
190 QgsDebugMsgLevel( QStringLiteral(
"QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
191 QStringList capabilities;
193 capabilities = QCA::supportedFeatures();
194 QgsDebugMsgLevel( QStringLiteral(
"QCA supports: %1" ).arg( capabilities.join(
"," ) ), 2 );
197 if ( !QCA::isSupported(
"cert", QStringLiteral(
"qca-ossl" ) ) )
199 mAuthDisabled =
true;
200 mAuthDisabledMessage = tr(
"QCA's OpenSSL plugin (qca-ossl) is missing" );
204 QgsDebugMsgLevel( QStringLiteral(
"Prioritizing qca-ossl over all other QCA providers..." ), 2 );
205 const QCA::ProviderList provds = QCA::providers();
207 for ( QCA::Provider *p : provds )
209 QString pn = p->name();
211 if ( pn != QLatin1String(
"qca-ossl" ) )
213 pr = QCA::providerPriority( pn ) + 1;
215 QCA::setProviderPriority( pn, pr );
216 prlist << QStringLiteral(
"%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
218 QgsDebugMsgLevel( QStringLiteral(
"QCA provider priorities: %1" ).arg( prlist.join(
", " ) ), 2 );
225 QgsDebugMsgLevel( QStringLiteral(
"Authentication methods found: %1" ).arg( methods.join(
", " ) ), 2 );
227 if ( methods.isEmpty() )
229 mAuthDisabled =
true;
230 mAuthDisabledMessage = tr(
"No authentication method plugins found" );
236 mAuthDisabled =
true;
237 mAuthDisabledMessage = tr(
"No authentication method plugins could be loaded" );
241 mAuthDbPath = QDir::cleanPath( authDatabasePath );
245 QFileInfo dbdirinfo( dbinfo.path() );
246 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
248 if ( !dbdirinfo.exists() )
250 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
251 if ( !QDir().mkpath( dbdirinfo.filePath() ) )
253 const char *err = QT_TR_NOOP(
"Auth db directory path could not be created" );
260 if ( dbinfo.exists() )
262 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
264 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
269 if ( dbinfo.size() > 0 )
273 if ( !createCertTables() )
283 const char *passenv =
"QGIS_AUTH_PASSWORD_FILE";
286 QString passpath( getenv( passenv ) );
295 QFile passfile( passpath );
296 if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
298 QTextStream passin( &passfile );
299 while ( !passin.atEnd() )
301 masterpass = passin.readLine();
306 if ( !masterpass.isEmpty() )
310 QgsDebugMsgLevel( QStringLiteral(
"Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
314 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
320 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
330 QgsDebugMsgLevel( QStringLiteral(
"Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
332 if ( !createConfigTables() )
335 if ( !createCertTables() )
346bool QgsAuthManager::createConfigTables()
348 QMutexLocker locker( mMutex.get() );
352 const char *err = QT_TR_NOOP(
"Auth db could not be created and opened" );
363 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
364 " 'salt' TEXT NOT NULL,\n"
365 " 'civ' TEXT NOT NULL\n"
366 ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
367 query.prepare( qstr );
368 if ( !authDbQuery( &query ) )
372 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
373 " 'id' TEXT NOT NULL,\n"
374 " 'name' TEXT NOT NULL,\n"
376 " 'type' TEXT NOT NULL,\n"
377 " 'version' INTEGER NOT NULL\n"
379 query.prepare( qstr );
380 if ( !authDbQuery( &query ) )
385 query.prepare( qstr );
386 if ( !authDbQuery( &query ) )
391 query.prepare( qstr );
392 if ( !authDbQuery( &query ) )
399bool QgsAuthManager::createCertTables()
401 QMutexLocker locker( mMutex.get() );
410 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
411 " 'setting' TEXT NOT NULL\n"
412 ", 'value' TEXT);" ).arg( authDbSettingsTable() );
413 query.prepare( qstr );
414 if ( !authDbQuery( &query ) )
419 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
420 " 'id' TEXT NOT NULL,\n"
421 " 'key' TEXT NOT NULL\n"
422 ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
423 query.prepare( qstr );
424 if ( !authDbQuery( &query ) )
428 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
429 query.prepare( qstr );
430 if ( !authDbQuery( &query ) )
435 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
436 " 'id' TEXT NOT NULL,\n"
437 " 'host' TEXT NOT NULL,\n"
440 query.prepare( qstr );
441 if ( !authDbQuery( &query ) )
445 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg(
authDatabaseServersTable() );
446 query.prepare( qstr );
447 if ( !authDbQuery( &query ) )
452 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
453 " 'id' TEXT NOT NULL\n"
454 ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
455 query.prepare( qstr );
456 if ( !authDbQuery( &query ) )
460 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
461 query.prepare( qstr );
462 if ( !authDbQuery( &query ) )
466 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
467 " 'id' TEXT NOT NULL\n"
468 ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
469 query.prepare( qstr );
470 if ( !authDbQuery( &query ) )
474 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
475 query.prepare( qstr );
476 if ( !authDbQuery( &query ) )
487 QgsDebugMsg( QStringLiteral(
"Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
489 return mAuthDisabled;
494 return tr(
"Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
499 QMutexLocker locker( mMasterPasswordMutex.get() );
503 if ( mScheduledDbErase )
506 if ( mMasterPass.isEmpty() )
508 QgsDebugMsgLevel( QStringLiteral(
"Master password is not yet set by user" ), 2 );
509 if ( !masterPasswordInput() )
511 QgsDebugMsgLevel( QStringLiteral(
"Master password input canceled by user" ), 2 );
525 QgsDebugMsgLevel( QStringLiteral(
"Master password is set and verified" ), 2 );
531 QMutexLocker locker( mMutex.get() );
535 if ( mScheduledDbErase )
539 QString prevpass = QString( mMasterPass );
543 mMasterPass = prevpass;
544 const char *err = QT_TR_NOOP(
"Master password set: FAILED to verify, reset to previous" );
550 QgsDebugMsgLevel( QStringLiteral(
"Master password set: SUCCESS%1" ).arg( verify ?
" and verified" :
"" ), 2 );
560 if ( !masterPasswordRowsInDb( &rows ) )
562 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
570 QgsDebugMsgLevel( QStringLiteral(
"Master password: %1 rows in database" ).arg( rows ), 2 );
574 const char *err = QT_TR_NOOP(
"Master password: FAILED to find just one master password record in database" );
581 else if ( rows == 1 )
583 if ( !masterPasswordCheckAgainstDb( compare ) )
585 if ( compare.isNull() )
587 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
596 if ( mPassTries >= 5 )
598 mAuthDisabled =
true;
599 const char *err = QT_TR_NOOP(
"Master password: failed 5 times authentication system DISABLED" );
607 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
608 if ( compare.isNull() )
612 else if ( compare.isNull() )
614 if ( !masterPasswordStoreInDb() )
616 const char *err = QT_TR_NOOP(
"Master password: hash FAILED to be stored in database" );
625 QgsDebugMsgLevel( QStringLiteral(
"Master password: hash stored in database" ), 2 );
628 if ( !masterPasswordCheckAgainstDb() )
630 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
640 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
650 return !mMasterPass.isEmpty();
655 return mMasterPass == pass;
659 bool keepbackup, QString *backuppath )
673 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up current database" ), 2 );
679 QString prevpass = QString( mMasterPass );
680 QString prevciv = QString( masterPasswordCiv() );
686 if ( ok && !masterPasswordClearDb() )
689 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not clear current password from database" );
695 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: cleared current password from database" ), 2 );
702 if ( ok && !masterPasswordStoreInDb() )
705 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not store new password in database" );
711 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: stored new password in database" ), 2 );
718 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify new password in database" );
724 if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
727 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt configs in database" );
733 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: re-encrypted configs in database" ), 2 );
737 if ( ok && !verifyPasswordCanDecryptConfigs() )
740 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
745 if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
748 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt settings in database" );
753 if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
756 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt identities in database" );
766 QString errdbbackup( dbbackup );
767 errdbbackup.replace( QLatin1String(
".db" ), QLatin1String(
"_ERROR.db" ) );
769 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
773 mMasterPass = prevpass;
775 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: reinstated previous password and database" ) );
779 *backuppath = errdbbackup;
785 if ( !keepbackup && !QFile::remove( dbbackup ) )
787 const char *err = QT_TR_NOOP(
"Master password reset: could not remove old database backup" );
795 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
797 *backuppath = dbbackup;
807 mScheduledDbErase = scheduleErase;
809 mScheduledDbEraseRequestEmitted =
false;
810 mScheduledDbEraseRequestCount = 0;
814 if ( !mScheduledDbEraseTimer )
816 mScheduledDbEraseTimer =
new QTimer(
this );
817 connect( mScheduledDbEraseTimer, &QTimer::timeout,
this, &QgsAuthManager::tryToStartDbErase );
818 mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
820 else if ( !mScheduledDbEraseTimer->isActive() )
822 mScheduledDbEraseTimer->start();
827 if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
828 mScheduledDbEraseTimer->stop();
837 qDeleteAll( mAuthMethods );
838 mAuthMethods.clear();
840 for (
const auto &authMethodKey : methods )
845 return !mAuthMethods.isEmpty();
855 QTimer::singleShot( 3, &loop, &QEventLoop::quit );
858#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
859 uint seed =
static_cast< uint
>( QTime::currentTime().msec() );
866 for (
int i = 0; i < len; i++ )
868#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
869 switch ( qrand() % 2 )
871 switch ( QRandomGenerator::system()->generate() % 2 )
875#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
876 id += (
'0' + qrand() % 10 );
878 id +=
static_cast<char>(
'0' + QRandomGenerator::system()->generate() % 10 );
882#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
883 id += (
'a' + qrand() % 26 );
885 id +=
static_cast<char>(
'a' + QRandomGenerator::system()->generate() % 26 );
890 if ( !configids.contains(
id ) )
895 QgsDebugMsgLevel( QStringLiteral(
"Generated unique ID: %1" ).arg(
id ), 2 );
906 const char *err = QT_TR_NOOP(
"Config ID is empty" );
912 return !configids.contains(
id );
917 const thread_local QRegularExpression authCfgRegExp( AUTH_CFG_REGEX );
918 return txt.indexOf( authCfgRegExp ) != -1;
923 QMutexLocker locker( mMutex.get() );
924 QStringList providerAuthMethodsKeys;
925 if ( !dataprovider.isEmpty() )
936 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1" ).arg(
authDatabaseConfigTable() ) );
938 if ( !authDbQuery( &query ) )
943 if ( query.isActive() && query.isSelect() )
945 while ( query.next() )
947 QString authcfg = query.value( 0 ).toString();
949 config.
setId( authcfg );
950 config.
setName( query.value( 1 ).toString() );
951 config.
setUri( query.value( 2 ).toString() );
952 config.
setMethod( query.value( 3 ).toString() );
953 config.
setVersion( query.value( 4 ).toInt() );
955 if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.
method() ) )
960 baseConfigs.insert( authcfg, config );
968 QMutexLocker locker( mMutex.get() );
975 if ( !authDbQuery( &query ) )
980 if ( query.isActive() )
982 QgsDebugMsgLevel( QStringLiteral(
"Syncing existing auth config and their auth methods" ), 2 );
983 mConfigAuthMethods.clear();
984 QStringList cfgmethods;
985 while ( query.next() )
987 mConfigAuthMethods.insert( query.value( 0 ).toString(),
988 query.value( 1 ).toString() );
989 cfgmethods << QStringLiteral(
"%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
991 QgsDebugMsgLevel( QStringLiteral(
"Stored auth config/methods:\n%1" ).arg( cfgmethods.join(
", " ) ), 2 );
1000 if ( !mConfigAuthMethods.contains( authcfg ) )
1002 QgsDebugMsg( QStringLiteral(
"No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1006 QString authMethodKey = mConfigAuthMethods.value( authcfg );
1016 return mConfigAuthMethods.value( authcfg, QString() );
1027 if ( !mAuthMethods.contains( authMethodKey ) )
1029 QgsDebugMsg( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1033 return mAuthMethods.value( authMethodKey );
1038 if ( !mAuthMethods.contains( authMethodKey ) )
1040 QgsDebugMsg( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1050 if ( dataprovider.isEmpty() )
1052 return mAuthMethods;
1056 QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1057 while ( i != mAuthMethods.constEnd() )
1060 && ( i.value()->supportedDataProviders().contains( QStringLiteral(
"all" ) )
1061 || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1063 filteredmap.insert( i.key(), i.value() );
1071QWidget *QgsAuthManager::authMethodEditWidget(
const QString &authMethodKey, QWidget *parent )
1075 return method->editWidget( parent );
1084 return QgsAuthMethod::Expansions();
1091 return QgsAuthMethod::Expansions();
1096 QMutexLocker locker( mMutex.get() );
1103 const char *err = QT_TR_NOOP(
"Store config: FAILED because config is invalid" );
1109 QString uid = mconfig.
id();
1110 bool passedinID = !uid.isEmpty();
1111 if ( uid.isEmpty() )
1119 const char *err = QT_TR_NOOP(
"Store config: FAILED because pre-defined config ID %1 is not unique" );
1130 if ( configstring.isEmpty() )
1132 const char *err = QT_TR_NOOP(
"Store config: FAILED because config string is empty" );
1138 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1139 QgsDebugMsg( QStringLiteral(
"name: %1" ).arg( config.name() ) );
1140 QgsDebugMsg( QStringLiteral(
"uri: %1" ).arg( config.uri() ) );
1141 QgsDebugMsg( QStringLiteral(
"type: %1" ).arg( config.method() ) );
1142 QgsDebugMsg( QStringLiteral(
"version: %1" ).arg( config.version() ) );
1143 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1147 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, name, uri, type, version, config) "
1150 query.bindValue( QStringLiteral(
":id" ), uid );
1151 query.bindValue( QStringLiteral(
":name" ), mconfig.
name() );
1152 query.bindValue( QStringLiteral(
":uri" ), mconfig.
uri() );
1153 query.bindValue( QStringLiteral(
":type" ), mconfig.
method() );
1154 query.bindValue( QStringLiteral(
":version" ), mconfig.
version() );
1155 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1157 if ( !authDbStartTransaction() )
1160 if ( !authDbQuery( &query ) )
1163 if ( !authDbCommit() )
1168 mconfig.
setId( uid );
1172 QgsDebugMsgLevel( QStringLiteral(
"Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1178 QMutexLocker locker( mMutex.get() );
1183 if ( !config.
isValid(
true ) )
1185 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is invalid" );
1192 if ( configstring.isEmpty() )
1194 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is empty" );
1201 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1202 QgsDebugMsg( QStringLiteral(
"id: %1" ).arg( config.
id() ) );
1207 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1211 if ( !query.prepare( QStringLiteral(
"UPDATE %1 "
1212 "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1215 const char *err = QT_TR_NOOP(
"Update config: FAILED to prepare query" );
1221 query.bindValue( QStringLiteral(
":id" ), config.
id() );
1222 query.bindValue( QStringLiteral(
":name" ), config.
name() );
1223 query.bindValue( QStringLiteral(
":uri" ), config.
uri() );
1224 query.bindValue( QStringLiteral(
":type" ), config.
method() );
1225 query.bindValue( QStringLiteral(
":version" ), config.
version() );
1226 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1228 if ( !authDbStartTransaction() )
1231 if ( !authDbQuery( &query ) )
1234 if ( !authDbCommit() )
1242 QgsDebugMsgLevel( QStringLiteral(
"Update config SUCCESS for authcfg: %1" ).arg( config.
id() ), 2 );
1255 QMutexLocker locker( mMutex.get() );
1260 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version, config FROM %1 "
1265 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1 "
1269 query.bindValue( QStringLiteral(
":id" ), authcfg );
1271 if ( !authDbQuery( &query ) )
1276 if ( query.isActive() && query.isSelect() )
1278 if ( query.first() )
1280 mconfig.
setId( query.value( 0 ).toString() );
1281 mconfig.
setName( query.value( 1 ).toString() );
1282 mconfig.
setUri( query.value( 2 ).toString() );
1283 mconfig.
setMethod( query.value( 3 ).toString() );
1284 mconfig.
setVersion( query.value( 4 ).toInt() );
1299 QgsDebugMsg( QStringLiteral(
"Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1302 QgsDebugMsgLevel( QStringLiteral(
"Load %1 config SUCCESS for authcfg: %2" ).arg( full ?
"full" :
"base", authcfg ), 2 );
1307 QgsDebugMsg( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1317 QMutexLocker locker( mMutex.get() );
1321 if ( authcfg.isEmpty() )
1328 query.bindValue( QStringLiteral(
":id" ), authcfg );
1330 if ( !authDbStartTransaction() )
1333 if ( !authDbQuery( &query ) )
1336 if ( !authDbCommit() )
1343 QgsDebugMsgLevel( QStringLiteral(
"REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1350 if ( filename.isEmpty() )
1353 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1354 QDomElement root = document.createElement( QStringLiteral(
"qgis_authentication" ) );
1355 document.appendChild( root );
1358 if ( !password.isEmpty() )
1363 root.setAttribute( QStringLiteral(
"salt" ), salt );
1364 root.setAttribute( QStringLiteral(
"hash" ), hash );
1365 root.setAttribute( QStringLiteral(
"civ" ), civ );
1368 QDomElement configurations = document.createElement( QStringLiteral(
"configurations" ) );
1369 for (
const QString &authcfg : authcfgs )
1376 authMethodConfig.
writeXml( configurations, document );
1379 if ( !password.isEmpty() )
1381 QString configurationsString;
1382 QTextStream ts( &configurationsString );
1383#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1384 ts.setCodec(
"UTF-8" );
1386 configurations.save( ts, 2 );
1387 root.appendChild( document.createTextNode(
QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
1391 root.appendChild( configurations );
1394 QFile file( filename );
1395 if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
1398 QTextStream ts( &file );
1399#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1400 ts.setCodec(
"UTF-8" );
1402 document.save( ts, 2 );
1409 QFile file( filename );
1410 if ( !file.open( QFile::ReadOnly ) )
1415 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1416 if ( !document.setContent( &file ) )
1423 QDomElement root = document.documentElement();
1424 if ( root.tagName() != QLatin1String(
"qgis_authentication" ) )
1429 QDomElement configurations;
1430 if ( root.hasAttribute( QStringLiteral(
"salt" ) ) )
1432 QString salt = root.attribute( QStringLiteral(
"salt" ) );
1433 QString hash = root.attribute( QStringLiteral(
"hash" ) );
1434 QString civ = root.attribute( QStringLiteral(
"civ" ) );
1439 configurations = document.firstChild().toElement();
1443 configurations = root.firstChildElement( QStringLiteral(
"configurations" ) );
1446 QDomElement configuration = configurations.firstChildElement();
1447 while ( !configuration.isNull() )
1450 authMethodConfig.
readXml( configuration );
1453 configuration = configuration.nextSiblingElement();
1460 QMutexLocker locker( mMutex.get() );
1466 bool res = authDbTransactionQuery( &query );
1474 QgsDebugMsgLevel( QStringLiteral(
"Remove configs from database: %1" ).arg( res ?
"SUCCEEDED" :
"FAILED" ), 2 );
1481 QMutexLocker locker( mMutex.get() );
1484 const char *err = QT_TR_NOOP(
"No authentication database found" );
1492 if ( authConn.isValid() && authConn.isOpen() )
1496 QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral(
"yyyy-MM-dd-hhmmss" ) ) );
1498 dbbackup.replace( QLatin1String(
".db" ), QStringLiteral(
"_%1.db" ).arg( datestamp ) );
1502 const char *err = QT_TR_NOOP(
"Could not back up authentication database" );
1509 *backuppath = dbbackup;
1511 QgsDebugMsgLevel( QStringLiteral(
"Backed up auth database at %1" ).arg( dbbackup ), 2 );
1517 QMutexLocker locker( mMutex.get() );
1527 if ( backuppath && !dbbackup.isEmpty() )
1528 *backuppath = dbbackup;
1531 if ( dbinfo.exists() )
1533 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1535 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
1543 const char *err = QT_TR_NOOP(
"No authentication database found" );
1551 const char *err = QT_TR_NOOP(
"Authentication database could not be deleted" );
1557 mMasterPass = QString();
1559 QgsDebugMsgLevel( QStringLiteral(
"Creating Auth db through QSqlDatabase initial connection" ), 2 );
1562 if ( !authConn.isValid() || !authConn.isOpen() )
1564 const char *err = QT_TR_NOOP(
"Authentication database could not be initialized" );
1570 if ( !createConfigTables() )
1572 const char *err = QT_TR_NOOP(
"FAILED to create auth database config tables" );
1578 if ( !createCertTables() )
1580 const char *err = QT_TR_NOOP(
"FAILED to create auth database cert tables" );
1596 const QString &dataprovider )
1606 QgsDebugMsg( QStringLiteral(
"Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1621 const QString &dataprovider )
1631 QgsDebugMsg( QStringLiteral(
"Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1647 const QString &dataprovider )
1657 QgsDebugMsg( QStringLiteral(
"Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1682 QgsDebugMsg( QStringLiteral(
"Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1691 QgsDebugMsgLevel( QStringLiteral(
"Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1700 QMutexLocker locker( mMutex.get() );
1701 if ( key.isEmpty() )
1704 QString storeval( value.toString() );
1720 query.prepare( QStringLiteral(
"INSERT INTO %1 (setting, value) "
1721 "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1723 query.bindValue( QStringLiteral(
":setting" ), key );
1724 query.bindValue( QStringLiteral(
":value" ), storeval );
1726 if ( !authDbStartTransaction() )
1729 if ( !authDbQuery( &query ) )
1732 if ( !authDbCommit() )
1735 QgsDebugMsgLevel( QStringLiteral(
"Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1741 QMutexLocker locker( mMutex.get() );
1742 if ( key.isEmpty() )
1748 QVariant value = defaultValue;
1750 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1751 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1753 query.bindValue( QStringLiteral(
":setting" ), key );
1755 if ( !authDbQuery( &query ) )
1758 if ( query.isActive() && query.isSelect() )
1760 if ( query.first() )
1764 value = QVariant(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1768 value = query.value( 0 );
1770 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1774 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1784 QMutexLocker locker( mMutex.get() );
1785 if ( key.isEmpty() )
1789 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1790 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1792 query.bindValue( QStringLiteral(
":setting" ), key );
1794 if ( !authDbQuery( &query ) )
1798 if ( query.isActive() && query.isSelect() )
1800 if ( query.first() )
1802 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting exists for key: %1" ).arg( key ), 2 );
1807 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1817 QMutexLocker locker( mMutex.get() );
1818 if ( key.isEmpty() )
1823 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1825 query.bindValue( QStringLiteral(
":setting" ), key );
1827 if ( !authDbStartTransaction() )
1830 if ( !authDbQuery( &query ) )
1833 if ( !authDbCommit() )
1836 QgsDebugMsgLevel( QStringLiteral(
"REMOVED setting for key: %1" ).arg( key ), 2 );
1850 QMutexLocker locker( mMutex.get() );
1856 mCustomConfigByHostCache.clear();
1857 mHasCheckedIfCustomConfigByHostExists =
false;
1860 QgsDebugMsg( QStringLiteral(
"Init of SSL caches FAILED" ) );
1866 QMutexLocker locker( mMutex.get() );
1867 if ( cert.isNull() )
1869 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
1874 QgsDebugMsg( QStringLiteral(
"Passed private key is null" ) );
1884 QString certpem( cert.toPem() );
1888 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, key, cert) "
1889 "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1891 query.bindValue( QStringLiteral(
":id" ),
id );
1892 query.bindValue( QStringLiteral(
":key" ), keypem );
1893 query.bindValue( QStringLiteral(
":cert" ), certpem );
1895 if ( !authDbStartTransaction() )
1898 if ( !authDbQuery( &query ) )
1901 if ( !authDbCommit() )
1904 QgsDebugMsgLevel( QStringLiteral(
"Store certificate identity SUCCESS for id: %1" ).arg(
id ), 2 );
1910 QMutexLocker locker( mMutex.get() );
1911 QSslCertificate emptycert;
1912 QSslCertificate cert;
1917 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
1918 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1920 query.bindValue( QStringLiteral(
":id" ),
id );
1922 if ( !authDbQuery( &query ) )
1925 if ( query.isActive() && query.isSelect() )
1927 if ( query.first() )
1929 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1930 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity retrieved for id: %1" ).arg(
id ), 2 );
1934 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1944 QMutexLocker locker( mMutex.get() );
1945 QPair<QSslCertificate, QSslKey> bundle;
1953 query.prepare( QStringLiteral(
"SELECT key, cert FROM %1 "
1954 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1956 query.bindValue( QStringLiteral(
":id" ),
id );
1958 if ( !authDbQuery( &query ) )
1961 if ( query.isActive() && query.isSelect() )
1963 QSslCertificate cert;
1965 if ( query.first() )
1967 key = QSslKey(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1968 QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1971 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create private key" );
1976 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1977 if ( cert.isNull() )
1979 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create certificate" );
1984 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity bundle retrieved for id: %1" ).arg(
id ), 2 );
1988 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1992 bundle = qMakePair( cert, key );
1999 QMutexLocker locker( mMutex.get() );
2003 return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
2005 return QStringList();
2010 QMutexLocker locker( mMutex.get() );
2011 QList<QSslCertificate> certs;
2014 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2016 if ( !authDbQuery( &query ) )
2019 if ( query.isActive() && query.isSelect() )
2021 while ( query.next() )
2023 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2032 QMutexLocker locker( mMutex.get() );
2033 QStringList identityids = QStringList();
2039 query.prepare( QStringLiteral(
"SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2041 if ( !authDbQuery( &query ) )
2046 if ( query.isActive() )
2048 while ( query.next() )
2050 identityids << query.value( 0 ).toString();
2058 QMutexLocker locker( mMutex.get() );
2063 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2064 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2066 query.bindValue( QStringLiteral(
":id" ),
id );
2068 if ( !authDbQuery( &query ) )
2072 if ( query.isActive() && query.isSelect() )
2074 if ( query.first() )
2076 QgsDebugMsgLevel( QStringLiteral(
"Certificate bundle exists for id: %1" ).arg(
id ), 2 );
2081 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate bundle for id: %1" ).arg(
id ) );
2091 QMutexLocker locker( mMutex.get() );
2094 QgsDebugMsg( QStringLiteral(
"Passed bundle ID is empty" ) );
2100 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2102 query.bindValue( QStringLiteral(
":id" ),
id );
2104 if ( !authDbStartTransaction() )
2107 if ( !authDbQuery( &query ) )
2110 if ( !authDbCommit() )
2113 QgsDebugMsgLevel( QStringLiteral(
"REMOVED certificate identity for id: %1" ).arg(
id ), 2 );
2119 QMutexLocker locker( mMutex.get() );
2122 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2131 QString certpem( cert.toPem() );
2134 query.prepare( QStringLiteral(
"INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2137 query.bindValue( QStringLiteral(
":id" ),
id );
2138 query.bindValue( QStringLiteral(
":host" ), config.
sslHostPort().trimmed() );
2139 query.bindValue( QStringLiteral(
":cert" ), certpem );
2140 query.bindValue( QStringLiteral(
":config" ), config.
configString() );
2142 if ( !authDbStartTransaction() )
2145 if ( !authDbQuery( &query ) )
2148 if ( !authDbCommit() )
2151 QgsDebugMsgLevel( QStringLiteral(
"Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2155 mHasCheckedIfCustomConfigByHostExists =
false;
2156 mCustomConfigByHostCache.clear();
2163 QMutexLocker locker( mMutex.get() );
2166 if (
id.isEmpty() || hostport.isEmpty() )
2168 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2173 query.prepare( QStringLiteral(
"SELECT id, host, cert, config FROM %1 "
2176 query.bindValue( QStringLiteral(
":id" ),
id );
2177 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2179 if ( !authDbQuery( &query ) )
2182 if ( query.isActive() && query.isSelect() )
2184 if ( query.first() )
2186 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2189 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2193 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2194 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2206 if ( hostport.isEmpty() )
2211 QMutexLocker locker( mMutex.get() );
2212 if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2214 if ( mCustomConfigByHostCache.contains( hostport ) )
2215 return mCustomConfigByHostCache.value( hostport );
2220 if ( !mHasCheckedIfCustomConfigByHostExists )
2222 mHasCheckedIfCustomConfigByHostExists =
true;
2224 if ( !authDbQuery( &query ) )
2226 mHasCustomConfigByHost =
false;
2229 if ( query.isActive() && query.isSelect() && query.first() )
2231 mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2232 if ( !mHasCustomConfigByHost )
2237 mHasCustomConfigByHost =
false;
2242 query.prepare( QString(
"SELECT id, host, cert, config FROM %1 "
2245 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2247 if ( !authDbQuery( &query ) )
2249 mCustomConfigByHostCache.insert( hostport, config );
2253 if ( query.isActive() && query.isSelect() )
2255 if ( query.first() )
2257 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2260 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2264 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2265 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2268 mCustomConfigByHostCache.insert( hostport, emptyconfig );
2273 mCustomConfigByHostCache.insert( hostport, config );
2279 QMutexLocker locker( mMutex.get() );
2280 QList<QgsAuthConfigSslServer> configs;
2285 if ( !authDbQuery( &query ) )
2288 if ( query.isActive() && query.isSelect() )
2290 while ( query.next() )
2293 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2297 configs.append( config );
2306 QMutexLocker locker( mMutex.get() );
2307 if (
id.isEmpty() || hostport.isEmpty() )
2309 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2314 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2317 query.bindValue( QStringLiteral(
":id" ),
id );
2318 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2320 if ( !authDbQuery( &query ) )
2324 if ( query.isActive() && query.isSelect() )
2326 if ( query.first() )
2328 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2333 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2334 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2344 QMutexLocker locker( mMutex.get() );
2345 if (
id.isEmpty() || hostport.isEmpty() )
2347 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2351 mHasCheckedIfCustomConfigByHostExists =
false;
2352 mCustomConfigByHostCache.clear();
2356 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id AND host = :host" ).arg(
authDatabaseServersTable() ) );
2358 query.bindValue( QStringLiteral(
":id" ),
id );
2359 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2361 if ( !authDbStartTransaction() )
2364 if ( !authDbQuery( &query ) )
2367 if ( !authDbCommit() )
2370 QString shahostport( QStringLiteral(
"%1:%2" ).arg(
id, hostport ) );
2371 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2373 mIgnoredSslErrorsCache.remove( shahostport );
2376 QgsDebugMsgLevel( QStringLiteral(
"REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2383 QMutexLocker locker( mMutex.get() );
2384 if ( !mIgnoredSslErrorsCache.isEmpty() )
2386 QgsDebugMsg( QStringLiteral(
"Ignored SSL errors cache items:" ) );
2387 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2388 while ( i != mIgnoredSslErrorsCache.constEnd() )
2391 for (
auto err : i.value() )
2395 QgsDebugMsg( QStringLiteral(
"%1 = %2" ).arg( i.key(), errs.join(
", " ) ) );
2407 QMutexLocker locker( mMutex.get() );
2410 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2414 QString shahostport( QStringLiteral(
"%1:%2" )
2417 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2419 mIgnoredSslErrorsCache.remove( shahostport );
2422 if ( !errenums.isEmpty() )
2424 mIgnoredSslErrorsCache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2425 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2430 QgsDebugMsgLevel( QStringLiteral(
"No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2436 QMutexLocker locker( mMutex.get() );
2437 const thread_local QRegularExpression rx( QRegularExpression::anchoredPattern(
"\\S+:\\S+:\\d+" ) );
2438 if ( !rx.match( shahostport ).hasMatch() )
2440 QgsDebugMsg(
"Passed shahostport does not match \\S+:\\S+:\\d+, "
2441 "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2445 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2447 mIgnoredSslErrorsCache.remove( shahostport );
2450 if ( errors.isEmpty() )
2452 QgsDebugMsg( QStringLiteral(
"Passed errors list empty" ) );
2456 QSet<QSslError::SslError> errs;
2457 for (
const auto &error : errors )
2459 if ( error.error() == QSslError::NoError )
2462 errs.insert( error.error() );
2465 if ( errs.isEmpty() )
2467 QgsDebugMsg( QStringLiteral(
"Passed errors list does not contain errors" ) );
2471 mIgnoredSslErrorsCache.insert( shahostport, errs );
2473 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2480 QMutexLocker locker( mMutex.get() );
2481 QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2482 QHash<QString, QSet<QSslError::SslError> > nextcache;
2487 if ( !authDbQuery( &query ) )
2489 QgsDebugMsg( QStringLiteral(
"Rebuild of ignored SSL errors cache FAILED" ) );
2493 if ( query.isActive() && query.isSelect() )
2495 while ( query.next() )
2497 QString shahostport( QStringLiteral(
"%1:%2" )
2498 .arg( query.value( 0 ).toString().trimmed(),
2499 query.value( 1 ).toString().trimmed() ) );
2503 if ( !errenums.isEmpty() )
2505 nextcache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2507 if ( prevcache.contains( shahostport ) )
2509 prevcache.remove( shahostport );
2514 if ( !prevcache.isEmpty() )
2517 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2518 while ( i != prevcache.constEnd() )
2520 nextcache.insert( i.key(), i.value() );
2525 if ( nextcache != mIgnoredSslErrorsCache )
2527 mIgnoredSslErrorsCache.clear();
2528 mIgnoredSslErrorsCache = nextcache;
2529 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2534 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2542 QMutexLocker locker( mMutex.get() );
2543 if ( certs.isEmpty() )
2545 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2549 for (
const auto &cert : certs )
2559 QMutexLocker locker( mMutex.get() );
2562 if ( cert.isNull() )
2564 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2571 QString pem( cert.toPem() );
2574 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, cert) "
2575 "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2577 query.bindValue( QStringLiteral(
":id" ),
id );
2578 query.bindValue( QStringLiteral(
":cert" ), pem );
2580 if ( !authDbStartTransaction() )
2583 if ( !authDbQuery( &query ) )
2586 if ( !authDbCommit() )
2589 QgsDebugMsgLevel( QStringLiteral(
"Store certificate authority SUCCESS for id: %1" ).arg(
id ), 2 );
2595 QMutexLocker locker( mMutex.get() );
2596 QSslCertificate emptycert;
2597 QSslCertificate cert;
2602 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2603 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2605 query.bindValue( QStringLiteral(
":id" ),
id );
2607 if ( !authDbQuery( &query ) )
2610 if ( query.isActive() && query.isSelect() )
2612 if ( query.first() )
2614 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2615 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority retrieved for id: %1" ).arg(
id ), 2 );
2619 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2629 QMutexLocker locker( mMutex.get() );
2630 if ( cert.isNull() )
2632 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2639 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
2640 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2642 query.bindValue( QStringLiteral(
":id" ),
id );
2644 if ( !authDbQuery( &query ) )
2648 if ( query.isActive() && query.isSelect() )
2650 if ( query.first() )
2652 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority exists for id: %1" ).arg(
id ), 2 );
2657 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2667 QMutexLocker locker( mMutex.get() );
2668 if ( cert.isNull() )
2670 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2678 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2680 query.bindValue( QStringLiteral(
":id" ),
id );
2682 if ( !authDbStartTransaction() )
2685 if ( !authDbQuery( &query ) )
2688 if ( !authDbCommit() )
2691 QgsDebugMsgLevel( QStringLiteral(
"REMOVED authority for id: %1" ).arg(
id ), 2 );
2697 return QSslConfiguration::systemCaCertificates();
2702 QMutexLocker locker( mMutex.get() );
2703 QList<QSslCertificate> certs;
2704 QList<QSslCertificate> filecerts;
2713 QString cafile( cafileval.toString() );
2714 if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2719 for (
const auto &cert : std::as_const( filecerts ) )
2721 if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2723 || cert.expiryDate() <= QDateTime::currentDateTime()
2724 || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2739 QMutexLocker locker( mMutex.get() );
2740 QList<QSslCertificate> certs;
2743 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2745 if ( !authDbQuery( &query ) )
2748 if ( query.isActive() && query.isSelect() )
2750 while ( query.next() )
2752 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2761 QMutexLocker locker( mMutex.get() );
2767 QMutexLocker locker( mMutex.get() );
2768 mCaCertsCache.clear();
2774 bool res = !mCaCertsCache.isEmpty();
2776 QgsDebugMsg( QStringLiteral(
"Rebuild of CA certs cache FAILED" ) );
2782 QMutexLocker locker( mMutex.get() );
2783 if ( cert.isNull() )
2785 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2795 QgsDebugMsg( QStringLiteral(
"Passed policy was default, all cert records in database were removed for id: %1" ).arg(
id ) );
2800 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, policy) "
2801 "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2803 query.bindValue( QStringLiteral(
":id" ),
id );
2804 query.bindValue( QStringLiteral(
":policy" ),
static_cast< int >( policy ) );
2806 if ( !authDbStartTransaction() )
2809 if ( !authDbQuery( &query ) )
2812 if ( !authDbCommit() )
2815 QgsDebugMsgLevel( QStringLiteral(
"Store certificate trust policy SUCCESS for id: %1" ).arg(
id ), 2 );
2821 QMutexLocker locker( mMutex.get() );
2822 if ( cert.isNull() )
2824 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2831 query.prepare( QStringLiteral(
"SELECT policy FROM %1 "
2832 "WHERE id = :id" ).arg( authDbTrustTable() ) );
2834 query.bindValue( QStringLiteral(
":id" ),
id );
2836 if ( !authDbQuery( &query ) )
2840 if ( query.isActive() && query.isSelect() )
2842 if ( query.first() )
2845 QgsDebugMsgLevel( QStringLiteral(
"Authentication cert trust policy retrieved for id: %1" ).arg(
id ), 2 );
2849 QgsDebugMsg( QStringLiteral(
"Select contains more than one cert trust policy for id: %1" ).arg(
id ) );
2859 QMutexLocker locker( mMutex.get() );
2860 if ( certs.empty() )
2862 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2866 for (
const auto &cert : certs )
2876 QMutexLocker locker( mMutex.get() );
2877 if ( cert.isNull() )
2879 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2887 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2889 query.bindValue( QStringLiteral(
":id" ),
id );
2891 if ( !authDbStartTransaction() )
2894 if ( !authDbQuery( &query ) )
2897 if ( !authDbCommit() )
2900 QgsDebugMsgLevel( QStringLiteral(
"REMOVED cert trust policy for id: %1" ).arg(
id ), 2 );
2907 QMutexLocker locker( mMutex.get() );
2908 if ( cert.isNull() )
2918 if ( trustedids.contains(
id ) )
2922 else if ( untrustedids.contains(
id ) )
2937 return storeAuthSetting( QStringLiteral(
"certdefaulttrust" ),
static_cast< int >( policy ) );
2942 QMutexLocker locker( mMutex.get() );
2943 QVariant policy(
authSetting( QStringLiteral(
"certdefaulttrust" ) ) );
2953 QMutexLocker locker( mMutex.get() );
2954 mCertTrustCache.clear();
2957 query.prepare( QStringLiteral(
"SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2959 if ( !authDbQuery( &query ) )
2961 QgsDebugMsg( QStringLiteral(
"Rebuild of cert trust policy cache FAILED" ) );
2965 if ( query.isActive() && query.isSelect() )
2967 while ( query.next() )
2969 QString
id = query.value( 0 ).toString();
2973 if ( mCertTrustCache.contains( policy ) )
2975 ids = mCertTrustCache.value( policy );
2977 mCertTrustCache.insert( policy, ids <<
id );
2981 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2987 QMutexLocker locker( mMutex.get() );
2991 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2993 QList<QSslCertificate> trustedcerts;
2994 for (
int i = 0; i < certpairs.size(); ++i )
2996 QSslCertificate cert( certpairs.at( i ).second );
2998 if ( trustedids.contains( certid ) )
3001 trustedcerts.append( cert );
3007 trustedcerts.append( cert );
3012 QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
3013 sslconfig.setCaCertificates( trustedcerts );
3014 QSslConfiguration::setDefaultConfiguration( sslconfig );
3016 return trustedcerts;
3021 QMutexLocker locker( mMutex.get() );
3022 if ( trustedCAs.isEmpty() )
3024 if ( mTrustedCaCertsCache.isEmpty() )
3031 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3033 QList<QSslCertificate> untrustedCAs;
3034 for (
int i = 0; i < certpairs.size(); ++i )
3036 QSslCertificate cert( certpairs.at( i ).second );
3037 if ( !trustedCAs.contains( cert ) )
3039 untrustedCAs.append( cert );
3042 return untrustedCAs;
3047 QMutexLocker locker( mMutex.get() );
3049 QgsDebugMsgLevel( QStringLiteral(
"Rebuilt trusted cert authorities cache" ), 2 );
3056 QMutexLocker locker( mMutex.get() );
3062 QMutexLocker locker( mMutex.get() );
3065 return passwordHelperWrite( mMasterPass );
3081 for (
const auto &authcfg : ids )
3099void QgsAuthManager::writeToConsole(
const QString &message,
3113 msg += QLatin1String(
"WARNING: " );
3116 msg += QLatin1String(
"ERROR: " );
3123 QTextStream out( stdout, QIODevice::WriteOnly );
3124 out << msg << Qt::endl;
3127void QgsAuthManager::tryToStartDbErase()
3129 ++mScheduledDbEraseRequestCount;
3131 int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3132 if ( mScheduledDbEraseRequestCount >= trycutoff )
3135 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3140 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest attempt (%1 of %2)" )
3141 .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3147 mScheduledDbEraseRequestEmitted =
true;
3152 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitted" ), 2 );
3155 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emit skipped" ), 2 );
3161 QMutexLocker locker( mMutex.get() );
3162 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3163 while ( iterator.hasNext() )
3166 QThread::disconnect( iterator.value() );
3173 qDeleteAll( mAuthMethods );
3176 if ( authConn.isValid() && authConn.isOpen() )
3179 delete mScheduledDbEraseTimer;
3180 mScheduledDbEraseTimer =
nullptr;
3181 QSqlDatabase::removeDatabase( QStringLiteral(
"authentication.configs" ) );
3185QString QgsAuthManager::passwordHelperName()
const
3187 return tr(
"Password Helper" );
3191void QgsAuthManager::passwordHelperLog(
const QString &msg )
const
3203 QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3205 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3206 job.setAutoDelete(
false );
3207 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3209 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3214 mPasswordHelperErrorCode = job.error();
3215 mPasswordHelperErrorMessage = tr(
"Delete password failed: %1." ).arg( job.errorString() );
3226 passwordHelperProcessError();
3230QString QgsAuthManager::passwordHelperRead()
3235 QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3237 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3238 job.setAutoDelete(
false );
3239 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3241 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3246 mPasswordHelperErrorCode = job.error();
3253 password = job.textData();
3255 if ( password.isEmpty() )
3257 mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3268 passwordHelperProcessError();
3272bool QgsAuthManager::passwordHelperWrite(
const QString &password )
3274 Q_ASSERT( !password.isEmpty() );
3277 QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3279 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3280 job.setAutoDelete(
false );
3281 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3282 job.setTextData( password );
3284 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3289 mPasswordHelperErrorCode = job.error();
3297 passwordHelperClearErrors();
3302 passwordHelperProcessError();
3310 return settings.
value( QStringLiteral(
"use_password_helper" ),
true, QgsSettings::Section::Auth ).toBool();
3316 settings.
setValue( QStringLiteral(
"use_password_helper" ), enabled, QgsSettings::Section::Auth );
3317 emit
messageOut( enabled ? tr(
"Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3319 tr(
"Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3327 return settings.
value( QStringLiteral(
"password_helper_logging" ),
false, QgsSettings::Section::Auth ).toBool();
3333 settings.
setValue( QStringLiteral(
"password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3336void QgsAuthManager::passwordHelperClearErrors()
3338 mPasswordHelperErrorCode = QKeychain::NoError;
3339 mPasswordHelperErrorMessage.clear();
3342void QgsAuthManager::passwordHelperProcessError()
3344 if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3345 mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3346 mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3347 mPasswordHelperErrorCode == QKeychain::NotImplemented )
3353 mPasswordHelperErrorMessage = tr(
"There was an error and integration with your %1 system has been disabled. "
3354 "You can re-enable it at any time through the \"Utilities\" menu "
3355 "in the Authentication pane of the options dialog. %2" )
3358 if ( mPasswordHelperErrorCode != QKeychain::NoError )
3364 passwordHelperClearErrors();
3368bool QgsAuthManager::masterPasswordInput()
3374 bool storedPasswordIsValid =
false;
3380 pass = passwordHelperRead();
3381 if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3387 storedPasswordIsValid =
true;
3403 if ( ok && !pass.isEmpty() && mMasterPass != pass )
3408 if ( passwordHelperWrite( pass ) )
3422bool QgsAuthManager::masterPasswordRowsInDb(
int *rows )
const
3428 query.prepare( QStringLiteral(
"SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3430 bool ok = authDbQuery( &query );
3431 if ( query.first() )
3433 *rows = query.value( 0 ).toInt();
3445 if ( !masterPasswordRowsInDb( &rows ) )
3447 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
3453 return ( rows == 1 );
3456bool QgsAuthManager::masterPasswordCheckAgainstDb(
const QString &compare )
const
3464 query.prepare( QStringLiteral(
"SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3465 if ( !authDbQuery( &query ) )
3468 if ( !query.first() )
3471 QString salt = query.value( 0 ).toString();
3472 QString hash = query.value( 1 ).toString();
3477bool QgsAuthManager::masterPasswordStoreInDb()
const
3482 QString salt, hash, civ;
3486 query.prepare( QStringLiteral(
"INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3488 query.bindValue( QStringLiteral(
":salt" ), salt );
3489 query.bindValue( QStringLiteral(
":hash" ), hash );
3490 query.bindValue( QStringLiteral(
":civ" ), civ );
3492 if ( !authDbStartTransaction() )
3495 if ( !authDbQuery( &query ) )
3498 if ( !authDbCommit() )
3504bool QgsAuthManager::masterPasswordClearDb()
3510 query.prepare( QStringLiteral(
"DELETE FROM %1" ).arg( authDbPassTable() ) );
3511 bool res = authDbTransactionQuery( &query );
3517const QString QgsAuthManager::masterPasswordCiv()
const
3523 query.prepare( QStringLiteral(
"SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3524 if ( !authDbQuery( &query ) )
3527 if ( !query.first() )
3530 return query.value( 0 ).toString();
3535 QStringList configids = QStringList();
3543 if ( !authDbQuery( &query ) )
3548 if ( query.isActive() )
3550 while ( query.next() )
3552 configids << query.value( 0 ).toString();
3558bool QgsAuthManager::verifyPasswordCanDecryptConfigs()
const
3569 if ( !authDbQuery( &query ) )
3572 if ( !query.isActive() || !query.isSelect() )
3574 QgsDebugMsg( QStringLiteral(
"Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3579 while ( query.next() )
3582 QString configstring(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3583 if ( configstring.isEmpty() )
3585 QgsDebugMsg( QStringLiteral(
"Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3586 .arg( query.value( 0 ).toString() ) );
3591 QgsDebugMsgLevel( QStringLiteral(
"Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3595bool QgsAuthManager::reencryptAllAuthenticationConfigs(
const QString &prevpass,
const QString &prevciv )
3602 for (
const auto &configid : ids )
3604 res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3609bool QgsAuthManager::reencryptAuthenticationConfig(
const QString &authcfg,
const QString &prevpass,
const QString &prevciv )
3618 query.prepare( QStringLiteral(
"SELECT config FROM %1 "
3621 query.bindValue( QStringLiteral(
":id" ), authcfg );
3623 if ( !authDbQuery( &query ) )
3626 if ( !query.isActive() || !query.isSelect() )
3628 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3632 if ( query.first() )
3638 QgsDebugMsg( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3645 query.prepare( QStringLiteral(
"UPDATE %1 "
3646 "SET config = :config "
3649 query.bindValue( QStringLiteral(
":id" ), authcfg );
3650 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3652 if ( !authDbStartTransaction() )
3655 if ( !authDbQuery( &query ) )
3658 if ( !authDbCommit() )
3661 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3666 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3671bool QgsAuthManager::reencryptAllAuthenticationSettings(
const QString &prevpass,
const QString &prevciv )
3674 Q_UNUSED( prevpass )
3687 QStringList encryptedsettings;
3688 encryptedsettings <<
"";
3690 for (
const auto & sett, std::as_const( encryptedsettings ) )
3697 QSqlQuery query( authDbConnection() );
3699 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
3700 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3702 query.bindValue(
":setting", sett );
3704 if ( !authDbQuery( &query ) )
3707 if ( !query.isActive() || !query.isSelect() )
3709 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3713 if ( query.first() )
3719 query.prepare( QStringLiteral(
"UPDATE %1 "
3720 "SET value = :value "
3721 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3723 query.bindValue(
":setting", sett );
3726 if ( !authDbStartTransaction() )
3729 if ( !authDbQuery( &query ) )
3732 if ( !authDbCommit() )
3735 QgsDebugMsg( QStringLiteral(
"Reencrypt SUCCESS for setting: %2" ).arg( sett ) );
3740 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3746 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting: %1" ).arg( sett ) );
3757bool QgsAuthManager::reencryptAllAuthenticationIdentities(
const QString &prevpass,
const QString &prevciv )
3764 for (
const auto &identid : ids )
3766 res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3771bool QgsAuthManager::reencryptAuthenticationIdentity(
3772 const QString &identid,
3773 const QString &prevpass,
3774 const QString &prevciv )
3783 query.prepare( QStringLiteral(
"SELECT key FROM %1 "
3784 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3786 query.bindValue( QStringLiteral(
":id" ), identid );
3788 if ( !authDbQuery( &query ) )
3791 if ( !query.isActive() || !query.isSelect() )
3793 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3797 if ( query.first() )
3803 QgsDebugMsg( QStringLiteral(
"Select contains more than one for identity id: %1" ).arg( identid ) );
3810 query.prepare( QStringLiteral(
"UPDATE %1 "
3812 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3814 query.bindValue( QStringLiteral(
":id" ), identid );
3815 query.bindValue( QStringLiteral(
":key" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
3817 if ( !authDbStartTransaction() )
3820 if ( !authDbQuery( &query ) )
3823 if ( !authDbCommit() )
3826 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
3831 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3836bool QgsAuthManager::authDbOpen()
const
3842 if ( !authdb.isOpen() )
3844 if ( !authdb.open() )
3846 QgsDebugMsg( QStringLiteral(
"Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3848 authdb.lastError().driverText(),
3849 authdb.lastError().databaseText() ) );
3857bool QgsAuthManager::authDbQuery( QSqlQuery *query )
const
3862 query->setForwardOnly(
true );
3863 if ( !query->exec() )
3865 const char *err = QT_TR_NOOP(
"Auth db query exec() FAILED" );
3871 if ( query->lastError().isValid() )
3873 QgsDebugMsg( QStringLiteral(
"Auth db query FAILED: %1\nError: %2" )
3874 .arg( query->executedQuery(),
3875 query->lastError().text() ) );
3883bool QgsAuthManager::authDbStartTransaction()
const
3890 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3899bool QgsAuthManager::authDbCommit()
const
3906 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3916bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query )
const
3923 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3929 bool ok = authDbQuery( query );
3933 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3945 for (
const auto &cert : certs )
3948 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.
bool isNull() const
Whether configuration is null (missing components)
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
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.
const QList< QSslCertificate > trustedCaCertsCache()
trustedCaCertsCache cache of trusted certificate authorities, ready for network connections
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.
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.
const QgsAuthMethodMetadata * authMethodMetadata(const QString &authMethodKey)
Gets authentication method metadata via its key.
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.
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.
const QgsAuthMethodMetadata * authMethodMetadata(const QString &authMethodKey) const
Returns metadata of the auth method or nullptr if not found.
static QgsAuthMethodRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
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.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
QHash< QString, QgsAuthMethodConfig > QgsAuthMethodConfigsMap
QHash< QString, QgsAuthMethod * > QgsAuthMethodsMap
#define QgsDebugMsgLevel(str, level)