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 QgsDebugError(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
320 QgsDebugError(
"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 QgsDebugError( 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 QgsDebugError( QStringLiteral(
"Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
773 mMasterPass = prevpass;
775 QgsDebugError( 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 QgsDebugError( 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 QgsDebugError( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1033 return mAuthMethods.value( authMethodKey );
1038 if ( !mAuthMethods.contains( authMethodKey ) )
1040 QgsDebugError( 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 QgsDebugMsgLevel( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ), 2 );
1141 QgsDebugMsgLevel( QStringLiteral(
"type: %1" ).arg( config.method() ), 2 );
1142 QgsDebugMsgLevel( QStringLiteral(
"version: %1" ).arg( config.version() ), 2 );
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 QgsDebugMsgLevel( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ), 2 );
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 QgsDebugError( 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 QgsDebugError( 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" );
1599 const QString &dataprovider )
1609 QgsDebugError( QStringLiteral(
"Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1624 const QString &dataprovider )
1634 QgsDebugError( QStringLiteral(
"Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1650 const QString &dataprovider )
1660 QgsDebugError( QStringLiteral(
"Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1685 QgsDebugError( QStringLiteral(
"Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1694 QgsDebugMsgLevel( QStringLiteral(
"Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1703 QMutexLocker locker( mMutex.get() );
1704 if ( key.isEmpty() )
1707 QString storeval( value.toString() );
1723 query.prepare( QStringLiteral(
"INSERT INTO %1 (setting, value) "
1724 "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1726 query.bindValue( QStringLiteral(
":setting" ), key );
1727 query.bindValue( QStringLiteral(
":value" ), storeval );
1729 if ( !authDbStartTransaction() )
1732 if ( !authDbQuery( &query ) )
1735 if ( !authDbCommit() )
1738 QgsDebugMsgLevel( QStringLiteral(
"Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1744 QMutexLocker locker( mMutex.get() );
1745 if ( key.isEmpty() )
1751 QVariant value = defaultValue;
1753 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1754 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1756 query.bindValue( QStringLiteral(
":setting" ), key );
1758 if ( !authDbQuery( &query ) )
1761 if ( query.isActive() && query.isSelect() )
1763 if ( query.first() )
1767 value = QVariant(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1771 value = query.value( 0 );
1773 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1777 QgsDebugError( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1787 QMutexLocker locker( mMutex.get() );
1788 if ( key.isEmpty() )
1792 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1793 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1795 query.bindValue( QStringLiteral(
":setting" ), key );
1797 if ( !authDbQuery( &query ) )
1801 if ( query.isActive() && query.isSelect() )
1803 if ( query.first() )
1805 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting exists for key: %1" ).arg( key ), 2 );
1810 QgsDebugError( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1820 QMutexLocker locker( mMutex.get() );
1821 if ( key.isEmpty() )
1826 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1828 query.bindValue( QStringLiteral(
":setting" ), key );
1830 if ( !authDbStartTransaction() )
1833 if ( !authDbQuery( &query ) )
1836 if ( !authDbCommit() )
1839 QgsDebugMsgLevel( QStringLiteral(
"REMOVED setting for key: %1" ).arg( key ), 2 );
1853 QMutexLocker locker( mMutex.get() );
1859 mCustomConfigByHostCache.clear();
1860 mHasCheckedIfCustomConfigByHostExists =
false;
1863 QgsDebugError( QStringLiteral(
"Init of SSL caches FAILED" ) );
1869 QMutexLocker locker( mMutex.get() );
1870 if ( cert.isNull() )
1872 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
1877 QgsDebugError( QStringLiteral(
"Passed private key is null" ) );
1887 QString certpem( cert.toPem() );
1891 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, key, cert) "
1892 "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1894 query.bindValue( QStringLiteral(
":id" ),
id );
1895 query.bindValue( QStringLiteral(
":key" ), keypem );
1896 query.bindValue( QStringLiteral(
":cert" ), certpem );
1898 if ( !authDbStartTransaction() )
1901 if ( !authDbQuery( &query ) )
1904 if ( !authDbCommit() )
1907 QgsDebugMsgLevel( QStringLiteral(
"Store certificate identity SUCCESS for id: %1" ).arg(
id ), 2 );
1913 QMutexLocker locker( mMutex.get() );
1914 QSslCertificate emptycert;
1915 QSslCertificate cert;
1920 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
1921 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1923 query.bindValue( QStringLiteral(
":id" ),
id );
1925 if ( !authDbQuery( &query ) )
1928 if ( query.isActive() && query.isSelect() )
1930 if ( query.first() )
1932 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1933 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity retrieved for id: %1" ).arg(
id ), 2 );
1937 QgsDebugError( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1947 QMutexLocker locker( mMutex.get() );
1948 QPair<QSslCertificate, QSslKey> bundle;
1956 query.prepare( QStringLiteral(
"SELECT key, cert FROM %1 "
1957 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1959 query.bindValue( QStringLiteral(
":id" ),
id );
1961 if ( !authDbQuery( &query ) )
1964 if ( query.isActive() && query.isSelect() )
1966 QSslCertificate cert;
1968 if ( query.first() )
1970 key = QSslKey(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1971 QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1974 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create private key" );
1979 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1980 if ( cert.isNull() )
1982 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create certificate" );
1987 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity bundle retrieved for id: %1" ).arg(
id ), 2 );
1991 QgsDebugError( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1995 bundle = qMakePair( cert, key );
2002 QMutexLocker locker( mMutex.get() );
2006 return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
2008 return QStringList();
2013 QMutexLocker locker( mMutex.get() );
2014 QList<QSslCertificate> certs;
2017 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2019 if ( !authDbQuery( &query ) )
2022 if ( query.isActive() && query.isSelect() )
2024 while ( query.next() )
2026 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2035 QMutexLocker locker( mMutex.get() );
2036 QStringList identityids = QStringList();
2042 query.prepare( QStringLiteral(
"SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2044 if ( !authDbQuery( &query ) )
2049 if ( query.isActive() )
2051 while ( query.next() )
2053 identityids << query.value( 0 ).toString();
2061 QMutexLocker locker( mMutex.get() );
2066 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2067 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2069 query.bindValue( QStringLiteral(
":id" ),
id );
2071 if ( !authDbQuery( &query ) )
2075 if ( query.isActive() && query.isSelect() )
2077 if ( query.first() )
2079 QgsDebugMsgLevel( QStringLiteral(
"Certificate bundle exists for id: %1" ).arg(
id ), 2 );
2084 QgsDebugError( QStringLiteral(
"Select contains more than one certificate bundle for id: %1" ).arg(
id ) );
2094 QMutexLocker locker( mMutex.get() );
2097 QgsDebugError( QStringLiteral(
"Passed bundle ID is empty" ) );
2103 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2105 query.bindValue( QStringLiteral(
":id" ),
id );
2107 if ( !authDbStartTransaction() )
2110 if ( !authDbQuery( &query ) )
2113 if ( !authDbCommit() )
2116 QgsDebugMsgLevel( QStringLiteral(
"REMOVED certificate identity for id: %1" ).arg(
id ), 2 );
2122 QMutexLocker locker( mMutex.get() );
2134 QString certpem( cert.toPem() );
2137 query.prepare( QStringLiteral(
"INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2140 query.bindValue( QStringLiteral(
":id" ),
id );
2141 query.bindValue( QStringLiteral(
":host" ), config.
sslHostPort().trimmed() );
2142 query.bindValue( QStringLiteral(
":cert" ), certpem );
2143 query.bindValue( QStringLiteral(
":config" ), config.
configString() );
2145 if ( !authDbStartTransaction() )
2148 if ( !authDbQuery( &query ) )
2151 if ( !authDbCommit() )
2154 QgsDebugMsgLevel( QStringLiteral(
"Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2158 mHasCheckedIfCustomConfigByHostExists =
false;
2159 mCustomConfigByHostCache.clear();
2166 QMutexLocker locker( mMutex.get() );
2169 if (
id.isEmpty() || hostport.isEmpty() )
2171 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2176 query.prepare( QStringLiteral(
"SELECT id, host, cert, config FROM %1 "
2179 query.bindValue( QStringLiteral(
":id" ),
id );
2180 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2182 if ( !authDbQuery( &query ) )
2185 if ( query.isActive() && query.isSelect() )
2187 if ( query.first() )
2189 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2192 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2196 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2197 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2209 if ( hostport.isEmpty() )
2214 QMutexLocker locker( mMutex.get() );
2215 if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2217 if ( mCustomConfigByHostCache.contains( hostport ) )
2218 return mCustomConfigByHostCache.value( hostport );
2223 if ( !mHasCheckedIfCustomConfigByHostExists )
2225 mHasCheckedIfCustomConfigByHostExists =
true;
2227 if ( !authDbQuery( &query ) )
2229 mHasCustomConfigByHost =
false;
2232 if ( query.isActive() && query.isSelect() && query.first() )
2234 mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2235 if ( !mHasCustomConfigByHost )
2240 mHasCustomConfigByHost =
false;
2245 query.prepare( QString(
"SELECT id, host, cert, config FROM %1 "
2248 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2250 if ( !authDbQuery( &query ) )
2252 mCustomConfigByHostCache.insert( hostport, config );
2256 if ( query.isActive() && query.isSelect() )
2258 if ( query.first() )
2260 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2263 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2267 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2268 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2271 mCustomConfigByHostCache.insert( hostport, emptyconfig );
2276 mCustomConfigByHostCache.insert( hostport, config );
2282 QMutexLocker locker( mMutex.get() );
2283 QList<QgsAuthConfigSslServer> configs;
2288 if ( !authDbQuery( &query ) )
2291 if ( query.isActive() && query.isSelect() )
2293 while ( query.next() )
2296 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2300 configs.append( config );
2309 QMutexLocker locker( mMutex.get() );
2310 if (
id.isEmpty() || hostport.isEmpty() )
2312 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2317 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2320 query.bindValue( QStringLiteral(
":id" ),
id );
2321 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2323 if ( !authDbQuery( &query ) )
2327 if ( query.isActive() && query.isSelect() )
2329 if ( query.first() )
2331 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2336 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2337 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2347 QMutexLocker locker( mMutex.get() );
2348 if (
id.isEmpty() || hostport.isEmpty() )
2350 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2354 mHasCheckedIfCustomConfigByHostExists =
false;
2355 mCustomConfigByHostCache.clear();
2359 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id AND host = :host" ).arg(
authDatabaseServersTable() ) );
2361 query.bindValue( QStringLiteral(
":id" ),
id );
2362 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2364 if ( !authDbStartTransaction() )
2367 if ( !authDbQuery( &query ) )
2370 if ( !authDbCommit() )
2373 QString shahostport( QStringLiteral(
"%1:%2" ).arg(
id, hostport ) );
2374 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2376 mIgnoredSslErrorsCache.remove( shahostport );
2379 QgsDebugMsgLevel( QStringLiteral(
"REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2386 QMutexLocker locker( mMutex.get() );
2387 if ( !mIgnoredSslErrorsCache.isEmpty() )
2389 QgsDebugMsgLevel( QStringLiteral(
"Ignored SSL errors cache items:" ), 1 );
2390 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2391 while ( i != mIgnoredSslErrorsCache.constEnd() )
2394 for (
auto err : i.value() )
2398 QgsDebugMsgLevel( QStringLiteral(
"%1 = %2" ).arg( i.key(), errs.join(
", " ) ), 1 );
2410 QMutexLocker locker( mMutex.get() );
2417 QString shahostport( QStringLiteral(
"%1:%2" )
2420 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2422 mIgnoredSslErrorsCache.remove( shahostport );
2425 if ( !errenums.isEmpty() )
2427 mIgnoredSslErrorsCache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2428 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2433 QgsDebugMsgLevel( QStringLiteral(
"No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2439 QMutexLocker locker( mMutex.get() );
2440 const thread_local QRegularExpression rx( QRegularExpression::anchoredPattern(
"\\S+:\\S+:\\d+" ) );
2441 if ( !rx.match( shahostport ).hasMatch() )
2443 QgsDebugError(
"Passed shahostport does not match \\S+:\\S+:\\d+, "
2444 "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2448 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2450 mIgnoredSslErrorsCache.remove( shahostport );
2453 if ( errors.isEmpty() )
2455 QgsDebugError( QStringLiteral(
"Passed errors list empty" ) );
2459 QSet<QSslError::SslError> errs;
2460 for (
const auto &error : errors )
2462 if ( error.error() == QSslError::NoError )
2465 errs.insert( error.error() );
2468 if ( errs.isEmpty() )
2470 QgsDebugError( QStringLiteral(
"Passed errors list does not contain errors" ) );
2474 mIgnoredSslErrorsCache.insert( shahostport, errs );
2476 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2483 QMutexLocker locker( mMutex.get() );
2484 QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2485 QHash<QString, QSet<QSslError::SslError> > nextcache;
2490 if ( !authDbQuery( &query ) )
2492 QgsDebugError( QStringLiteral(
"Rebuild of ignored SSL errors cache FAILED" ) );
2496 if ( query.isActive() && query.isSelect() )
2498 while ( query.next() )
2500 QString shahostport( QStringLiteral(
"%1:%2" )
2501 .arg( query.value( 0 ).toString().trimmed(),
2502 query.value( 1 ).toString().trimmed() ) );
2506 if ( !errenums.isEmpty() )
2508 nextcache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2510 if ( prevcache.contains( shahostport ) )
2512 prevcache.remove( shahostport );
2517 if ( !prevcache.isEmpty() )
2520 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2521 while ( i != prevcache.constEnd() )
2523 nextcache.insert( i.key(), i.value() );
2528 if ( nextcache != mIgnoredSslErrorsCache )
2530 mIgnoredSslErrorsCache.clear();
2531 mIgnoredSslErrorsCache = nextcache;
2532 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2537 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2545 QMutexLocker locker( mMutex.get() );
2546 if ( certs.isEmpty() )
2548 QgsDebugError( QStringLiteral(
"Passed certificate list has no certs" ) );
2552 for (
const auto &cert : certs )
2562 QMutexLocker locker( mMutex.get() );
2565 if ( cert.isNull() )
2567 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2574 QString pem( cert.toPem() );
2577 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, cert) "
2578 "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2580 query.bindValue( QStringLiteral(
":id" ),
id );
2581 query.bindValue( QStringLiteral(
":cert" ), pem );
2583 if ( !authDbStartTransaction() )
2586 if ( !authDbQuery( &query ) )
2589 if ( !authDbCommit() )
2592 QgsDebugMsgLevel( QStringLiteral(
"Store certificate authority SUCCESS for id: %1" ).arg(
id ), 2 );
2598 QMutexLocker locker( mMutex.get() );
2599 QSslCertificate emptycert;
2600 QSslCertificate cert;
2605 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2606 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2608 query.bindValue( QStringLiteral(
":id" ),
id );
2610 if ( !authDbQuery( &query ) )
2613 if ( query.isActive() && query.isSelect() )
2615 if ( query.first() )
2617 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2618 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority retrieved for id: %1" ).arg(
id ), 2 );
2622 QgsDebugError( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2632 QMutexLocker locker( mMutex.get() );
2633 if ( cert.isNull() )
2635 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2642 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
2643 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2645 query.bindValue( QStringLiteral(
":id" ),
id );
2647 if ( !authDbQuery( &query ) )
2651 if ( query.isActive() && query.isSelect() )
2653 if ( query.first() )
2655 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority exists for id: %1" ).arg(
id ), 2 );
2660 QgsDebugError( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2670 QMutexLocker locker( mMutex.get() );
2671 if ( cert.isNull() )
2673 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2681 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2683 query.bindValue( QStringLiteral(
":id" ),
id );
2685 if ( !authDbStartTransaction() )
2688 if ( !authDbQuery( &query ) )
2691 if ( !authDbCommit() )
2694 QgsDebugMsgLevel( QStringLiteral(
"REMOVED authority for id: %1" ).arg(
id ), 2 );
2700 return QSslConfiguration::systemCaCertificates();
2705 QMutexLocker locker( mMutex.get() );
2706 QList<QSslCertificate> certs;
2707 QList<QSslCertificate> filecerts;
2716 QString cafile( cafileval.toString() );
2717 if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2722 for (
const auto &cert : std::as_const( filecerts ) )
2724 if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2726 || cert.expiryDate() <= QDateTime::currentDateTime()
2727 || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2742 QMutexLocker locker( mMutex.get() );
2743 QList<QSslCertificate> certs;
2746 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2748 if ( !authDbQuery( &query ) )
2751 if ( query.isActive() && query.isSelect() )
2753 while ( query.next() )
2755 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2764 QMutexLocker locker( mMutex.get() );
2770 QMutexLocker locker( mMutex.get() );
2771 mCaCertsCache.clear();
2777 bool res = !mCaCertsCache.isEmpty();
2779 QgsDebugError( QStringLiteral(
"Rebuild of CA certs cache FAILED" ) );
2785 QMutexLocker locker( mMutex.get() );
2786 if ( cert.isNull() )
2788 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2798 QgsDebugMsgLevel( QStringLiteral(
"Passed policy was default, all cert records in database were removed for id: %1" ).arg(
id ), 2 );
2803 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, policy) "
2804 "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2806 query.bindValue( QStringLiteral(
":id" ),
id );
2807 query.bindValue( QStringLiteral(
":policy" ),
static_cast< int >( policy ) );
2809 if ( !authDbStartTransaction() )
2812 if ( !authDbQuery( &query ) )
2815 if ( !authDbCommit() )
2818 QgsDebugMsgLevel( QStringLiteral(
"Store certificate trust policy SUCCESS for id: %1" ).arg(
id ), 2 );
2824 QMutexLocker locker( mMutex.get() );
2825 if ( cert.isNull() )
2827 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2834 query.prepare( QStringLiteral(
"SELECT policy FROM %1 "
2835 "WHERE id = :id" ).arg( authDbTrustTable() ) );
2837 query.bindValue( QStringLiteral(
":id" ),
id );
2839 if ( !authDbQuery( &query ) )
2843 if ( query.isActive() && query.isSelect() )
2845 if ( query.first() )
2848 QgsDebugMsgLevel( QStringLiteral(
"Authentication cert trust policy retrieved for id: %1" ).arg(
id ), 2 );
2852 QgsDebugError( QStringLiteral(
"Select contains more than one cert trust policy for id: %1" ).arg(
id ) );
2862 QMutexLocker locker( mMutex.get() );
2863 if ( certs.empty() )
2865 QgsDebugError( QStringLiteral(
"Passed certificate list has no certs" ) );
2869 for (
const auto &cert : certs )
2879 QMutexLocker locker( mMutex.get() );
2880 if ( cert.isNull() )
2882 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2890 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2892 query.bindValue( QStringLiteral(
":id" ),
id );
2894 if ( !authDbStartTransaction() )
2897 if ( !authDbQuery( &query ) )
2900 if ( !authDbCommit() )
2903 QgsDebugMsgLevel( QStringLiteral(
"REMOVED cert trust policy for id: %1" ).arg(
id ), 2 );
2910 QMutexLocker locker( mMutex.get() );
2911 if ( cert.isNull() )
2921 if ( trustedids.contains(
id ) )
2925 else if ( untrustedids.contains(
id ) )
2940 return storeAuthSetting( QStringLiteral(
"certdefaulttrust" ),
static_cast< int >( policy ) );
2945 QMutexLocker locker( mMutex.get() );
2946 QVariant policy(
authSetting( QStringLiteral(
"certdefaulttrust" ) ) );
2956 QMutexLocker locker( mMutex.get() );
2957 mCertTrustCache.clear();
2960 query.prepare( QStringLiteral(
"SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2962 if ( !authDbQuery( &query ) )
2964 QgsDebugError( QStringLiteral(
"Rebuild of cert trust policy cache FAILED" ) );
2968 if ( query.isActive() && query.isSelect() )
2970 while ( query.next() )
2972 QString
id = query.value( 0 ).toString();
2976 if ( mCertTrustCache.contains( policy ) )
2978 ids = mCertTrustCache.value( policy );
2980 mCertTrustCache.insert( policy, ids <<
id );
2984 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2990 QMutexLocker locker( mMutex.get() );
2994 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2996 QList<QSslCertificate> trustedcerts;
2997 for (
int i = 0; i < certpairs.size(); ++i )
2999 QSslCertificate cert( certpairs.at( i ).second );
3001 if ( trustedids.contains( certid ) )
3004 trustedcerts.append( cert );
3010 trustedcerts.append( cert );
3015 QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
3016 sslconfig.setCaCertificates( trustedcerts );
3017 QSslConfiguration::setDefaultConfiguration( sslconfig );
3019 return trustedcerts;
3024 QMutexLocker locker( mMutex.get() );
3025 if ( trustedCAs.isEmpty() )
3027 if ( mTrustedCaCertsCache.isEmpty() )
3034 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3036 QList<QSslCertificate> untrustedCAs;
3037 for (
int i = 0; i < certpairs.size(); ++i )
3039 QSslCertificate cert( certpairs.at( i ).second );
3040 if ( !trustedCAs.contains( cert ) )
3042 untrustedCAs.append( cert );
3045 return untrustedCAs;
3050 QMutexLocker locker( mMutex.get() );
3052 QgsDebugMsgLevel( QStringLiteral(
"Rebuilt trusted cert authorities cache" ), 2 );
3059 QMutexLocker locker( mMutex.get() );
3065 QMutexLocker locker( mMutex.get() );
3068 return passwordHelperWrite( mMasterPass );
3084 for (
const auto &authcfg : ids )
3102void QgsAuthManager::writeToConsole(
const QString &message,
3116 msg += QLatin1String(
"WARNING: " );
3119 msg += QLatin1String(
"ERROR: " );
3126 QTextStream out( stdout, QIODevice::WriteOnly );
3127 out << msg << Qt::endl;
3130void QgsAuthManager::tryToStartDbErase()
3132 ++mScheduledDbEraseRequestCount;
3134 int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3135 if ( mScheduledDbEraseRequestCount >= trycutoff )
3138 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3143 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest attempt (%1 of %2)" )
3144 .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3150 mScheduledDbEraseRequestEmitted =
true;
3155 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitted" ), 2 );
3158 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emit skipped" ), 2 );
3164 QMutexLocker locker( mMutex.get() );
3165 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3166 while ( iterator.hasNext() )
3169 QThread::disconnect( iterator.value() );
3176 qDeleteAll( mAuthMethods );
3179 if ( authConn.isValid() && authConn.isOpen() )
3182 delete mScheduledDbEraseTimer;
3183 mScheduledDbEraseTimer =
nullptr;
3184 QSqlDatabase::removeDatabase( QStringLiteral(
"authentication.configs" ) );
3188QString QgsAuthManager::passwordHelperName()
const
3190 return tr(
"Password Helper" );
3194void QgsAuthManager::passwordHelperLog(
const QString &msg )
const
3206 QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3208 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3209 job.setAutoDelete(
false );
3210 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3212 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3217 mPasswordHelperErrorCode = job.error();
3218 mPasswordHelperErrorMessage = tr(
"Delete password failed: %1." ).arg( job.errorString() );
3229 passwordHelperProcessError();
3233QString QgsAuthManager::passwordHelperRead()
3238 QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3240 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3241 job.setAutoDelete(
false );
3242 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3244 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3249 mPasswordHelperErrorCode = job.error();
3256 password = job.textData();
3258 if ( password.isEmpty() )
3260 mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3271 passwordHelperProcessError();
3275bool QgsAuthManager::passwordHelperWrite(
const QString &password )
3277 Q_ASSERT( !password.isEmpty() );
3280 QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3282 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3283 job.setAutoDelete(
false );
3284 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3285 job.setTextData( password );
3287 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3292 mPasswordHelperErrorCode = job.error();
3300 passwordHelperClearErrors();
3305 passwordHelperProcessError();
3313 return settings.
value( QStringLiteral(
"use_password_helper" ),
true, QgsSettings::Section::Auth ).toBool();
3319 settings.
setValue( QStringLiteral(
"use_password_helper" ), enabled, QgsSettings::Section::Auth );
3320 emit
messageOut( enabled ? tr(
"Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3322 tr(
"Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3330 return settings.
value( QStringLiteral(
"password_helper_logging" ),
false, QgsSettings::Section::Auth ).toBool();
3336 settings.
setValue( QStringLiteral(
"password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3339void QgsAuthManager::passwordHelperClearErrors()
3341 mPasswordHelperErrorCode = QKeychain::NoError;
3342 mPasswordHelperErrorMessage.clear();
3345void QgsAuthManager::passwordHelperProcessError()
3347 if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3348 mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3349 mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3350 mPasswordHelperErrorCode == QKeychain::NotImplemented )
3356 mPasswordHelperErrorMessage = tr(
"There was an error and integration with your %1 system has been disabled. "
3357 "You can re-enable it at any time through the \"Utilities\" menu "
3358 "in the Authentication pane of the options dialog. %2" )
3361 if ( mPasswordHelperErrorCode != QKeychain::NoError )
3367 passwordHelperClearErrors();
3371bool QgsAuthManager::masterPasswordInput()
3377 bool storedPasswordIsValid =
false;
3383 pass = passwordHelperRead();
3384 if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3390 storedPasswordIsValid =
true;
3406 if ( ok && !pass.isEmpty() && mMasterPass != pass )
3411 if ( passwordHelperWrite( pass ) )
3425bool QgsAuthManager::masterPasswordRowsInDb(
int *rows )
const
3431 query.prepare( QStringLiteral(
"SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3433 bool ok = authDbQuery( &query );
3434 if ( query.first() )
3436 *rows = query.value( 0 ).toInt();
3448 if ( !masterPasswordRowsInDb( &rows ) )
3450 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
3456 return ( rows == 1 );
3459bool QgsAuthManager::masterPasswordCheckAgainstDb(
const QString &compare )
const
3467 query.prepare( QStringLiteral(
"SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3468 if ( !authDbQuery( &query ) )
3471 if ( !query.first() )
3474 QString salt = query.value( 0 ).toString();
3475 QString hash = query.value( 1 ).toString();
3480bool QgsAuthManager::masterPasswordStoreInDb()
const
3485 QString salt, hash, civ;
3489 query.prepare( QStringLiteral(
"INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3491 query.bindValue( QStringLiteral(
":salt" ), salt );
3492 query.bindValue( QStringLiteral(
":hash" ), hash );
3493 query.bindValue( QStringLiteral(
":civ" ), civ );
3495 if ( !authDbStartTransaction() )
3498 if ( !authDbQuery( &query ) )
3501 if ( !authDbCommit() )
3507bool QgsAuthManager::masterPasswordClearDb()
3513 query.prepare( QStringLiteral(
"DELETE FROM %1" ).arg( authDbPassTable() ) );
3514 bool res = authDbTransactionQuery( &query );
3520const QString QgsAuthManager::masterPasswordCiv()
const
3526 query.prepare( QStringLiteral(
"SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3527 if ( !authDbQuery( &query ) )
3530 if ( !query.first() )
3533 return query.value( 0 ).toString();
3538 QStringList configids = QStringList();
3546 if ( !authDbQuery( &query ) )
3551 if ( query.isActive() )
3553 while ( query.next() )
3555 configids << query.value( 0 ).toString();
3561bool QgsAuthManager::verifyPasswordCanDecryptConfigs()
const
3572 if ( !authDbQuery( &query ) )
3575 if ( !query.isActive() || !query.isSelect() )
3577 QgsDebugError( QStringLiteral(
"Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3585 while ( query.next() )
3590 QString configstring(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3591 if ( configstring.isEmpty() )
3593 QgsDebugError( QStringLiteral(
"Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3594 .arg( query.value( 0 ).toString() ) );
3599 QgsDebugMsgLevel( QStringLiteral(
"Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3603bool QgsAuthManager::reencryptAllAuthenticationConfigs(
const QString &prevpass,
const QString &prevciv )
3610 for (
const auto &configid : ids )
3612 res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3617bool QgsAuthManager::reencryptAuthenticationConfig(
const QString &authcfg,
const QString &prevpass,
const QString &prevciv )
3626 query.prepare( QStringLiteral(
"SELECT config FROM %1 "
3629 query.bindValue( QStringLiteral(
":id" ), authcfg );
3631 if ( !authDbQuery( &query ) )
3634 if ( !query.isActive() || !query.isSelect() )
3636 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3640 if ( query.first() )
3646 QgsDebugError( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3653 query.prepare( QStringLiteral(
"UPDATE %1 "
3654 "SET config = :config "
3657 query.bindValue( QStringLiteral(
":id" ), authcfg );
3658 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3660 if ( !authDbStartTransaction() )
3663 if ( !authDbQuery( &query ) )
3666 if ( !authDbCommit() )
3669 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3674 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3679bool QgsAuthManager::reencryptAllAuthenticationSettings(
const QString &prevpass,
const QString &prevciv )
3682 Q_UNUSED( prevpass )
3695 QStringList encryptedsettings;
3696 encryptedsettings <<
"";
3698 for (
const auto & sett, std::as_const( encryptedsettings ) )
3705 QSqlQuery query( authDbConnection() );
3707 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
3708 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3710 query.bindValue(
":setting", sett );
3712 if ( !authDbQuery( &query ) )
3715 if ( !query.isActive() || !query.isSelect() )
3717 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3721 if ( query.first() )
3727 query.prepare( QStringLiteral(
"UPDATE %1 "
3728 "SET value = :value "
3729 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3731 query.bindValue(
":setting", sett );
3734 if ( !authDbStartTransaction() )
3737 if ( !authDbQuery( &query ) )
3740 if ( !authDbCommit() )
3743 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for setting: %2" ).arg( sett ), 2 );
3748 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3754 QgsDebugError( QStringLiteral(
"Select contains more than one for setting: %1" ).arg( sett ) );
3765bool QgsAuthManager::reencryptAllAuthenticationIdentities(
const QString &prevpass,
const QString &prevciv )
3772 for (
const auto &identid : ids )
3774 res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3779bool QgsAuthManager::reencryptAuthenticationIdentity(
3780 const QString &identid,
3781 const QString &prevpass,
3782 const QString &prevciv )
3791 query.prepare( QStringLiteral(
"SELECT key FROM %1 "
3792 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3794 query.bindValue( QStringLiteral(
":id" ), identid );
3796 if ( !authDbQuery( &query ) )
3799 if ( !query.isActive() || !query.isSelect() )
3801 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3805 if ( query.first() )
3811 QgsDebugError( QStringLiteral(
"Select contains more than one for identity id: %1" ).arg( identid ) );
3818 query.prepare( QStringLiteral(
"UPDATE %1 "
3820 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3822 query.bindValue( QStringLiteral(
":id" ), identid );
3823 query.bindValue( QStringLiteral(
":key" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
3825 if ( !authDbStartTransaction() )
3828 if ( !authDbQuery( &query ) )
3831 if ( !authDbCommit() )
3834 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
3839 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3844bool QgsAuthManager::authDbOpen()
const
3850 if ( !authdb.isOpen() )
3852 if ( !authdb.open() )
3854 QgsDebugError( QStringLiteral(
"Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3856 authdb.lastError().driverText(),
3857 authdb.lastError().databaseText() ) );
3865bool QgsAuthManager::authDbQuery( QSqlQuery *query )
const
3870 query->setForwardOnly(
true );
3871 if ( !query->exec() )
3873 const char *err = QT_TR_NOOP(
"Auth db query exec() FAILED" );
3879 if ( query->lastError().isValid() )
3881 QgsDebugError( QStringLiteral(
"Auth db query FAILED: %1\nError: %2" )
3882 .arg( query->executedQuery(),
3883 query->lastError().text() ) );
3891bool QgsAuthManager::authDbStartTransaction()
const
3898 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3907bool QgsAuthManager::authDbCommit()
const
3914 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3924bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query )
const
3931 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3937 bool ok = authDbQuery( query );
3941 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3953 for (
const auto &cert : certs )
3956 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)
#define QgsDebugError(str)