23 #include <QMutexLocker>
26 #include <QSqlDatabase>
29 #include <QTextStream>
34 #include <QDomElement>
35 #include <QDomDocument>
36 #include <QRegularExpression>
38 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
39 #include <QRandomGenerator>
45 #include <QSslConfiguration>
66 const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral(
"auth_configs" );
67 const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral(
"auth_pass" );
68 const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral(
"auth_settings" );
69 const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral(
"auth_identities" );
70 const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral(
"auth_servers" );
71 const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral(
"auth_authorities" );
72 const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral(
"auth_trust" );
74 const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral(
"authcfg=([a-z]|[A-Z]|[0-9]){7}" );
77 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME(
"QGIS-Master-Password" );
78 const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME(
"QGIS" );
84 #elif defined(Q_OS_WIN)
86 #elif defined(Q_OS_LINUX)
95 QMutexLocker locker( &sMutex );
106 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
107 mMutex.reset(
new QMutex( QMutex::Recursive ) );
108 mMasterPasswordMutex.reset(
new QMutex( QMutex::Recursive ) );
110 mMutex = std::make_unique<QRecursiveMutex>();
111 mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
114 this, &QgsAuthManager::writeToConsole );
125 QMutexLocker locker( mMutex.get() );
130 const QString connectionName = QStringLiteral(
"authentication.configs:0x%1" ).arg(
reinterpret_cast<quintptr
>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char(
'0' ) );
131 QgsDebugMsgLevel( QStringLiteral(
"Using auth db connection name: %1 " ).arg( connectionName ), 2 );
132 if ( !QSqlDatabase::contains( connectionName ) )
134 QgsDebugMsgLevel( QStringLiteral(
"No existing connection, creating a new one" ), 2 );
135 authdb = QSqlDatabase::addDatabase( QStringLiteral(
"QSQLITE" ), connectionName );
138 if ( QThread::currentThread() != qApp->thread() )
140 QgsDebugMsgLevel( QStringLiteral(
"Scheduled auth db remove on thread close" ), 2 );
150 QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName,
this ]
152 QMutexLocker locker( mMutex.get() );
153 QSqlDatabase::removeDatabase( connectionName );
154 mConnectedThreads.remove( QThread::currentThread() );
155 }, Qt::DirectConnection );
157 mConnectedThreads.insert( QThread::currentThread(), connection );
163 authdb = QSqlDatabase::database( connectionName );
167 if ( !authdb.isOpen() )
169 if ( !authdb.open() )
171 QString err = tr(
"Opening of authentication db FAILED : %1" ).arg( authdb.lastError().text() );
189 mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
192 QCA::scanForPlugins();
194 QgsDebugMsgLevel( QStringLiteral(
"QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
195 QStringList capabilities;
197 capabilities = QCA::supportedFeatures();
198 QgsDebugMsgLevel( QStringLiteral(
"QCA supports: %1" ).arg( capabilities.join(
"," ) ), 2 );
201 if ( !QCA::isSupported(
"cert", QStringLiteral(
"qca-ossl" ) ) )
203 mAuthDisabled =
true;
204 mAuthDisabledMessage = tr(
"QCA's OpenSSL plugin (qca-ossl) is missing" );
208 QgsDebugMsgLevel( QStringLiteral(
"Prioritizing qca-ossl over all other QCA providers..." ), 2 );
209 const QCA::ProviderList provds = QCA::providers();
211 for ( QCA::Provider *p : provds )
213 QString pn = p->name();
215 if ( pn != QLatin1String(
"qca-ossl" ) )
217 pr = QCA::providerPriority( pn ) + 1;
219 QCA::setProviderPriority( pn, pr );
220 prlist << QStringLiteral(
"%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
222 QgsDebugMsgLevel( QStringLiteral(
"QCA provider priorities: %1" ).arg( prlist.join(
", " ) ), 2 );
229 QgsDebugMsgLevel( QStringLiteral(
"Authentication methods found: %1" ).arg( methods.join(
", " ) ), 2 );
231 if ( methods.isEmpty() )
233 mAuthDisabled =
true;
234 mAuthDisabledMessage = tr(
"No authentication method plugins found" );
240 mAuthDisabled =
true;
241 mAuthDisabledMessage = tr(
"No authentication method plugins could be loaded" );
245 mAuthDbPath = QDir::cleanPath( authDatabasePath );
249 QFileInfo dbdirinfo( dbinfo.path() );
250 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
252 if ( !dbdirinfo.exists() )
254 QgsDebugMsgLevel( QStringLiteral(
"Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
255 if ( !QDir().mkpath( dbdirinfo.filePath() ) )
257 const char *err = QT_TR_NOOP(
"Auth db directory path could not be created" );
264 if ( dbinfo.exists() )
266 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
268 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
273 if ( dbinfo.size() > 0 )
277 if ( !createCertTables() )
287 const char *passenv =
"QGIS_AUTH_PASSWORD_FILE";
290 QString passpath( getenv( passenv ) );
299 QFile passfile( passpath );
300 if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
302 QTextStream passin( &passfile );
303 while ( !passin.atEnd() )
305 masterpass = passin.readLine();
310 if ( !masterpass.isEmpty() )
314 QgsDebugMsgLevel( QStringLiteral(
"Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
318 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
324 QgsDebugMsg(
"QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
334 QgsDebugMsgLevel( QStringLiteral(
"Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
336 if ( !createConfigTables() )
339 if ( !createCertTables() )
350 bool QgsAuthManager::createConfigTables()
352 QMutexLocker locker( mMutex.get() );
356 const char *err = QT_TR_NOOP(
"Auth db could not be created and opened" );
367 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
368 " 'salt' TEXT NOT NULL,\n"
369 " 'civ' TEXT NOT NULL\n"
370 ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
371 query.prepare( qstr );
372 if ( !authDbQuery( &query ) )
376 qstr = QStringLiteral(
"CREATE TABLE %1 (\n"
377 " 'id' TEXT NOT NULL,\n"
378 " 'name' TEXT NOT NULL,\n"
380 " 'type' TEXT NOT NULL,\n"
381 " 'version' INTEGER NOT NULL\n"
383 query.prepare( qstr );
384 if ( !authDbQuery( &query ) )
389 query.prepare( qstr );
390 if ( !authDbQuery( &query ) )
395 query.prepare( qstr );
396 if ( !authDbQuery( &query ) )
403 bool QgsAuthManager::createCertTables()
405 QMutexLocker locker( mMutex.get() );
414 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
415 " 'setting' TEXT NOT NULL\n"
416 ", 'value' TEXT);" ).arg( authDbSettingsTable() );
417 query.prepare( qstr );
418 if ( !authDbQuery( &query ) )
423 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
424 " 'id' TEXT NOT NULL,\n"
425 " 'key' TEXT NOT NULL\n"
426 ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
427 query.prepare( qstr );
428 if ( !authDbQuery( &query ) )
432 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
433 query.prepare( qstr );
434 if ( !authDbQuery( &query ) )
439 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
440 " 'id' TEXT NOT NULL,\n"
441 " 'host' TEXT NOT NULL,\n"
444 query.prepare( qstr );
445 if ( !authDbQuery( &query ) )
449 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg(
authDatabaseServersTable() );
450 query.prepare( qstr );
451 if ( !authDbQuery( &query ) )
456 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
457 " 'id' TEXT NOT NULL\n"
458 ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
459 query.prepare( qstr );
460 if ( !authDbQuery( &query ) )
464 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
465 query.prepare( qstr );
466 if ( !authDbQuery( &query ) )
470 qstr = QStringLiteral(
"CREATE TABLE IF NOT EXISTS %1 (\n"
471 " 'id' TEXT NOT NULL\n"
472 ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
473 query.prepare( qstr );
474 if ( !authDbQuery( &query ) )
478 qstr = QStringLiteral(
"CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
479 query.prepare( qstr );
480 if ( !authDbQuery( &query ) )
491 QgsDebugMsg( QStringLiteral(
"Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
493 return mAuthDisabled;
498 return tr(
"Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
503 QMutexLocker locker( mMasterPasswordMutex.get() );
507 if ( mScheduledDbErase )
510 if ( mMasterPass.isEmpty() )
512 QgsDebugMsgLevel( QStringLiteral(
"Master password is not yet set by user" ), 2 );
513 if ( !masterPasswordInput() )
515 QgsDebugMsgLevel( QStringLiteral(
"Master password input canceled by user" ), 2 );
529 QgsDebugMsgLevel( QStringLiteral(
"Master password is set and verified" ), 2 );
535 QMutexLocker locker( mMutex.get() );
539 if ( mScheduledDbErase )
543 QString prevpass = QString( mMasterPass );
547 mMasterPass = prevpass;
548 const char *err = QT_TR_NOOP(
"Master password set: FAILED to verify, reset to previous" );
554 QgsDebugMsgLevel( QStringLiteral(
"Master password set: SUCCESS%1" ).arg( verify ?
" and verified" :
"" ), 2 );
564 if ( !masterPasswordRowsInDb( &rows ) )
566 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
574 QgsDebugMsgLevel( QStringLiteral(
"Master password: %1 rows in database" ).arg( rows ), 2 );
578 const char *err = QT_TR_NOOP(
"Master password: FAILED to find just one master password record in database" );
585 else if ( rows == 1 )
587 if ( !masterPasswordCheckAgainstDb( compare ) )
589 if ( compare.isNull() )
591 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
600 if ( mPassTries >= 5 )
602 mAuthDisabled =
true;
603 const char *err = QT_TR_NOOP(
"Master password: failed 5 times authentication system DISABLED" );
611 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
612 if ( compare.isNull() )
616 else if ( compare.isNull() )
618 if ( !masterPasswordStoreInDb() )
620 const char *err = QT_TR_NOOP(
"Master password: hash FAILED to be stored in database" );
629 QgsDebugMsgLevel( QStringLiteral(
"Master password: hash stored in database" ), 2 );
632 if ( !masterPasswordCheckAgainstDb() )
634 const char *err = QT_TR_NOOP(
"Master password: FAILED to verify against hash in database" );
644 QgsDebugMsgLevel( QStringLiteral(
"Master password: verified against hash in database" ), 2 );
654 return !mMasterPass.isEmpty();
659 return mMasterPass == pass;
663 bool keepbackup, QString *backuppath )
677 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up current database" ), 2 );
683 QString prevpass = QString( mMasterPass );
684 QString prevciv = QString( masterPasswordCiv() );
690 if ( ok && !masterPasswordClearDb() )
693 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not clear current password from database" );
699 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: cleared current password from database" ), 2 );
706 if ( ok && !masterPasswordStoreInDb() )
709 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not store new password in database" );
715 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: stored new password in database" ), 2 );
722 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify new password in database" );
728 if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
731 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt configs in database" );
737 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: re-encrypted configs in database" ), 2 );
741 if ( ok && !verifyPasswordCanDecryptConfigs() )
744 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
749 if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
752 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt settings in database" );
757 if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
760 const char *err = QT_TR_NOOP(
"Master password reset FAILED: could not re-encrypt identities in database" );
770 QString errdbbackup( dbbackup );
771 errdbbackup.replace( QLatin1String(
".db" ), QLatin1String(
"_ERROR.db" ) );
773 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
777 mMasterPass = prevpass;
779 QgsDebugMsg( QStringLiteral(
"Master password reset FAILED: reinstated previous password and database" ) );
783 *backuppath = errdbbackup;
789 if ( !keepbackup && !QFile::remove( dbbackup ) )
791 const char *err = QT_TR_NOOP(
"Master password reset: could not remove old database backup" );
799 QgsDebugMsgLevel( QStringLiteral(
"Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
801 *backuppath = dbbackup;
811 mScheduledDbErase = scheduleErase;
813 mScheduledDbEraseRequestEmitted =
false;
814 mScheduledDbEraseRequestCount = 0;
818 if ( !mScheduledDbEraseTimer )
820 mScheduledDbEraseTimer =
new QTimer(
this );
821 connect( mScheduledDbEraseTimer, &QTimer::timeout,
this, &QgsAuthManager::tryToStartDbErase );
822 mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
824 else if ( !mScheduledDbEraseTimer->isActive() )
826 mScheduledDbEraseTimer->start();
831 if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
832 mScheduledDbEraseTimer->stop();
841 qDeleteAll( mAuthMethods );
842 mAuthMethods.clear();
844 for (
const auto &authMethodKey : methods )
849 return !mAuthMethods.isEmpty();
859 QTimer::singleShot( 3, &loop, &QEventLoop::quit );
862 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
863 uint seed =
static_cast< uint
>( QTime::currentTime().msec() );
870 for (
int i = 0; i < len; i++ )
872 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
873 switch ( qrand() % 2 )
875 switch ( QRandomGenerator::system()->generate() % 2 )
879 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
880 id += (
'0' + qrand() % 10 );
882 id +=
static_cast<char>(
'0' + QRandomGenerator::system()->generate() % 10 );
886 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
887 id += (
'a' + qrand() % 26 );
889 id +=
static_cast<char>(
'a' + QRandomGenerator::system()->generate() % 26 );
894 if ( !configids.contains(
id ) )
899 QgsDebugMsgLevel( QStringLiteral(
"Generated unique ID: %1" ).arg(
id ), 2 );
910 const char *err = QT_TR_NOOP(
"Config ID is empty" );
916 return !configids.contains(
id );
921 const thread_local QRegularExpression authCfgRegExp( AUTH_CFG_REGEX );
922 return txt.indexOf( authCfgRegExp ) != -1;
927 QMutexLocker locker( mMutex.get() );
928 QStringList providerAuthMethodsKeys;
929 if ( !dataprovider.isEmpty() )
940 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1" ).arg(
authDatabaseConfigTable() ) );
942 if ( !authDbQuery( &query ) )
947 if ( query.isActive() && query.isSelect() )
949 while ( query.next() )
951 QString authcfg = query.value( 0 ).toString();
953 config.
setId( authcfg );
954 config.
setName( query.value( 1 ).toString() );
955 config.
setUri( query.value( 2 ).toString() );
956 config.
setMethod( query.value( 3 ).toString() );
957 config.
setVersion( query.value( 4 ).toInt() );
959 if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.
method() ) )
964 baseConfigs.insert( authcfg, config );
972 QMutexLocker locker( mMutex.get() );
979 if ( !authDbQuery( &query ) )
984 if ( query.isActive() )
986 QgsDebugMsgLevel( QStringLiteral(
"Syncing existing auth config and their auth methods" ), 2 );
987 mConfigAuthMethods.clear();
988 QStringList cfgmethods;
989 while ( query.next() )
991 mConfigAuthMethods.insert( query.value( 0 ).toString(),
992 query.value( 1 ).toString() );
993 cfgmethods << QStringLiteral(
"%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
995 QgsDebugMsgLevel( QStringLiteral(
"Stored auth config/methods:\n%1" ).arg( cfgmethods.join(
", " ) ), 2 );
1004 if ( !mConfigAuthMethods.contains( authcfg ) )
1006 QgsDebugMsg( QStringLiteral(
"No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1010 QString authMethodKey = mConfigAuthMethods.value( authcfg );
1020 return mConfigAuthMethods.value( authcfg, QString() );
1031 if ( !mAuthMethods.contains( authMethodKey ) )
1033 QgsDebugMsg( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1037 return mAuthMethods.value( authMethodKey );
1042 if ( !mAuthMethods.contains( authMethodKey ) )
1044 QgsDebugMsg( QStringLiteral(
"No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1054 if ( dataprovider.isEmpty() )
1056 return mAuthMethods;
1060 QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1061 while ( i != mAuthMethods.constEnd() )
1064 && ( i.value()->supportedDataProviders().contains( QStringLiteral(
"all" ) )
1065 || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1067 filteredmap.insert( i.key(), i.value() );
1075 QWidget *QgsAuthManager::authMethodEditWidget(
const QString &authMethodKey, QWidget *parent )
1079 return method->editWidget( parent );
1088 return QgsAuthMethod::Expansions();
1095 return QgsAuthMethod::Expansions();
1100 QMutexLocker locker( mMutex.get() );
1107 const char *err = QT_TR_NOOP(
"Store config: FAILED because config is invalid" );
1113 QString uid = mconfig.
id();
1114 bool passedinID = !uid.isEmpty();
1115 if ( uid.isEmpty() )
1123 const char *err = QT_TR_NOOP(
"Store config: FAILED because pre-defined config ID %1 is not unique" );
1134 if ( configstring.isEmpty() )
1136 const char *err = QT_TR_NOOP(
"Store config: FAILED because config string is empty" );
1142 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1143 QgsDebugMsg( QStringLiteral(
"name: %1" ).arg( config.name() ) );
1144 QgsDebugMsg( QStringLiteral(
"uri: %1" ).arg( config.uri() ) );
1145 QgsDebugMsg( QStringLiteral(
"type: %1" ).arg( config.method() ) );
1146 QgsDebugMsg( QStringLiteral(
"version: %1" ).arg( config.version() ) );
1147 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1151 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, name, uri, type, version, config) "
1154 query.bindValue( QStringLiteral(
":id" ), uid );
1155 query.bindValue( QStringLiteral(
":name" ), mconfig.
name() );
1156 query.bindValue( QStringLiteral(
":uri" ), mconfig.
uri() );
1157 query.bindValue( QStringLiteral(
":type" ), mconfig.
method() );
1158 query.bindValue( QStringLiteral(
":version" ), mconfig.
version() );
1159 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1161 if ( !authDbStartTransaction() )
1164 if ( !authDbQuery( &query ) )
1167 if ( !authDbCommit() )
1172 mconfig.
setId( uid );
1176 QgsDebugMsgLevel( QStringLiteral(
"Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1182 QMutexLocker locker( mMutex.get() );
1187 if ( !config.
isValid(
true ) )
1189 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is invalid" );
1196 if ( configstring.isEmpty() )
1198 const char *err = QT_TR_NOOP(
"Update config: FAILED because config is empty" );
1205 QgsDebugMsg( QStringLiteral(
"authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1206 QgsDebugMsg( QStringLiteral(
"id: %1" ).arg( config.
id() ) );
1211 QgsDebugMsg( QStringLiteral(
"config: %1" ).arg( configstring ) );
1215 if ( !query.prepare( QStringLiteral(
"UPDATE %1 "
1216 "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1219 const char *err = QT_TR_NOOP(
"Update config: FAILED to prepare query" );
1225 query.bindValue( QStringLiteral(
":id" ), config.
id() );
1226 query.bindValue( QStringLiteral(
":name" ), config.
name() );
1227 query.bindValue( QStringLiteral(
":uri" ), config.
uri() );
1228 query.bindValue( QStringLiteral(
":type" ), config.
method() );
1229 query.bindValue( QStringLiteral(
":version" ), config.
version() );
1230 query.bindValue( QStringLiteral(
":config" ),
QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1232 if ( !authDbStartTransaction() )
1235 if ( !authDbQuery( &query ) )
1238 if ( !authDbCommit() )
1246 QgsDebugMsgLevel( QStringLiteral(
"Update config SUCCESS for authcfg: %1" ).arg( config.
id() ), 2 );
1259 QMutexLocker locker( mMutex.get() );
1264 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version, config FROM %1 "
1269 query.prepare( QStringLiteral(
"SELECT id, name, uri, type, version FROM %1 "
1273 query.bindValue( QStringLiteral(
":id" ), authcfg );
1275 if ( !authDbQuery( &query ) )
1280 if ( query.isActive() && query.isSelect() )
1282 if ( query.first() )
1284 mconfig.
setId( query.value( 0 ).toString() );
1285 mconfig.
setName( query.value( 1 ).toString() );
1286 mconfig.
setUri( query.value( 2 ).toString() );
1287 mconfig.
setMethod( query.value( 3 ).toString() );
1288 mconfig.
setVersion( query.value( 4 ).toInt() );
1303 QgsDebugMsg( QStringLiteral(
"Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1306 QgsDebugMsgLevel( QStringLiteral(
"Load %1 config SUCCESS for authcfg: %2" ).arg( full ?
"full" :
"base", authcfg ), 2 );
1311 QgsDebugMsg( QStringLiteral(
"Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1321 QMutexLocker locker( mMutex.get() );
1325 if ( authcfg.isEmpty() )
1332 query.bindValue( QStringLiteral(
":id" ), authcfg );
1334 if ( !authDbStartTransaction() )
1337 if ( !authDbQuery( &query ) )
1340 if ( !authDbCommit() )
1347 QgsDebugMsgLevel( QStringLiteral(
"REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1354 if ( filename.isEmpty() )
1357 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1358 QDomElement root = document.createElement( QStringLiteral(
"qgis_authentication" ) );
1359 document.appendChild( root );
1362 if ( !password.isEmpty() )
1367 root.setAttribute( QStringLiteral(
"salt" ), salt );
1368 root.setAttribute( QStringLiteral(
"hash" ), hash );
1369 root.setAttribute( QStringLiteral(
"civ" ), civ );
1372 QDomElement configurations = document.createElement( QStringLiteral(
"configurations" ) );
1373 for (
const QString &authcfg : authcfgs )
1380 authMethodConfig.
writeXml( configurations, document );
1383 if ( !password.isEmpty() )
1385 QString configurationsString;
1386 QTextStream ts( &configurationsString );
1387 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1388 ts.setCodec(
"UTF-8" );
1390 configurations.save( ts, 2 );
1391 root.appendChild( document.createTextNode(
QgsAuthCrypto::encrypt( password, civ, configurationsString ) ) );
1395 root.appendChild( configurations );
1398 QFile file( filename );
1399 if ( !file.open( QFile::WriteOnly | QIODevice::Truncate ) )
1402 QTextStream ts( &file );
1403 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1404 ts.setCodec(
"UTF-8" );
1406 document.save( ts, 2 );
1413 QFile file( filename );
1414 if ( !file.open( QFile::ReadOnly ) )
1419 QDomDocument document( QStringLiteral(
"qgis_authentication" ) );
1420 if ( !document.setContent( &file ) )
1427 QDomElement root = document.documentElement();
1428 if ( root.tagName() != QLatin1String(
"qgis_authentication" ) )
1433 QDomElement configurations;
1434 if ( root.hasAttribute( QStringLiteral(
"salt" ) ) )
1436 QString salt = root.attribute( QStringLiteral(
"salt" ) );
1437 QString hash = root.attribute( QStringLiteral(
"hash" ) );
1438 QString civ = root.attribute( QStringLiteral(
"civ" ) );
1443 configurations = document.firstChild().toElement();
1447 configurations = root.firstChildElement( QStringLiteral(
"configurations" ) );
1450 QDomElement configuration = configurations.firstChildElement();
1451 while ( !configuration.isNull() )
1454 authMethodConfig.
readXml( configuration );
1457 configuration = configuration.nextSiblingElement();
1464 QMutexLocker locker( mMutex.get() );
1470 bool res = authDbTransactionQuery( &query );
1478 QgsDebugMsgLevel( QStringLiteral(
"Remove configs from database: %1" ).arg( res ?
"SUCCEEDED" :
"FAILED" ), 2 );
1485 QMutexLocker locker( mMutex.get() );
1488 const char *err = QT_TR_NOOP(
"No authentication database found" );
1496 if ( authConn.isValid() && authConn.isOpen() )
1500 QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral(
"yyyy-MM-dd-hhmmss" ) ) );
1502 dbbackup.replace( QLatin1String(
".db" ), QStringLiteral(
"_%1.db" ).arg( datestamp ) );
1506 const char *err = QT_TR_NOOP(
"Could not back up authentication database" );
1513 *backuppath = dbbackup;
1515 QgsDebugMsgLevel( QStringLiteral(
"Backed up auth database at %1" ).arg( dbbackup ), 2 );
1521 QMutexLocker locker( mMutex.get() );
1531 if ( backuppath && !dbbackup.isEmpty() )
1532 *backuppath = dbbackup;
1535 if ( dbinfo.exists() )
1537 if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1539 const char *err = QT_TR_NOOP(
"Auth db is not readable or writable by user" );
1547 const char *err = QT_TR_NOOP(
"No authentication database found" );
1555 const char *err = QT_TR_NOOP(
"Authentication database could not be deleted" );
1561 mMasterPass = QString();
1563 QgsDebugMsgLevel( QStringLiteral(
"Creating Auth db through QSqlDatabase initial connection" ), 2 );
1566 if ( !authConn.isValid() || !authConn.isOpen() )
1568 const char *err = QT_TR_NOOP(
"Authentication database could not be initialized" );
1574 if ( !createConfigTables() )
1576 const char *err = QT_TR_NOOP(
"FAILED to create auth database config tables" );
1582 if ( !createCertTables() )
1584 const char *err = QT_TR_NOOP(
"FAILED to create auth database cert tables" );
1600 const QString &dataprovider )
1610 QgsDebugMsg( QStringLiteral(
"Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1625 const QString &dataprovider )
1635 QgsDebugMsg( QStringLiteral(
"Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1651 const QString &dataprovider )
1661 QgsDebugMsg( QStringLiteral(
"Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1686 QgsDebugMsg( QStringLiteral(
"Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1695 QgsDebugMsgLevel( QStringLiteral(
"Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1704 QMutexLocker locker( mMutex.get() );
1705 if ( key.isEmpty() )
1708 QString storeval( value.toString() );
1724 query.prepare( QStringLiteral(
"INSERT INTO %1 (setting, value) "
1725 "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1727 query.bindValue( QStringLiteral(
":setting" ), key );
1728 query.bindValue( QStringLiteral(
":value" ), storeval );
1730 if ( !authDbStartTransaction() )
1733 if ( !authDbQuery( &query ) )
1736 if ( !authDbCommit() )
1739 QgsDebugMsgLevel( QStringLiteral(
"Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1745 QMutexLocker locker( mMutex.get() );
1746 if ( key.isEmpty() )
1752 QVariant value = defaultValue;
1754 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1755 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1757 query.bindValue( QStringLiteral(
":setting" ), key );
1759 if ( !authDbQuery( &query ) )
1762 if ( query.isActive() && query.isSelect() )
1764 if ( query.first() )
1768 value = QVariant(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1772 value = query.value( 0 );
1774 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1778 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1788 QMutexLocker locker( mMutex.get() );
1789 if ( key.isEmpty() )
1793 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
1794 "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1796 query.bindValue( QStringLiteral(
":setting" ), key );
1798 if ( !authDbQuery( &query ) )
1802 if ( query.isActive() && query.isSelect() )
1804 if ( query.first() )
1806 QgsDebugMsgLevel( QStringLiteral(
"Authentication setting exists for key: %1" ).arg( key ), 2 );
1811 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting key: %1" ).arg( key ) );
1821 QMutexLocker locker( mMutex.get() );
1822 if ( key.isEmpty() )
1827 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1829 query.bindValue( QStringLiteral(
":setting" ), key );
1831 if ( !authDbStartTransaction() )
1834 if ( !authDbQuery( &query ) )
1837 if ( !authDbCommit() )
1840 QgsDebugMsgLevel( QStringLiteral(
"REMOVED setting for key: %1" ).arg( key ), 2 );
1854 QMutexLocker locker( mMutex.get() );
1860 mCustomConfigByHostCache.clear();
1861 mHasCheckedIfCustomConfigByHostExists =
false;
1864 QgsDebugMsg( QStringLiteral(
"Init of SSL caches FAILED" ) );
1870 QMutexLocker locker( mMutex.get() );
1871 if ( cert.isNull() )
1873 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
1878 QgsDebugMsg( QStringLiteral(
"Passed private key is null" ) );
1888 QString certpem( cert.toPem() );
1892 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, key, cert) "
1893 "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1895 query.bindValue( QStringLiteral(
":id" ),
id );
1896 query.bindValue( QStringLiteral(
":key" ), keypem );
1897 query.bindValue( QStringLiteral(
":cert" ), certpem );
1899 if ( !authDbStartTransaction() )
1902 if ( !authDbQuery( &query ) )
1905 if ( !authDbCommit() )
1908 QgsDebugMsgLevel( QStringLiteral(
"Store certificate identity SUCCESS for id: %1" ).arg(
id ), 2 );
1914 QMutexLocker locker( mMutex.get() );
1915 QSslCertificate emptycert;
1916 QSslCertificate cert;
1921 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
1922 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1924 query.bindValue( QStringLiteral(
":id" ),
id );
1926 if ( !authDbQuery( &query ) )
1929 if ( query.isActive() && query.isSelect() )
1931 if ( query.first() )
1933 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1934 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity retrieved for id: %1" ).arg(
id ), 2 );
1938 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1948 QMutexLocker locker( mMutex.get() );
1949 QPair<QSslCertificate, QSslKey> bundle;
1957 query.prepare( QStringLiteral(
"SELECT key, cert FROM %1 "
1958 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1960 query.bindValue( QStringLiteral(
":id" ),
id );
1962 if ( !authDbQuery( &query ) )
1965 if ( query.isActive() && query.isSelect() )
1967 QSslCertificate cert;
1969 if ( query.first() )
1971 key = QSslKey(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1972 QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1975 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create private key" );
1980 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1981 if ( cert.isNull() )
1983 const char *err = QT_TR_NOOP(
"Retrieve certificate identity bundle: FAILED to create certificate" );
1988 QgsDebugMsgLevel( QStringLiteral(
"Certificate identity bundle retrieved for id: %1" ).arg(
id ), 2 );
1992 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate identity for id: %1" ).arg(
id ) );
1996 bundle = qMakePair( cert, key );
2003 QMutexLocker locker( mMutex.get() );
2007 return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
2009 return QStringList();
2014 QMutexLocker locker( mMutex.get() );
2015 QList<QSslCertificate> certs;
2018 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
2020 if ( !authDbQuery( &query ) )
2023 if ( query.isActive() && query.isSelect() )
2025 while ( query.next() )
2027 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2036 QMutexLocker locker( mMutex.get() );
2037 QStringList identityids = QStringList();
2043 query.prepare( QStringLiteral(
"SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
2045 if ( !authDbQuery( &query ) )
2050 if ( query.isActive() )
2052 while ( query.next() )
2054 identityids << query.value( 0 ).toString();
2062 QMutexLocker locker( mMutex.get() );
2067 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2068 "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2070 query.bindValue( QStringLiteral(
":id" ),
id );
2072 if ( !authDbQuery( &query ) )
2076 if ( query.isActive() && query.isSelect() )
2078 if ( query.first() )
2080 QgsDebugMsgLevel( QStringLiteral(
"Certificate bundle exists for id: %1" ).arg(
id ), 2 );
2085 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate bundle for id: %1" ).arg(
id ) );
2095 QMutexLocker locker( mMutex.get() );
2098 QgsDebugMsg( QStringLiteral(
"Passed bundle ID is empty" ) );
2104 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
2106 query.bindValue( QStringLiteral(
":id" ),
id );
2108 if ( !authDbStartTransaction() )
2111 if ( !authDbQuery( &query ) )
2114 if ( !authDbCommit() )
2117 QgsDebugMsgLevel( QStringLiteral(
"REMOVED certificate identity for id: %1" ).arg(
id ), 2 );
2123 QMutexLocker locker( mMutex.get() );
2126 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2135 QString certpem( cert.toPem() );
2138 query.prepare( QStringLiteral(
"INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2141 query.bindValue( QStringLiteral(
":id" ),
id );
2142 query.bindValue( QStringLiteral(
":host" ), config.
sslHostPort().trimmed() );
2143 query.bindValue( QStringLiteral(
":cert" ), certpem );
2144 query.bindValue( QStringLiteral(
":config" ), config.
configString() );
2146 if ( !authDbStartTransaction() )
2149 if ( !authDbQuery( &query ) )
2152 if ( !authDbCommit() )
2155 QgsDebugMsgLevel( QStringLiteral(
"Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2159 mHasCheckedIfCustomConfigByHostExists =
false;
2160 mCustomConfigByHostCache.clear();
2167 QMutexLocker locker( mMutex.get() );
2170 if (
id.isEmpty() || hostport.isEmpty() )
2172 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2177 query.prepare( QStringLiteral(
"SELECT id, host, cert, config FROM %1 "
2180 query.bindValue( QStringLiteral(
":id" ),
id );
2181 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2183 if ( !authDbQuery( &query ) )
2186 if ( query.isActive() && query.isSelect() )
2188 if ( query.first() )
2190 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2193 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2197 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2198 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2210 if ( hostport.isEmpty() )
2215 QMutexLocker locker( mMutex.get() );
2216 if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2218 if ( mCustomConfigByHostCache.contains( hostport ) )
2219 return mCustomConfigByHostCache.value( hostport );
2224 if ( !mHasCheckedIfCustomConfigByHostExists )
2226 mHasCheckedIfCustomConfigByHostExists =
true;
2228 if ( !authDbQuery( &query ) )
2230 mHasCustomConfigByHost =
false;
2233 if ( query.isActive() && query.isSelect() && query.first() )
2235 mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2236 if ( !mHasCustomConfigByHost )
2241 mHasCustomConfigByHost =
false;
2246 query.prepare( QString(
"SELECT id, host, cert, config FROM %1 "
2249 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2251 if ( !authDbQuery( &query ) )
2253 mCustomConfigByHostCache.insert( hostport, config );
2257 if ( query.isActive() && query.isSelect() )
2259 if ( query.first() )
2261 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2264 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2268 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2269 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2272 mCustomConfigByHostCache.insert( hostport, emptyconfig );
2277 mCustomConfigByHostCache.insert( hostport, config );
2283 QMutexLocker locker( mMutex.get() );
2284 QList<QgsAuthConfigSslServer> configs;
2289 if ( !authDbQuery( &query ) )
2292 if ( query.isActive() && query.isSelect() )
2294 while ( query.next() )
2297 config.
setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2301 configs.append( config );
2310 QMutexLocker locker( mMutex.get() );
2311 if (
id.isEmpty() || hostport.isEmpty() )
2313 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2318 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2321 query.bindValue( QStringLiteral(
":id" ),
id );
2322 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2324 if ( !authDbQuery( &query ) )
2328 if ( query.isActive() && query.isSelect() )
2330 if ( query.first() )
2332 QgsDebugMsgLevel( QStringLiteral(
"SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2337 QgsDebugMsg( QStringLiteral(
"Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ) );
2338 emit
messageOut( tr(
"Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2348 QMutexLocker locker( mMutex.get() );
2349 if (
id.isEmpty() || hostport.isEmpty() )
2351 QgsDebugMsg( QStringLiteral(
"Passed config ID or host:port is empty" ) );
2355 mHasCheckedIfCustomConfigByHostExists =
false;
2356 mCustomConfigByHostCache.clear();
2360 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id AND host = :host" ).arg(
authDatabaseServersTable() ) );
2362 query.bindValue( QStringLiteral(
":id" ),
id );
2363 query.bindValue( QStringLiteral(
":host" ), hostport.trimmed() );
2365 if ( !authDbStartTransaction() )
2368 if ( !authDbQuery( &query ) )
2371 if ( !authDbCommit() )
2374 QString shahostport( QStringLiteral(
"%1:%2" ).arg(
id, hostport ) );
2375 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2377 mIgnoredSslErrorsCache.remove( shahostport );
2380 QgsDebugMsgLevel( QStringLiteral(
"REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport,
id ), 2 );
2387 QMutexLocker locker( mMutex.get() );
2388 if ( !mIgnoredSslErrorsCache.isEmpty() )
2390 QgsDebugMsg( QStringLiteral(
"Ignored SSL errors cache items:" ) );
2391 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2392 while ( i != mIgnoredSslErrorsCache.constEnd() )
2395 for (
auto err : i.value() )
2399 QgsDebugMsg( QStringLiteral(
"%1 = %2" ).arg( i.key(), errs.join(
", " ) ) );
2411 QMutexLocker locker( mMutex.get() );
2414 QgsDebugMsg( QStringLiteral(
"Passed config is null" ) );
2418 QString shahostport( QStringLiteral(
"%1:%2" )
2421 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2423 mIgnoredSslErrorsCache.remove( shahostport );
2426 if ( !errenums.isEmpty() )
2428 mIgnoredSslErrorsCache.insert( shahostport, qgis::listToSet( errenums ) );
2429 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2434 QgsDebugMsgLevel( QStringLiteral(
"No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2440 QMutexLocker locker( mMutex.get() );
2441 const thread_local QRegularExpression rx( QRegularExpression::anchoredPattern(
"\\S+:\\S+:\\d+" ) );
2442 if ( !rx.match( shahostport ).hasMatch() )
2444 QgsDebugMsg(
"Passed shahostport does not match \\S+:\\S+:\\d+, "
2445 "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2449 if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2451 mIgnoredSslErrorsCache.remove( shahostport );
2454 if ( errors.isEmpty() )
2456 QgsDebugMsg( QStringLiteral(
"Passed errors list empty" ) );
2460 QSet<QSslError::SslError> errs;
2461 for (
const auto &error : errors )
2463 if ( error.error() == QSslError::NoError )
2466 errs.insert( error.error() );
2469 if ( errs.isEmpty() )
2471 QgsDebugMsg( QStringLiteral(
"Passed errors list does not contain errors" ) );
2475 mIgnoredSslErrorsCache.insert( shahostport, errs );
2477 QgsDebugMsgLevel( QStringLiteral(
"Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2484 QMutexLocker locker( mMutex.get() );
2485 QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2486 QHash<QString, QSet<QSslError::SslError> > nextcache;
2491 if ( !authDbQuery( &query ) )
2493 QgsDebugMsg( QStringLiteral(
"Rebuild of ignored SSL errors cache FAILED" ) );
2497 if ( query.isActive() && query.isSelect() )
2499 while ( query.next() )
2501 QString shahostport( QStringLiteral(
"%1:%2" )
2502 .arg( query.value( 0 ).toString().trimmed(),
2503 query.value( 1 ).toString().trimmed() ) );
2507 if ( !errenums.isEmpty() )
2509 nextcache.insert( shahostport, qgis::listToSet( errenums ) );
2511 if ( prevcache.contains( shahostport ) )
2513 prevcache.remove( shahostport );
2518 if ( !prevcache.isEmpty() )
2521 QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2522 while ( i != prevcache.constEnd() )
2524 nextcache.insert( i.key(), i.value() );
2529 if ( nextcache != mIgnoredSslErrorsCache )
2531 mIgnoredSslErrorsCache.clear();
2532 mIgnoredSslErrorsCache = nextcache;
2533 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2538 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2546 QMutexLocker locker( mMutex.get() );
2547 if ( certs.isEmpty() )
2549 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2553 for (
const auto &cert : certs )
2563 QMutexLocker locker( mMutex.get() );
2566 if ( cert.isNull() )
2568 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2575 QString pem( cert.toPem() );
2578 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, cert) "
2579 "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2581 query.bindValue( QStringLiteral(
":id" ),
id );
2582 query.bindValue( QStringLiteral(
":cert" ), pem );
2584 if ( !authDbStartTransaction() )
2587 if ( !authDbQuery( &query ) )
2590 if ( !authDbCommit() )
2593 QgsDebugMsgLevel( QStringLiteral(
"Store certificate authority SUCCESS for id: %1" ).arg(
id ), 2 );
2599 QMutexLocker locker( mMutex.get() );
2600 QSslCertificate emptycert;
2601 QSslCertificate cert;
2606 query.prepare( QStringLiteral(
"SELECT cert FROM %1 "
2607 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2609 query.bindValue( QStringLiteral(
":id" ),
id );
2611 if ( !authDbQuery( &query ) )
2614 if ( query.isActive() && query.isSelect() )
2616 if ( query.first() )
2618 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2619 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority retrieved for id: %1" ).arg(
id ), 2 );
2623 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2633 QMutexLocker locker( mMutex.get() );
2634 if ( cert.isNull() )
2636 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2643 query.prepare( QStringLiteral(
"SELECT value FROM %1 "
2644 "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2646 query.bindValue( QStringLiteral(
":id" ),
id );
2648 if ( !authDbQuery( &query ) )
2652 if ( query.isActive() && query.isSelect() )
2654 if ( query.first() )
2656 QgsDebugMsgLevel( QStringLiteral(
"Certificate authority exists for id: %1" ).arg(
id ), 2 );
2661 QgsDebugMsg( QStringLiteral(
"Select contains more than one certificate authority for id: %1" ).arg(
id ) );
2671 QMutexLocker locker( mMutex.get() );
2672 if ( cert.isNull() )
2674 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2682 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2684 query.bindValue( QStringLiteral(
":id" ),
id );
2686 if ( !authDbStartTransaction() )
2689 if ( !authDbQuery( &query ) )
2692 if ( !authDbCommit() )
2695 QgsDebugMsgLevel( QStringLiteral(
"REMOVED authority for id: %1" ).arg(
id ), 2 );
2701 return QSslConfiguration::systemCaCertificates();
2706 QMutexLocker locker( mMutex.get() );
2707 QList<QSslCertificate> certs;
2708 QList<QSslCertificate> filecerts;
2710 if ( cafileval.isNull() )
2714 if ( allowinvalid.isNull() )
2717 QString cafile( cafileval.toString() );
2718 if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2723 for (
const auto &cert : std::as_const( filecerts ) )
2725 if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2727 || cert.expiryDate() <= QDateTime::currentDateTime()
2728 || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2743 QMutexLocker locker( mMutex.get() );
2744 QList<QSslCertificate> certs;
2747 query.prepare( QStringLiteral(
"SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2749 if ( !authDbQuery( &query ) )
2752 if ( query.isActive() && query.isSelect() )
2754 while ( query.next() )
2756 certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2765 QMutexLocker locker( mMutex.get() );
2771 QMutexLocker locker( mMutex.get() );
2772 mCaCertsCache.clear();
2778 bool res = !mCaCertsCache.isEmpty();
2780 QgsDebugMsg( QStringLiteral(
"Rebuild of CA certs cache FAILED" ) );
2786 QMutexLocker locker( mMutex.get() );
2787 if ( cert.isNull() )
2789 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2799 QgsDebugMsg( QStringLiteral(
"Passed policy was default, all cert records in database were removed for id: %1" ).arg(
id ) );
2804 query.prepare( QStringLiteral(
"INSERT INTO %1 (id, policy) "
2805 "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2807 query.bindValue( QStringLiteral(
":id" ),
id );
2808 query.bindValue( QStringLiteral(
":policy" ),
static_cast< int >( policy ) );
2810 if ( !authDbStartTransaction() )
2813 if ( !authDbQuery( &query ) )
2816 if ( !authDbCommit() )
2819 QgsDebugMsgLevel( QStringLiteral(
"Store certificate trust policy SUCCESS for id: %1" ).arg(
id ), 2 );
2825 QMutexLocker locker( mMutex.get() );
2826 if ( cert.isNull() )
2828 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2835 query.prepare( QStringLiteral(
"SELECT policy FROM %1 "
2836 "WHERE id = :id" ).arg( authDbTrustTable() ) );
2838 query.bindValue( QStringLiteral(
":id" ),
id );
2840 if ( !authDbQuery( &query ) )
2844 if ( query.isActive() && query.isSelect() )
2846 if ( query.first() )
2849 QgsDebugMsgLevel( QStringLiteral(
"Authentication cert trust policy retrieved for id: %1" ).arg(
id ), 2 );
2853 QgsDebugMsg( QStringLiteral(
"Select contains more than one cert trust policy for id: %1" ).arg(
id ) );
2863 QMutexLocker locker( mMutex.get() );
2864 if ( certs.empty() )
2866 QgsDebugMsg( QStringLiteral(
"Passed certificate list has no certs" ) );
2870 for (
const auto &cert : certs )
2880 QMutexLocker locker( mMutex.get() );
2881 if ( cert.isNull() )
2883 QgsDebugMsg( QStringLiteral(
"Passed certificate is null" ) );
2891 query.prepare( QStringLiteral(
"DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2893 query.bindValue( QStringLiteral(
":id" ),
id );
2895 if ( !authDbStartTransaction() )
2898 if ( !authDbQuery( &query ) )
2901 if ( !authDbCommit() )
2904 QgsDebugMsgLevel( QStringLiteral(
"REMOVED cert trust policy for id: %1" ).arg(
id ), 2 );
2911 QMutexLocker locker( mMutex.get() );
2912 if ( cert.isNull() )
2922 if ( trustedids.contains(
id ) )
2926 else if ( untrustedids.contains(
id ) )
2941 return storeAuthSetting( QStringLiteral(
"certdefaulttrust" ),
static_cast< int >( policy ) );
2946 QMutexLocker locker( mMutex.get() );
2947 QVariant policy(
authSetting( QStringLiteral(
"certdefaulttrust" ) ) );
2948 if ( policy.isNull() )
2957 QMutexLocker locker( mMutex.get() );
2958 mCertTrustCache.clear();
2961 query.prepare( QStringLiteral(
"SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2963 if ( !authDbQuery( &query ) )
2965 QgsDebugMsg( QStringLiteral(
"Rebuild of cert trust policy cache FAILED" ) );
2969 if ( query.isActive() && query.isSelect() )
2971 while ( query.next() )
2973 QString
id = query.value( 0 ).toString();
2977 if ( mCertTrustCache.contains( policy ) )
2979 ids = mCertTrustCache.value( policy );
2981 mCertTrustCache.insert( policy, ids <<
id );
2985 QgsDebugMsgLevel( QStringLiteral(
"Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2991 QMutexLocker locker( mMutex.get() );
2995 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2997 QList<QSslCertificate> trustedcerts;
2998 for (
int i = 0; i < certpairs.size(); ++i )
3000 QSslCertificate cert( certpairs.at( i ).second );
3002 if ( trustedids.contains( certid ) )
3005 trustedcerts.append( cert );
3011 trustedcerts.append( cert );
3016 QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
3017 sslconfig.setCaCertificates( trustedcerts );
3018 QSslConfiguration::setDefaultConfiguration( sslconfig );
3020 return trustedcerts;
3025 QMutexLocker locker( mMutex.get() );
3026 if ( trustedCAs.isEmpty() )
3028 if ( mTrustedCaCertsCache.isEmpty() )
3035 const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
3037 QList<QSslCertificate> untrustedCAs;
3038 for (
int i = 0; i < certpairs.size(); ++i )
3040 QSslCertificate cert( certpairs.at( i ).second );
3041 if ( !trustedCAs.contains( cert ) )
3043 untrustedCAs.append( cert );
3046 return untrustedCAs;
3051 QMutexLocker locker( mMutex.get() );
3053 QgsDebugMsgLevel( QStringLiteral(
"Rebuilt trusted cert authorities cache" ), 2 );
3060 QMutexLocker locker( mMutex.get() );
3066 QMutexLocker locker( mMutex.get() );
3069 return passwordHelperWrite( mMasterPass );
3085 for (
const auto &authcfg : ids )
3103 void QgsAuthManager::writeToConsole(
const QString &message,
3117 msg += QLatin1String(
"WARNING: " );
3120 msg += QLatin1String(
"ERROR: " );
3127 QTextStream out( stdout, QIODevice::WriteOnly );
3128 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
3131 out << msg << Qt::endl;
3135 void QgsAuthManager::tryToStartDbErase()
3137 ++mScheduledDbEraseRequestCount;
3139 int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3140 if ( mScheduledDbEraseRequestCount >= trycutoff )
3143 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3148 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest attempt (%1 of %2)" )
3149 .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3155 mScheduledDbEraseRequestEmitted =
true;
3160 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emitted" ), 2 );
3163 QgsDebugMsgLevel( QStringLiteral(
"authDatabaseEraseRequest emit skipped" ), 2 );
3169 QMutexLocker locker( mMutex.get() );
3170 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3171 while ( iterator.hasNext() )
3174 QThread::disconnect( iterator.value() );
3181 qDeleteAll( mAuthMethods );
3184 if ( authConn.isValid() && authConn.isOpen() )
3187 delete mScheduledDbEraseTimer;
3188 mScheduledDbEraseTimer =
nullptr;
3189 QSqlDatabase::removeDatabase( QStringLiteral(
"authentication.configs" ) );
3193 QString QgsAuthManager::passwordHelperName()
const
3195 return tr(
"Password Helper" );
3199 void QgsAuthManager::passwordHelperLog(
const QString &msg )
const
3211 QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3213 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3214 job.setAutoDelete(
false );
3215 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3217 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3222 mPasswordHelperErrorCode = job.error();
3223 mPasswordHelperErrorMessage = tr(
"Delete password failed: %1." ).arg( job.errorString() );
3234 passwordHelperProcessError();
3238 QString QgsAuthManager::passwordHelperRead()
3243 QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3245 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3246 job.setAutoDelete(
false );
3247 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3249 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3254 mPasswordHelperErrorCode = job.error();
3261 password = job.textData();
3263 if ( password.isEmpty() )
3265 mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3276 passwordHelperProcessError();
3280 bool QgsAuthManager::passwordHelperWrite(
const QString &password )
3282 Q_ASSERT( !password.isEmpty() );
3285 QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3287 job.setInsecureFallback( settings.
value( QStringLiteral(
"password_helper_insecure_fallback" ),
false, QgsSettings::Section::Auth ).toBool() );
3288 job.setAutoDelete(
false );
3289 job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3290 job.setTextData( password );
3292 connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3297 mPasswordHelperErrorCode = job.error();
3305 passwordHelperClearErrors();
3310 passwordHelperProcessError();
3318 return settings.
value( QStringLiteral(
"use_password_helper" ),
true, QgsSettings::Section::Auth ).toBool();
3324 settings.
setValue( QStringLiteral(
"use_password_helper" ), enabled, QgsSettings::Section::Auth );
3325 emit
messageOut( enabled ? tr(
"Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3327 tr(
"Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3335 return settings.
value( QStringLiteral(
"password_helper_logging" ),
false, QgsSettings::Section::Auth ).toBool();
3341 settings.
setValue( QStringLiteral(
"password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3344 void QgsAuthManager::passwordHelperClearErrors()
3346 mPasswordHelperErrorCode = QKeychain::NoError;
3347 mPasswordHelperErrorMessage.clear();
3350 void QgsAuthManager::passwordHelperProcessError()
3352 if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3353 mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3354 mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3355 mPasswordHelperErrorCode == QKeychain::NotImplemented )
3361 mPasswordHelperErrorMessage = tr(
"There was an error and integration with your %1 system has been disabled. "
3362 "You can re-enable it at any time through the \"Utilities\" menu "
3363 "in the Authentication pane of the options dialog. %2" )
3366 if ( mPasswordHelperErrorCode != QKeychain::NoError )
3372 passwordHelperClearErrors();
3376 bool QgsAuthManager::masterPasswordInput()
3382 bool storedPasswordIsValid =
false;
3388 pass = passwordHelperRead();
3389 if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3395 storedPasswordIsValid =
true;
3411 if ( ok && !pass.isEmpty() && mMasterPass != pass )
3416 if ( passwordHelperWrite( pass ) )
3430 bool QgsAuthManager::masterPasswordRowsInDb(
int *rows )
const
3436 query.prepare( QStringLiteral(
"SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3438 bool ok = authDbQuery( &query );
3439 if ( query.first() )
3441 *rows = query.value( 0 ).toInt();
3453 if ( !masterPasswordRowsInDb( &rows ) )
3455 const char *err = QT_TR_NOOP(
"Master password: FAILED to access database" );
3461 return ( rows == 1 );
3464 bool QgsAuthManager::masterPasswordCheckAgainstDb(
const QString &compare )
const
3472 query.prepare( QStringLiteral(
"SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3473 if ( !authDbQuery( &query ) )
3476 if ( !query.first() )
3479 QString salt = query.value( 0 ).toString();
3480 QString hash = query.value( 1 ).toString();
3485 bool QgsAuthManager::masterPasswordStoreInDb()
const
3490 QString salt, hash, civ;
3494 query.prepare( QStringLiteral(
"INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3496 query.bindValue( QStringLiteral(
":salt" ), salt );
3497 query.bindValue( QStringLiteral(
":hash" ), hash );
3498 query.bindValue( QStringLiteral(
":civ" ), civ );
3500 if ( !authDbStartTransaction() )
3503 if ( !authDbQuery( &query ) )
3506 if ( !authDbCommit() )
3512 bool QgsAuthManager::masterPasswordClearDb()
3518 query.prepare( QStringLiteral(
"DELETE FROM %1" ).arg( authDbPassTable() ) );
3519 bool res = authDbTransactionQuery( &query );
3525 const QString QgsAuthManager::masterPasswordCiv()
const
3531 query.prepare( QStringLiteral(
"SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3532 if ( !authDbQuery( &query ) )
3535 if ( !query.first() )
3538 return query.value( 0 ).toString();
3543 QStringList configids = QStringList();
3551 if ( !authDbQuery( &query ) )
3556 if ( query.isActive() )
3558 while ( query.next() )
3560 configids << query.value( 0 ).toString();
3566 bool QgsAuthManager::verifyPasswordCanDecryptConfigs()
const
3577 if ( !authDbQuery( &query ) )
3580 if ( !query.isActive() || !query.isSelect() )
3582 QgsDebugMsg( QStringLiteral(
"Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3587 while ( query.next() )
3590 QString configstring(
QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3591 if ( configstring.isEmpty() )
3593 QgsDebugMsg( 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 );
3603 bool QgsAuthManager::reencryptAllAuthenticationConfigs(
const QString &prevpass,
const QString &prevciv )
3610 for (
const auto &configid : ids )
3612 res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3617 bool 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 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3640 if ( query.first() )
3646 QgsDebugMsg( 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 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3679 bool 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 QgsDebugMsg( 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 QgsDebugMsg( QStringLiteral(
"Reencrypt SUCCESS for setting: %2" ).arg( sett ) );
3748 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3754 QgsDebugMsg( QStringLiteral(
"Select contains more than one for setting: %1" ).arg( sett ) );
3765 bool QgsAuthManager::reencryptAllAuthenticationIdentities(
const QString &prevpass,
const QString &prevciv )
3772 for (
const auto &identid : ids )
3774 res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3779 bool 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 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3805 if ( query.first() )
3811 QgsDebugMsg( 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 QgsDebugMsg( QStringLiteral(
"Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3844 bool QgsAuthManager::authDbOpen()
const
3850 if ( !authdb.isOpen() )
3852 if ( !authdb.open() )
3854 QgsDebugMsg( QStringLiteral(
"Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3856 authdb.lastError().driverText(),
3857 authdb.lastError().databaseText() ) );
3865 bool 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 QgsDebugMsg( QStringLiteral(
"Auth db query FAILED: %1\nError: %2" )
3882 .arg( query->executedQuery(),
3883 query->lastError().text() ) );
3891 bool QgsAuthManager::authDbStartTransaction()
const
3898 const char *err = QT_TR_NOOP(
"Auth db FAILED to start transaction" );
3907 bool QgsAuthManager::authDbCommit()
const
3914 const char *err = QT_TR_NOOP(
"Auth db FAILED to rollback changes" );
3924 bool 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.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
bool isNull() const
Whether configuration is null (missing components)
const QSslCertificate sslCertificate() const
Server certificate object.
const QString sslHostPort() const
Server host:port string.
const QString configString() const
Configuration as a concatenated string.
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
static void passwordKeyHash(const QString &pass, QString *salt, QString *hash, QString *cipheriv=nullptr)
Generate SHA256 hash for master password, with iterations and salt.
static const QString encrypt(const QString &pass, const QString &cipheriv, const QString &text)
Encrypt data using master password.
static bool verifyPasswordKeyHash(const QString &pass, const QString &salt, const QString &hash, QString *hashderived=nullptr)
Verify existing master password hash to a re-generated one.
static const QString decrypt(const QString &pass, const QString &cipheriv, const QString &text)
Decrypt data using master password.
Singleton offering an interface to manage the authentication configuration database and to utilize co...
bool storeAuthSetting(const QString &key, const QVariant &value, bool encrypt=false)
Store an authentication setting (stored as string via QVariant( value ).toString() )
const QString authDatabaseServersTable() const
Name of the authentication database table that stores server exceptions/configs.
bool setDefaultCertTrustPolicy(QgsAuthCertUtils::CertTrustPolicy policy)
Sets the default certificate trust policy preferred by user.
void clearAllCachedConfigs()
Clear all authentication configs from authentication method caches.
const QSslCertificate certIdentity(const QString &id)
certIdentity get a certificate identity by id (sha hash)
const QStringList certIdentityBundleToPem(const QString &id)
certIdentityBundleToPem get a certificate identity bundle by id (sha hash) returned as PEM text
bool updateIgnoredSslErrorsCache(const QString &shahostport, const QList< QSslError > &errors)
Update ignored SSL error cache with possible ignored SSL errors, using sha:host:port key.
bool verifyMasterPassword(const QString &compare=QString())
Verify the supplied master password against any existing hash in authentication database.
bool updateIgnoredSslErrorsCacheFromConfig(const QgsAuthConfigSslServer &config)
Update ignored SSL error cache with possible ignored SSL errors, using server config.
MessageLevel
Message log level (mirrors that of QgsMessageLog, so it can also output there)
const QString disabledMessage() const
Standard message for when QCA's qca-ossl plugin is missing and system is disabled.
QgsAuthMethod * configAuthMethod(const QString &authcfg)
Gets authentication method from the config/provider cache.
void messageOut(const QString &message, const QString &tag=QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level=QgsAuthManager::INFO) const
Custom logging signal to relay to console output and QgsMessageLog.
bool storeCertIdentity(const QSslCertificate &cert, const QSslKey &key)
Store a certificate identity.
QgsAuthMethodsMap authMethodsMap(const QString &dataprovider=QString())
Gets available authentication methods mapped to their key.
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.
const QList< QSslCertificate > trustedCaCertsCache()
trustedCaCertsCache cache of trusted certificate authorities, ready for network connections
Configuration storage class for authentication method configurations.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
QString method() const
Textual key of the associated authentication method.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
void setName(const QString &name)
Sets name of configuration.
void setVersion(int version)
Sets version of the configuration.
bool readXml(const QDomElement &element)
from a DOM element.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
const QString name() const
Gets name of configuration.
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
void loadConfigString(const QString &configstr)
Load existing extended configuration.
bool writeXml(QDomElement &parentElement, QDomDocument &document)
Stores the configuration in a DOM.
int version() const
Gets version of the configuration.
void setMethod(const QString &method)
void setUri(const QString &uri)
void setId(const QString &id)
Sets auth config ID.
A registry / canonical manager of authentication methods.
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.
QHash< QString, QgsAuthMethodConfig > QgsAuthMethodConfigsMap
QHash< QString, QgsAuthMethod * > QgsAuthMethodsMap
#define QgsDebugMsgLevel(str, level)