24#include <QMutexLocker>
27#include <QSqlDatabase>
36#include <QDomDocument>
37#include <QRegularExpression>
38#include <QCoreApplication>
39#include <QRandomGenerator>
44#include <QSslConfiguration>
61const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral(
"auth_configs" );
62const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral(
"auth_pass" );
63const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral(
"auth_settings" );
64const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral(
"auth_identities" );
65const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral(
"auth_servers" );
66const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral(
"auth_authorities" );
67const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral(
"auth_trust" );
69const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral(
"authcfg=([a-z]|[A-Z]|[0-9]){7}" );
72const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME_BASE(
"QGIS-Master-Password" );
73const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME(
"QGIS" );
79#elif defined(Q_OS_WIN)
81#elif defined(Q_OS_LINUX)
90 QMutexLocker locker( &sMutex );
101 mMutex = std::make_unique<QRecursiveMutex>();
102 mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
104 this, &QgsAuthManager::writeToConsole );
117 QMutexLocker locker( mMutex.get() );
122 const QString connectionName = QStringLiteral(
"authentication.configs:0x%1" ).arg(
reinterpret_cast<quintptr
>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) );
123 QgsDebugMsgLevel( QStringLiteral(
"Using auth db connection name: %1 " ).arg( connectionName ), 2 );
124 if ( !QSqlDatabase::contains( connectionName ) )
126 QgsDebugMsgLevel( QStringLiteral(
"No existing connection, creating a new one" ), 2 );
127 authdb = QSqlDatabase::addDatabase( QStringLiteral(
"QSQLITE" ), connectionName );
130 if ( QThread::currentThread() != QCoreApplication::instance()->thread() )
132 QgsDebugMsgLevel( QStringLiteral(
"Scheduled auth db remove on thread close" ), 2 );
142 QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName,
this ]
144 QMutexLocker locker( mMutex.get() );
145 QSqlDatabase::removeDatabase( connectionName );
146 mConnectedThreads.remove( QThread::currentThread() );
147 }, Qt::DirectConnection );
149 mConnectedThreads.insert( QThread::currentThread(), connection );
155 authdb = QSqlDatabase::database( connectionName );
159 if ( !authdb.isOpen() )
161 if ( !authdb.open() )
163 QString err = tr(
"Opening of authentication db FAILED : %1" ).arg( authdb.lastError().text() );
174 return initPrivate( pluginPath, authDatabasePath );
177bool QgsAuthManager::ensureInitialized()
const
179 static QRecursiveMutex sInitializationMutex;
180 static bool sInitialized =
false;
182 sInitializationMutex.lock();
185 sInitializationMutex.unlock();
186 return mLazyInitResult;
189 mLazyInitResult =
const_cast< QgsAuthManager *
>( this )->initPrivate( mPluginPath, mAuthDatabasePath );
191 sInitializationMutex.unlock();
193 return mLazyInitResult;
196bool QgsAuthManager::initPrivate(
const QString &pluginPath,
const QString &authDatabasePath )
205 mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
208 QCA::scanForPlugins();
210 QgsDebugMsgLevel( QStringLiteral(
"QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
211 QStringList capabilities;
213 capabilities = QCA::supportedFeatures();
214 QgsDebugMsgLevel( QStringLiteral(
"QCA supports: %1" ).arg( capabilities.join(
"," ) ), 2 );
217 if ( !QCA::isSupported(
"cert", QStringLiteral(
"qca-ossl" ) ) )
219 mAuthDisabled =
true;
220 mAuthDisabledMessage = tr(
"QCA's OpenSSL plugin (qca-ossl) is missing" );
224 QgsDebugMsgLevel( QStringLiteral(
"Prioritizing qca-ossl over all other QCA providers..." ), 2 );
225 const QCA::ProviderList provds = QCA::providers();
227 for ( QCA::Provider *p : provds )
229 QString pn = p->name();
231 if ( pn != QLatin1String(
"qca-ossl" ) )
233 pr = QCA::providerPriority( pn ) + 1;
235 QCA::setProviderPriority( pn, pr );
236 prlist << QStringLiteral(
"%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
238 QgsDebugMsgLevel( QStringLiteral(
"QCA provider priorities: %1" ).arg( prlist.join(
", " ) ), 2 );
245 QgsDebugMsgLevel( QStringLiteral(
"Authentication methods found: %1" ).arg( methods.join(
", " ) ), 2 );
247 if ( methods.isEmpty() )
249 mAuthDisabled =
true;
250 mAuthDisabledMessage = tr(
"No authentication method plugins found" );
256 mAuthDisabled =
true;
257 mAuthDisabledMessage = tr(
"No authentication method plugins could be loaded" );
261 mAuthDbPath = QDir::cleanPath( authDatabasePath );
265 QFileInfo dbdirinfo( dbinfo.path() );
266 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
268 if ( !dbdirinfo.exists() )
270 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
271 if ( !QDir().mkpath( dbdirinfo.filePath() ) )
273 const char *err = QT_TR_NOOP(
"Auth db directory path could not be created" );
280 if ( dbinfo.exists() )
282 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
284 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
289 if ( dbinfo.size() > 0 )
293 if ( !createCertTables() )
303 const char *passenv =
"QGIS_AUTH_PASSWORD_FILE";
306 QString passpath( getenv( passenv ) );
315 QFile passfile( passpath );
316 if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
318 QTextStream passin( &passfile );
319 while ( !passin.atEnd() )
321 masterpass = passin.readLine();
326 if ( !masterpass.isEmpty() )
330 QgsDebugMsgLevel( QStringLiteral(
"Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
334 QgsDebugError(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
340 QgsDebugError(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
350 QgsDebugMsgLevel( QStringLiteral(
"Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
352 if ( !createConfigTables() )
355 if ( !createCertTables() )
368 mPluginPath = pluginPath;
369 mAuthDatabasePath = authDatabasePath;
372bool QgsAuthManager::createConfigTables()
374 QMutexLocker locker( mMutex.get() );
378 const char *err = QT_TR_NOOP(
"Auth db could not be created and opened" );
389 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
390 " 'salt' TEXT NOT NULL,\n"
391 " 'civ' TEXT NOT NULL\n"
392 ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
393 query.prepare( qstr );
394 if ( !authDbQuery( &query ) )
398 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
399 " 'id' TEXT NOT NULL,\n"
400 " 'name' TEXT NOT NULL,\n"
402 " 'type' TEXT NOT NULL,\n"
403 " 'version' INTEGER NOT NULL\n"
405 query.prepare( qstr );
406 if ( !authDbQuery( &query ) )
411 query.prepare( qstr );
412 if ( !authDbQuery( &query ) )
417 query.prepare( qstr );
418 if ( !authDbQuery( &query ) )
425bool QgsAuthManager::createCertTables()
427 QMutexLocker locker( mMutex.get() );
436 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
437 " 'setting' TEXT NOT NULL\n"
438 ", 'value' TEXT);" ).arg( authDbSettingsTable() );
439 query.prepare( qstr );
440 if ( !authDbQuery( &query ) )
445 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
446 " 'id' TEXT NOT NULL,\n"
447 " 'key' TEXT NOT NULL\n"
448 ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
449 query.prepare( qstr );
450 if ( !authDbQuery( &query ) )
454 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
455 query.prepare( qstr );
456 if ( !authDbQuery( &query ) )
461 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
462 " 'id' TEXT NOT NULL,\n"
463 " 'host' TEXT NOT NULL,\n"
466 query.prepare( qstr );
467 if ( !authDbQuery( &query ) )
471 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg(
authDatabaseServersTable() );
472 query.prepare( qstr );
473 if ( !authDbQuery( &query ) )
478 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
479 " 'id' TEXT NOT NULL\n"
480 ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
481 query.prepare( qstr );
482 if ( !authDbQuery( &query ) )
486 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
487 query.prepare( qstr );
488 if ( !authDbQuery( &query ) )
492 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
493 " 'id' TEXT NOT NULL\n"
494 ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
495 query.prepare( qstr );
496 if ( !authDbQuery( &query ) )
500 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
501 query.prepare( qstr );
502 if ( !authDbQuery( &query ) )
515 QgsDebugError( QStringLiteral(
"Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
517 return mAuthDisabled;
524 return tr(
"Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
531 QMutexLocker locker( mMasterPasswordMutex.get() );
535 if ( mScheduledDbErase )
538 if ( mMasterPass.isEmpty() )
540 QgsDebugMsgLevel( QStringLiteral(
"Master password is not yet set by user" ), 2 );
541 if ( !masterPasswordInput() )
543 QgsDebugMsgLevel( QStringLiteral(
"Master password input canceled by user" ), 2 );
557 QgsDebugMsgLevel( QStringLiteral(
"Master password is set and verified" ), 2 );
565 QMutexLocker locker( mMutex.get() );
569 if ( mScheduledDbErase )
573 QString prevpass = QString( mMasterPass );
577 mMasterPass = prevpass;
578 const char *err = QT_TR_NOOP(
"Master password set: FAILED to verify, reset to previous" );
584 QgsDebugMsgLevel( QStringLiteral(
"Master password set: SUCCESS%1" ).arg( verify ?
" and verified" :
"" ), 2 );
596 if ( !masterPasswordRowsInDb( &rows ) )
598 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
606 QgsDebugMsgLevel( QStringLiteral(
"Master password: %1 rows in database" ).arg( rows ), 2 );
610 const char *err = QT_TR_NOOP(
"Master password: FAILED to find just one master password record in database" );
617 else if ( rows == 1 )
619 if ( !masterPasswordCheckAgainstDb( compare ) )
621 if ( compare.isNull() )
623 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
632 if ( mPassTries >= 5 )
634 mAuthDisabled =
true;
635 const char *err = QT_TR_NOOP(
"Master password: failed 5 times authentication system DISABLED" );
643 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
644 if ( compare.isNull() )
648 else if ( compare.isNull() )
650 if ( !masterPasswordStoreInDb() )
652 const char *err = QT_TR_NOOP(
"Master password: hash FAILED to be stored in database" );
661 QgsDebugMsgLevel( QStringLiteral(
"Master password: hash stored in database" ), 2 );
664 if ( !masterPasswordCheckAgainstDb() )
666 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
676 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
688 return !mMasterPass.isEmpty();
695 return mMasterPass == pass;
699 bool keepbackup, QString *backuppath )
715 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up current database" ), 2 );
721 QString prevpass = QString( mMasterPass );
722 QString prevciv = QString( masterPasswordCiv() );
728 if ( ok && !masterPasswordClearDb() )
731 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not clear current password from database" );
737 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: cleared current password from database" ), 2 );
744 if ( ok && !masterPasswordStoreInDb() )
747 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not store new password in database" );
753 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: stored new password in database" ), 2 );
760 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify new password in database" );
766 if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
769 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt configs in database" );
775 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: re-encrypted configs in database" ), 2 );
779 if ( ok && !verifyPasswordCanDecryptConfigs() )
782 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
787 if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
790 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt settings in database" );
795 if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
798 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt identities in database" );
808 QString errdbbackup( dbbackup );
809 errdbbackup.replace( QLatin1String(
".db" ), QLatin1String(
"_ERROR.db" ) );
811 QgsDebugError( QStringLiteral(
"Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
815 mMasterPass = prevpass;
817 QgsDebugError( QStringLiteral(
"Master password reset FAILED: reinstated previous password and database" ) );
821 *backuppath = errdbbackup;
827 if ( !keepbackup && !QFile::remove( dbbackup ) )
829 const char *err = QT_TR_NOOP(
"Master password reset: could not remove old database backup" );
837 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
839 *backuppath = dbbackup;
851 mScheduledDbErase = scheduleErase;
853 mScheduledDbEraseRequestEmitted =
false;
854 mScheduledDbEraseRequestCount = 0;
858 if ( !mScheduledDbEraseTimer )
860 mScheduledDbEraseTimer =
new QTimer(
this );
861 connect( mScheduledDbEraseTimer, &QTimer::timeout,
this, &QgsAuthManager::tryToStartDbErase );
862 mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
864 else if ( !mScheduledDbEraseTimer->isActive() )
866 mScheduledDbEraseTimer->start();
871 if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
872 mScheduledDbEraseTimer->stop();
881 qDeleteAll( mAuthMethods );
882 mAuthMethods.clear();
884 for (
const auto &authMethodKey : methods )
889 return !mAuthMethods.isEmpty();
901 QTimer::singleShot( 3, &loop, &QEventLoop::quit );
907 for (
int i = 0; i < len; i++ )
909 switch ( QRandomGenerator::system()->generate() % 2 )
912 id +=
static_cast<char>(
'0' + QRandomGenerator::system()->generate() % 10 );
915 id +=
static_cast<char>(
'a' + QRandomGenerator::system()->generate() % 26 );
919 if ( !configids.contains(
id ) )
924 QgsDebugMsgLevel( QStringLiteral(
"Generated unique ID: %1" ).arg(
id ), 2 );
937 const char *err = QT_TR_NOOP(
"Config ID is empty" );
943 return !configids.contains(
id );
948 const thread_local QRegularExpression authCfgRegExp( AUTH_CFG_REGEX );
949 return txt.indexOf( authCfgRegExp ) != -1;
956 QMutexLocker locker( mMutex.get() );
957 QStringList providerAuthMethodsKeys;
958 if ( !dataprovider.isEmpty() )
969 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1" ).arg(
authDatabaseConfigTable() ) );
971 if ( !authDbQuery( &query ) )
976 if ( query.isActive() && query.isSelect() )
978 while ( query.next() )
980 QString authcfg = query.value( 0 ).toString();
982 config.
setId( authcfg );
983 config.
setName( query.value( 1 ).toString() );
984 config.
setUri( query.value( 2 ).toString() );
985 config.
setMethod( query.value( 3 ).toString() );
986 config.
setVersion( query.value( 4 ).toInt() );
988 if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.
method() ) )
993 baseConfigs.insert( authcfg, config );
1001 ensureInitialized();
1003 QMutexLocker locker( mMutex.get() );
1010 if ( !authDbQuery( &query ) )
1015 if ( query.isActive() )
1017 QgsDebugMsgLevel( QStringLiteral(
"Syncing existing auth config and their auth methods" ), 2 );
1018 mConfigAuthMethods.clear();
1019 QStringList cfgmethods;
1020 while ( query.next() )
1022 mConfigAuthMethods.insert( query.value( 0 ).toString(),
1023 query.value( 1 ).toString() );
1024 cfgmethods << QStringLiteral(
"%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
1026 QgsDebugMsgLevel( QStringLiteral(
"Stored auth config/methods:\n%1" ).arg( cfgmethods.join(
", " ) ), 2 );
1032 ensureInitialized();
1037 if ( !mConfigAuthMethods.contains( authcfg ) )
1039 QgsDebugError( QStringLiteral(
"No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1043 QString authMethodKey = mConfigAuthMethods.value( authcfg );
1050 ensureInitialized();
1055 return mConfigAuthMethods.value( authcfg, QString() );
1061 ensureInitialized();
1068 ensureInitialized();
1070 if ( !mAuthMethods.contains( authMethodKey ) )
1072 QgsDebugError( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1076 return mAuthMethods.value( authMethodKey );
1081 ensureInitialized();
1083 if ( !mAuthMethods.contains( authMethodKey ) )
1085 QgsDebugError( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1095 ensureInitialized();
1097 if ( dataprovider.isEmpty() )
1099 return mAuthMethods;
1103 QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1104 while ( i != mAuthMethods.constEnd() )
1107 && ( i.value()->supportedDataProviders().contains( QStringLiteral(
"all" ) )
1108 || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1110 filteredmap.insert( i.key(), i.value() );
1118QWidget *QgsAuthManager::authMethodEditWidget(
const QString &authMethodKey, QWidget *parent )
1120 ensureInitialized();
1124 return method->editWidget( parent );
1132 ensureInitialized();
1147 ensureInitialized();
1149 QMutexLocker locker( mMutex.get() );
1156 const char *err = QT_TR_NOOP(
"Store config: FAILED because config is invalid" );
1162 QString uid = mconfig.
id();
1163 bool passedinID = !uid.isEmpty();
1164 if ( uid.isEmpty() )
1172 const char *err = QT_TR_NOOP(
"Store config: FAILED because pre-defined config ID %1 is not unique" );
1183 if ( configstring.isEmpty() )
1185 const char *err = QT_TR_NOOP(
"Store config: FAILED because config string is empty" );
1191 QgsDebugMsgLevel( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ), 2 );
1194 QgsDebugMsgLevel( QStringLiteral(
"type: %1" ).arg( config.method() ), 2 );
1195 QgsDebugMsgLevel( QStringLiteral(
"version: %1" ).arg( config.version() ), 2 );
1200 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, name, uri, type, version, config) "
1203 query.bindValue( QStringLiteral(
":id" ), uid );
1204 query.bindValue( QStringLiteral(
":name" ), mconfig.
name() );
1205 query.bindValue( QStringLiteral(
":uri" ), mconfig.
uri() );
1206 query.bindValue( QStringLiteral(
":type" ), mconfig.
method() );
1207 query.bindValue( QStringLiteral(
":version" ), mconfig.
version() );
1208 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1210 if ( !authDbStartTransaction() )
1213 if ( !authDbQuery( &query ) )
1216 if ( !authDbCommit() )
1221 mconfig.
setId( uid );
1225 QgsDebugMsgLevel( QStringLiteral(
"Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1231 ensureInitialized();
1233 QMutexLocker locker( mMutex.get() );
1238 if ( !config.
isValid(
true ) )
1240 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is invalid" );
1247 if ( configstring.isEmpty() )
1249 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is empty" );
1256 QgsDebugMsgLevel( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ), 2 );
1266 if ( !query.prepare( QStringLiteral(
"UPDATE %1 "
1267 "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1270 const char *err = QT_TR_NOOP(
"Update config: FAILED to prepare query" );
1276 query.bindValue( QStringLiteral(
":id" ), config.
id() );
1277 query.bindValue( QStringLiteral(
":name" ), config.
name() );
1278 query.bindValue( QStringLiteral(
":uri" ), config.
uri() );
1279 query.bindValue( QStringLiteral(
":type" ), config.
method() );
1280 query.bindValue( QStringLiteral(
":version" ), config.
version() );
1281 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1283 if ( !authDbStartTransaction() )
1286 if ( !authDbQuery( &query ) )
1289 if ( !authDbCommit() )
1297 QgsDebugMsgLevel( QStringLiteral(
"Update config SUCCESS for authcfg: %1" ).arg( config.
id() ), 2 );
1304 ensureInitialized();
1312 QMutexLocker locker( mMutex.get() );
1317 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version, config FROM %1 "
1322 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1 "
1326 query.bindValue( QStringLiteral(
":id" ), authcfg );
1328 if ( !authDbQuery( &query ) )
1333 if ( query.isActive() && query.isSelect() )
1335 if ( query.first() )
1337 mconfig.
setId( query.value( 0 ).toString() );
1338 mconfig.
setName( query.value( 1 ).toString() );
1339 mconfig.
setUri( query.value( 2 ).toString() );
1340 mconfig.
setMethod( query.value( 3 ).toString() );
1341 mconfig.
setVersion( query.value( 4 ).toInt() );
1356 QgsDebugError( QStringLiteral(
"Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1359 QgsDebugMsgLevel( QStringLiteral(
"Load %1 config SUCCESS for authcfg: %2" ).arg( full ?
"full" :
"base", authcfg ), 2 );
1364 QgsDebugError( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1374 ensureInitialized();
1376 QMutexLocker locker( mMutex.get() );
1380 if ( authcfg.isEmpty() )
1387 query.bindValue( QStringLiteral(
":id" ), authcfg );
1389 if ( !authDbStartTransaction() )
1392 if ( !authDbQuery( &query ) )
1395 if ( !authDbCommit() )
1402 QgsDebugMsgLevel( QStringLiteral(
"REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1409 ensureInitialized();
1411 if ( filename.isEmpty() )
1414 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1415 QDomElement root = document.createElement( QStringLiteral(
"qgis_authentication" ) );
1416 document.appendChild( root );
1419 if ( !password.isEmpty() )
1424 root.setAttribute( QStringLiteral(
"salt" ), salt );
1425 root.setAttribute( QStringLiteral(
"hash" ), hash );
1426 root.setAttribute( QStringLiteral(
"civ" ), civ );
1429 QDomElement configurations = document.createElement( QStringLiteral(
"configurations" ) );
1430 for (
const QString &authcfg : authcfgs )
1437 authMethodConfig.
writeXml( configurations, document );
1440 if ( !password.isEmpty() )
1442 QString configurationsString;
1443 QTextStream ts( &configurationsString );
1444#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1445 ts.setCodec(
"UTF-8" );
1447 configurations.save( ts, 2 );
1448 root.appendChild( document.createTextNode(
QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
1452 root.appendChild( configurations );
1455 QFile file( filename );
1456 if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
1459 QTextStream ts( &file );
1460#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1461 ts.setCodec(
"UTF-8" );
1463 document.save( ts, 2 );
1470 ensureInitialized();
1472 QFile file( filename );
1473 if ( !file.open( QFile::ReadOnly ) )
1478 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1479 if ( !document.setContent( &file ) )
1486 QDomElement root = document.documentElement();
1487 if ( root.tagName() != QLatin1String(
"qgis_authentication" ) )
1492 QDomElement configurations;
1493 if ( root.hasAttribute( QStringLiteral(
"salt" ) ) )
1495 QString salt = root.attribute( QStringLiteral(
"salt" ) );
1496 QString hash = root.attribute( QStringLiteral(
"hash" ) );
1497 QString civ = root.attribute( QStringLiteral(
"civ" ) );
1502 configurations = document.firstChild().toElement();
1506 configurations = root.firstChildElement( QStringLiteral(
"configurations" ) );
1509 QDomElement configuration = configurations.firstChildElement();
1510 while ( !configuration.isNull() )
1513 authMethodConfig.
readXml( configuration );
1516 configuration = configuration.nextSiblingElement();
1523 ensureInitialized();
1525 QMutexLocker locker( mMutex.get() );
1531 bool res = authDbTransactionQuery( &query );
1539 QgsDebugMsgLevel( QStringLiteral(
"Remove configs from database: %1" ).arg( res ?
"SUCCEEDED" :
"FAILED" ), 2 );
1546 ensureInitialized();
1548 QMutexLocker locker( mMutex.get() );
1551 const char *err = QT_TR_NOOP(
"No authentication database found" );
1559 if ( authConn.isValid() && authConn.isOpen() )
1563 QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral(
"yyyy-MM-dd-hhmmss" ) ) );
1565 dbbackup.replace( QLatin1String(
".db" ), QStringLiteral(
"_%1.db" ).arg( datestamp ) );
1569 const char *err = QT_TR_NOOP(
"Could not back up authentication database" );
1576 *backuppath = dbbackup;
1578 QgsDebugMsgLevel( QStringLiteral(
"Backed up auth database at %1" ).arg( dbbackup ), 2 );
1584 ensureInitialized();
1586 QMutexLocker locker( mMutex.get() );
1596 if ( backuppath && !dbbackup.isEmpty() )
1597 *backuppath = dbbackup;
1600 if ( dbinfo.exists() )
1602 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1604 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
1612 const char *err = QT_TR_NOOP(
"No authentication database found" );
1620 const char *err = QT_TR_NOOP(
"Authentication database could not be deleted" );
1626 mMasterPass = QString();
1628 QgsDebugMsgLevel( QStringLiteral(
"Creating Auth db through QSqlDatabase initial connection" ), 2 );
1631 if ( !authConn.isValid() || !authConn.isOpen() )
1633 const char *err = QT_TR_NOOP(
"Authentication database could not be initialized" );
1639 if ( !createConfigTables() )
1641 const char *err = QT_TR_NOOP(
"FAILED to create auth database config tables" );
1647 if ( !createCertTables() )
1649 const char *err = QT_TR_NOOP(
"FAILED to create auth database cert tables" );
1668 const QString &dataprovider )
1670 ensureInitialized();
1680 QgsDebugError( QStringLiteral(
"Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1695 const QString &dataprovider )
1697 ensureInitialized();
1707 QgsDebugError( QStringLiteral(
"Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1723 const QString &dataprovider )
1725 ensureInitialized();
1735 QgsDebugError( QStringLiteral(
"Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1752 ensureInitialized();
1762 QgsDebugError( QStringLiteral(
"Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1771 QgsDebugMsgLevel( QStringLiteral(
"Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1780 ensureInitialized();
1782 QMutexLocker locker( mMutex.get() );
1783 if ( key.isEmpty() )
1786 QString storeval( value.toString() );
1802 query.prepare( QStringLiteral(
"INSERT INTO %1 (setting, value) "
1803 "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1805 query.bindValue( QStringLiteral(
":setting" ), key );
1806 query.bindValue( QStringLiteral(
":value" ), storeval );
1808 if ( !authDbStartTransaction() )
1811 if ( !authDbQuery( &query ) )
1814 if ( !authDbCommit() )
1817 QgsDebugMsgLevel( QStringLiteral(
"Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1823 ensureInitialized();
1825 QMutexLocker locker( mMutex.get() );
1826 if ( key.isEmpty() )
1832 QVariant value = defaultValue;
1834 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1835 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1837 query.bindValue( QStringLiteral(
":setting" ), key );
1839 if ( !authDbQuery( &query ) )
1842 if ( query.isActive() && query.isSelect() )
1844 if ( query.first() )
1848 value = QVariant(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1852 value = query.value( 0 );
1854 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1858 QgsDebugError( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1868 ensureInitialized();
1870 QMutexLocker locker( mMutex.get() );
1871 if ( key.isEmpty() )
1875 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1876 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1878 query.bindValue( QStringLiteral(
":setting" ), key );
1880 if ( !authDbQuery( &query ) )
1884 if ( query.isActive() && query.isSelect() )
1886 if ( query.first() )
1888 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting exists for key: %1" ).arg( key ), 2 );
1893 QgsDebugError( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1903 ensureInitialized();
1905 QMutexLocker locker( mMutex.get() );
1906 if ( key.isEmpty() )
1911 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1913 query.bindValue( QStringLiteral(
":setting" ), key );
1915 if ( !authDbStartTransaction() )
1918 if ( !authDbQuery( &query ) )
1921 if ( !authDbCommit() )
1924 QgsDebugMsgLevel( QStringLiteral(
"REMOVED setting for key: %1" ).arg( key ), 2 );
1938 QMutexLocker locker( mMutex.get() );
1944 mCustomConfigByHostCache.clear();
1945 mHasCheckedIfCustomConfigByHostExists =
false;
1948 QgsDebugError( QStringLiteral(
"Init of SSL caches FAILED" ) );
1954 ensureInitialized();
1956 QMutexLocker locker( mMutex.get() );
1957 if ( cert.isNull() )
1959 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
1964 QgsDebugError( QStringLiteral(
"Passed private key is null" ) );
1974 QString certpem( cert.toPem() );
1978 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, key, cert) "
1979 "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1981 query.bindValue( QStringLiteral(
":id" ),
id );
1982 query.bindValue( QStringLiteral(
":key" ), keypem );
1983 query.bindValue( QStringLiteral(
":cert" ), certpem );
1985 if ( !authDbStartTransaction() )
1988 if ( !authDbQuery( &query ) )
1991 if ( !authDbCommit() )
1994 QgsDebugMsgLevel( QStringLiteral(
"Store certificate identity SUCCESS for id: %1" ).arg(
id ), 2 );
2000 ensureInitialized();
2002 QMutexLocker locker( mMutex.get() );
2003 QSslCertificate emptycert;
2004 QSslCertificate cert;
2009 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2010 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2012 query.bindValue( QStringLiteral(
":id" ),
id );
2014 if ( !authDbQuery( &query ) )
2017 if ( query.isActive() && query.isSelect() )
2019 if ( query.first() )
2021 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2022 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity retrieved for id: %1" ).arg(
id ), 2 );
2026 QgsDebugError( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
2036 ensureInitialized();
2038 QMutexLocker locker( mMutex.get() );
2039 QPair<QSslCertificate, QSslKey> bundle;
2047 query.prepare( QStringLiteral(
"SELECT key, cert FROM %1 "
2048 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2050 query.bindValue( QStringLiteral(
":id" ),
id );
2052 if ( !authDbQuery( &query ) )
2055 if ( query.isActive() && query.isSelect() )
2057 QSslCertificate cert;
2059 if ( query.first() )
2061 key = QSslKey(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
2062 QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
2065 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create private key" );
2070 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2071 if ( cert.isNull() )
2073 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create certificate" );
2078 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity bundle retrieved for id: %1" ).arg(
id ), 2 );
2082 QgsDebugError( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
2086 bundle = qMakePair( cert, key );
2093 ensureInitialized();
2095 QMutexLocker locker( mMutex.get() );
2099 return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
2101 return QStringList();
2106 ensureInitialized();
2108 QMutexLocker locker( mMutex.get() );
2109 QList<QSslCertificate> certs;
2112 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2114 if ( !authDbQuery( &query ) )
2117 if ( query.isActive() && query.isSelect() )
2119 while ( query.next() )
2121 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2130 ensureInitialized();
2132 QMutexLocker locker( mMutex.get() );
2133 QStringList identityids = QStringList();
2139 query.prepare( QStringLiteral(
"SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2141 if ( !authDbQuery( &query ) )
2146 if ( query.isActive() )
2148 while ( query.next() )
2150 identityids << query.value( 0 ).toString();
2158 ensureInitialized();
2160 QMutexLocker locker( mMutex.get() );
2165 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2166 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2168 query.bindValue( QStringLiteral(
":id" ),
id );
2170 if ( !authDbQuery( &query ) )
2174 if ( query.isActive() && query.isSelect() )
2176 if ( query.first() )
2178 QgsDebugMsgLevel( QStringLiteral(
"Certificate bundle exists for id: %1" ).arg(
id ), 2 );
2183 QgsDebugError( QStringLiteral(
"Select contains more than one certificate bundle for id: %1" ).arg(
id ) );
2193 ensureInitialized();
2195 QMutexLocker locker( mMutex.get() );
2198 QgsDebugError( QStringLiteral(
"Passed bundle ID is empty" ) );
2204 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2206 query.bindValue( QStringLiteral(
":id" ),
id );
2208 if ( !authDbStartTransaction() )
2211 if ( !authDbQuery( &query ) )
2214 if ( !authDbCommit() )
2217 QgsDebugMsgLevel( QStringLiteral(
"REMOVED certificate identity for id: %1" ).arg(
id ), 2 );
2223 ensureInitialized();
2225 QMutexLocker locker( mMutex.get() );
2237 QString certpem( cert.toPem() );
2240 query.prepare( QStringLiteral(
"INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2243 query.bindValue( QStringLiteral(
":id" ),
id );
2244 query.bindValue( QStringLiteral(
":host" ), config.
sslHostPort().trimmed() );
2245 query.bindValue( QStringLiteral(
":cert" ), certpem );
2246 query.bindValue( QStringLiteral(
":config" ), config.
configString() );
2248 if ( !authDbStartTransaction() )
2251 if ( !authDbQuery( &query ) )
2254 if ( !authDbCommit() )
2257 QgsDebugMsgLevel( QStringLiteral(
"Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2261 mHasCheckedIfCustomConfigByHostExists =
false;
2262 mCustomConfigByHostCache.clear();
2269 ensureInitialized();
2271 QMutexLocker locker( mMutex.get() );
2274 if (
id.isEmpty() || hostport.isEmpty() )
2276 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2281 query.prepare( QStringLiteral(
"SELECT id, host, cert, config FROM %1 "
2284 query.bindValue( QStringLiteral(
":id" ),
id );
2285 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2287 if ( !authDbQuery( &query ) )
2290 if ( query.isActive() && query.isSelect() )
2292 if ( query.first() )
2294 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2297 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2301 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2302 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2313 ensureInitialized();
2316 if ( hostport.isEmpty() )
2321 QMutexLocker locker( mMutex.get() );
2322 if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2324 if ( mCustomConfigByHostCache.contains( hostport ) )
2325 return mCustomConfigByHostCache.value( hostport );
2330 if ( !mHasCheckedIfCustomConfigByHostExists )
2332 mHasCheckedIfCustomConfigByHostExists =
true;
2334 if ( !authDbQuery( &query ) )
2336 mHasCustomConfigByHost =
false;
2339 if ( query.isActive() && query.isSelect() && query.first() )
2341 mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2342 if ( !mHasCustomConfigByHost )
2347 mHasCustomConfigByHost =
false;
2352 query.prepare( QString(
"SELECT id, host, cert, config FROM %1 "
2355 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2357 if ( !authDbQuery( &query ) )
2359 mCustomConfigByHostCache.insert( hostport, config );
2363 if ( query.isActive() && query.isSelect() )
2365 if ( query.first() )
2367 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2370 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2374 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2375 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2378 mCustomConfigByHostCache.insert( hostport, emptyconfig );
2383 mCustomConfigByHostCache.insert( hostport, config );
2389 ensureInitialized();
2391 QMutexLocker locker( mMutex.get() );
2392 QList<QgsAuthConfigSslServer> configs;
2397 if ( !authDbQuery( &query ) )
2400 if ( query.isActive() && query.isSelect() )
2402 while ( query.next() )
2405 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2409 configs.append( config );
2418 ensureInitialized();
2420 QMutexLocker locker( mMutex.get() );
2421 if (
id.isEmpty() || hostport.isEmpty() )
2423 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2428 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2431 query.bindValue( QStringLiteral(
":id" ),
id );
2432 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2434 if ( !authDbQuery( &query ) )
2438 if ( query.isActive() && query.isSelect() )
2440 if ( query.first() )
2442 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2447 QgsDebugError( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2448 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2458 ensureInitialized();
2460 QMutexLocker locker( mMutex.get() );
2461 if (
id.isEmpty() || hostport.isEmpty() )
2463 QgsDebugError( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2467 mHasCheckedIfCustomConfigByHostExists =
false;
2468 mCustomConfigByHostCache.clear();
2472 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id AND host = :host" ).arg(
authDatabaseServersTable() ) );
2474 query.bindValue( QStringLiteral(
":id" ),
id );
2475 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2477 if ( !authDbStartTransaction() )
2480 if ( !authDbQuery( &query ) )
2483 if ( !authDbCommit() )
2486 QString shahostport( QStringLiteral(
"%1:%2" ).arg(
id, hostport ) );
2487 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2489 mIgnoredSslErrorsCache.remove( shahostport );
2492 QgsDebugMsgLevel( QStringLiteral(
"REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2499 ensureInitialized();
2501 QMutexLocker locker( mMutex.get() );
2502 if ( !mIgnoredSslErrorsCache.isEmpty() )
2504 QgsDebugMsgLevel( QStringLiteral(
"Ignored SSL errors cache items:" ), 1 );
2505 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2506 while ( i != mIgnoredSslErrorsCache.constEnd() )
2509 for (
auto err : i.value() )
2513 QgsDebugMsgLevel( QStringLiteral(
"%1 = %2" ).arg( i.key(), errs.join(
", " ) ), 1 );
2525 ensureInitialized();
2527 QMutexLocker locker( mMutex.get() );
2534 QString shahostport( QStringLiteral(
"%1:%2" )
2537 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2539 mIgnoredSslErrorsCache.remove( shahostport );
2542 if ( !errenums.isEmpty() )
2544 mIgnoredSslErrorsCache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2545 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2550 QgsDebugMsgLevel( QStringLiteral(
"No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2556 ensureInitialized();
2558 QMutexLocker locker( mMutex.get() );
2559 const thread_local QRegularExpression rx( QRegularExpression::anchoredPattern(
"\\S+:\\S+:\\d+" ) );
2560 if ( !rx.match( shahostport ).hasMatch() )
2562 QgsDebugError(
"Passed shahostport does not match \\S+:\\S+:\\d+, "
2563 "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2567 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2569 mIgnoredSslErrorsCache.remove( shahostport );
2572 if ( errors.isEmpty() )
2574 QgsDebugError( QStringLiteral(
"Passed errors list empty" ) );
2578 QSet<QSslError::SslError> errs;
2579 for (
const auto &error : errors )
2581 if ( error.error() == QSslError::NoError )
2584 errs.insert( error.error() );
2587 if ( errs.isEmpty() )
2589 QgsDebugError( QStringLiteral(
"Passed errors list does not contain errors" ) );
2593 mIgnoredSslErrorsCache.insert( shahostport, errs );
2595 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2602 ensureInitialized();
2604 QMutexLocker locker( mMutex.get() );
2605 QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2606 QHash<QString, QSet<QSslError::SslError> > nextcache;
2611 if ( !authDbQuery( &query ) )
2613 QgsDebugError( QStringLiteral(
"Rebuild of ignored SSL errors cache FAILED" ) );
2617 if ( query.isActive() && query.isSelect() )
2619 while ( query.next() )
2621 QString shahostport( QStringLiteral(
"%1:%2" )
2622 .arg( query.value( 0 ).toString().trimmed(),
2623 query.value( 1 ).toString().trimmed() ) );
2627 if ( !errenums.isEmpty() )
2629 nextcache.insert( shahostport, QSet<QSslError::SslError>( errenums.begin(), errenums.end() ) );
2631 if ( prevcache.contains( shahostport ) )
2633 prevcache.remove( shahostport );
2638 if ( !prevcache.isEmpty() )
2641 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2642 while ( i != prevcache.constEnd() )
2644 nextcache.insert( i.key(), i.value() );
2649 if ( nextcache != mIgnoredSslErrorsCache )
2651 mIgnoredSslErrorsCache.clear();
2652 mIgnoredSslErrorsCache = nextcache;
2653 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2658 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2666 ensureInitialized();
2668 QMutexLocker locker( mMutex.get() );
2669 if ( certs.isEmpty() )
2671 QgsDebugError( QStringLiteral(
"Passed certificate list has no certs" ) );
2675 for (
const auto &cert : certs )
2685 ensureInitialized();
2687 QMutexLocker locker( mMutex.get() );
2690 if ( cert.isNull() )
2692 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2699 QString pem( cert.toPem() );
2702 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, cert) "
2703 "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2705 query.bindValue( QStringLiteral(
":id" ),
id );
2706 query.bindValue( QStringLiteral(
":cert" ), pem );
2708 if ( !authDbStartTransaction() )
2711 if ( !authDbQuery( &query ) )
2714 if ( !authDbCommit() )
2717 QgsDebugMsgLevel( QStringLiteral(
"Store certificate authority SUCCESS for id: %1" ).arg(
id ), 2 );
2723 ensureInitialized();
2725 QMutexLocker locker( mMutex.get() );
2726 QSslCertificate emptycert;
2727 QSslCertificate cert;
2732 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2733 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2735 query.bindValue( QStringLiteral(
":id" ),
id );
2737 if ( !authDbQuery( &query ) )
2740 if ( query.isActive() && query.isSelect() )
2742 if ( query.first() )
2744 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2745 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority retrieved for id: %1" ).arg(
id ), 2 );
2749 QgsDebugError( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2759 ensureInitialized();
2761 QMutexLocker locker( mMutex.get() );
2762 if ( cert.isNull() )
2764 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2771 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
2772 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2774 query.bindValue( QStringLiteral(
":id" ),
id );
2776 if ( !authDbQuery( &query ) )
2780 if ( query.isActive() && query.isSelect() )
2782 if ( query.first() )
2784 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority exists for id: %1" ).arg(
id ), 2 );
2789 QgsDebugError( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2799 ensureInitialized();
2801 QMutexLocker locker( mMutex.get() );
2802 if ( cert.isNull() )
2804 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2812 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2814 query.bindValue( QStringLiteral(
":id" ),
id );
2816 if ( !authDbStartTransaction() )
2819 if ( !authDbQuery( &query ) )
2822 if ( !authDbCommit() )
2825 QgsDebugMsgLevel( QStringLiteral(
"REMOVED authority for id: %1" ).arg(
id ), 2 );
2831 return QSslConfiguration::systemCaCertificates();
2836 ensureInitialized();
2838 QMutexLocker locker( mMutex.get() );
2839 QList<QSslCertificate> certs;
2840 QList<QSslCertificate> filecerts;
2849 QString cafile( cafileval.toString() );
2850 if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2855 for (
const auto &cert : std::as_const( filecerts ) )
2857 if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2859 || cert.expiryDate() <= QDateTime::currentDateTime()
2860 || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2875 ensureInitialized();
2877 QMutexLocker locker( mMutex.get() );
2878 QList<QSslCertificate> certs;
2881 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2883 if ( !authDbQuery( &query ) )
2886 if ( query.isActive() && query.isSelect() )
2888 while ( query.next() )
2890 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2899 ensureInitialized();
2901 QMutexLocker locker( mMutex.get() );
2907 ensureInitialized();
2909 QMutexLocker locker( mMutex.get() );
2910 mCaCertsCache.clear();
2916 bool res = !mCaCertsCache.isEmpty();
2918 QgsDebugError( QStringLiteral(
"Rebuild of CA certs cache FAILED" ) );
2924 ensureInitialized();
2926 QMutexLocker locker( mMutex.get() );
2927 if ( cert.isNull() )
2929 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2939 QgsDebugMsgLevel( QStringLiteral(
"Passed policy was default, all cert records in database were removed for id: %1" ).arg(
id ), 2 );
2944 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, policy) "
2945 "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2947 query.bindValue( QStringLiteral(
":id" ),
id );
2948 query.bindValue( QStringLiteral(
":policy" ),
static_cast< int >( policy ) );
2950 if ( !authDbStartTransaction() )
2953 if ( !authDbQuery( &query ) )
2956 if ( !authDbCommit() )
2959 QgsDebugMsgLevel( QStringLiteral(
"Store certificate trust policy SUCCESS for id: %1" ).arg(
id ), 2 );
2965 ensureInitialized();
2967 QMutexLocker locker( mMutex.get() );
2968 if ( cert.isNull() )
2970 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
2977 query.prepare( QStringLiteral(
"SELECT policy FROM %1 "
2978 "WHERE id = :id" ).arg( authDbTrustTable() ) );
2980 query.bindValue( QStringLiteral(
":id" ),
id );
2982 if ( !authDbQuery( &query ) )
2986 if ( query.isActive() && query.isSelect() )
2988 if ( query.first() )
2991 QgsDebugMsgLevel( QStringLiteral(
"Authentication cert trust policy retrieved for id: %1" ).arg(
id ), 2 );
2995 QgsDebugError( QStringLiteral(
"Select contains more than one cert trust policy for id: %1" ).arg(
id ) );
3005 ensureInitialized();
3007 QMutexLocker locker( mMutex.get() );
3008 if ( certs.empty() )
3010 QgsDebugError( QStringLiteral(
"Passed certificate list has no certs" ) );
3014 for (
const auto &cert : certs )
3024 ensureInitialized();
3026 QMutexLocker locker( mMutex.get() );
3027 if ( cert.isNull() )
3029 QgsDebugError( QStringLiteral(
"Passed certificate is null" ) );
3037 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
3039 query.bindValue( QStringLiteral(
":id" ),
id );
3041 if ( !authDbStartTransaction() )
3044 if ( !authDbQuery( &query ) )
3047 if ( !authDbCommit() )
3050 QgsDebugMsgLevel( QStringLiteral(
"REMOVED cert trust policy for id: %1" ).arg(
id ), 2 );
3057 ensureInitialized();
3059 QMutexLocker locker( mMutex.get() );
3060 if ( cert.isNull() )
3070 if ( trustedids.contains(
id ) )
3074 else if ( untrustedids.contains(
id ) )
3083 ensureInitialized();
3090 return storeAuthSetting( QStringLiteral(
"certdefaulttrust" ),
static_cast< int >( policy ) );
3095 ensureInitialized();
3097 QMutexLocker locker( mMutex.get() );
3098 QVariant policy(
authSetting( QStringLiteral(
"certdefaulttrust" ) ) );
3108 ensureInitialized();
3110 QMutexLocker locker( mMutex.get() );
3111 mCertTrustCache.clear();
3114 query.prepare( QStringLiteral(
"SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
3116 if ( !authDbQuery( &query ) )
3118 QgsDebugError( QStringLiteral(
"Rebuild of cert trust policy cache FAILED" ) );
3122 if ( query.isActive() && query.isSelect() )
3124 while ( query.next() )
3126 QString
id = query.value( 0 ).toString();
3130 if ( mCertTrustCache.contains( policy ) )
3132 ids = mCertTrustCache.value( policy );
3134 mCertTrustCache.insert( policy, ids <<
id );
3138 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
3144 ensureInitialized();
3146 QMutexLocker locker( mMutex.get() );
3150 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3152 QList<QSslCertificate> trustedcerts;
3153 for (
int i = 0; i < certpairs.size(); ++i )
3155 QSslCertificate cert( certpairs.at( i ).second );
3157 if ( trustedids.contains( certid ) )
3160 trustedcerts.append( cert );
3166 trustedcerts.append( cert );
3171 QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
3172 sslconfig.setCaCertificates( trustedcerts );
3173 QSslConfiguration::setDefaultConfiguration( sslconfig );
3175 return trustedcerts;
3180 ensureInitialized();
3182 QMutexLocker locker( mMutex.get() );
3183 if ( trustedCAs.isEmpty() )
3185 if ( mTrustedCaCertsCache.isEmpty() )
3192 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3194 QList<QSslCertificate> untrustedCAs;
3195 for (
int i = 0; i < certpairs.size(); ++i )
3197 QSslCertificate cert( certpairs.at( i ).second );
3198 if ( !trustedCAs.contains( cert ) )
3200 untrustedCAs.append( cert );
3203 return untrustedCAs;
3208 ensureInitialized();
3210 QMutexLocker locker( mMutex.get() );
3212 QgsDebugMsgLevel( QStringLiteral(
"Rebuilt trusted cert authorities cache" ), 2 );
3219 ensureInitialized();
3221 QMutexLocker locker( mMutex.get() );
3227 ensureInitialized();
3229 QMutexLocker locker( mMutex.get() );
3232 return passwordHelperWrite( mMasterPass );
3244 ensureInitialized();
3250 for (
const auto &authcfg : ids )
3258 ensureInitialized();
3270void QgsAuthManager::writeToConsole(
const QString &message,
3276 ensureInitialized();
3286 msg += QLatin1String(
"WARNING: " );
3289 msg += QLatin1String(
"ERROR: " );
3296 QTextStream out( stdout, QIODevice::WriteOnly );
3297 out << msg << Qt::endl;
3300void QgsAuthManager::tryToStartDbErase()
3302 ensureInitialized();
3304 ++mScheduledDbEraseRequestCount;
3306 int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3307 if ( mScheduledDbEraseRequestCount >= trycutoff )
3310 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3315 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest attempt (%1 of %2)" )
3316 .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3322 mScheduledDbEraseRequestEmitted =
true;
3327 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitted" ), 2 );
3330 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emit skipped" ), 2 );
3336 QMutexLocker locker( mMutex.get() );
3338 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3339 while ( iterator.hasNext() )
3342 QThread::disconnect( iterator.value() );
3353 qDeleteAll( mAuthMethods );
3356 if ( authConn.isValid() && authConn.isOpen() )
3359 delete mScheduledDbEraseTimer;
3360 mScheduledDbEraseTimer =
nullptr;
3361 QSqlDatabase::removeDatabase( QStringLiteral(
"authentication.configs" ) );
3365QString QgsAuthManager::passwordHelperName()
const
3367 return tr(
"Password Helper" );
3371void QgsAuthManager::passwordHelperLog(
const QString &msg )
const
3373 ensureInitialized();
3383 ensureInitialized();
3387 QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3390 job.setAutoDelete(
false );
3391 job.setKey( authPasswordHelperKeyName() );
3393 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3398 mPasswordHelperErrorCode = job.error();
3399 mPasswordHelperErrorMessage = tr(
"Delete password failed: %1." ).arg( job.errorString() );
3410 passwordHelperProcessError();
3414QString QgsAuthManager::passwordHelperRead()
3416 ensureInitialized();
3421 QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3424 job.setAutoDelete(
false );
3425 job.setKey( authPasswordHelperKeyName() );
3427 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3432 mPasswordHelperErrorCode = job.error();
3439 password = job.textData();
3441 if ( password.isEmpty() )
3443 mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3454 passwordHelperProcessError();
3458bool QgsAuthManager::passwordHelperWrite(
const QString &password )
3460 ensureInitialized();
3462 Q_ASSERT( !password.isEmpty() );
3465 QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3468 job.setAutoDelete(
false );
3469 job.setKey( authPasswordHelperKeyName() );
3470 job.setTextData( password );
3472 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3477 mPasswordHelperErrorCode = job.error();
3485 passwordHelperClearErrors();
3490 passwordHelperProcessError();
3505 emit
messageOut( enabled ? tr(
"Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3507 tr(
"Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3524void QgsAuthManager::passwordHelperClearErrors()
3526 mPasswordHelperErrorCode = QKeychain::NoError;
3527 mPasswordHelperErrorMessage.clear();
3530void QgsAuthManager::passwordHelperProcessError()
3532 ensureInitialized();
3534 if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3535 mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3536 mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3537 mPasswordHelperErrorCode == QKeychain::NotImplemented )
3543 mPasswordHelperErrorMessage = tr(
"There was an error and integration with your %1 system has been disabled. "
3544 "You can re-enable it at any time through the \"Utilities\" menu "
3545 "in the Authentication pane of the options dialog. %2" )
3548 if ( mPasswordHelperErrorCode != QKeychain::NoError )
3554 passwordHelperClearErrors();
3558bool QgsAuthManager::masterPasswordInput()
3560 ensureInitialized();
3566 bool storedPasswordIsValid =
false;
3572 pass = passwordHelperRead();
3573 if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3579 storedPasswordIsValid =
true;
3595 if ( ok && !pass.isEmpty() && mMasterPass != pass )
3600 if ( passwordHelperWrite( pass ) )
3614bool QgsAuthManager::masterPasswordRowsInDb(
int *rows )
const
3616 ensureInitialized();
3622 query.prepare( QStringLiteral(
"SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3624 bool ok = authDbQuery( &query );
3625 if ( query.first() )
3627 *rows = query.value( 0 ).toInt();
3635 ensureInitialized();
3641 if ( !masterPasswordRowsInDb( &rows ) )
3643 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
3649 return ( rows == 1 );
3652bool QgsAuthManager::masterPasswordCheckAgainstDb(
const QString &compare )
const
3654 ensureInitialized();
3662 query.prepare( QStringLiteral(
"SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3663 if ( !authDbQuery( &query ) )
3666 if ( !query.first() )
3669 QString salt = query.value( 0 ).toString();
3670 QString hash = query.value( 1 ).toString();
3675bool QgsAuthManager::masterPasswordStoreInDb()
const
3677 ensureInitialized();
3682 QString salt, hash, civ;
3686 query.prepare( QStringLiteral(
"INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3688 query.bindValue( QStringLiteral(
":salt" ), salt );
3689 query.bindValue( QStringLiteral(
":hash" ), hash );
3690 query.bindValue( QStringLiteral(
":civ" ), civ );
3692 if ( !authDbStartTransaction() )
3695 if ( !authDbQuery( &query ) )
3698 if ( !authDbCommit() )
3704bool QgsAuthManager::masterPasswordClearDb()
3706 ensureInitialized();
3712 query.prepare( QStringLiteral(
"DELETE FROM %1" ).arg( authDbPassTable() ) );
3713 bool res = authDbTransactionQuery( &query );
3719const QString QgsAuthManager::masterPasswordCiv()
const
3721 ensureInitialized();
3727 query.prepare( QStringLiteral(
"SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3728 if ( !authDbQuery( &query ) )
3731 if ( !query.first() )
3734 return query.value( 0 ).toString();
3739 ensureInitialized();
3741 QStringList configids = QStringList();
3749 if ( !authDbQuery( &query ) )
3754 if ( query.isActive() )
3756 while ( query.next() )
3758 configids << query.value( 0 ).toString();
3764bool QgsAuthManager::verifyPasswordCanDecryptConfigs()
const
3766 ensureInitialized();
3777 if ( !authDbQuery( &query ) )
3780 if ( !query.isActive() || !query.isSelect() )
3782 QgsDebugError( QStringLiteral(
"Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3790 while ( query.next() )
3795 QString configstring(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3796 if ( configstring.isEmpty() )
3798 QgsDebugError( QStringLiteral(
"Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3799 .arg( query.value( 0 ).toString() ) );
3804 QgsDebugMsgLevel( QStringLiteral(
"Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3808bool QgsAuthManager::reencryptAllAuthenticationConfigs(
const QString &prevpass,
const QString &prevciv )
3810 ensureInitialized();
3817 for (
const auto &configid : ids )
3819 res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3824bool QgsAuthManager::reencryptAuthenticationConfig(
const QString &authcfg,
const QString &prevpass,
const QString &prevciv )
3826 ensureInitialized();
3835 query.prepare( QStringLiteral(
"SELECT config FROM %1 "
3838 query.bindValue( QStringLiteral(
":id" ), authcfg );
3840 if ( !authDbQuery( &query ) )
3843 if ( !query.isActive() || !query.isSelect() )
3845 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3849 if ( query.first() )
3855 QgsDebugError( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3862 query.prepare( QStringLiteral(
"UPDATE %1 "
3863 "SET config = :config "
3866 query.bindValue( QStringLiteral(
":id" ), authcfg );
3867 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3869 if ( !authDbStartTransaction() )
3872 if ( !authDbQuery( &query ) )
3875 if ( !authDbCommit() )
3878 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3883 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3888bool QgsAuthManager::reencryptAllAuthenticationSettings(
const QString &prevpass,
const QString &prevciv )
3890 ensureInitialized();
3893 Q_UNUSED( prevpass )
3906 QStringList encryptedsettings;
3907 encryptedsettings <<
"";
3909 for (
const auto & sett, std::as_const( encryptedsettings ) )
3916 QSqlQuery query( authDbConnection() );
3918 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
3919 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3921 query.bindValue(
":setting", sett );
3923 if ( !authDbQuery( &query ) )
3926 if ( !query.isActive() || !query.isSelect() )
3928 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3932 if ( query.first() )
3938 query.prepare( QStringLiteral(
"UPDATE %1 "
3939 "SET value = :value "
3940 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3942 query.bindValue(
":setting", sett );
3945 if ( !authDbStartTransaction() )
3948 if ( !authDbQuery( &query ) )
3951 if ( !authDbCommit() )
3954 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for setting: %2" ).arg( sett ), 2 );
3959 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3965 QgsDebugError( QStringLiteral(
"Select contains more than one for setting: %1" ).arg( sett ) );
3976bool QgsAuthManager::reencryptAllAuthenticationIdentities(
const QString &prevpass,
const QString &prevciv )
3978 ensureInitialized();
3985 for (
const auto &identid : ids )
3987 res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3992bool QgsAuthManager::reencryptAuthenticationIdentity(
3993 const QString &identid,
3994 const QString &prevpass,
3995 const QString &prevciv )
3997 ensureInitialized();
4006 query.prepare( QStringLiteral(
"SELECT key FROM %1 "
4007 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
4009 query.bindValue( QStringLiteral(
":id" ), identid );
4011 if ( !authDbQuery( &query ) )
4014 if ( !query.isActive() || !query.isSelect() )
4016 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
4020 if ( query.first() )
4026 QgsDebugError( QStringLiteral(
"Select contains more than one for identity id: %1" ).arg( identid ) );
4033 query.prepare( QStringLiteral(
"UPDATE %1 "
4035 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
4037 query.bindValue( QStringLiteral(
":id" ), identid );
4038 query.bindValue( QStringLiteral(
":key" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
4040 if ( !authDbStartTransaction() )
4043 if ( !authDbQuery( &query ) )
4046 if ( !authDbCommit() )
4049 QgsDebugMsgLevel( QStringLiteral(
"Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
4054 QgsDebugError( QStringLiteral(
"Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
4059bool QgsAuthManager::authDbOpen()
const
4061 ensureInitialized();
4067 if ( !authdb.isOpen() )
4069 if ( !authdb.open() )
4071 QgsDebugError( QStringLiteral(
"Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
4073 authdb.lastError().driverText(),
4074 authdb.lastError().databaseText() ) );
4082bool QgsAuthManager::authDbQuery( QSqlQuery *query )
const
4084 ensureInitialized();
4089 query->setForwardOnly(
true );
4090 if ( !query->exec() )
4092 const char *err = QT_TR_NOOP(
"Auth db query exec() FAILED" );
4098 if ( query->lastError().isValid() )
4100 QgsDebugError( QStringLiteral(
"Auth db query FAILED: %1\nError: %2" )
4101 .arg( query->executedQuery(),
4102 query->lastError().text() ) );
4110bool QgsAuthManager::authDbStartTransaction()
const
4112 ensureInitialized();
4119 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
4128bool QgsAuthManager::authDbCommit()
const
4130 ensureInitialized();
4137 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
4147bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query )
const
4149 ensureInitialized();
4156 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
4162 bool ok = authDbQuery( query );
4166 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
4178 ensureInitialized();
4180 for (
const auto &cert : certs )
4183 QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate>( source, cert ) );
4187QString QgsAuthManager::authPasswordHelperKeyName()
const
4189 ensureInitialized();
4191 const QFileInfo info( mAuthDbPath );
4192 const QString dbProfilePath = info.dir().dirName();
4195 return AUTH_PASSWORD_HELPER_KEY_NAME_BASE + ( dbProfilePath.compare( QLatin1String(
"default" ), Qt::CaseInsensitive ) == 0 ? QString() : dbProfilePath );
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.
static bool passwordHelperEnabled()
Password helper enabled getter.
bool exportAuthenticationConfigsToXml(const QString &filename, const QStringList &authcfgs, const QString &password=QString())
Export authentication configurations to an XML file.
Q_DECL_DEPRECATED 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
static bool hasConfigId(const QString &txt)
Returns whether a string includes an authcfg ID token.
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
static bool passwordHelperLoggingEnabled()
Password helper logging enabled getter.
bool rebuildCertTrustCache()
Rebuild certificate authority cache.
const QString authenticationDatabasePath() const
The standard authentication database file in ~/.qgis3/ or defined location.
static 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.
void setup(const QString &pluginPath=QString(), const QString &authDatabasePath=QString())
Sets up the authentication manager configuration.
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.
static 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.
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.
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.
QgsAuthMethod * createAuthMethod(const QString &authMethodKey)
Create an instance of the auth method.
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.
QFlags< Expansion > Expansions
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, bool silenceNullWarnings=false)
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)