Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsauthmanager.cpp
3 : : ---------------------
4 : : begin : October 5, 2014
5 : : copyright : (C) 2014 by Boundless Spatial, Inc. USA
6 : : author : Larry Shaffer
7 : : email : lshaffer at boundlessgeo dot com
8 : : ***************************************************************************
9 : : * *
10 : : * This program is free software; you can redistribute it and/or modify *
11 : : * it under the terms of the GNU General Public License as published by *
12 : : * the Free Software Foundation; either version 2 of the License, or *
13 : : * (at your option) any later version. *
14 : : * *
15 : : ***************************************************************************/
16 : :
17 : : #include "qgsauthmanager.h"
18 : :
19 : : #include <QDir>
20 : : #include <QEventLoop>
21 : : #include <QFile>
22 : : #include <QFileInfo>
23 : : #include <QMutexLocker>
24 : : #include <QObject>
25 : : #include <QSet>
26 : : #include <QSqlDatabase>
27 : : #include <QSqlError>
28 : : #include <QSqlQuery>
29 : : #include <QTextStream>
30 : : #include <QTime>
31 : : #include <QTimer>
32 : : #include <QVariant>
33 : : #include <QSqlDriver>
34 : :
35 : : #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
36 : : #include <QRandomGenerator>
37 : : #endif
38 : :
39 : : #include <QtCrypto>
40 : :
41 : : #ifndef QT_NO_SSL
42 : : #include <QSslConfiguration>
43 : : #endif
44 : :
45 : : // QtKeyChain library
46 : : #include "keychain.h"
47 : :
48 : : // QGIS includes
49 : : #include "qgsapplication.h"
50 : : #include "qgsauthcertutils.h"
51 : : #include "qgsauthcrypto.h"
52 : : #include "qgsauthmethod.h"
53 : : #include "qgsauthmethodmetadata.h"
54 : : #include "qgsauthmethodregistry.h"
55 : : #include "qgscredentials.h"
56 : : #include "qgslogger.h"
57 : : #include "qgsmessagelog.h"
58 : : #include "qgssettings.h"
59 : : #include "qgsruntimeprofiler.h"
60 : :
61 : : QgsAuthManager *QgsAuthManager::sInstance = nullptr;
62 : :
63 : 10 : const QString QgsAuthManager::AUTH_CONFIG_TABLE = QStringLiteral( "auth_configs" );
64 : 10 : const QString QgsAuthManager::AUTH_PASS_TABLE = QStringLiteral( "auth_pass" );
65 : 10 : const QString QgsAuthManager::AUTH_SETTINGS_TABLE = QStringLiteral( "auth_settings" );
66 : 10 : const QString QgsAuthManager::AUTH_IDENTITIES_TABLE = QStringLiteral( "auth_identities" );
67 : 10 : const QString QgsAuthManager::AUTH_SERVERS_TABLE = QStringLiteral( "auth_servers" );
68 : 10 : const QString QgsAuthManager::AUTH_AUTHORITIES_TABLE = QStringLiteral( "auth_authorities" );
69 : 10 : const QString QgsAuthManager::AUTH_TRUST_TABLE = QStringLiteral( "auth_trust" );
70 : 5 : const QString QgsAuthManager::AUTH_MAN_TAG = QObject::tr( "Authentication Manager" );
71 : 10 : const QString QgsAuthManager::AUTH_CFG_REGEX = QStringLiteral( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
72 : :
73 : :
74 : : const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_KEY_NAME( "QGIS-Master-Password" );
75 : : const QLatin1String QgsAuthManager::AUTH_PASSWORD_HELPER_FOLDER_NAME( "QGIS" );
76 : :
77 : :
78 : :
79 : : #if defined(Q_OS_MAC)
80 : : const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Keychain" );
81 : : #elif defined(Q_OS_WIN)
82 : : const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
83 : : #elif defined(Q_OS_LINUX)
84 : : const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( QStringLiteral( "Wallet/KeyRing" ) );
85 : : #else
86 : 5 : const QString QgsAuthManager::AUTH_PASSWORD_HELPER_DISPLAY_NAME( "Password Manager" );
87 : : #endif
88 : :
89 : 3 : QgsAuthManager *QgsAuthManager::instance()
90 : : {
91 : 3 : static QMutex sMutex;
92 : 3 : QMutexLocker locker( &sMutex );
93 : 3 : if ( !sInstance )
94 : : {
95 : 3 : sInstance = new QgsAuthManager( );
96 : 3 : }
97 : 3 : return sInstance;
98 : 3 : }
99 : :
100 : :
101 : 3 : QgsAuthManager::QgsAuthManager()
102 : 6 : {
103 : : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
104 : : mMutex.reset( new QMutex( QMutex::Recursive ) );
105 : : mMasterPasswordMutex.reset( new QMutex( QMutex::Recursive ) );
106 : : #else
107 : 3 : mMutex = std::make_unique<QRecursiveMutex>();
108 : 3 : mMasterPasswordMutex = std::make_unique<QRecursiveMutex>();
109 : : #endif
110 : 3 : connect( this, &QgsAuthManager::messageOut,
111 : : this, &QgsAuthManager::writeToConsole );
112 : 3 : }
113 : :
114 : 0 : QSqlDatabase QgsAuthManager::authDatabaseConnection() const
115 : : {
116 : 0 : QSqlDatabase authdb;
117 : 0 : if ( isDisabled() )
118 : 0 : return authdb;
119 : :
120 : : // while everything we use from QSqlDatabase here is thread safe, we need to ensure
121 : : // that the connection cleanup on thread finalization happens in a predictable order
122 : 0 : QMutexLocker locker( mMutex.get() );
123 : :
124 : : // Sharing the same connection between threads is not allowed.
125 : : // We use a dedicated connection for each thread requiring access to the database,
126 : : // using the thread address as connection name.
127 : 0 : const QString connectionName = QStringLiteral( "authentication.configs:0x%1" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) );
128 : 0 : QgsDebugMsgLevel( QStringLiteral( "Using auth db connection name: %1 " ).arg( connectionName ), 2 );
129 : 0 : if ( !QSqlDatabase::contains( connectionName ) )
130 : : {
131 : 0 : QgsDebugMsgLevel( QStringLiteral( "No existing connection, creating a new one" ), 2 );
132 : 0 : authdb = QSqlDatabase::addDatabase( QStringLiteral( "QSQLITE" ), connectionName );
133 : 0 : authdb.setDatabaseName( authenticationDatabasePath() );
134 : : // for background threads, remove database when current thread finishes
135 : 0 : if ( QThread::currentThread() != qApp->thread() )
136 : : {
137 : 0 : QgsDebugMsgLevel( QStringLiteral( "Scheduled auth db remove on thread close" ), 2 );
138 : :
139 : : // IMPORTANT - we use a direct connection here, because the database removal must happen immediately
140 : : // when the thread finishes, and we cannot let this get queued on the main thread's event loop (where
141 : : // QgsAuthManager lives).
142 : : // Otherwise, the QSqlDatabase's private data's thread gets reset immediately the QThread::finished,
143 : : // and a subsequent call to QSqlDatabase::database with the same thread address (yep it happens, actually a lot)
144 : : // triggers a condition in QSqlDatabase which detects the nullptr private thread data and returns an invalid database instead.
145 : : // QSqlDatabase::removeDatabase is thread safe, so this is ok to do.
146 : : // Right about now is a good time to re-evaluate your selected career ;)
147 : 0 : QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName, this ]
148 : : {
149 : 0 : QMutexLocker locker( mMutex.get() );
150 : 0 : QSqlDatabase::removeDatabase( connectionName );
151 : 0 : mConnectedThreads.remove( QThread::currentThread() );
152 : 0 : }, Qt::DirectConnection );
153 : :
154 : 0 : mConnectedThreads.insert( QThread::currentThread(), connection );
155 : 0 : }
156 : 0 : }
157 : : else
158 : : {
159 : 0 : QgsDebugMsgLevel( QStringLiteral( "Reusing existing connection" ), 2 );
160 : 0 : authdb = QSqlDatabase::database( connectionName );
161 : : }
162 : 0 : locker.unlock();
163 : :
164 : 0 : if ( !authdb.isOpen() )
165 : : {
166 : 0 : if ( !authdb.open() )
167 : : {
168 : 0 : const char *err = QT_TR_NOOP( "Opening of authentication db FAILED" );
169 : 0 : QgsDebugMsg( err );
170 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
171 : 0 : }
172 : 0 : }
173 : :
174 : 0 : return authdb;
175 : 0 : }
176 : :
177 : 3 : bool QgsAuthManager::init( const QString &pluginPath, const QString &authDatabasePath )
178 : : {
179 : 3 : if ( mAuthInit )
180 : 0 : return true;
181 : 3 : mAuthInit = true;
182 : :
183 : 3 : QgsScopedRuntimeProfile profile( tr( "Initializing authentication manager" ) );
184 : :
185 : 3 : QgsDebugMsgLevel( QStringLiteral( "Initializing QCA..." ), 2 );
186 : 3 : mQcaInitializer = std::make_unique<QCA::Initializer>( QCA::Practical, 256 );
187 : :
188 : 3 : QgsDebugMsgLevel( QStringLiteral( "QCA initialized." ), 2 );
189 : 3 : QCA::scanForPlugins();
190 : :
191 : 3 : QgsDebugMsgLevel( QStringLiteral( "QCA Plugin Diagnostics Context: %1" ).arg( QCA::pluginDiagnosticText() ), 2 );
192 : 3 : QStringList capabilities;
193 : :
194 : 3 : capabilities = QCA::supportedFeatures();
195 : 3 : QgsDebugMsgLevel( QStringLiteral( "QCA supports: %1" ).arg( capabilities.join( "," ) ), 2 );
196 : :
197 : : // do run-time check for qca-ossl plugin
198 : 6 : if ( !QCA::isSupported( "cert", QStringLiteral( "qca-ossl" ) ) )
199 : : {
200 : 0 : mAuthDisabled = true;
201 : 0 : mAuthDisabledMessage = tr( "QCA's OpenSSL plugin (qca-ossl) is missing" );
202 : 0 : return isDisabled();
203 : : }
204 : :
205 : 3 : QgsDebugMsgLevel( QStringLiteral( "Prioritizing qca-ossl over all other QCA providers..." ), 2 );
206 : 3 : const QCA::ProviderList provds = QCA::providers();
207 : 3 : QStringList prlist;
208 : 9 : for ( QCA::Provider *p : provds )
209 : : {
210 : 6 : QString pn = p->name();
211 : 6 : int pr = 0;
212 : 6 : if ( pn != QLatin1String( "qca-ossl" ) )
213 : : {
214 : 3 : pr = QCA::providerPriority( pn ) + 1;
215 : 3 : }
216 : 6 : QCA::setProviderPriority( pn, pr );
217 : 12 : prlist << QStringLiteral( "%1:%2" ).arg( pn ).arg( QCA::providerPriority( pn ) );
218 : 6 : }
219 : 3 : QgsDebugMsgLevel( QStringLiteral( "QCA provider priorities: %1" ).arg( prlist.join( ", " ) ), 2 );
220 : :
221 : 3 : QgsDebugMsgLevel( QStringLiteral( "Populating auth method registry" ), 3 );
222 : 3 : QgsAuthMethodRegistry *authreg = QgsAuthMethodRegistry::instance( pluginPath );
223 : :
224 : 3 : QStringList methods = authreg->authMethodList();
225 : :
226 : 3 : QgsDebugMsgLevel( QStringLiteral( "Authentication methods found: %1" ).arg( methods.join( ", " ) ), 2 );
227 : :
228 : 3 : if ( methods.isEmpty() )
229 : : {
230 : 3 : mAuthDisabled = true;
231 : 3 : mAuthDisabledMessage = tr( "No authentication method plugins found" );
232 : 3 : return isDisabled();
233 : : }
234 : :
235 : 0 : if ( !registerCoreAuthMethods() )
236 : : {
237 : 0 : mAuthDisabled = true;
238 : 0 : mAuthDisabledMessage = tr( "No authentication method plugins could be loaded" );
239 : 0 : return isDisabled();
240 : : }
241 : :
242 : 0 : mAuthDbPath = QDir::cleanPath( authDatabasePath );
243 : 0 : QgsDebugMsgLevel( QStringLiteral( "Auth database path: %1" ).arg( authenticationDatabasePath() ), 2 );
244 : :
245 : 0 : QFileInfo dbinfo( authenticationDatabasePath() );
246 : 0 : QFileInfo dbdirinfo( dbinfo.path() );
247 : 0 : QgsDebugMsgLevel( QStringLiteral( "Auth db directory path: %1" ).arg( dbdirinfo.filePath() ), 2 );
248 : :
249 : 0 : if ( !dbdirinfo.exists() )
250 : : {
251 : 0 : QgsDebugMsgLevel( QStringLiteral( "Auth db directory path does not exist, making path: %1" ).arg( dbdirinfo.filePath() ), 2 );
252 : 0 : if ( !QDir().mkpath( dbdirinfo.filePath() ) )
253 : : {
254 : 0 : const char *err = QT_TR_NOOP( "Auth db directory path could not be created" );
255 : 0 : QgsDebugMsg( err );
256 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
257 : 0 : return false;
258 : : }
259 : 0 : }
260 : :
261 : 0 : if ( dbinfo.exists() )
262 : : {
263 : 0 : if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
264 : : {
265 : 0 : const char *err = QT_TR_NOOP( "Auth db is not readable or writable by user" );
266 : 0 : QgsDebugMsg( err );
267 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
268 : 0 : return false;
269 : : }
270 : 0 : if ( dbinfo.size() > 0 )
271 : : {
272 : 0 : QgsDebugMsgLevel( QStringLiteral( "Auth db exists and has data" ), 2 );
273 : :
274 : 0 : if ( !createCertTables() )
275 : 0 : return false;
276 : :
277 : 0 : updateConfigAuthMethods();
278 : :
279 : : #ifndef QT_NO_SSL
280 : 0 : initSslCaches();
281 : : #endif
282 : :
283 : : // set the master password from first line of file defined by QGIS_AUTH_PASSWORD_FILE env variable
284 : 0 : const char *passenv = "QGIS_AUTH_PASSWORD_FILE";
285 : 0 : if ( getenv( passenv ) && masterPasswordHashInDatabase() )
286 : : {
287 : 0 : QString passpath( getenv( passenv ) );
288 : : // clear the env variable, so it can not be accessed from plugins, etc.
289 : : // (note: stored QgsApplication::systemEnvVars() skips this env variable as well)
290 : : #ifdef Q_OS_WIN
291 : : putenv( passenv );
292 : : #else
293 : 0 : unsetenv( passenv );
294 : : #endif
295 : 0 : QString masterpass;
296 : 0 : QFile passfile( passpath );
297 : 0 : if ( passfile.exists() && passfile.open( QIODevice::ReadOnly | QIODevice::Text ) )
298 : : {
299 : 0 : QTextStream passin( &passfile );
300 : 0 : while ( !passin.atEnd() )
301 : : {
302 : 0 : masterpass = passin.readLine();
303 : 0 : break;
304 : : }
305 : 0 : passfile.close();
306 : 0 : }
307 : 0 : if ( !masterpass.isEmpty() )
308 : : {
309 : 0 : if ( setMasterPassword( masterpass, true ) )
310 : : {
311 : 0 : QgsDebugMsgLevel( QStringLiteral( "Authentication master password set from QGIS_AUTH_PASSWORD_FILE" ), 2 );
312 : 0 : }
313 : : else
314 : : {
315 : 0 : QgsDebugMsg( "QGIS_AUTH_PASSWORD_FILE set, but FAILED to set password using: " + passpath );
316 : 0 : return false;
317 : : }
318 : 0 : }
319 : : else
320 : : {
321 : 0 : QgsDebugMsg( "QGIS_AUTH_PASSWORD_FILE set, but FAILED to read password from: " + passpath );
322 : 0 : return false;
323 : : }
324 : 0 : }
325 : :
326 : 0 : return true;
327 : : }
328 : 0 : }
329 : : else
330 : : {
331 : 0 : QgsDebugMsgLevel( QStringLiteral( "Auth db does not exist: creating through QSqlDatabase initial connection" ), 2 );
332 : :
333 : 0 : if ( !createConfigTables() )
334 : 0 : return false;
335 : :
336 : 0 : if ( !createCertTables() )
337 : 0 : return false;
338 : : }
339 : :
340 : : #ifndef QT_NO_SSL
341 : 0 : initSslCaches();
342 : : #endif
343 : :
344 : 0 : return true;
345 : 3 : }
346 : :
347 : 0 : bool QgsAuthManager::createConfigTables()
348 : : {
349 : 0 : QMutexLocker locker( mMutex.get() );
350 : : // create and open the db
351 : 0 : if ( !authDbOpen() )
352 : : {
353 : 0 : const char *err = QT_TR_NOOP( "Auth db could not be created and opened" );
354 : 0 : QgsDebugMsg( err );
355 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
356 : 0 : return false;
357 : : }
358 : :
359 : 0 : QSqlQuery query( authDatabaseConnection() );
360 : :
361 : : // create the tables
362 : 0 : QString qstr;
363 : :
364 : 0 : qstr = QStringLiteral( "CREATE TABLE %1 (\n"
365 : : " 'salt' TEXT NOT NULL,\n"
366 : : " 'civ' TEXT NOT NULL\n"
367 : 0 : ", 'hash' TEXT NOT NULL);" ).arg( authDbPassTable() );
368 : 0 : query.prepare( qstr );
369 : 0 : if ( !authDbQuery( &query ) )
370 : 0 : return false;
371 : 0 : query.clear();
372 : :
373 : 0 : qstr = QStringLiteral( "CREATE TABLE %1 (\n"
374 : : " 'id' TEXT NOT NULL,\n"
375 : : " 'name' TEXT NOT NULL,\n"
376 : : " 'uri' TEXT,\n"
377 : : " 'type' TEXT NOT NULL,\n"
378 : : " 'version' INTEGER NOT NULL\n"
379 : 0 : ", 'config' TEXT NOT NULL);" ).arg( authDatabaseConfigTable() );
380 : 0 : query.prepare( qstr );
381 : 0 : if ( !authDbQuery( &query ) )
382 : 0 : return false;
383 : 0 : query.clear();
384 : :
385 : 0 : qstr = QStringLiteral( "CREATE UNIQUE INDEX 'id_index' on %1 (id ASC);" ).arg( authDatabaseConfigTable() );
386 : 0 : query.prepare( qstr );
387 : 0 : if ( !authDbQuery( &query ) )
388 : 0 : return false;
389 : 0 : query.clear();
390 : :
391 : 0 : qstr = QStringLiteral( "CREATE INDEX 'uri_index' on %1 (uri ASC);" ).arg( authDatabaseConfigTable() );
392 : 0 : query.prepare( qstr );
393 : 0 : if ( !authDbQuery( &query ) )
394 : 0 : return false;
395 : 0 : query.clear();
396 : :
397 : 0 : return true;
398 : 0 : }
399 : :
400 : 0 : bool QgsAuthManager::createCertTables()
401 : : {
402 : 0 : QMutexLocker locker( mMutex.get() );
403 : : // NOTE: these tables were added later, so IF NOT EXISTS is used
404 : 0 : QgsDebugMsgLevel( QStringLiteral( "Creating cert tables in auth db" ), 2 );
405 : :
406 : 0 : QSqlQuery query( authDatabaseConnection() );
407 : :
408 : : // create the tables
409 : 0 : QString qstr;
410 : :
411 : 0 : qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
412 : : " 'setting' TEXT NOT NULL\n"
413 : 0 : ", 'value' TEXT);" ).arg( authDbSettingsTable() );
414 : 0 : query.prepare( qstr );
415 : 0 : if ( !authDbQuery( &query ) )
416 : 0 : return false;
417 : 0 : query.clear();
418 : :
419 : :
420 : 0 : qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
421 : : " 'id' TEXT NOT NULL,\n"
422 : : " 'key' TEXT NOT NULL\n"
423 : 0 : ", 'cert' TEXT NOT NULL);" ).arg( authDbIdentitiesTable() );
424 : 0 : query.prepare( qstr );
425 : 0 : if ( !authDbQuery( &query ) )
426 : 0 : return false;
427 : 0 : query.clear();
428 : :
429 : 0 : qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbIdentitiesTable() );
430 : 0 : query.prepare( qstr );
431 : 0 : if ( !authDbQuery( &query ) )
432 : 0 : return false;
433 : 0 : query.clear();
434 : :
435 : :
436 : 0 : qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
437 : : " 'id' TEXT NOT NULL,\n"
438 : : " 'host' TEXT NOT NULL,\n"
439 : : " 'cert' TEXT\n"
440 : 0 : ", 'config' TEXT NOT NULL);" ).arg( authDatabaseServersTable() );
441 : 0 : query.prepare( qstr );
442 : 0 : if ( !authDbQuery( &query ) )
443 : 0 : return false;
444 : 0 : query.clear();
445 : :
446 : 0 : qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'host_index' on %1 (host ASC);" ).arg( authDatabaseServersTable() );
447 : 0 : query.prepare( qstr );
448 : 0 : if ( !authDbQuery( &query ) )
449 : 0 : return false;
450 : 0 : query.clear();
451 : :
452 : :
453 : 0 : qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
454 : : " 'id' TEXT NOT NULL\n"
455 : 0 : ", 'cert' TEXT NOT NULL);" ).arg( authDbAuthoritiesTable() );
456 : 0 : query.prepare( qstr );
457 : 0 : if ( !authDbQuery( &query ) )
458 : 0 : return false;
459 : 0 : query.clear();
460 : :
461 : 0 : qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbAuthoritiesTable() );
462 : 0 : query.prepare( qstr );
463 : 0 : if ( !authDbQuery( &query ) )
464 : 0 : return false;
465 : 0 : query.clear();
466 : :
467 : 0 : qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
468 : : " 'id' TEXT NOT NULL\n"
469 : 0 : ", 'policy' TEXT NOT NULL);" ).arg( authDbTrustTable() );
470 : 0 : query.prepare( qstr );
471 : 0 : if ( !authDbQuery( &query ) )
472 : 0 : return false;
473 : 0 : query.clear();
474 : :
475 : 0 : qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS 'id_index' on %1 (id ASC);" ).arg( authDbTrustTable() );
476 : 0 : query.prepare( qstr );
477 : 0 : if ( !authDbQuery( &query ) )
478 : 0 : return false;
479 : 0 : query.clear();
480 : :
481 : 0 : return true;
482 : 0 : }
483 : :
484 : 6 : bool QgsAuthManager::isDisabled() const
485 : : {
486 : 6 : if ( mAuthDisabled )
487 : : {
488 : 6 : QgsDebugMsg( QStringLiteral( "Authentication system DISABLED: QCA's qca-ossl (OpenSSL) plugin is missing" ) );
489 : 6 : }
490 : 6 : return mAuthDisabled;
491 : : }
492 : :
493 : 0 : const QString QgsAuthManager::disabledMessage() const
494 : : {
495 : 0 : return tr( "Authentication system is DISABLED:\n%1" ).arg( mAuthDisabledMessage );
496 : 0 : }
497 : :
498 : 0 : bool QgsAuthManager::setMasterPassword( bool verify )
499 : : {
500 : 0 : QMutexLocker locker( mMasterPasswordMutex.get() );
501 : 0 : if ( isDisabled() )
502 : 0 : return false;
503 : :
504 : 0 : if ( mScheduledDbErase )
505 : 0 : return false;
506 : :
507 : 0 : if ( mMasterPass.isEmpty() )
508 : : {
509 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password is not yet set by user" ), 2 );
510 : 0 : if ( !masterPasswordInput() )
511 : : {
512 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password input canceled by user" ), 2 );
513 : 0 : return false;
514 : : }
515 : 0 : }
516 : : else
517 : : {
518 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password is set" ), 2 );
519 : 0 : if ( !verify )
520 : 0 : return true;
521 : : }
522 : :
523 : 0 : if ( !verifyMasterPassword() )
524 : 0 : return false;
525 : :
526 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password is set and verified" ), 2 );
527 : 0 : return true;
528 : 0 : }
529 : :
530 : 0 : bool QgsAuthManager::setMasterPassword( const QString &pass, bool verify )
531 : : {
532 : 0 : QMutexLocker locker( mMutex.get() );
533 : 0 : if ( isDisabled() )
534 : 0 : return false;
535 : :
536 : 0 : if ( mScheduledDbErase )
537 : 0 : return false;
538 : :
539 : : // since this is generally for automation, we don't care if passed-in is same as existing
540 : 0 : QString prevpass = QString( mMasterPass );
541 : 0 : mMasterPass = pass;
542 : 0 : if ( verify && !verifyMasterPassword() )
543 : : {
544 : 0 : mMasterPass = prevpass;
545 : 0 : const char *err = QT_TR_NOOP( "Master password set: FAILED to verify, reset to previous" );
546 : 0 : QgsDebugMsg( err );
547 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
548 : 0 : return false;
549 : : }
550 : :
551 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password set: SUCCESS%1" ).arg( verify ? " and verified" : "" ), 2 );
552 : 0 : return true;
553 : 0 : }
554 : :
555 : 0 : bool QgsAuthManager::verifyMasterPassword( const QString &compare )
556 : : {
557 : 0 : if ( isDisabled() )
558 : 0 : return false;
559 : :
560 : 0 : int rows = 0;
561 : 0 : if ( !masterPasswordRowsInDb( &rows ) )
562 : : {
563 : 0 : const char *err = QT_TR_NOOP( "Master password: FAILED to access database" );
564 : 0 : QgsDebugMsg( err );
565 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
566 : :
567 : 0 : clearMasterPassword();
568 : 0 : return false;
569 : : }
570 : :
571 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password: %1 rows in database" ).arg( rows ), 2 );
572 : :
573 : 0 : if ( rows > 1 )
574 : : {
575 : 0 : const char *err = QT_TR_NOOP( "Master password: FAILED to find just one master password record in database" );
576 : 0 : QgsDebugMsg( err );
577 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
578 : :
579 : 0 : clearMasterPassword();
580 : 0 : return false;
581 : : }
582 : 0 : else if ( rows == 1 )
583 : : {
584 : 0 : if ( !masterPasswordCheckAgainstDb( compare ) )
585 : : {
586 : 0 : if ( compare.isNull() ) // don't complain when comparing, since it could be an incomplete comparison string
587 : : {
588 : 0 : const char *err = QT_TR_NOOP( "Master password: FAILED to verify against hash in database" );
589 : 0 : QgsDebugMsg( err );
590 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
591 : :
592 : 0 : clearMasterPassword();
593 : :
594 : 0 : emit masterPasswordVerified( false );
595 : 0 : }
596 : 0 : ++mPassTries;
597 : 0 : if ( mPassTries >= 5 )
598 : : {
599 : 0 : mAuthDisabled = true;
600 : 0 : const char *err = QT_TR_NOOP( "Master password: failed 5 times authentication system DISABLED" );
601 : 0 : QgsDebugMsg( err );
602 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
603 : 0 : }
604 : 0 : return false;
605 : : }
606 : : else
607 : : {
608 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password: verified against hash in database" ), 2 );
609 : 0 : if ( compare.isNull() )
610 : 0 : emit masterPasswordVerified( true );
611 : : }
612 : 0 : }
613 : 0 : else if ( compare.isNull() ) // compares should never be stored
614 : : {
615 : 0 : if ( !masterPasswordStoreInDb() )
616 : : {
617 : 0 : const char *err = QT_TR_NOOP( "Master password: hash FAILED to be stored in database" );
618 : 0 : QgsDebugMsg( err );
619 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
620 : :
621 : 0 : clearMasterPassword();
622 : 0 : return false;
623 : : }
624 : : else
625 : : {
626 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password: hash stored in database" ), 2 );
627 : : }
628 : : // double-check storing
629 : 0 : if ( !masterPasswordCheckAgainstDb() )
630 : : {
631 : 0 : const char *err = QT_TR_NOOP( "Master password: FAILED to verify against hash in database" );
632 : 0 : QgsDebugMsg( err );
633 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
634 : :
635 : 0 : clearMasterPassword();
636 : 0 : emit masterPasswordVerified( false );
637 : 0 : return false;
638 : : }
639 : : else
640 : : {
641 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password: verified against hash in database" ), 2 );
642 : 0 : emit masterPasswordVerified( true );
643 : : }
644 : 0 : }
645 : :
646 : 0 : return true;
647 : 0 : }
648 : :
649 : 0 : bool QgsAuthManager::masterPasswordIsSet() const
650 : : {
651 : 0 : return !mMasterPass.isEmpty();
652 : : }
653 : :
654 : 0 : bool QgsAuthManager::masterPasswordSame( const QString &pass ) const
655 : : {
656 : 0 : return mMasterPass == pass;
657 : : }
658 : :
659 : 0 : bool QgsAuthManager::resetMasterPassword( const QString &newpass, const QString &oldpass,
660 : : bool keepbackup, QString *backuppath )
661 : : {
662 : 0 : if ( isDisabled() )
663 : 0 : return false;
664 : :
665 : : // verify caller knows the current master password
666 : : // this means that the user will have had to already set the master password as well
667 : 0 : if ( !masterPasswordSame( oldpass ) )
668 : 0 : return false;
669 : :
670 : 0 : QString dbbackup;
671 : 0 : if ( !backupAuthenticationDatabase( &dbbackup ) )
672 : 0 : return false;
673 : :
674 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: backed up current database" ), 2 );
675 : :
676 : : // create new database and connection
677 : 0 : authDatabaseConnection();
678 : :
679 : : // store current password and civ
680 : 0 : QString prevpass = QString( mMasterPass );
681 : 0 : QString prevciv = QString( masterPasswordCiv() );
682 : :
683 : : // on ANY FAILURE from this point, reinstate previous password and database
684 : 0 : bool ok = true;
685 : :
686 : : // clear password hash table (also clears mMasterPass)
687 : 0 : if ( ok && !masterPasswordClearDb() )
688 : : {
689 : 0 : ok = false;
690 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not clear current password from database" );
691 : 0 : QgsDebugMsg( err );
692 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
693 : 0 : }
694 : 0 : if ( ok )
695 : : {
696 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: cleared current password from database" ), 2 );
697 : 0 : }
698 : :
699 : : // mMasterPass empty, set new password (don't verify, since not stored yet)
700 : 0 : setMasterPassword( newpass, false );
701 : :
702 : : // store new password hash
703 : 0 : if ( ok && !masterPasswordStoreInDb() )
704 : : {
705 : 0 : ok = false;
706 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not store new password in database" );
707 : 0 : QgsDebugMsg( err );
708 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
709 : 0 : }
710 : 0 : if ( ok )
711 : : {
712 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: stored new password in database" ), 2 );
713 : 0 : }
714 : :
715 : : // verify it stored password properly
716 : 0 : if ( ok && !verifyMasterPassword() )
717 : : {
718 : 0 : ok = false;
719 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not verify new password in database" );
720 : 0 : QgsDebugMsg( err );
721 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
722 : 0 : }
723 : :
724 : : // re-encrypt everything with new password
725 : 0 : if ( ok && !reencryptAllAuthenticationConfigs( prevpass, prevciv ) )
726 : : {
727 : 0 : ok = false;
728 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt configs in database" );
729 : 0 : QgsDebugMsg( err );
730 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
731 : 0 : }
732 : 0 : if ( ok )
733 : : {
734 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: re-encrypted configs in database" ), 2 );
735 : 0 : }
736 : :
737 : : // verify it all worked
738 : 0 : if ( ok && !verifyPasswordCanDecryptConfigs() )
739 : : {
740 : 0 : ok = false;
741 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not verify password can decrypt re-encrypted configs" );
742 : 0 : QgsDebugMsg( err );
743 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
744 : 0 : }
745 : :
746 : 0 : if ( ok && !reencryptAllAuthenticationSettings( prevpass, prevciv ) )
747 : : {
748 : 0 : ok = false;
749 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt settings in database" );
750 : 0 : QgsDebugMsg( err );
751 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
752 : 0 : }
753 : :
754 : 0 : if ( ok && !reencryptAllAuthenticationIdentities( prevpass, prevciv ) )
755 : : {
756 : 0 : ok = false;
757 : 0 : const char *err = QT_TR_NOOP( "Master password reset FAILED: could not re-encrypt identities in database" );
758 : 0 : QgsDebugMsg( err );
759 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
760 : 0 : }
761 : :
762 : : // something went wrong, reinstate previous password and database
763 : 0 : if ( !ok )
764 : : {
765 : : // backup database of failed attempt, for inspection
766 : 0 : authDatabaseConnection().close();
767 : 0 : QString errdbbackup( dbbackup );
768 : 0 : errdbbackup.replace( QLatin1String( ".db" ), QLatin1String( "_ERROR.db" ) );
769 : 0 : QFile::rename( authenticationDatabasePath(), errdbbackup );
770 : 0 : QgsDebugMsg( QStringLiteral( "Master password reset FAILED: backed up failed db at %1" ).arg( errdbbackup ) );
771 : :
772 : : // reinstate previous database and password
773 : 0 : QFile::rename( dbbackup, authenticationDatabasePath() );
774 : 0 : mMasterPass = prevpass;
775 : 0 : authDatabaseConnection();
776 : 0 : QgsDebugMsg( QStringLiteral( "Master password reset FAILED: reinstated previous password and database" ) );
777 : :
778 : : // assign error db backup
779 : 0 : if ( backuppath )
780 : 0 : *backuppath = errdbbackup;
781 : :
782 : 0 : return false;
783 : 0 : }
784 : :
785 : :
786 : 0 : if ( !keepbackup && !QFile::remove( dbbackup ) )
787 : : {
788 : 0 : const char *err = QT_TR_NOOP( "Master password reset: could not remove old database backup" );
789 : 0 : QgsDebugMsg( err );
790 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
791 : : // a non-blocking error, continue
792 : 0 : }
793 : :
794 : 0 : if ( keepbackup )
795 : : {
796 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: backed up previous db at %1" ).arg( dbbackup ), 2 );
797 : 0 : if ( backuppath )
798 : 0 : *backuppath = dbbackup;
799 : 0 : }
800 : :
801 : 0 : QgsDebugMsgLevel( QStringLiteral( "Master password reset: SUCCESS" ), 2 );
802 : 0 : emit authDatabaseChanged();
803 : 0 : return true;
804 : 0 : }
805 : :
806 : 0 : void QgsAuthManager::setScheduledAuthDatabaseErase( bool scheduleErase )
807 : : {
808 : 0 : mScheduledDbErase = scheduleErase;
809 : : // any call (start or stop) should reset these
810 : 0 : mScheduledDbEraseRequestEmitted = false;
811 : 0 : mScheduledDbEraseRequestCount = 0;
812 : :
813 : 0 : if ( scheduleErase )
814 : : {
815 : 0 : if ( !mScheduledDbEraseTimer )
816 : : {
817 : 0 : mScheduledDbEraseTimer = new QTimer( this );
818 : 0 : connect( mScheduledDbEraseTimer, &QTimer::timeout, this, &QgsAuthManager::tryToStartDbErase );
819 : 0 : mScheduledDbEraseTimer->start( mScheduledDbEraseRequestWait * 1000 );
820 : 0 : }
821 : 0 : else if ( !mScheduledDbEraseTimer->isActive() )
822 : : {
823 : 0 : mScheduledDbEraseTimer->start();
824 : 0 : }
825 : 0 : }
826 : : else
827 : : {
828 : 0 : if ( mScheduledDbEraseTimer && mScheduledDbEraseTimer->isActive() )
829 : 0 : mScheduledDbEraseTimer->stop();
830 : : }
831 : 0 : }
832 : :
833 : 0 : bool QgsAuthManager::registerCoreAuthMethods()
834 : : {
835 : 0 : if ( isDisabled() )
836 : 0 : return false;
837 : :
838 : 0 : qDeleteAll( mAuthMethods );
839 : 0 : mAuthMethods.clear();
840 : 0 : const QStringList methods = QgsAuthMethodRegistry::instance()->authMethodList();
841 : 0 : for ( const auto &authMethodKey : methods )
842 : : {
843 : 0 : mAuthMethods.insert( authMethodKey, QgsAuthMethodRegistry::instance()->authMethod( authMethodKey ).release() );
844 : : }
845 : :
846 : 0 : return !mAuthMethods.isEmpty();
847 : 0 : }
848 : :
849 : 0 : const QString QgsAuthManager::uniqueConfigId() const
850 : : {
851 : 0 : QStringList configids = configIds();
852 : 0 : QString id;
853 : 0 : int len = 7;
854 : : // sleep just a bit to make sure the current time has changed
855 : 0 : QEventLoop loop;
856 : 0 : QTimer::singleShot( 3, &loop, &QEventLoop::quit );
857 : 0 : loop.exec();
858 : :
859 : 3 : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
860 : : uint seed = static_cast< uint >( QTime::currentTime().msec() );
861 : : qsrand( seed );
862 : : #endif
863 : :
864 : 0 : while ( true )
865 : : {
866 : 0 : id.clear();
867 : 0 : for ( int i = 0; i < len; i++ )
868 : 3 : {
869 : 3 : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
870 : : switch ( qrand() % 2 )
871 : 3 : #else
872 : 3 : switch ( QRandomGenerator::system()->generate() % 2 )
873 : 3 : #endif
874 : 3 : {
875 : 3 : case 0:
876 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
877 : : id += ( '0' + qrand() % 10 );
878 : : #else
879 : 0 : id += static_cast<char>( '0' + QRandomGenerator::system()->generate() % 10 );
880 : : #endif
881 : 0 : break;
882 : : case 1:
883 : : #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
884 : : id += ( 'a' + qrand() % 26 );
885 : : #else
886 : 0 : id += static_cast<char>( 'a' + QRandomGenerator::system()->generate() % 26 );
887 : : #endif
888 : 0 : break;
889 : : }
890 : 0 : }
891 : 0 : if ( !configids.contains( id ) )
892 : : {
893 : 0 : break;
894 : : }
895 : 3 : }
896 : 3 : QgsDebugMsgLevel( QStringLiteral( "Generated unique ID: %1" ).arg( id ), 2 );
897 : 0 : return id;
898 : 0 : }
899 : :
900 : 0 : bool QgsAuthManager::configIdUnique( const QString &id ) const
901 : : {
902 : 0 : if ( isDisabled() )
903 : 0 : return false;
904 : 3 :
905 : 0 : if ( id.isEmpty() )
906 : : {
907 : 0 : const char *err = QT_TR_NOOP( "Config ID is empty" );
908 : 0 : QgsDebugMsg( err );
909 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
910 : 3 : return false;
911 : : }
912 : 0 : QStringList configids = configIds();
913 : 3 : return !configids.contains( id );
914 : 0 : }
915 : :
916 : 3 : bool QgsAuthManager::hasConfigId( const QString &txt ) const
917 : : {
918 : 0 : QRegExp rx( AUTH_CFG_REGEX );
919 : 0 : return rx.indexIn( txt ) != -1;
920 : 0 : }
921 : :
922 : 0 : QgsAuthMethodConfigsMap QgsAuthManager::availableAuthMethodConfigs( const QString &dataprovider )
923 : : {
924 : 0 : QMutexLocker locker( mMutex.get() );
925 : 0 : QStringList providerAuthMethodsKeys;
926 : 0 : if ( !dataprovider.isEmpty() )
927 : : {
928 : 0 : providerAuthMethodsKeys = authMethodsKeys( dataprovider.toLower() );
929 : 0 : }
930 : :
931 : 0 : QgsAuthMethodConfigsMap baseConfigs;
932 : :
933 : 0 : if ( isDisabled() )
934 : 0 : return baseConfigs;
935 : :
936 : 0 : QSqlQuery query( authDatabaseConnection() );
937 : 0 : query.prepare( QStringLiteral( "SELECT id, name, uri, type, version FROM %1" ).arg( authDatabaseConfigTable() ) );
938 : :
939 : 0 : if ( !authDbQuery( &query ) )
940 : : {
941 : 0 : return baseConfigs;
942 : : }
943 : :
944 : 0 : if ( query.isActive() && query.isSelect() )
945 : : {
946 : 0 : while ( query.next() )
947 : : {
948 : 0 : QString authcfg = query.value( 0 ).toString();
949 : 0 : QgsAuthMethodConfig config;
950 : 0 : config.setId( authcfg );
951 : 0 : config.setName( query.value( 1 ).toString() );
952 : 0 : config.setUri( query.value( 2 ).toString() );
953 : 0 : config.setMethod( query.value( 3 ).toString() );
954 : 0 : config.setVersion( query.value( 4 ).toInt() );
955 : :
956 : 0 : if ( !dataprovider.isEmpty() && !providerAuthMethodsKeys.contains( config.method() ) )
957 : : {
958 : 0 : continue;
959 : : }
960 : :
961 : 0 : baseConfigs.insert( authcfg, config );
962 : 0 : }
963 : 0 : }
964 : 0 : return baseConfigs;
965 : 0 : }
966 : :
967 : 0 : void QgsAuthManager::updateConfigAuthMethods()
968 : : {
969 : 0 : QMutexLocker locker( mMutex.get() );
970 : 0 : if ( isDisabled() )
971 : 0 : return;
972 : :
973 : 0 : QSqlQuery query( authDatabaseConnection() );
974 : 0 : query.prepare( QStringLiteral( "SELECT id, type FROM %1" ).arg( authDatabaseConfigTable() ) );
975 : :
976 : 0 : if ( !authDbQuery( &query ) )
977 : : {
978 : 0 : return;
979 : : }
980 : :
981 : 0 : if ( query.isActive() )
982 : : {
983 : 0 : QgsDebugMsgLevel( QStringLiteral( "Syncing existing auth config and their auth methods" ), 2 );
984 : 0 : mConfigAuthMethods.clear();
985 : 0 : QStringList cfgmethods;
986 : 0 : while ( query.next() )
987 : : {
988 : 0 : mConfigAuthMethods.insert( query.value( 0 ).toString(),
989 : 0 : query.value( 1 ).toString() );
990 : 0 : cfgmethods << QStringLiteral( "%1=%2" ).arg( query.value( 0 ).toString(), query.value( 1 ).toString() );
991 : : }
992 : 0 : QgsDebugMsgLevel( QStringLiteral( "Stored auth config/methods:\n%1" ).arg( cfgmethods.join( ", " ) ), 2 );
993 : 0 : }
994 : 0 : }
995 : :
996 : 0 : QgsAuthMethod *QgsAuthManager::configAuthMethod( const QString &authcfg )
997 : : {
998 : 0 : if ( isDisabled() )
999 : 0 : return nullptr;
1000 : :
1001 : 0 : if ( !mConfigAuthMethods.contains( authcfg ) )
1002 : : {
1003 : 0 : QgsDebugMsg( QStringLiteral( "No config auth method found in database for authcfg: %1" ).arg( authcfg ) );
1004 : 0 : return nullptr;
1005 : : }
1006 : :
1007 : 0 : QString authMethodKey = mConfigAuthMethods.value( authcfg );
1008 : :
1009 : 0 : return authMethod( authMethodKey );
1010 : 0 : }
1011 : :
1012 : 0 : QString QgsAuthManager::configAuthMethodKey( const QString &authcfg ) const
1013 : : {
1014 : 0 : if ( isDisabled() )
1015 : 0 : return QString();
1016 : :
1017 : 0 : return mConfigAuthMethods.value( authcfg, QString() );
1018 : 0 : }
1019 : :
1020 : :
1021 : 0 : QStringList QgsAuthManager::authMethodsKeys( const QString &dataprovider )
1022 : : {
1023 : 0 : return authMethodsMap( dataprovider.toLower() ).keys();
1024 : 0 : }
1025 : :
1026 : 0 : QgsAuthMethod *QgsAuthManager::authMethod( const QString &authMethodKey )
1027 : : {
1028 : 0 : if ( !mAuthMethods.contains( authMethodKey ) )
1029 : : {
1030 : 0 : QgsDebugMsg( QStringLiteral( "No auth method registered for auth method key: %1" ).arg( authMethodKey ) );
1031 : 0 : return nullptr;
1032 : : }
1033 : :
1034 : 0 : return mAuthMethods.value( authMethodKey );
1035 : 0 : }
1036 : :
1037 : 0 : QgsAuthMethodsMap QgsAuthManager::authMethodsMap( const QString &dataprovider )
1038 : : {
1039 : 0 : if ( dataprovider.isEmpty() )
1040 : : {
1041 : 0 : return mAuthMethods;
1042 : : }
1043 : :
1044 : 0 : QgsAuthMethodsMap filteredmap;
1045 : 0 : QgsAuthMethodsMap::const_iterator i = mAuthMethods.constBegin();
1046 : 0 : while ( i != mAuthMethods.constEnd() )
1047 : : {
1048 : 0 : if ( i.value()
1049 : 0 : && ( i.value()->supportedDataProviders().contains( QStringLiteral( "all" ) )
1050 : 0 : || i.value()->supportedDataProviders().contains( dataprovider ) ) )
1051 : : {
1052 : 0 : filteredmap.insert( i.key(), i.value() );
1053 : 0 : }
1054 : 0 : ++i;
1055 : : }
1056 : 0 : return filteredmap;
1057 : 0 : }
1058 : :
1059 : 0 : QWidget *QgsAuthManager::authMethodEditWidget( const QString &authMethodKey, QWidget *parent )
1060 : : {
1061 : 0 : return QgsAuthMethodRegistry::instance()->editWidget( authMethodKey, parent );
1062 : 0 : }
1063 : :
1064 : 0 : QgsAuthMethod::Expansions QgsAuthManager::supportedAuthMethodExpansions( const QString &authcfg )
1065 : : {
1066 : 0 : if ( isDisabled() )
1067 : 0 : return QgsAuthMethod::Expansions();
1068 : :
1069 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1070 : 0 : if ( authmethod )
1071 : : {
1072 : 0 : return authmethod->supportedExpansions();
1073 : : }
1074 : 0 : return QgsAuthMethod::Expansions();
1075 : 0 : }
1076 : :
1077 : 0 : bool QgsAuthManager::storeAuthenticationConfig( QgsAuthMethodConfig &mconfig )
1078 : : {
1079 : 0 : QMutexLocker locker( mMutex.get() );
1080 : 0 : if ( !setMasterPassword( true ) )
1081 : 0 : return false;
1082 : :
1083 : : // don't need to validate id, since it has not be defined yet
1084 : 0 : if ( !mconfig.isValid() )
1085 : : {
1086 : 0 : const char *err = QT_TR_NOOP( "Store config: FAILED because config is invalid" );
1087 : 0 : QgsDebugMsg( err );
1088 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1089 : 0 : return false;
1090 : : }
1091 : :
1092 : 0 : QString uid = mconfig.id();
1093 : 0 : bool passedinID = !uid.isEmpty();
1094 : 0 : if ( uid.isEmpty() )
1095 : : {
1096 : 0 : uid = uniqueConfigId();
1097 : 0 : }
1098 : 0 : else if ( configIds().contains( uid ) )
1099 : : {
1100 : 0 : const char *err = QT_TR_NOOP( "Store config: FAILED because pre-defined config ID is not unique" );
1101 : 0 : QgsDebugMsg( err );
1102 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1103 : 0 : return false;
1104 : : }
1105 : :
1106 : 0 : QString configstring = mconfig.configString();
1107 : 0 : if ( configstring.isEmpty() )
1108 : : {
1109 : 0 : const char *err = QT_TR_NOOP( "Store config: FAILED because config string is empty" );
1110 : 0 : QgsDebugMsg( err );
1111 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1112 : 0 : return false;
1113 : : }
1114 : : #if( 0 )
1115 : : QgsDebugMsg( QStringLiteral( "authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1116 : : QgsDebugMsg( QStringLiteral( "name: %1" ).arg( config.name() ) );
1117 : : QgsDebugMsg( QStringLiteral( "uri: %1" ).arg( config.uri() ) );
1118 : : QgsDebugMsg( QStringLiteral( "type: %1" ).arg( config.method() ) );
1119 : : QgsDebugMsg( QStringLiteral( "version: %1" ).arg( config.version() ) );
1120 : : QgsDebugMsg( QStringLiteral( "config: %1" ).arg( configstring ) ); // DO NOT LEAVE THIS LINE UNCOMMENTED !
1121 : : #endif
1122 : :
1123 : 0 : QSqlQuery query( authDatabaseConnection() );
1124 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (id, name, uri, type, version, config) "
1125 : 0 : "VALUES (:id, :name, :uri, :type, :version, :config)" ).arg( authDatabaseConfigTable() ) );
1126 : :
1127 : 0 : query.bindValue( QStringLiteral( ":id" ), uid );
1128 : 0 : query.bindValue( QStringLiteral( ":name" ), mconfig.name() );
1129 : 0 : query.bindValue( QStringLiteral( ":uri" ), mconfig.uri() );
1130 : 0 : query.bindValue( QStringLiteral( ":type" ), mconfig.method() );
1131 : 0 : query.bindValue( QStringLiteral( ":version" ), mconfig.version() );
1132 : 0 : query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1133 : :
1134 : 0 : if ( !authDbStartTransaction() )
1135 : 0 : return false;
1136 : :
1137 : 0 : if ( !authDbQuery( &query ) )
1138 : 0 : return false;
1139 : :
1140 : 0 : if ( !authDbCommit() )
1141 : 0 : return false;
1142 : :
1143 : : // passed-in config should now be like as if it was just loaded from db
1144 : 0 : if ( !passedinID )
1145 : 0 : mconfig.setId( uid );
1146 : :
1147 : 0 : updateConfigAuthMethods();
1148 : :
1149 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store config SUCCESS for authcfg: %1" ).arg( uid ), 2 );
1150 : 0 : return true;
1151 : :
1152 : 0 : }
1153 : :
1154 : 0 : bool QgsAuthManager::updateAuthenticationConfig( const QgsAuthMethodConfig &config )
1155 : : {
1156 : 0 : QMutexLocker locker( mMutex.get() );
1157 : 0 : if ( !setMasterPassword( true ) )
1158 : 0 : return false;
1159 : :
1160 : : // validate id
1161 : 0 : if ( !config.isValid( true ) )
1162 : : {
1163 : 0 : const char *err = QT_TR_NOOP( "Update config: FAILED because config is invalid" );
1164 : 0 : QgsDebugMsg( err );
1165 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1166 : 0 : return false;
1167 : : }
1168 : :
1169 : 0 : QString configstring = config.configString();
1170 : 0 : if ( configstring.isEmpty() )
1171 : : {
1172 : 0 : const char *err = QT_TR_NOOP( "Update config: FAILED because config is empty" );
1173 : 0 : QgsDebugMsg( err );
1174 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1175 : 0 : return false;
1176 : : }
1177 : :
1178 : : #if( 0 )
1179 : : QgsDebugMsg( QStringLiteral( "authDbConfigTable(): %1" ).arg( authDbConfigTable() ) );
1180 : : QgsDebugMsg( QStringLiteral( "id: %1" ).arg( config.id() ) );
1181 : : QgsDebugMsg( QStringLiteral( "name: %1" ).arg( config.name() ) );
1182 : : QgsDebugMsg( QStringLiteral( "uri: %1" ).arg( config.uri() ) );
1183 : : QgsDebugMsg( QStringLiteral( "type: %1" ).arg( config.method() ) );
1184 : : QgsDebugMsg( QStringLiteral( "version: %1" ).arg( config.version() ) );
1185 : : QgsDebugMsg( QStringLiteral( "config: %1" ).arg( configstring ) ); // DO NOT LEAVE THIS LINE UNCOMMENTED !
1186 : : #endif
1187 : :
1188 : 0 : QSqlQuery query( authDatabaseConnection() );
1189 : 0 : if ( !query.prepare( QStringLiteral( "UPDATE %1 "
1190 : : "SET name = :name, uri = :uri, type = :type, version = :version, config = :config "
1191 : 0 : "WHERE id = :id" ).arg( authDatabaseConfigTable() ) ) )
1192 : : {
1193 : 0 : const char *err = QT_TR_NOOP( "Update config: FAILED to prepare query" );
1194 : 0 : QgsDebugMsg( err );
1195 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1196 : 0 : return false;
1197 : : }
1198 : :
1199 : 0 : query.bindValue( QStringLiteral( ":id" ), config.id() );
1200 : 0 : query.bindValue( QStringLiteral( ":name" ), config.name() );
1201 : 0 : query.bindValue( QStringLiteral( ":uri" ), config.uri() );
1202 : 0 : query.bindValue( QStringLiteral( ":type" ), config.method() );
1203 : 0 : query.bindValue( QStringLiteral( ":version" ), config.version() );
1204 : 0 : query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
1205 : :
1206 : 0 : if ( !authDbStartTransaction() )
1207 : 0 : return false;
1208 : :
1209 : 0 : if ( !authDbQuery( &query ) )
1210 : 0 : return false;
1211 : :
1212 : 0 : if ( !authDbCommit() )
1213 : 0 : return false;
1214 : :
1215 : : // should come before updating auth methods, in case user switched auth methods in config
1216 : 0 : clearCachedConfig( config.id() );
1217 : :
1218 : 0 : updateConfigAuthMethods();
1219 : :
1220 : 0 : QgsDebugMsgLevel( QStringLiteral( "Update config SUCCESS for authcfg: %1" ).arg( config.id() ), 2 );
1221 : :
1222 : 0 : return true;
1223 : 0 : }
1224 : :
1225 : 0 : bool QgsAuthManager::loadAuthenticationConfig( const QString &authcfg, QgsAuthMethodConfig &mconfig, bool full )
1226 : : {
1227 : 0 : if ( isDisabled() )
1228 : 0 : return false;
1229 : :
1230 : 0 : if ( full && !setMasterPassword( true ) )
1231 : 0 : return false;
1232 : :
1233 : 0 : QMutexLocker locker( mMutex.get() );
1234 : :
1235 : 0 : QSqlQuery query( authDatabaseConnection() );
1236 : 0 : if ( full )
1237 : : {
1238 : 0 : query.prepare( QStringLiteral( "SELECT id, name, uri, type, version, config FROM %1 "
1239 : 0 : "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1240 : 0 : }
1241 : : else
1242 : : {
1243 : 0 : query.prepare( QStringLiteral( "SELECT id, name, uri, type, version FROM %1 "
1244 : 0 : "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1245 : : }
1246 : :
1247 : 0 : query.bindValue( QStringLiteral( ":id" ), authcfg );
1248 : :
1249 : 0 : if ( !authDbQuery( &query ) )
1250 : : {
1251 : 0 : return false;
1252 : : }
1253 : :
1254 : 0 : if ( query.isActive() && query.isSelect() )
1255 : : {
1256 : 0 : if ( query.first() )
1257 : : {
1258 : 0 : mconfig.setId( query.value( 0 ).toString() );
1259 : 0 : mconfig.setName( query.value( 1 ).toString() );
1260 : 0 : mconfig.setUri( query.value( 2 ).toString() );
1261 : 0 : mconfig.setMethod( query.value( 3 ).toString() );
1262 : 0 : mconfig.setVersion( query.value( 4 ).toInt() );
1263 : :
1264 : 0 : if ( full )
1265 : : {
1266 : 0 : mconfig.loadConfigString( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 5 ).toString() ) );
1267 : 0 : }
1268 : :
1269 : 0 : QString authMethodKey = configAuthMethodKey( authcfg );
1270 : 0 : QgsAuthMethod *authmethod = authMethod( authMethodKey );
1271 : 0 : if ( authmethod )
1272 : : {
1273 : 0 : authmethod->updateMethodConfig( mconfig );
1274 : 0 : }
1275 : : else
1276 : : {
1277 : 0 : QgsDebugMsg( QStringLiteral( "Update of authcfg %1 FAILED for auth method %2" ).arg( authcfg, authMethodKey ) );
1278 : : }
1279 : :
1280 : 0 : QgsDebugMsgLevel( QStringLiteral( "Load %1 config SUCCESS for authcfg: %2" ).arg( full ? "full" : "base", authcfg ), 2 );
1281 : 0 : return true;
1282 : 0 : }
1283 : 0 : if ( query.next() )
1284 : : {
1285 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one for authcfg: %1" ).arg( authcfg ) );
1286 : 0 : emit messageOut( tr( "Authentication database contains duplicate configuration IDs" ), authManTag(), WARNING );
1287 : 0 : }
1288 : 0 : }
1289 : :
1290 : 0 : return false;
1291 : 0 : }
1292 : :
1293 : 0 : bool QgsAuthManager::removeAuthenticationConfig( const QString &authcfg )
1294 : : {
1295 : 0 : QMutexLocker locker( mMutex.get() );
1296 : 0 : if ( isDisabled() )
1297 : 0 : return false;
1298 : :
1299 : 0 : if ( authcfg.isEmpty() )
1300 : 0 : return false;
1301 : :
1302 : 0 : QSqlQuery query( authDatabaseConnection() );
1303 : :
1304 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
1305 : :
1306 : 0 : query.bindValue( QStringLiteral( ":id" ), authcfg );
1307 : :
1308 : 0 : if ( !authDbStartTransaction() )
1309 : 0 : return false;
1310 : :
1311 : 0 : if ( !authDbQuery( &query ) )
1312 : 0 : return false;
1313 : :
1314 : 0 : if ( !authDbCommit() )
1315 : 0 : return false;
1316 : :
1317 : 0 : clearCachedConfig( authcfg );
1318 : :
1319 : 0 : updateConfigAuthMethods();
1320 : :
1321 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED config for authcfg: %1" ).arg( authcfg ), 2 );
1322 : :
1323 : 0 : return true;
1324 : 0 : }
1325 : :
1326 : 0 : bool QgsAuthManager::removeAllAuthenticationConfigs()
1327 : : {
1328 : 0 : QMutexLocker locker( mMutex.get() );
1329 : 0 : if ( isDisabled() )
1330 : 0 : return false;
1331 : :
1332 : 0 : QSqlQuery query( authDatabaseConnection() );
1333 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1" ).arg( authDatabaseConfigTable() ) );
1334 : 0 : bool res = authDbTransactionQuery( &query );
1335 : :
1336 : 0 : if ( res )
1337 : : {
1338 : 0 : clearAllCachedConfigs();
1339 : 0 : updateConfigAuthMethods();
1340 : 0 : }
1341 : :
1342 : 0 : QgsDebugMsgLevel( QStringLiteral( "Remove configs from database: %1" ).arg( res ? "SUCCEEDED" : "FAILED" ), 2 );
1343 : :
1344 : 0 : return res;
1345 : 0 : }
1346 : :
1347 : 0 : bool QgsAuthManager::backupAuthenticationDatabase( QString *backuppath )
1348 : : {
1349 : 0 : QMutexLocker locker( mMutex.get() );
1350 : 0 : if ( !QFile::exists( authenticationDatabasePath() ) )
1351 : : {
1352 : 0 : const char *err = QT_TR_NOOP( "No authentication database found" );
1353 : 0 : QgsDebugMsg( err );
1354 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1355 : 0 : return false;
1356 : : }
1357 : :
1358 : : // close any connection to current db
1359 : 0 : QSqlDatabase authConn = authDatabaseConnection();
1360 : 0 : if ( authConn.isValid() && authConn.isOpen() )
1361 : 0 : authConn.close();
1362 : :
1363 : : // duplicate current db file to 'qgis-auth_YYYY-MM-DD-HHMMSS.db' backup
1364 : 0 : QString datestamp( QDateTime::currentDateTime().toString( QStringLiteral( "yyyy-MM-dd-hhmmss" ) ) );
1365 : 0 : QString dbbackup( authenticationDatabasePath() );
1366 : 0 : dbbackup.replace( QLatin1String( ".db" ), QStringLiteral( "_%1.db" ).arg( datestamp ) );
1367 : :
1368 : 0 : if ( !QFile::copy( authenticationDatabasePath(), dbbackup ) )
1369 : : {
1370 : 0 : const char *err = QT_TR_NOOP( "Could not back up authentication database" );
1371 : 0 : QgsDebugMsg( err );
1372 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1373 : 0 : return false;
1374 : : }
1375 : :
1376 : 0 : if ( backuppath )
1377 : 0 : *backuppath = dbbackup;
1378 : :
1379 : 0 : QgsDebugMsgLevel( QStringLiteral( "Backed up auth database at %1" ).arg( dbbackup ), 2 );
1380 : 0 : return true;
1381 : 0 : }
1382 : :
1383 : 0 : bool QgsAuthManager::eraseAuthenticationDatabase( bool backup, QString *backuppath )
1384 : : {
1385 : 0 : QMutexLocker locker( mMutex.get() );
1386 : 0 : if ( isDisabled() )
1387 : 0 : return false;
1388 : :
1389 : 0 : QString dbbackup;
1390 : 0 : if ( backup && !backupAuthenticationDatabase( &dbbackup ) )
1391 : : {
1392 : 0 : return false;
1393 : : }
1394 : :
1395 : 0 : if ( backuppath && !dbbackup.isEmpty() )
1396 : 0 : *backuppath = dbbackup;
1397 : :
1398 : 0 : QFileInfo dbinfo( authenticationDatabasePath() );
1399 : 0 : if ( dbinfo.exists() )
1400 : : {
1401 : 0 : if ( !dbinfo.permission( QFile::ReadOwner | QFile::WriteOwner ) )
1402 : : {
1403 : 0 : const char *err = QT_TR_NOOP( "Auth db is not readable or writable by user" );
1404 : 0 : QgsDebugMsg( err );
1405 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
1406 : 0 : return false;
1407 : : }
1408 : 0 : }
1409 : : else
1410 : : {
1411 : 0 : const char *err = QT_TR_NOOP( "No authentication database found" );
1412 : 0 : QgsDebugMsg( err );
1413 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1414 : 0 : return false;
1415 : : }
1416 : :
1417 : 0 : if ( !QFile::remove( authenticationDatabasePath() ) )
1418 : : {
1419 : 0 : const char *err = QT_TR_NOOP( "Authentication database could not be deleted" );
1420 : 0 : QgsDebugMsg( err );
1421 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1422 : 0 : return false;
1423 : : }
1424 : :
1425 : 0 : mMasterPass = QString();
1426 : :
1427 : 0 : QgsDebugMsgLevel( QStringLiteral( "Creating Auth db through QSqlDatabase initial connection" ), 2 );
1428 : :
1429 : 0 : QSqlDatabase authConn = authDatabaseConnection();
1430 : 0 : if ( !authConn.isValid() || !authConn.isOpen() )
1431 : : {
1432 : 0 : const char *err = QT_TR_NOOP( "Authentication database could not be initialized" );
1433 : 0 : QgsDebugMsg( err );
1434 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1435 : 0 : return false;
1436 : : }
1437 : :
1438 : 0 : if ( !createConfigTables() )
1439 : : {
1440 : 0 : const char *err = QT_TR_NOOP( "FAILED to create auth database config tables" );
1441 : 0 : QgsDebugMsg( err );
1442 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1443 : 0 : return false;
1444 : : }
1445 : :
1446 : 0 : if ( !createCertTables() )
1447 : : {
1448 : 0 : const char *err = QT_TR_NOOP( "FAILED to create auth database cert tables" );
1449 : 0 : QgsDebugMsg( err );
1450 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1451 : 0 : return false;
1452 : : }
1453 : :
1454 : 0 : clearAllCachedConfigs();
1455 : 0 : updateConfigAuthMethods();
1456 : 0 : initSslCaches();
1457 : :
1458 : 0 : emit authDatabaseChanged();
1459 : :
1460 : 0 : return true;
1461 : 0 : }
1462 : :
1463 : 0 : bool QgsAuthManager::updateNetworkRequest( QNetworkRequest &request, const QString &authcfg,
1464 : : const QString &dataprovider )
1465 : : {
1466 : 0 : if ( isDisabled() )
1467 : 0 : return false;
1468 : :
1469 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1470 : 0 : if ( authmethod )
1471 : : {
1472 : 0 : if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkRequest ) )
1473 : : {
1474 : 0 : QgsDebugMsg( QStringLiteral( "Network request updating not supported by authcfg: %1" ).arg( authcfg ) );
1475 : 0 : return true;
1476 : : }
1477 : :
1478 : 0 : if ( !authmethod->updateNetworkRequest( request, authcfg, dataprovider.toLower() ) )
1479 : : {
1480 : 0 : authmethod->clearCachedConfig( authcfg );
1481 : 0 : return false;
1482 : : }
1483 : 0 : return true;
1484 : : }
1485 : 0 : return false;
1486 : 0 : }
1487 : :
1488 : 0 : bool QgsAuthManager::updateNetworkReply( QNetworkReply *reply, const QString &authcfg,
1489 : : const QString &dataprovider )
1490 : : {
1491 : 0 : if ( isDisabled() )
1492 : 0 : return false;
1493 : :
1494 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1495 : 0 : if ( authmethod )
1496 : : {
1497 : 0 : if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkReply ) )
1498 : : {
1499 : 0 : QgsDebugMsg( QStringLiteral( "Network reply updating not supported by authcfg: %1" ).arg( authcfg ) );
1500 : 0 : return true;
1501 : : }
1502 : :
1503 : 0 : if ( !authmethod->updateNetworkReply( reply, authcfg, dataprovider.toLower() ) )
1504 : : {
1505 : 0 : authmethod->clearCachedConfig( authcfg );
1506 : 0 : return false;
1507 : : }
1508 : 0 : return true;
1509 : : }
1510 : :
1511 : 0 : return false;
1512 : 0 : }
1513 : :
1514 : 0 : bool QgsAuthManager::updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
1515 : : const QString &dataprovider )
1516 : : {
1517 : 0 : if ( isDisabled() )
1518 : 0 : return false;
1519 : :
1520 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1521 : 0 : if ( authmethod )
1522 : : {
1523 : 0 : if ( !( authmethod->supportedExpansions() & QgsAuthMethod::DataSourceUri ) )
1524 : : {
1525 : 0 : QgsDebugMsg( QStringLiteral( "Data source URI updating not supported by authcfg: %1" ).arg( authcfg ) );
1526 : 0 : return true;
1527 : : }
1528 : :
1529 : 0 : if ( !authmethod->updateDataSourceUriItems( connectionItems, authcfg, dataprovider.toLower() ) )
1530 : : {
1531 : 0 : authmethod->clearCachedConfig( authcfg );
1532 : 0 : return false;
1533 : : }
1534 : 0 : return true;
1535 : : }
1536 : :
1537 : 0 : return false;
1538 : 0 : }
1539 : :
1540 : 0 : bool QgsAuthManager::updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider )
1541 : : {
1542 : 0 : if ( isDisabled() )
1543 : 0 : return false;
1544 : :
1545 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
1546 : 0 : if ( authmethod )
1547 : : {
1548 : 0 : if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkProxy ) )
1549 : : {
1550 : 0 : QgsDebugMsg( QStringLiteral( "Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
1551 : 0 : return true;
1552 : : }
1553 : :
1554 : 0 : if ( !authmethod->updateNetworkProxy( proxy, authcfg, dataprovider.toLower() ) )
1555 : : {
1556 : 0 : authmethod->clearCachedConfig( authcfg );
1557 : 0 : return false;
1558 : : }
1559 : 0 : QgsDebugMsgLevel( QStringLiteral( "Proxy updated successfully from authcfg: %1" ).arg( authcfg ), 2 );
1560 : 0 : return true;
1561 : : }
1562 : :
1563 : 0 : return false;
1564 : 0 : }
1565 : :
1566 : 0 : bool QgsAuthManager::storeAuthSetting( const QString &key, const QVariant &value, bool encrypt )
1567 : : {
1568 : 0 : QMutexLocker locker( mMutex.get() );
1569 : 0 : if ( key.isEmpty() )
1570 : 0 : return false;
1571 : :
1572 : 0 : QString storeval( value.toString() );
1573 : 0 : if ( encrypt )
1574 : : {
1575 : 0 : if ( !setMasterPassword( true ) )
1576 : : {
1577 : 0 : return false;
1578 : : }
1579 : : else
1580 : : {
1581 : 0 : storeval = QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), value.toString() );
1582 : : }
1583 : 0 : }
1584 : :
1585 : 0 : removeAuthSetting( key );
1586 : :
1587 : 0 : QSqlQuery query( authDatabaseConnection() );
1588 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (setting, value) "
1589 : 0 : "VALUES (:setting, :value)" ).arg( authDbSettingsTable() ) );
1590 : :
1591 : 0 : query.bindValue( QStringLiteral( ":setting" ), key );
1592 : 0 : query.bindValue( QStringLiteral( ":value" ), storeval );
1593 : :
1594 : 0 : if ( !authDbStartTransaction() )
1595 : 0 : return false;
1596 : :
1597 : 0 : if ( !authDbQuery( &query ) )
1598 : 0 : return false;
1599 : :
1600 : 0 : if ( !authDbCommit() )
1601 : 0 : return false;
1602 : :
1603 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store setting SUCCESS for key: %1" ).arg( key ), 2 );
1604 : 0 : return true;
1605 : 0 : }
1606 : :
1607 : 0 : QVariant QgsAuthManager::authSetting( const QString &key, const QVariant &defaultValue, bool decrypt )
1608 : : {
1609 : 0 : QMutexLocker locker( mMutex.get() );
1610 : 0 : if ( key.isEmpty() )
1611 : 0 : return QVariant();
1612 : :
1613 : 0 : if ( decrypt && !setMasterPassword( true ) )
1614 : 0 : return QVariant();
1615 : :
1616 : 0 : QVariant value = defaultValue;
1617 : 0 : QSqlQuery query( authDatabaseConnection() );
1618 : 0 : query.prepare( QStringLiteral( "SELECT value FROM %1 "
1619 : 0 : "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1620 : :
1621 : 0 : query.bindValue( QStringLiteral( ":setting" ), key );
1622 : :
1623 : 0 : if ( !authDbQuery( &query ) )
1624 : 0 : return QVariant();
1625 : :
1626 : 0 : if ( query.isActive() && query.isSelect() )
1627 : : {
1628 : 0 : if ( query.first() )
1629 : : {
1630 : 0 : if ( decrypt )
1631 : : {
1632 : 0 : value = QVariant( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ) );
1633 : 0 : }
1634 : : else
1635 : : {
1636 : 0 : value = query.value( 0 );
1637 : : }
1638 : 0 : QgsDebugMsgLevel( QStringLiteral( "Authentication setting retrieved for key: %1" ).arg( key ), 2 );
1639 : 0 : }
1640 : 0 : if ( query.next() )
1641 : : {
1642 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one for setting key: %1" ).arg( key ) );
1643 : 0 : emit messageOut( tr( "Authentication database contains duplicate settings" ), authManTag(), WARNING );
1644 : 0 : return QVariant();
1645 : : }
1646 : 0 : }
1647 : 0 : return value;
1648 : 0 : }
1649 : :
1650 : 0 : bool QgsAuthManager::existsAuthSetting( const QString &key )
1651 : : {
1652 : 0 : QMutexLocker locker( mMutex.get() );
1653 : 0 : if ( key.isEmpty() )
1654 : 0 : return false;
1655 : :
1656 : 0 : QSqlQuery query( authDatabaseConnection() );
1657 : 0 : query.prepare( QStringLiteral( "SELECT value FROM %1 "
1658 : 0 : "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1659 : :
1660 : 0 : query.bindValue( QStringLiteral( ":setting" ), key );
1661 : :
1662 : 0 : if ( !authDbQuery( &query ) )
1663 : 0 : return false;
1664 : :
1665 : 0 : bool res = false;
1666 : 0 : if ( query.isActive() && query.isSelect() )
1667 : : {
1668 : 0 : if ( query.first() )
1669 : : {
1670 : 0 : QgsDebugMsgLevel( QStringLiteral( "Authentication setting exists for key: %1" ).arg( key ), 2 );
1671 : 0 : res = true;
1672 : 0 : }
1673 : 0 : if ( query.next() )
1674 : : {
1675 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one for setting key: %1" ).arg( key ) );
1676 : 0 : emit messageOut( tr( "Authentication database contains duplicate settings" ), authManTag(), WARNING );
1677 : 0 : return false;
1678 : : }
1679 : 0 : }
1680 : 0 : return res;
1681 : 0 : }
1682 : :
1683 : 0 : bool QgsAuthManager::removeAuthSetting( const QString &key )
1684 : : {
1685 : 0 : QMutexLocker locker( mMutex.get() );
1686 : 0 : if ( key.isEmpty() )
1687 : 0 : return false;
1688 : :
1689 : 0 : QSqlQuery query( authDatabaseConnection() );
1690 : :
1691 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
1692 : :
1693 : 0 : query.bindValue( QStringLiteral( ":setting" ), key );
1694 : :
1695 : 0 : if ( !authDbStartTransaction() )
1696 : 0 : return false;
1697 : :
1698 : 0 : if ( !authDbQuery( &query ) )
1699 : 0 : return false;
1700 : :
1701 : 0 : if ( !authDbCommit() )
1702 : 0 : return false;
1703 : :
1704 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED setting for key: %1" ).arg( key ), 2 );
1705 : :
1706 : 0 : return true;
1707 : 0 : }
1708 : :
1709 : :
1710 : : #ifndef QT_NO_SSL
1711 : :
1712 : : ////////////////// Certificate calls ///////////////////////
1713 : :
1714 : 0 : bool QgsAuthManager::initSslCaches()
1715 : : {
1716 : 0 : QgsScopedRuntimeProfile profile( "Initialize SSL cache" );
1717 : :
1718 : 0 : QMutexLocker locker( mMutex.get() );
1719 : 0 : bool res = true;
1720 : 0 : res = res && rebuildCaCertsCache();
1721 : 0 : res = res && rebuildCertTrustCache();
1722 : 0 : res = res && rebuildTrustedCaCertsCache();
1723 : 0 : res = res && rebuildIgnoredSslErrorCache();
1724 : 0 : mCustomConfigByHostCache.clear();
1725 : 0 : mHasCheckedIfCustomConfigByHostExists = false;
1726 : :
1727 : 0 : if ( !res )
1728 : 0 : QgsDebugMsg( QStringLiteral( "Init of SSL caches FAILED" ) );
1729 : 0 : return res;
1730 : 0 : }
1731 : :
1732 : 0 : bool QgsAuthManager::storeCertIdentity( const QSslCertificate &cert, const QSslKey &key )
1733 : : {
1734 : 0 : QMutexLocker locker( mMutex.get() );
1735 : 0 : if ( cert.isNull() )
1736 : : {
1737 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
1738 : 0 : return false;
1739 : : }
1740 : 0 : if ( key.isNull() )
1741 : : {
1742 : 0 : QgsDebugMsg( QStringLiteral( "Passed private key is null" ) );
1743 : 0 : return false;
1744 : : }
1745 : :
1746 : 0 : if ( !setMasterPassword( true ) )
1747 : 0 : return false;
1748 : :
1749 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1750 : 0 : removeCertIdentity( id );
1751 : :
1752 : 0 : QString certpem( cert.toPem() );
1753 : 0 : QString keypem( QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), key.toPem() ) );
1754 : :
1755 : 0 : QSqlQuery query( authDatabaseConnection() );
1756 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (id, key, cert) "
1757 : 0 : "VALUES (:id, :key, :cert)" ).arg( authDbIdentitiesTable() ) );
1758 : :
1759 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
1760 : 0 : query.bindValue( QStringLiteral( ":key" ), keypem );
1761 : 0 : query.bindValue( QStringLiteral( ":cert" ), certpem );
1762 : :
1763 : 0 : if ( !authDbStartTransaction() )
1764 : 0 : return false;
1765 : :
1766 : 0 : if ( !authDbQuery( &query ) )
1767 : 0 : return false;
1768 : :
1769 : 0 : if ( !authDbCommit() )
1770 : 0 : return false;
1771 : :
1772 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store certificate identity SUCCESS for id: %1" ).arg( id ), 2 );
1773 : 0 : return true;
1774 : 0 : }
1775 : :
1776 : 0 : const QSslCertificate QgsAuthManager::certIdentity( const QString &id )
1777 : : {
1778 : 0 : QMutexLocker locker( mMutex.get() );
1779 : 0 : QSslCertificate emptycert;
1780 : 0 : QSslCertificate cert;
1781 : 0 : if ( id.isEmpty() )
1782 : 0 : return emptycert;
1783 : :
1784 : 0 : QSqlQuery query( authDatabaseConnection() );
1785 : 0 : query.prepare( QStringLiteral( "SELECT cert FROM %1 "
1786 : 0 : "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1787 : :
1788 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
1789 : :
1790 : 0 : if ( !authDbQuery( &query ) )
1791 : 0 : return emptycert;
1792 : :
1793 : 0 : if ( query.isActive() && query.isSelect() )
1794 : : {
1795 : 0 : if ( query.first() )
1796 : : {
1797 : 0 : cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1798 : 0 : QgsDebugMsgLevel( QStringLiteral( "Certificate identity retrieved for id: %1" ).arg( id ), 2 );
1799 : 0 : }
1800 : 0 : if ( query.next() )
1801 : : {
1802 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one certificate identity for id: %1" ).arg( id ) );
1803 : 0 : emit messageOut( tr( "Authentication database contains duplicate certificate identity" ), authManTag(), WARNING );
1804 : 0 : return emptycert;
1805 : : }
1806 : 0 : }
1807 : 0 : return cert;
1808 : 0 : }
1809 : :
1810 : 0 : const QPair<QSslCertificate, QSslKey> QgsAuthManager::certIdentityBundle( const QString &id )
1811 : : {
1812 : 0 : QMutexLocker locker( mMutex.get() );
1813 : 0 : QPair<QSslCertificate, QSslKey> bundle;
1814 : 0 : if ( id.isEmpty() )
1815 : 0 : return bundle;
1816 : :
1817 : 0 : if ( !setMasterPassword( true ) )
1818 : 0 : return bundle;
1819 : :
1820 : 0 : QSqlQuery query( authDatabaseConnection() );
1821 : 0 : query.prepare( QStringLiteral( "SELECT key, cert FROM %1 "
1822 : 0 : "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1823 : :
1824 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
1825 : :
1826 : 0 : if ( !authDbQuery( &query ) )
1827 : 0 : return bundle;
1828 : :
1829 : 0 : if ( query.isActive() && query.isSelect() )
1830 : : {
1831 : 0 : QSslCertificate cert;
1832 : 0 : QSslKey key;
1833 : 0 : if ( query.first() )
1834 : : {
1835 : 0 : key = QSslKey( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 0 ).toString() ).toLatin1(),
1836 : : QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey );
1837 : 0 : if ( key.isNull() )
1838 : : {
1839 : 0 : const char *err = QT_TR_NOOP( "Retrieve certificate identity bundle: FAILED to create private key" );
1840 : 0 : QgsDebugMsg( err );
1841 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1842 : 0 : return bundle;
1843 : : }
1844 : 0 : cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1845 : 0 : if ( cert.isNull() )
1846 : : {
1847 : 0 : const char *err = QT_TR_NOOP( "Retrieve certificate identity bundle: FAILED to create certificate" );
1848 : 0 : QgsDebugMsg( err );
1849 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
1850 : 0 : return bundle;
1851 : : }
1852 : 0 : QgsDebugMsgLevel( QStringLiteral( "Certificate identity bundle retrieved for id: %1" ).arg( id ), 2 );
1853 : 0 : }
1854 : 0 : if ( query.next() )
1855 : : {
1856 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one certificate identity for id: %1" ).arg( id ) );
1857 : 0 : emit messageOut( tr( "Authentication database contains duplicate certificate identity" ), authManTag(), WARNING );
1858 : 0 : return bundle;
1859 : : }
1860 : 0 : bundle = qMakePair( cert, key );
1861 : 0 : }
1862 : 0 : return bundle;
1863 : 0 : }
1864 : :
1865 : 0 : const QStringList QgsAuthManager::certIdentityBundleToPem( const QString &id )
1866 : : {
1867 : 0 : QMutexLocker locker( mMutex.get() );
1868 : 0 : QPair<QSslCertificate, QSslKey> bundle( certIdentityBundle( id ) );
1869 : 0 : if ( QgsAuthCertUtils::certIsViable( bundle.first ) && !bundle.second.isNull() )
1870 : : {
1871 : 0 : return QStringList() << QString( bundle.first.toPem() ) << QString( bundle.second.toPem() );
1872 : : }
1873 : 0 : return QStringList();
1874 : 0 : }
1875 : :
1876 : 0 : const QList<QSslCertificate> QgsAuthManager::certIdentities()
1877 : : {
1878 : 0 : QMutexLocker locker( mMutex.get() );
1879 : 0 : QList<QSslCertificate> certs;
1880 : :
1881 : 0 : QSqlQuery query( authDatabaseConnection() );
1882 : 0 : query.prepare( QStringLiteral( "SELECT id, cert FROM %1" ).arg( authDbIdentitiesTable() ) );
1883 : :
1884 : 0 : if ( !authDbQuery( &query ) )
1885 : 0 : return certs;
1886 : :
1887 : 0 : if ( query.isActive() && query.isSelect() )
1888 : : {
1889 : 0 : while ( query.next() )
1890 : : {
1891 : 0 : certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
1892 : : }
1893 : 0 : }
1894 : :
1895 : 0 : return certs;
1896 : 0 : }
1897 : :
1898 : 0 : QStringList QgsAuthManager::certIdentityIds() const
1899 : : {
1900 : 0 : QMutexLocker locker( mMutex.get() );
1901 : 0 : QStringList identityids = QStringList();
1902 : :
1903 : 0 : if ( isDisabled() )
1904 : 0 : return identityids;
1905 : :
1906 : 0 : QSqlQuery query( authDatabaseConnection() );
1907 : 0 : query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( authDbIdentitiesTable() ) );
1908 : :
1909 : 0 : if ( !authDbQuery( &query ) )
1910 : : {
1911 : 0 : return identityids;
1912 : : }
1913 : :
1914 : 0 : if ( query.isActive() )
1915 : : {
1916 : 0 : while ( query.next() )
1917 : : {
1918 : 0 : identityids << query.value( 0 ).toString();
1919 : : }
1920 : 0 : }
1921 : 0 : return identityids;
1922 : 0 : }
1923 : :
1924 : 0 : bool QgsAuthManager::existsCertIdentity( const QString &id )
1925 : : {
1926 : 0 : QMutexLocker locker( mMutex.get() );
1927 : 0 : if ( id.isEmpty() )
1928 : 0 : return false;
1929 : :
1930 : 0 : QSqlQuery query( authDatabaseConnection() );
1931 : 0 : query.prepare( QStringLiteral( "SELECT cert FROM %1 "
1932 : 0 : "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1933 : :
1934 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
1935 : :
1936 : 0 : if ( !authDbQuery( &query ) )
1937 : 0 : return false;
1938 : :
1939 : 0 : bool res = false;
1940 : 0 : if ( query.isActive() && query.isSelect() )
1941 : : {
1942 : 0 : if ( query.first() )
1943 : : {
1944 : 0 : QgsDebugMsgLevel( QStringLiteral( "Certificate bundle exists for id: %1" ).arg( id ), 2 );
1945 : 0 : res = true;
1946 : 0 : }
1947 : 0 : if ( query.next() )
1948 : : {
1949 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one certificate bundle for id: %1" ).arg( id ) );
1950 : 0 : emit messageOut( tr( "Authentication database contains duplicate certificate bundles" ), authManTag(), WARNING );
1951 : 0 : return false;
1952 : : }
1953 : 0 : }
1954 : 0 : return res;
1955 : 0 : }
1956 : :
1957 : 0 : bool QgsAuthManager::removeCertIdentity( const QString &id )
1958 : : {
1959 : 0 : QMutexLocker locker( mMutex.get() );
1960 : 0 : if ( id.isEmpty() )
1961 : : {
1962 : 0 : QgsDebugMsg( QStringLiteral( "Passed bundle ID is empty" ) );
1963 : 0 : return false;
1964 : : }
1965 : :
1966 : 0 : QSqlQuery query( authDatabaseConnection() );
1967 : :
1968 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
1969 : :
1970 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
1971 : :
1972 : 0 : if ( !authDbStartTransaction() )
1973 : 0 : return false;
1974 : :
1975 : 0 : if ( !authDbQuery( &query ) )
1976 : 0 : return false;
1977 : :
1978 : 0 : if ( !authDbCommit() )
1979 : 0 : return false;
1980 : :
1981 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED certificate identity for id: %1" ).arg( id ), 2 );
1982 : 0 : return true;
1983 : 0 : }
1984 : :
1985 : 0 : bool QgsAuthManager::storeSslCertCustomConfig( const QgsAuthConfigSslServer &config )
1986 : : {
1987 : 0 : QMutexLocker locker( mMutex.get() );
1988 : 0 : if ( config.isNull() )
1989 : : {
1990 : 0 : QgsDebugMsg( QStringLiteral( "Passed config is null" ) );
1991 : 0 : return false;
1992 : : }
1993 : :
1994 : 0 : QSslCertificate cert( config.sslCertificate() );
1995 : :
1996 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1997 : 0 : removeSslCertCustomConfig( id, config.sslHostPort().trimmed() );
1998 : :
1999 : 0 : QString certpem( cert.toPem() );
2000 : :
2001 : 0 : QSqlQuery query( authDatabaseConnection() );
2002 : 0 : query.prepare( QStringLiteral( "INSERT OR REPLACE INTO %1 (id, host, cert, config) "
2003 : 0 : "VALUES (:id, :host, :cert, :config)" ).arg( authDatabaseServersTable() ) );
2004 : :
2005 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2006 : 0 : query.bindValue( QStringLiteral( ":host" ), config.sslHostPort().trimmed() );
2007 : 0 : query.bindValue( QStringLiteral( ":cert" ), certpem );
2008 : 0 : query.bindValue( QStringLiteral( ":config" ), config.configString() );
2009 : :
2010 : 0 : if ( !authDbStartTransaction() )
2011 : 0 : return false;
2012 : :
2013 : 0 : if ( !authDbQuery( &query ) )
2014 : 0 : return false;
2015 : :
2016 : 0 : if ( !authDbCommit() )
2017 : 0 : return false;
2018 : :
2019 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
2020 : : .arg( config.sslHostPort().trimmed(), id ), 2 );
2021 : :
2022 : 0 : updateIgnoredSslErrorsCacheFromConfig( config );
2023 : 0 : mHasCheckedIfCustomConfigByHostExists = false;
2024 : 0 : mCustomConfigByHostCache.clear();
2025 : :
2026 : 0 : return true;
2027 : 0 : }
2028 : :
2029 : 0 : const QgsAuthConfigSslServer QgsAuthManager::sslCertCustomConfig( const QString &id, const QString &hostport )
2030 : : {
2031 : 0 : QMutexLocker locker( mMutex.get() );
2032 : 0 : QgsAuthConfigSslServer config;
2033 : :
2034 : 0 : if ( id.isEmpty() || hostport.isEmpty() )
2035 : : {
2036 : 0 : QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2037 : 0 : return config;
2038 : : }
2039 : :
2040 : 0 : QSqlQuery query( authDatabaseConnection() );
2041 : 0 : query.prepare( QStringLiteral( "SELECT id, host, cert, config FROM %1 "
2042 : 0 : "WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2043 : :
2044 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2045 : 0 : query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2046 : :
2047 : 0 : if ( !authDbQuery( &query ) )
2048 : 0 : return config;
2049 : :
2050 : 0 : if ( query.isActive() && query.isSelect() )
2051 : : {
2052 : 0 : if ( query.first() )
2053 : : {
2054 : 0 : config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2055 : 0 : config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2056 : 0 : config.loadConfigString( query.value( 3 ).toString() );
2057 : 0 : QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2058 : 0 : }
2059 : 0 : if ( query.next() )
2060 : : {
2061 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ) );
2062 : 0 : emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2063 : 0 : .arg( hostport, id ), authManTag(), WARNING );
2064 : 0 : QgsAuthConfigSslServer emptyconfig;
2065 : 0 : return emptyconfig;
2066 : 0 : }
2067 : 0 : }
2068 : 0 : return config;
2069 : 0 : }
2070 : :
2071 : 0 : const QgsAuthConfigSslServer QgsAuthManager::sslCertCustomConfigByHost( const QString &hostport )
2072 : : {
2073 : 0 : QgsAuthConfigSslServer config;
2074 : 0 : if ( hostport.isEmpty() )
2075 : : {
2076 : 0 : return config;
2077 : : }
2078 : :
2079 : 0 : QMutexLocker locker( mMutex.get() );
2080 : 0 : if ( mHasCheckedIfCustomConfigByHostExists && !mHasCustomConfigByHost )
2081 : 0 : return config;
2082 : 0 : if ( mCustomConfigByHostCache.contains( hostport ) )
2083 : 0 : return mCustomConfigByHostCache.value( hostport );
2084 : :
2085 : 0 : QSqlQuery query( authDatabaseConnection() );
2086 : :
2087 : : // first run -- see if we have ANY custom config by host. If not, we can skip all future checks for any host
2088 : 0 : if ( !mHasCheckedIfCustomConfigByHostExists )
2089 : : {
2090 : 0 : mHasCheckedIfCustomConfigByHostExists = true;
2091 : 0 : query.prepare( QString( "SELECT count(*) FROM %1" ).arg( authDatabaseServersTable() ) );
2092 : 0 : if ( !authDbQuery( &query ) )
2093 : : {
2094 : 0 : mHasCustomConfigByHost = false;
2095 : 0 : return config;
2096 : : }
2097 : 0 : if ( query.isActive() && query.isSelect() && query.first() )
2098 : : {
2099 : 0 : mHasCustomConfigByHost = query.value( 0 ).toInt() > 0;
2100 : 0 : if ( !mHasCustomConfigByHost )
2101 : 0 : return config;
2102 : 0 : }
2103 : : else
2104 : : {
2105 : 0 : mHasCustomConfigByHost = false;
2106 : 0 : return config;
2107 : : }
2108 : 0 : }
2109 : :
2110 : 0 : query.prepare( QString( "SELECT id, host, cert, config FROM %1 "
2111 : 0 : "WHERE host = :host" ).arg( authDatabaseServersTable() ) );
2112 : :
2113 : 0 : query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2114 : :
2115 : 0 : if ( !authDbQuery( &query ) )
2116 : : {
2117 : 0 : mCustomConfigByHostCache.insert( hostport, config );
2118 : 0 : return config;
2119 : : }
2120 : :
2121 : 0 : if ( query.isActive() && query.isSelect() )
2122 : : {
2123 : 0 : if ( query.first() )
2124 : : {
2125 : 0 : config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2126 : 0 : config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2127 : 0 : config.loadConfigString( query.value( 3 ).toString() );
2128 : 0 : QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port: %1" ).arg( hostport ), 2 );
2129 : 0 : }
2130 : 0 : if ( query.next() )
2131 : : {
2132 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port: %1" ).arg( hostport ) );
2133 : 0 : emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port: %1" )
2134 : 0 : .arg( hostport ), authManTag(), WARNING );
2135 : 0 : QgsAuthConfigSslServer emptyconfig;
2136 : 0 : mCustomConfigByHostCache.insert( hostport, emptyconfig );
2137 : 0 : return emptyconfig;
2138 : 0 : }
2139 : 0 : }
2140 : :
2141 : 0 : mCustomConfigByHostCache.insert( hostport, config );
2142 : 0 : return config;
2143 : 0 : }
2144 : :
2145 : 0 : const QList<QgsAuthConfigSslServer> QgsAuthManager::sslCertCustomConfigs()
2146 : : {
2147 : 0 : QMutexLocker locker( mMutex.get() );
2148 : 0 : QList<QgsAuthConfigSslServer> configs;
2149 : :
2150 : 0 : QSqlQuery query( authDatabaseConnection() );
2151 : 0 : query.prepare( QStringLiteral( "SELECT id, host, cert, config FROM %1" ).arg( authDatabaseServersTable() ) );
2152 : :
2153 : 0 : if ( !authDbQuery( &query ) )
2154 : 0 : return configs;
2155 : :
2156 : 0 : if ( query.isActive() && query.isSelect() )
2157 : : {
2158 : 0 : while ( query.next() )
2159 : : {
2160 : 0 : QgsAuthConfigSslServer config;
2161 : 0 : config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
2162 : 0 : config.setSslHostPort( query.value( 1 ).toString().trimmed() );
2163 : 0 : config.loadConfigString( query.value( 3 ).toString() );
2164 : :
2165 : 0 : configs.append( config );
2166 : 0 : }
2167 : 0 : }
2168 : :
2169 : 0 : return configs;
2170 : 0 : }
2171 : :
2172 : 0 : bool QgsAuthManager::existsSslCertCustomConfig( const QString &id, const QString &hostport )
2173 : : {
2174 : 0 : QMutexLocker locker( mMutex.get() );
2175 : 0 : if ( id.isEmpty() || hostport.isEmpty() )
2176 : : {
2177 : 0 : QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2178 : 0 : return false;
2179 : : }
2180 : :
2181 : 0 : QSqlQuery query( authDatabaseConnection() );
2182 : 0 : query.prepare( QStringLiteral( "SELECT cert FROM %1 "
2183 : 0 : "WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2184 : :
2185 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2186 : 0 : query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2187 : :
2188 : 0 : if ( !authDbQuery( &query ) )
2189 : 0 : return false;
2190 : :
2191 : 0 : bool res = false;
2192 : 0 : if ( query.isActive() && query.isSelect() )
2193 : : {
2194 : 0 : if ( query.first() )
2195 : : {
2196 : 0 : QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2197 : 0 : res = true;
2198 : 0 : }
2199 : 0 : if ( query.next() )
2200 : : {
2201 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ) );
2202 : 0 : emit messageOut( tr( "Authentication database contains duplicate SSL cert custom configs for host:port, id: %1, %2" )
2203 : 0 : .arg( hostport, id ), authManTag(), WARNING );
2204 : 0 : return false;
2205 : : }
2206 : 0 : }
2207 : 0 : return res;
2208 : 0 : }
2209 : :
2210 : 0 : bool QgsAuthManager::removeSslCertCustomConfig( const QString &id, const QString &hostport )
2211 : : {
2212 : 0 : QMutexLocker locker( mMutex.get() );
2213 : 0 : if ( id.isEmpty() || hostport.isEmpty() )
2214 : : {
2215 : 0 : QgsDebugMsg( QStringLiteral( "Passed config ID or host:port is empty" ) );
2216 : 0 : return false;
2217 : : }
2218 : :
2219 : 0 : mHasCheckedIfCustomConfigByHostExists = false;
2220 : 0 : mCustomConfigByHostCache.clear();
2221 : :
2222 : 0 : QSqlQuery query( authDatabaseConnection() );
2223 : :
2224 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id AND host = :host" ).arg( authDatabaseServersTable() ) );
2225 : :
2226 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2227 : 0 : query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
2228 : :
2229 : 0 : if ( !authDbStartTransaction() )
2230 : 0 : return false;
2231 : :
2232 : 0 : if ( !authDbQuery( &query ) )
2233 : 0 : return false;
2234 : :
2235 : 0 : if ( !authDbCommit() )
2236 : 0 : return false;
2237 : :
2238 : 0 : QString shahostport( QStringLiteral( "%1:%2" ).arg( id, hostport ) );
2239 : 0 : if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2240 : : {
2241 : 0 : mIgnoredSslErrorsCache.remove( shahostport );
2242 : 0 : }
2243 : :
2244 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
2245 : 0 : dumpIgnoredSslErrorsCache_();
2246 : 0 : return true;
2247 : 0 : }
2248 : :
2249 : 0 : void QgsAuthManager::dumpIgnoredSslErrorsCache_()
2250 : : {
2251 : 0 : QMutexLocker locker( mMutex.get() );
2252 : 0 : if ( !mIgnoredSslErrorsCache.isEmpty() )
2253 : : {
2254 : 0 : QgsDebugMsg( QStringLiteral( "Ignored SSL errors cache items:" ) );
2255 : 0 : QHash<QString, QSet<QSslError::SslError> >::const_iterator i = mIgnoredSslErrorsCache.constBegin();
2256 : 0 : while ( i != mIgnoredSslErrorsCache.constEnd() )
2257 : : {
2258 : 0 : QStringList errs;
2259 : 0 : for ( auto err : i.value() )
2260 : : {
2261 : 0 : errs << QgsAuthCertUtils::sslErrorEnumString( err );
2262 : : }
2263 : 0 : QgsDebugMsg( QStringLiteral( "%1 = %2" ).arg( i.key(), errs.join( ", " ) ) );
2264 : 0 : ++i;
2265 : 0 : }
2266 : 0 : }
2267 : : else
2268 : : {
2269 : 0 : QgsDebugMsgLevel( QStringLiteral( "Ignored SSL errors cache EMPTY" ), 2 );
2270 : : }
2271 : 0 : }
2272 : :
2273 : 0 : bool QgsAuthManager::updateIgnoredSslErrorsCacheFromConfig( const QgsAuthConfigSslServer &config )
2274 : : {
2275 : 0 : QMutexLocker locker( mMutex.get() );
2276 : 0 : if ( config.isNull() )
2277 : : {
2278 : 0 : QgsDebugMsg( QStringLiteral( "Passed config is null" ) );
2279 : 0 : return false;
2280 : : }
2281 : :
2282 : 0 : QString shahostport( QStringLiteral( "%1:%2" )
2283 : 0 : .arg( QgsAuthCertUtils::shaHexForCert( config.sslCertificate() ).trimmed(),
2284 : 0 : config.sslHostPort().trimmed() ) );
2285 : 0 : if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2286 : : {
2287 : 0 : mIgnoredSslErrorsCache.remove( shahostport );
2288 : 0 : }
2289 : 0 : QList<QSslError::SslError> errenums( config.sslIgnoredErrorEnums() );
2290 : 0 : if ( !errenums.isEmpty() )
2291 : : {
2292 : 0 : mIgnoredSslErrorsCache.insert( shahostport, qgis::listToSet( errenums ) );
2293 : 0 : QgsDebugMsgLevel( QStringLiteral( "Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2294 : 0 : dumpIgnoredSslErrorsCache_();
2295 : 0 : return true;
2296 : : }
2297 : :
2298 : 0 : QgsDebugMsgLevel( QStringLiteral( "No ignored SSL errors to cache for sha:host:port = %1" ).arg( shahostport ), 2 );
2299 : 0 : return true;
2300 : 0 : }
2301 : :
2302 : 0 : bool QgsAuthManager::updateIgnoredSslErrorsCache( const QString &shahostport, const QList<QSslError> &errors )
2303 : : {
2304 : 0 : QMutexLocker locker( mMutex.get() );
2305 : 0 : QRegExp rx( "\\S+:\\S+:\\d+" );
2306 : 0 : if ( !rx.exactMatch( shahostport ) )
2307 : : {
2308 : 0 : QgsDebugMsg( "Passed shahostport does not match \\S+:\\S+:\\d+, "
2309 : : "e.g. 74a4ef5ea94512a43769b744cda0ca5049a72491:www.example.com:443" );
2310 : 0 : return false;
2311 : : }
2312 : :
2313 : 0 : if ( mIgnoredSslErrorsCache.contains( shahostport ) )
2314 : : {
2315 : 0 : mIgnoredSslErrorsCache.remove( shahostport );
2316 : 0 : }
2317 : :
2318 : 0 : if ( errors.isEmpty() )
2319 : : {
2320 : 0 : QgsDebugMsg( QStringLiteral( "Passed errors list empty" ) );
2321 : 0 : return false;
2322 : : }
2323 : :
2324 : 0 : QSet<QSslError::SslError> errs;
2325 : 0 : for ( const auto &error : errors )
2326 : : {
2327 : 0 : if ( error.error() == QSslError::NoError )
2328 : 0 : continue;
2329 : :
2330 : 0 : errs.insert( error.error() );
2331 : : }
2332 : :
2333 : 0 : if ( errs.isEmpty() )
2334 : : {
2335 : 0 : QgsDebugMsg( QStringLiteral( "Passed errors list does not contain errors" ) );
2336 : 0 : return false;
2337 : : }
2338 : :
2339 : 0 : mIgnoredSslErrorsCache.insert( shahostport, errs );
2340 : :
2341 : 0 : QgsDebugMsgLevel( QStringLiteral( "Update of ignored SSL errors cache SUCCEEDED for sha:host:port = %1" ).arg( shahostport ), 2 );
2342 : 0 : dumpIgnoredSslErrorsCache_();
2343 : 0 : return true;
2344 : 0 : }
2345 : :
2346 : 0 : bool QgsAuthManager::rebuildIgnoredSslErrorCache()
2347 : : {
2348 : 0 : QMutexLocker locker( mMutex.get() );
2349 : 0 : QHash<QString, QSet<QSslError::SslError> > prevcache( mIgnoredSslErrorsCache );
2350 : 0 : QHash<QString, QSet<QSslError::SslError> > nextcache;
2351 : :
2352 : 0 : QSqlQuery query( authDatabaseConnection() );
2353 : 0 : query.prepare( QStringLiteral( "SELECT id, host, config FROM %1" ).arg( authDatabaseServersTable() ) );
2354 : :
2355 : 0 : if ( !authDbQuery( &query ) )
2356 : : {
2357 : 0 : QgsDebugMsg( QStringLiteral( "Rebuild of ignored SSL errors cache FAILED" ) );
2358 : 0 : return false;
2359 : : }
2360 : :
2361 : 0 : if ( query.isActive() && query.isSelect() )
2362 : : {
2363 : 0 : while ( query.next() )
2364 : : {
2365 : 0 : QString shahostport( QStringLiteral( "%1:%2" )
2366 : 0 : .arg( query.value( 0 ).toString().trimmed(),
2367 : 0 : query.value( 1 ).toString().trimmed() ) );
2368 : 0 : QgsAuthConfigSslServer config;
2369 : 0 : config.loadConfigString( query.value( 2 ).toString() );
2370 : 0 : QList<QSslError::SslError> errenums( config.sslIgnoredErrorEnums() );
2371 : 0 : if ( !errenums.isEmpty() )
2372 : : {
2373 : 0 : nextcache.insert( shahostport, qgis::listToSet( errenums ) );
2374 : 0 : }
2375 : 0 : if ( prevcache.contains( shahostport ) )
2376 : : {
2377 : 0 : prevcache.remove( shahostport );
2378 : 0 : }
2379 : 0 : }
2380 : 0 : }
2381 : :
2382 : 0 : if ( !prevcache.isEmpty() )
2383 : : {
2384 : : // preserve any existing per-session ignored errors for hosts
2385 : 0 : QHash<QString, QSet<QSslError::SslError> >::const_iterator i = prevcache.constBegin();
2386 : 0 : while ( i != prevcache.constEnd() )
2387 : : {
2388 : 0 : nextcache.insert( i.key(), i.value() );
2389 : 0 : ++i;
2390 : : }
2391 : 0 : }
2392 : :
2393 : 0 : if ( nextcache != mIgnoredSslErrorsCache )
2394 : : {
2395 : 0 : mIgnoredSslErrorsCache.clear();
2396 : 0 : mIgnoredSslErrorsCache = nextcache;
2397 : 0 : QgsDebugMsgLevel( QStringLiteral( "Rebuild of ignored SSL errors cache SUCCEEDED" ), 2 );
2398 : 0 : dumpIgnoredSslErrorsCache_();
2399 : 0 : return true;
2400 : : }
2401 : :
2402 : 0 : QgsDebugMsgLevel( QStringLiteral( "Rebuild of ignored SSL errors cache SAME AS BEFORE" ), 2 );
2403 : 0 : dumpIgnoredSslErrorsCache_();
2404 : 0 : return true;
2405 : 0 : }
2406 : :
2407 : :
2408 : 0 : bool QgsAuthManager::storeCertAuthorities( const QList<QSslCertificate> &certs )
2409 : : {
2410 : 0 : QMutexLocker locker( mMutex.get() );
2411 : 0 : if ( certs.isEmpty() )
2412 : : {
2413 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate list has no certs" ) );
2414 : 0 : return false;
2415 : : }
2416 : :
2417 : 0 : for ( const auto &cert : certs )
2418 : : {
2419 : 0 : if ( !storeCertAuthority( cert ) )
2420 : 0 : return false;
2421 : : }
2422 : 0 : return true;
2423 : 0 : }
2424 : :
2425 : 0 : bool QgsAuthManager::storeCertAuthority( const QSslCertificate &cert )
2426 : : {
2427 : 0 : QMutexLocker locker( mMutex.get() );
2428 : : // don't refuse !cert.isValid() (actually just expired) CAs,
2429 : : // as user may want to ignore that SSL connection error
2430 : 0 : if ( cert.isNull() )
2431 : : {
2432 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2433 : 0 : return false;
2434 : : }
2435 : :
2436 : 0 : removeCertAuthority( cert );
2437 : :
2438 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2439 : 0 : QString pem( cert.toPem() );
2440 : :
2441 : 0 : QSqlQuery query( authDatabaseConnection() );
2442 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (id, cert) "
2443 : 0 : "VALUES (:id, :cert)" ).arg( authDbAuthoritiesTable() ) );
2444 : :
2445 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2446 : 0 : query.bindValue( QStringLiteral( ":cert" ), pem );
2447 : :
2448 : 0 : if ( !authDbStartTransaction() )
2449 : 0 : return false;
2450 : :
2451 : 0 : if ( !authDbQuery( &query ) )
2452 : 0 : return false;
2453 : :
2454 : 0 : if ( !authDbCommit() )
2455 : 0 : return false;
2456 : :
2457 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store certificate authority SUCCESS for id: %1" ).arg( id ), 2 );
2458 : 0 : return true;
2459 : 0 : }
2460 : :
2461 : 0 : const QSslCertificate QgsAuthManager::certAuthority( const QString &id )
2462 : : {
2463 : 0 : QMutexLocker locker( mMutex.get() );
2464 : 0 : QSslCertificate emptycert;
2465 : 0 : QSslCertificate cert;
2466 : 0 : if ( id.isEmpty() )
2467 : 0 : return emptycert;
2468 : :
2469 : 0 : QSqlQuery query( authDatabaseConnection() );
2470 : 0 : query.prepare( QStringLiteral( "SELECT cert FROM %1 "
2471 : 0 : "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2472 : :
2473 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2474 : :
2475 : 0 : if ( !authDbQuery( &query ) )
2476 : 0 : return emptycert;
2477 : :
2478 : 0 : if ( query.isActive() && query.isSelect() )
2479 : : {
2480 : 0 : if ( query.first() )
2481 : : {
2482 : 0 : cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
2483 : 0 : QgsDebugMsgLevel( QStringLiteral( "Certificate authority retrieved for id: %1" ).arg( id ), 2 );
2484 : 0 : }
2485 : 0 : if ( query.next() )
2486 : : {
2487 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one certificate authority for id: %1" ).arg( id ) );
2488 : 0 : emit messageOut( tr( "Authentication database contains duplicate certificate authorities" ), authManTag(), WARNING );
2489 : 0 : return emptycert;
2490 : : }
2491 : 0 : }
2492 : 0 : return cert;
2493 : 0 : }
2494 : :
2495 : 0 : bool QgsAuthManager::existsCertAuthority( const QSslCertificate &cert )
2496 : : {
2497 : 0 : QMutexLocker locker( mMutex.get() );
2498 : 0 : if ( cert.isNull() )
2499 : : {
2500 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2501 : 0 : return false;
2502 : : }
2503 : :
2504 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2505 : :
2506 : 0 : QSqlQuery query( authDatabaseConnection() );
2507 : 0 : query.prepare( QStringLiteral( "SELECT value FROM %1 "
2508 : 0 : "WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2509 : :
2510 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2511 : :
2512 : 0 : if ( !authDbQuery( &query ) )
2513 : 0 : return false;
2514 : :
2515 : 0 : bool res = false;
2516 : 0 : if ( query.isActive() && query.isSelect() )
2517 : : {
2518 : 0 : if ( query.first() )
2519 : : {
2520 : 0 : QgsDebugMsgLevel( QStringLiteral( "Certificate authority exists for id: %1" ).arg( id ), 2 );
2521 : 0 : res = true;
2522 : 0 : }
2523 : 0 : if ( query.next() )
2524 : : {
2525 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one certificate authority for id: %1" ).arg( id ) );
2526 : 0 : emit messageOut( tr( "Authentication database contains duplicate certificate authorities" ), authManTag(), WARNING );
2527 : 0 : return false;
2528 : : }
2529 : 0 : }
2530 : 0 : return res;
2531 : 0 : }
2532 : :
2533 : 0 : bool QgsAuthManager::removeCertAuthority( const QSslCertificate &cert )
2534 : : {
2535 : 0 : QMutexLocker locker( mMutex.get() );
2536 : 0 : if ( cert.isNull() )
2537 : : {
2538 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2539 : 0 : return false;
2540 : : }
2541 : :
2542 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2543 : :
2544 : 0 : QSqlQuery query( authDatabaseConnection() );
2545 : :
2546 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbAuthoritiesTable() ) );
2547 : :
2548 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2549 : :
2550 : 0 : if ( !authDbStartTransaction() )
2551 : 0 : return false;
2552 : :
2553 : 0 : if ( !authDbQuery( &query ) )
2554 : 0 : return false;
2555 : :
2556 : 0 : if ( !authDbCommit() )
2557 : 0 : return false;
2558 : :
2559 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED authority for id: %1" ).arg( id ), 2 );
2560 : 0 : return true;
2561 : 0 : }
2562 : :
2563 : 0 : const QList<QSslCertificate> QgsAuthManager::systemRootCAs()
2564 : : {
2565 : 0 : return QSslConfiguration::systemCaCertificates();
2566 : : }
2567 : :
2568 : 0 : const QList<QSslCertificate> QgsAuthManager::extraFileCAs()
2569 : : {
2570 : 0 : QMutexLocker locker( mMutex.get() );
2571 : 0 : QList<QSslCertificate> certs;
2572 : 0 : QList<QSslCertificate> filecerts;
2573 : 0 : QVariant cafileval = QgsAuthManager::instance()->authSetting( QStringLiteral( "cafile" ) );
2574 : 0 : if ( cafileval.isNull() )
2575 : 0 : return certs;
2576 : :
2577 : 0 : QVariant allowinvalid = QgsAuthManager::instance()->authSetting( QStringLiteral( "cafileallowinvalid" ), QVariant( false ) );
2578 : 0 : if ( allowinvalid.isNull() )
2579 : 0 : return certs;
2580 : :
2581 : 0 : QString cafile( cafileval.toString() );
2582 : 0 : if ( !cafile.isEmpty() && QFile::exists( cafile ) )
2583 : : {
2584 : 0 : filecerts = QgsAuthCertUtils::certsFromFile( cafile );
2585 : 0 : }
2586 : : // only CAs or certs capable of signing other certs are allowed
2587 : 0 : for ( const auto &cert : std::as_const( filecerts ) )
2588 : : {
2589 : 0 : if ( !allowinvalid.toBool() && ( cert.isBlacklisted()
2590 : 0 : || cert.isNull()
2591 : 0 : || cert.expiryDate() <= QDateTime::currentDateTime()
2592 : 0 : || cert.effectiveDate() > QDateTime::currentDateTime() ) )
2593 : : {
2594 : 0 : continue;
2595 : : }
2596 : :
2597 : 0 : if ( QgsAuthCertUtils::certificateIsAuthorityOrIssuer( cert ) )
2598 : : {
2599 : 0 : certs << cert;
2600 : 0 : }
2601 : : }
2602 : 0 : return certs;
2603 : 0 : }
2604 : :
2605 : 0 : const QList<QSslCertificate> QgsAuthManager::databaseCAs()
2606 : : {
2607 : 0 : QMutexLocker locker( mMutex.get() );
2608 : 0 : QList<QSslCertificate> certs;
2609 : :
2610 : 0 : QSqlQuery query( authDatabaseConnection() );
2611 : 0 : query.prepare( QStringLiteral( "SELECT id, cert FROM %1" ).arg( authDbAuthoritiesTable() ) );
2612 : :
2613 : 0 : if ( !authDbQuery( &query ) )
2614 : 0 : return certs;
2615 : :
2616 : 0 : if ( query.isActive() && query.isSelect() )
2617 : : {
2618 : 0 : while ( query.next() )
2619 : : {
2620 : 0 : certs << QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
2621 : : }
2622 : 0 : }
2623 : :
2624 : 0 : return certs;
2625 : 0 : }
2626 : :
2627 : 0 : const QMap<QString, QSslCertificate> QgsAuthManager::mappedDatabaseCAs()
2628 : : {
2629 : 0 : QMutexLocker locker( mMutex.get() );
2630 : 0 : return QgsAuthCertUtils::mapDigestToCerts( databaseCAs() );
2631 : 0 : }
2632 : :
2633 : 0 : bool QgsAuthManager::rebuildCaCertsCache()
2634 : : {
2635 : 0 : QMutexLocker locker( mMutex.get() );
2636 : 0 : mCaCertsCache.clear();
2637 : : // in reverse order of precedence, with regards to duplicates, so QMap inserts overwrite
2638 : 0 : insertCaCertInCache( QgsAuthCertUtils::SystemRoot, systemRootCAs() );
2639 : 0 : insertCaCertInCache( QgsAuthCertUtils::FromFile, extraFileCAs() );
2640 : 0 : insertCaCertInCache( QgsAuthCertUtils::InDatabase, databaseCAs() );
2641 : :
2642 : 0 : bool res = !mCaCertsCache.isEmpty(); // should at least contain system root CAs
2643 : 0 : if ( !res )
2644 : 0 : QgsDebugMsg( QStringLiteral( "Rebuild of CA certs cache FAILED" ) );
2645 : 0 : return res;
2646 : 0 : }
2647 : :
2648 : 0 : bool QgsAuthManager::storeCertTrustPolicy( const QSslCertificate &cert, QgsAuthCertUtils::CertTrustPolicy policy )
2649 : : {
2650 : 0 : QMutexLocker locker( mMutex.get() );
2651 : 0 : if ( cert.isNull() )
2652 : : {
2653 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2654 : 0 : return false;
2655 : : }
2656 : :
2657 : 0 : removeCertTrustPolicy( cert );
2658 : :
2659 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2660 : :
2661 : 0 : if ( policy == QgsAuthCertUtils::DefaultTrust )
2662 : : {
2663 : 0 : QgsDebugMsg( QStringLiteral( "Passed policy was default, all cert records in database were removed for id: %1" ).arg( id ) );
2664 : 0 : return true;
2665 : : }
2666 : :
2667 : 0 : QSqlQuery query( authDatabaseConnection() );
2668 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (id, policy) "
2669 : 0 : "VALUES (:id, :policy)" ).arg( authDbTrustTable() ) );
2670 : :
2671 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2672 : 0 : query.bindValue( QStringLiteral( ":policy" ), static_cast< int >( policy ) );
2673 : :
2674 : 0 : if ( !authDbStartTransaction() )
2675 : 0 : return false;
2676 : :
2677 : 0 : if ( !authDbQuery( &query ) )
2678 : 0 : return false;
2679 : :
2680 : 0 : if ( !authDbCommit() )
2681 : 0 : return false;
2682 : :
2683 : 0 : QgsDebugMsgLevel( QStringLiteral( "Store certificate trust policy SUCCESS for id: %1" ).arg( id ), 2 );
2684 : 0 : return true;
2685 : 0 : }
2686 : :
2687 : 0 : QgsAuthCertUtils::CertTrustPolicy QgsAuthManager::certTrustPolicy( const QSslCertificate &cert )
2688 : : {
2689 : 0 : QMutexLocker locker( mMutex.get() );
2690 : 0 : if ( cert.isNull() )
2691 : : {
2692 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2693 : 0 : return QgsAuthCertUtils::DefaultTrust;
2694 : : }
2695 : :
2696 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2697 : :
2698 : 0 : QSqlQuery query( authDatabaseConnection() );
2699 : 0 : query.prepare( QStringLiteral( "SELECT policy FROM %1 "
2700 : 0 : "WHERE id = :id" ).arg( authDbTrustTable() ) );
2701 : :
2702 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2703 : :
2704 : 0 : if ( !authDbQuery( &query ) )
2705 : 0 : return QgsAuthCertUtils::DefaultTrust;
2706 : :
2707 : 0 : QgsAuthCertUtils::CertTrustPolicy policy( QgsAuthCertUtils::DefaultTrust );
2708 : 0 : if ( query.isActive() && query.isSelect() )
2709 : : {
2710 : 0 : if ( query.first() )
2711 : : {
2712 : 0 : policy = static_cast< QgsAuthCertUtils::CertTrustPolicy >( query.value( 0 ).toInt() );
2713 : 0 : QgsDebugMsgLevel( QStringLiteral( "Authentication cert trust policy retrieved for id: %1" ).arg( id ), 2 );
2714 : 0 : }
2715 : 0 : if ( query.next() )
2716 : : {
2717 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one cert trust policy for id: %1" ).arg( id ) );
2718 : 0 : emit messageOut( tr( "Authentication database contains duplicate cert trust policies" ), authManTag(), WARNING );
2719 : 0 : return QgsAuthCertUtils::DefaultTrust;
2720 : : }
2721 : 0 : }
2722 : 0 : return policy;
2723 : 0 : }
2724 : :
2725 : 0 : bool QgsAuthManager::removeCertTrustPolicies( const QList<QSslCertificate> &certs )
2726 : : {
2727 : 0 : QMutexLocker locker( mMutex.get() );
2728 : 0 : if ( certs.empty() )
2729 : : {
2730 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate list has no certs" ) );
2731 : 0 : return false;
2732 : : }
2733 : :
2734 : 0 : for ( const auto &cert : certs )
2735 : : {
2736 : 0 : if ( !removeCertTrustPolicy( cert ) )
2737 : 0 : return false;
2738 : : }
2739 : 0 : return true;
2740 : 0 : }
2741 : :
2742 : 0 : bool QgsAuthManager::removeCertTrustPolicy( const QSslCertificate &cert )
2743 : : {
2744 : 0 : QMutexLocker locker( mMutex.get() );
2745 : 0 : if ( cert.isNull() )
2746 : : {
2747 : 0 : QgsDebugMsg( QStringLiteral( "Passed certificate is null" ) );
2748 : 0 : return false;
2749 : : }
2750 : :
2751 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2752 : :
2753 : 0 : QSqlQuery query( authDatabaseConnection() );
2754 : :
2755 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( authDbTrustTable() ) );
2756 : :
2757 : 0 : query.bindValue( QStringLiteral( ":id" ), id );
2758 : :
2759 : 0 : if ( !authDbStartTransaction() )
2760 : 0 : return false;
2761 : :
2762 : 0 : if ( !authDbQuery( &query ) )
2763 : 0 : return false;
2764 : :
2765 : 0 : if ( !authDbCommit() )
2766 : 0 : return false;
2767 : :
2768 : 0 : QgsDebugMsgLevel( QStringLiteral( "REMOVED cert trust policy for id: %1" ).arg( id ), 2 );
2769 : :
2770 : 0 : return true;
2771 : 0 : }
2772 : :
2773 : 0 : QgsAuthCertUtils::CertTrustPolicy QgsAuthManager::certificateTrustPolicy( const QSslCertificate &cert )
2774 : : {
2775 : 0 : QMutexLocker locker( mMutex.get() );
2776 : 0 : if ( cert.isNull() )
2777 : : {
2778 : 0 : return QgsAuthCertUtils::NoPolicy;
2779 : : }
2780 : :
2781 : 0 : QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
2782 : 0 : const QStringList &trustedids = mCertTrustCache.value( QgsAuthCertUtils::Trusted );
2783 : 0 : const QStringList &untrustedids = mCertTrustCache.value( QgsAuthCertUtils::Untrusted );
2784 : :
2785 : 0 : QgsAuthCertUtils::CertTrustPolicy policy( QgsAuthCertUtils::DefaultTrust );
2786 : 0 : if ( trustedids.contains( id ) )
2787 : : {
2788 : 0 : policy = QgsAuthCertUtils::Trusted;
2789 : 0 : }
2790 : 0 : else if ( untrustedids.contains( id ) )
2791 : : {
2792 : 0 : policy = QgsAuthCertUtils::Untrusted;
2793 : 0 : }
2794 : 0 : return policy;
2795 : 0 : }
2796 : :
2797 : 0 : bool QgsAuthManager::setDefaultCertTrustPolicy( QgsAuthCertUtils::CertTrustPolicy policy )
2798 : : {
2799 : :
2800 : 0 : if ( policy == QgsAuthCertUtils::DefaultTrust )
2801 : : {
2802 : : // set default trust policy to Trusted by removing setting
2803 : 0 : return removeAuthSetting( QStringLiteral( "certdefaulttrust" ) );
2804 : : }
2805 : 0 : return storeAuthSetting( QStringLiteral( "certdefaulttrust" ), static_cast< int >( policy ) );
2806 : 0 : }
2807 : :
2808 : 0 : QgsAuthCertUtils::CertTrustPolicy QgsAuthManager::defaultCertTrustPolicy()
2809 : : {
2810 : 0 : QMutexLocker locker( mMutex.get() );
2811 : 0 : QVariant policy( authSetting( QStringLiteral( "certdefaulttrust" ) ) );
2812 : 0 : if ( policy.isNull() )
2813 : : {
2814 : 0 : return QgsAuthCertUtils::Trusted;
2815 : : }
2816 : 0 : return static_cast< QgsAuthCertUtils::CertTrustPolicy >( policy.toInt() );
2817 : 0 : }
2818 : :
2819 : 0 : bool QgsAuthManager::rebuildCertTrustCache()
2820 : : {
2821 : 0 : QMutexLocker locker( mMutex.get() );
2822 : 0 : mCertTrustCache.clear();
2823 : :
2824 : 0 : QSqlQuery query( authDatabaseConnection() );
2825 : 0 : query.prepare( QStringLiteral( "SELECT id, policy FROM %1" ).arg( authDbTrustTable() ) );
2826 : :
2827 : 0 : if ( !authDbQuery( &query ) )
2828 : : {
2829 : 0 : QgsDebugMsg( QStringLiteral( "Rebuild of cert trust policy cache FAILED" ) );
2830 : 0 : return false;
2831 : : }
2832 : :
2833 : 0 : if ( query.isActive() && query.isSelect() )
2834 : : {
2835 : 0 : while ( query.next() )
2836 : : {
2837 : 0 : QString id = query.value( 0 ).toString();
2838 : 0 : QgsAuthCertUtils::CertTrustPolicy policy = static_cast< QgsAuthCertUtils::CertTrustPolicy >( query.value( 1 ).toInt() );
2839 : :
2840 : 0 : QStringList ids;
2841 : 0 : if ( mCertTrustCache.contains( policy ) )
2842 : : {
2843 : 0 : ids = mCertTrustCache.value( policy );
2844 : 0 : }
2845 : 0 : mCertTrustCache.insert( policy, ids << id );
2846 : 0 : }
2847 : 0 : }
2848 : :
2849 : 0 : QgsDebugMsgLevel( QStringLiteral( "Rebuild of cert trust policy cache SUCCEEDED" ), 2 );
2850 : 0 : return true;
2851 : 0 : }
2852 : :
2853 : 0 : const QList<QSslCertificate> QgsAuthManager::trustedCaCerts( bool includeinvalid )
2854 : : {
2855 : 0 : QMutexLocker locker( mMutex.get() );
2856 : 0 : QgsAuthCertUtils::CertTrustPolicy defaultpolicy( defaultCertTrustPolicy() );
2857 : 0 : QStringList trustedids = mCertTrustCache.value( QgsAuthCertUtils::Trusted );
2858 : 0 : QStringList untrustedids = mCertTrustCache.value( QgsAuthCertUtils::Untrusted );
2859 : 0 : const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2860 : :
2861 : 0 : QList<QSslCertificate> trustedcerts;
2862 : 0 : for ( int i = 0; i < certpairs.size(); ++i )
2863 : : {
2864 : 0 : QSslCertificate cert( certpairs.at( i ).second );
2865 : 0 : QString certid( QgsAuthCertUtils::shaHexForCert( cert ) );
2866 : 0 : if ( trustedids.contains( certid ) )
2867 : : {
2868 : : // trusted certs are always added regardless of their validity
2869 : 0 : trustedcerts.append( cert );
2870 : 0 : }
2871 : 0 : else if ( defaultpolicy == QgsAuthCertUtils::Trusted && !untrustedids.contains( certid ) )
2872 : : {
2873 : 0 : if ( !includeinvalid && !QgsAuthCertUtils::certIsViable( cert ) )
2874 : 0 : continue;
2875 : 0 : trustedcerts.append( cert );
2876 : 0 : }
2877 : 0 : }
2878 : :
2879 : : // update application default SSL config for new requests
2880 : 0 : QSslConfiguration sslconfig( QSslConfiguration::defaultConfiguration() );
2881 : 0 : sslconfig.setCaCertificates( trustedcerts );
2882 : 0 : QSslConfiguration::setDefaultConfiguration( sslconfig );
2883 : :
2884 : 0 : return trustedcerts;
2885 : 0 : }
2886 : :
2887 : 0 : const QList<QSslCertificate> QgsAuthManager::untrustedCaCerts( QList<QSslCertificate> trustedCAs )
2888 : : {
2889 : 0 : QMutexLocker locker( mMutex.get() );
2890 : 0 : if ( trustedCAs.isEmpty() )
2891 : : {
2892 : 0 : if ( mTrustedCaCertsCache.isEmpty() )
2893 : : {
2894 : 0 : rebuildTrustedCaCertsCache();
2895 : 0 : }
2896 : 0 : trustedCAs = trustedCaCertsCache();
2897 : 0 : }
2898 : :
2899 : 0 : const QList<QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate> > &certpairs( mCaCertsCache.values() );
2900 : :
2901 : 0 : QList<QSslCertificate> untrustedCAs;
2902 : 0 : for ( int i = 0; i < certpairs.size(); ++i )
2903 : : {
2904 : 0 : QSslCertificate cert( certpairs.at( i ).second );
2905 : 0 : if ( !trustedCAs.contains( cert ) )
2906 : : {
2907 : 0 : untrustedCAs.append( cert );
2908 : 0 : }
2909 : 0 : }
2910 : 0 : return untrustedCAs;
2911 : 0 : }
2912 : :
2913 : 0 : bool QgsAuthManager::rebuildTrustedCaCertsCache()
2914 : : {
2915 : 0 : QMutexLocker locker( mMutex.get() );
2916 : 0 : mTrustedCaCertsCache = trustedCaCerts();
2917 : 0 : QgsDebugMsgLevel( QStringLiteral( "Rebuilt trusted cert authorities cache" ), 2 );
2918 : : // TODO: add some error trapping for the operation
2919 : : return true;
2920 : 0 : }
2921 : :
2922 : 0 : const QByteArray QgsAuthManager::trustedCaCertsPemText()
2923 : : {
2924 : 0 : QMutexLocker locker( mMutex.get() );
2925 : 0 : return QgsAuthCertUtils::certsToPemText( trustedCaCertsCache() );
2926 : 0 : }
2927 : :
2928 : 0 : bool QgsAuthManager::passwordHelperSync()
2929 : : {
2930 : 0 : QMutexLocker locker( mMutex.get() );
2931 : 0 : if ( masterPasswordIsSet() )
2932 : : {
2933 : 0 : return passwordHelperWrite( mMasterPass );
2934 : : }
2935 : 0 : return false;
2936 : 0 : }
2937 : :
2938 : :
2939 : : ////////////////// Certificate calls - end ///////////////////////
2940 : :
2941 : : #endif
2942 : :
2943 : 0 : void QgsAuthManager::clearAllCachedConfigs()
2944 : : {
2945 : 0 : if ( isDisabled() )
2946 : 0 : return;
2947 : :
2948 : 0 : const QStringList ids = configIds();
2949 : 0 : for ( const auto &authcfg : ids )
2950 : : {
2951 : 0 : clearCachedConfig( authcfg );
2952 : : }
2953 : 0 : }
2954 : :
2955 : 0 : void QgsAuthManager::clearCachedConfig( const QString &authcfg )
2956 : : {
2957 : 0 : if ( isDisabled() )
2958 : 0 : return;
2959 : :
2960 : 0 : QgsAuthMethod *authmethod = configAuthMethod( authcfg );
2961 : 0 : if ( authmethod )
2962 : : {
2963 : 0 : authmethod->clearCachedConfig( authcfg );
2964 : 0 : }
2965 : 0 : }
2966 : :
2967 : 0 : void QgsAuthManager::writeToConsole( const QString &message,
2968 : : const QString &tag,
2969 : : QgsAuthManager::MessageLevel level )
2970 : : {
2971 : 0 : Q_UNUSED( tag )
2972 : :
2973 : : // only output WARNING and CRITICAL messages
2974 : 0 : if ( level == QgsAuthManager::INFO )
2975 : 0 : return;
2976 : :
2977 : 0 : QString msg;
2978 : 0 : switch ( level )
2979 : : {
2980 : : case QgsAuthManager::WARNING:
2981 : 0 : msg += QLatin1String( "WARNING: " );
2982 : 0 : break;
2983 : : case QgsAuthManager::CRITICAL:
2984 : 0 : msg += QLatin1String( "ERROR: " );
2985 : 0 : break;
2986 : : default:
2987 : 0 : break;
2988 : : }
2989 : 0 : msg += message;
2990 : :
2991 : 0 : QTextStream out( stdout, QIODevice::WriteOnly );
2992 : : #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
2993 : : out << msg << endl;
2994 : : #else
2995 : 0 : out << msg << Qt::endl;
2996 : : #endif
2997 : 0 : }
2998 : :
2999 : 0 : void QgsAuthManager::tryToStartDbErase()
3000 : : {
3001 : 0 : ++mScheduledDbEraseRequestCount;
3002 : : // wait a total of 90 seconds for GUI availiability or user interaction, then cancel schedule
3003 : 0 : int trycutoff = 90 / ( mScheduledDbEraseRequestWait ? mScheduledDbEraseRequestWait : 3 );
3004 : 0 : if ( mScheduledDbEraseRequestCount >= trycutoff )
3005 : : {
3006 : 0 : setScheduledAuthDatabaseErase( false );
3007 : 0 : QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emitting/scheduling canceled" ), 2 );
3008 : 0 : return;
3009 : : }
3010 : : else
3011 : : {
3012 : 0 : QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest attempt (%1 of %2)" )
3013 : : .arg( mScheduledDbEraseRequestCount ).arg( trycutoff ), 2 );
3014 : : }
3015 : :
3016 : 0 : if ( scheduledAuthDatabaseErase() && !mScheduledDbEraseRequestEmitted && mMutex->tryLock() )
3017 : : {
3018 : : // see note in header about this signal's use
3019 : 0 : mScheduledDbEraseRequestEmitted = true;
3020 : 0 : emit authDatabaseEraseRequested();
3021 : :
3022 : 0 : mMutex->unlock();
3023 : :
3024 : 0 : QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emitted" ), 2 );
3025 : 0 : return;
3026 : : }
3027 : 0 : QgsDebugMsgLevel( QStringLiteral( "authDatabaseEraseRequest emit skipped" ), 2 );
3028 : 0 : }
3029 : :
3030 : :
3031 : 6 : QgsAuthManager::~QgsAuthManager()
3032 : 6 : {
3033 : 3 : QMutexLocker locker( mMutex.get() );
3034 : 3 : QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
3035 : 3 : while ( iterator.hasNext() )
3036 : : {
3037 : 0 : iterator.next();
3038 : 0 : iterator.key()->disconnect( iterator.value() );
3039 : : }
3040 : 3 : locker.unlock();
3041 : :
3042 : 3 : if ( !isDisabled() )
3043 : : {
3044 : 0 : delete QgsAuthMethodRegistry::instance();
3045 : 0 : qDeleteAll( mAuthMethods );
3046 : :
3047 : 0 : QSqlDatabase authConn = authDatabaseConnection();
3048 : 0 : if ( authConn.isValid() && authConn.isOpen() )
3049 : 0 : authConn.close();
3050 : 0 : }
3051 : 3 : delete mScheduledDbEraseTimer;
3052 : 3 : mScheduledDbEraseTimer = nullptr;
3053 : 6 : QSqlDatabase::removeDatabase( QStringLiteral( "authentication.configs" ) );
3054 : 6 : }
3055 : :
3056 : :
3057 : 0 : QString QgsAuthManager::passwordHelperName() const
3058 : : {
3059 : 0 : return tr( "Password Helper" );
3060 : : }
3061 : :
3062 : :
3063 : 0 : void QgsAuthManager::passwordHelperLog( const QString &msg ) const
3064 : : {
3065 : 0 : if ( passwordHelperLoggingEnabled() )
3066 : : {
3067 : 0 : QgsMessageLog::logMessage( msg, passwordHelperName() );
3068 : 0 : }
3069 : 0 : }
3070 : :
3071 : 0 : bool QgsAuthManager::passwordHelperDelete()
3072 : : {
3073 : 0 : passwordHelperLog( tr( "Opening %1 for DELETE…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3074 : : bool result;
3075 : 0 : QKeychain::DeletePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3076 : 0 : QgsSettings settings;
3077 : 0 : job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3078 : 0 : job.setAutoDelete( false );
3079 : 0 : job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3080 : 0 : QEventLoop loop;
3081 : 0 : connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3082 : 0 : job.start();
3083 : 0 : loop.exec();
3084 : 0 : if ( job.error() )
3085 : : {
3086 : 0 : mPasswordHelperErrorCode = job.error();
3087 : 0 : mPasswordHelperErrorMessage = tr( "Delete password failed: %1." ).arg( job.errorString() );
3088 : : // Signals used in the tests to exit main application loop
3089 : 0 : emit passwordHelperFailure();
3090 : 0 : result = false;
3091 : 0 : }
3092 : : else
3093 : : {
3094 : : // Signals used in the tests to exit main application loop
3095 : 0 : emit passwordHelperSuccess();
3096 : 0 : result = true;
3097 : : }
3098 : 0 : passwordHelperProcessError();
3099 : 0 : return result;
3100 : 0 : }
3101 : :
3102 : 0 : QString QgsAuthManager::passwordHelperRead()
3103 : : {
3104 : : // Retrieve it!
3105 : 0 : QString password;
3106 : 0 : passwordHelperLog( tr( "Opening %1 for READ…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3107 : 0 : QKeychain::ReadPasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3108 : 0 : QgsSettings settings;
3109 : 0 : job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3110 : 0 : job.setAutoDelete( false );
3111 : 0 : job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3112 : 0 : QEventLoop loop;
3113 : 0 : connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3114 : 0 : job.start();
3115 : 0 : loop.exec();
3116 : 0 : if ( job.error() )
3117 : : {
3118 : 0 : mPasswordHelperErrorCode = job.error();
3119 : 0 : mPasswordHelperErrorMessage = tr( "Retrieving password from your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
3120 : : // Signals used in the tests to exit main application loop
3121 : 0 : emit passwordHelperFailure();
3122 : 0 : }
3123 : : else
3124 : : {
3125 : 0 : password = job.textData();
3126 : : // Password is there but it is empty, treat it like if it was not found
3127 : 0 : if ( password.isEmpty() )
3128 : : {
3129 : 0 : mPasswordHelperErrorCode = QKeychain::EntryNotFound;
3130 : 0 : mPasswordHelperErrorMessage = tr( "Empty password retrieved from your %1." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME );
3131 : : // Signals used in the tests to exit main application loop
3132 : 0 : emit passwordHelperFailure();
3133 : 0 : }
3134 : : else
3135 : : {
3136 : : // Signals used in the tests to exit main application loop
3137 : 0 : emit passwordHelperSuccess();
3138 : : }
3139 : : }
3140 : 0 : passwordHelperProcessError();
3141 : 0 : return password;
3142 : 0 : }
3143 : :
3144 : 0 : bool QgsAuthManager::passwordHelperWrite( const QString &password )
3145 : : {
3146 : : Q_ASSERT( !password.isEmpty() );
3147 : : bool result;
3148 : 0 : passwordHelperLog( tr( "Opening %1 for WRITE…" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3149 : 0 : QKeychain::WritePasswordJob job( AUTH_PASSWORD_HELPER_FOLDER_NAME );
3150 : 0 : QgsSettings settings;
3151 : 0 : job.setInsecureFallback( settings.value( QStringLiteral( "password_helper_insecure_fallback" ), false, QgsSettings::Section::Auth ).toBool() );
3152 : 0 : job.setAutoDelete( false );
3153 : 0 : job.setKey( AUTH_PASSWORD_HELPER_KEY_NAME );
3154 : 0 : job.setTextData( password );
3155 : 0 : QEventLoop loop;
3156 : 0 : connect( &job, &QKeychain::Job::finished, &loop, &QEventLoop::quit );
3157 : 0 : job.start();
3158 : 0 : loop.exec();
3159 : 0 : if ( job.error() )
3160 : : {
3161 : 0 : mPasswordHelperErrorCode = job.error();
3162 : 0 : mPasswordHelperErrorMessage = tr( "Storing password in your %1 failed: %2." ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, job.errorString() );
3163 : : // Signals used in the tests to exit main application loop
3164 : 0 : emit passwordHelperFailure();
3165 : 0 : result = false;
3166 : 0 : }
3167 : : else
3168 : : {
3169 : 0 : passwordHelperClearErrors();
3170 : : // Signals used in the tests to exit main application loop
3171 : 0 : emit passwordHelperSuccess();
3172 : 0 : result = true;
3173 : : }
3174 : 0 : passwordHelperProcessError();
3175 : 0 : return result;
3176 : 0 : }
3177 : :
3178 : 0 : bool QgsAuthManager::passwordHelperEnabled() const
3179 : : {
3180 : : // Does the user want to store the password in the wallet?
3181 : 0 : QgsSettings settings;
3182 : 0 : return settings.value( QStringLiteral( "use_password_helper" ), true, QgsSettings::Section::Auth ).toBool();
3183 : 0 : }
3184 : :
3185 : 0 : void QgsAuthManager::setPasswordHelperEnabled( const bool enabled )
3186 : : {
3187 : 0 : QgsSettings settings;
3188 : 0 : settings.setValue( QStringLiteral( "use_password_helper" ), enabled, QgsSettings::Section::Auth );
3189 : 0 : emit messageOut( enabled ? tr( "Your %1 will be <b>used from now</b> on to store and retrieve the master password." )
3190 : 0 : .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) :
3191 : 0 : tr( "Your %1 will <b>not be used anymore</b> to store and retrieve the master password." )
3192 : 0 : .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ) );
3193 : 0 : }
3194 : :
3195 : 0 : bool QgsAuthManager::passwordHelperLoggingEnabled() const
3196 : : {
3197 : : // Does the user want to store the password in the wallet?
3198 : 0 : QgsSettings settings;
3199 : 0 : return settings.value( QStringLiteral( "password_helper_logging" ), false, QgsSettings::Section::Auth ).toBool();
3200 : 0 : }
3201 : :
3202 : 0 : void QgsAuthManager::setPasswordHelperLoggingEnabled( const bool enabled )
3203 : : {
3204 : 0 : QgsSettings settings;
3205 : 0 : settings.setValue( QStringLiteral( "password_helper_logging" ), enabled, QgsSettings::Section::Auth );
3206 : 0 : }
3207 : :
3208 : 0 : void QgsAuthManager::passwordHelperClearErrors()
3209 : : {
3210 : 0 : mPasswordHelperErrorCode = QKeychain::NoError;
3211 : 0 : mPasswordHelperErrorMessage.clear();
3212 : 0 : }
3213 : :
3214 : 0 : void QgsAuthManager::passwordHelperProcessError()
3215 : : {
3216 : 0 : if ( mPasswordHelperErrorCode == QKeychain::AccessDenied ||
3217 : 0 : mPasswordHelperErrorCode == QKeychain::AccessDeniedByUser ||
3218 : 0 : mPasswordHelperErrorCode == QKeychain::NoBackendAvailable ||
3219 : 0 : mPasswordHelperErrorCode == QKeychain::NotImplemented )
3220 : : {
3221 : : // If the error is permanent or the user denied access to the wallet
3222 : : // we also want to disable the wallet system to prevent annoying
3223 : : // notification on each subsequent access.
3224 : 0 : setPasswordHelperEnabled( false );
3225 : 0 : mPasswordHelperErrorMessage = tr( "There was an error and integration with your %1 system has been disabled. "
3226 : : "You can re-enable it at any time through the \"Utilities\" menu "
3227 : : "in the Authentication pane of the options dialog. %2" )
3228 : 0 : .arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage );
3229 : 0 : }
3230 : 0 : if ( mPasswordHelperErrorCode != QKeychain::NoError )
3231 : : {
3232 : : // We've got an error from the wallet
3233 : 0 : passwordHelperLog( tr( "Error in %1: %2" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME, mPasswordHelperErrorMessage ) );
3234 : 0 : emit passwordHelperMessageOut( mPasswordHelperErrorMessage, authManTag(), CRITICAL );
3235 : 0 : }
3236 : 0 : passwordHelperClearErrors();
3237 : 0 : }
3238 : :
3239 : :
3240 : 0 : bool QgsAuthManager::masterPasswordInput()
3241 : : {
3242 : 0 : if ( isDisabled() )
3243 : 0 : return false;
3244 : :
3245 : 0 : QString pass;
3246 : 0 : bool storedPasswordIsValid = false;
3247 : 0 : bool ok = false;
3248 : :
3249 : : // Read the password from the wallet
3250 : 0 : if ( passwordHelperEnabled() )
3251 : : {
3252 : 0 : pass = passwordHelperRead();
3253 : 0 : if ( ! pass.isEmpty() && ( mPasswordHelperErrorCode == QKeychain::NoError ) )
3254 : : {
3255 : : // Let's check the password!
3256 : 0 : if ( verifyMasterPassword( pass ) )
3257 : : {
3258 : 0 : ok = true;
3259 : 0 : storedPasswordIsValid = true;
3260 : 0 : emit passwordHelperMessageOut( tr( "Master password has been successfully read from your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
3261 : 0 : }
3262 : : else
3263 : : {
3264 : 0 : emit passwordHelperMessageOut( tr( "Master password stored in your %1 is not valid" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
3265 : : }
3266 : 0 : }
3267 : 0 : }
3268 : :
3269 : 0 : if ( ! ok )
3270 : : {
3271 : 0 : pass.clear();
3272 : 0 : ok = QgsCredentials::instance()->getMasterPassword( pass, masterPasswordHashInDatabase() );
3273 : 0 : }
3274 : :
3275 : 0 : if ( ok && !pass.isEmpty() && mMasterPass != pass )
3276 : : {
3277 : 0 : mMasterPass = pass;
3278 : 0 : if ( passwordHelperEnabled() && ! storedPasswordIsValid )
3279 : : {
3280 : 0 : if ( passwordHelperWrite( pass ) )
3281 : : {
3282 : 0 : emit passwordHelperMessageOut( tr( "Master password has been successfully written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), INFO );
3283 : 0 : }
3284 : : else
3285 : : {
3286 : 0 : emit passwordHelperMessageOut( tr( "Master password could not be written to your %1" ).arg( AUTH_PASSWORD_HELPER_DISPLAY_NAME ), authManTag(), WARNING );
3287 : : }
3288 : 0 : }
3289 : 0 : return true;
3290 : : }
3291 : 0 : return false;
3292 : 0 : }
3293 : :
3294 : 0 : bool QgsAuthManager::masterPasswordRowsInDb( int *rows ) const
3295 : : {
3296 : 0 : if ( isDisabled() )
3297 : 0 : return false;
3298 : :
3299 : 0 : QSqlQuery query( authDatabaseConnection() );
3300 : 0 : query.prepare( QStringLiteral( "SELECT Count(*) FROM %1" ).arg( authDbPassTable() ) );
3301 : :
3302 : 0 : bool ok = authDbQuery( &query );
3303 : 0 : if ( query.first() )
3304 : : {
3305 : 0 : *rows = query.value( 0 ).toInt();
3306 : 0 : }
3307 : :
3308 : 0 : return ok;
3309 : 0 : }
3310 : :
3311 : 0 : bool QgsAuthManager::masterPasswordHashInDatabase() const
3312 : : {
3313 : 0 : if ( isDisabled() )
3314 : 0 : return false;
3315 : :
3316 : 0 : int rows = 0;
3317 : 0 : if ( !masterPasswordRowsInDb( &rows ) )
3318 : : {
3319 : 0 : const char *err = QT_TR_NOOP( "Master password: FAILED to access database" );
3320 : 0 : QgsDebugMsg( err );
3321 : 0 : emit messageOut( tr( err ), authManTag(), CRITICAL );
3322 : :
3323 : 0 : return false;
3324 : : }
3325 : 0 : return ( rows == 1 );
3326 : 0 : }
3327 : :
3328 : 0 : bool QgsAuthManager::masterPasswordCheckAgainstDb( const QString &compare ) const
3329 : : {
3330 : 0 : if ( isDisabled() )
3331 : 0 : return false;
3332 : :
3333 : : // first verify there is only one row in auth db (uses first found)
3334 : :
3335 : 0 : QSqlQuery query( authDatabaseConnection() );
3336 : 0 : query.prepare( QStringLiteral( "SELECT salt, hash FROM %1" ).arg( authDbPassTable() ) );
3337 : 0 : if ( !authDbQuery( &query ) )
3338 : 0 : return false;
3339 : :
3340 : 0 : if ( !query.first() )
3341 : 0 : return false;
3342 : :
3343 : 0 : QString salt = query.value( 0 ).toString();
3344 : 0 : QString hash = query.value( 1 ).toString();
3345 : :
3346 : 0 : return QgsAuthCrypto::verifyPasswordKeyHash( compare.isNull() ? mMasterPass : compare, salt, hash );
3347 : 0 : }
3348 : :
3349 : 0 : bool QgsAuthManager::masterPasswordStoreInDb() const
3350 : : {
3351 : 0 : if ( isDisabled() )
3352 : 0 : return false;
3353 : :
3354 : 0 : QString salt, hash, civ;
3355 : 0 : QgsAuthCrypto::passwordKeyHash( mMasterPass, &salt, &hash, &civ );
3356 : :
3357 : 0 : QSqlQuery query( authDatabaseConnection() );
3358 : 0 : query.prepare( QStringLiteral( "INSERT INTO %1 (salt, hash, civ) VALUES (:salt, :hash, :civ)" ).arg( authDbPassTable() ) );
3359 : :
3360 : 0 : query.bindValue( QStringLiteral( ":salt" ), salt );
3361 : 0 : query.bindValue( QStringLiteral( ":hash" ), hash );
3362 : 0 : query.bindValue( QStringLiteral( ":civ" ), civ );
3363 : :
3364 : 0 : if ( !authDbStartTransaction() )
3365 : 0 : return false;
3366 : :
3367 : 0 : if ( !authDbQuery( &query ) )
3368 : 0 : return false;
3369 : :
3370 : 0 : if ( !authDbCommit() )
3371 : 0 : return false;
3372 : :
3373 : 0 : return true;
3374 : 0 : }
3375 : :
3376 : 0 : bool QgsAuthManager::masterPasswordClearDb()
3377 : : {
3378 : 0 : if ( isDisabled() )
3379 : 0 : return false;
3380 : :
3381 : 0 : QSqlQuery query( authDatabaseConnection() );
3382 : 0 : query.prepare( QStringLiteral( "DELETE FROM %1" ).arg( authDbPassTable() ) );
3383 : 0 : bool res = authDbTransactionQuery( &query );
3384 : 0 : if ( res )
3385 : 0 : clearMasterPassword();
3386 : 0 : return res;
3387 : 0 : }
3388 : :
3389 : 0 : const QString QgsAuthManager::masterPasswordCiv() const
3390 : : {
3391 : 0 : if ( isDisabled() )
3392 : 0 : return QString();
3393 : :
3394 : 0 : QSqlQuery query( authDatabaseConnection() );
3395 : 0 : query.prepare( QStringLiteral( "SELECT civ FROM %1" ).arg( authDbPassTable() ) );
3396 : 0 : if ( !authDbQuery( &query ) )
3397 : 0 : return QString();
3398 : :
3399 : 0 : if ( !query.first() )
3400 : 0 : return QString();
3401 : :
3402 : 0 : return query.value( 0 ).toString();
3403 : 0 : }
3404 : :
3405 : 0 : QStringList QgsAuthManager::configIds() const
3406 : : {
3407 : 0 : QStringList configids = QStringList();
3408 : :
3409 : 0 : if ( isDisabled() )
3410 : 0 : return configids;
3411 : :
3412 : 0 : QSqlQuery query( authDatabaseConnection() );
3413 : 0 : query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( authDatabaseConfigTable() ) );
3414 : :
3415 : 0 : if ( !authDbQuery( &query ) )
3416 : : {
3417 : 0 : return configids;
3418 : : }
3419 : :
3420 : 0 : if ( query.isActive() )
3421 : : {
3422 : 0 : while ( query.next() )
3423 : : {
3424 : 0 : configids << query.value( 0 ).toString();
3425 : : }
3426 : 0 : }
3427 : 0 : return configids;
3428 : 0 : }
3429 : :
3430 : 0 : bool QgsAuthManager::verifyPasswordCanDecryptConfigs() const
3431 : : {
3432 : 0 : if ( isDisabled() )
3433 : 0 : return false;
3434 : :
3435 : : // no need to check for setMasterPassword, since this is private and it will be set
3436 : :
3437 : 0 : QSqlQuery query( authDatabaseConnection() );
3438 : :
3439 : 0 : query.prepare( QStringLiteral( "SELECT id, config FROM %1" ).arg( authDatabaseConfigTable() ) );
3440 : :
3441 : 0 : if ( !authDbQuery( &query ) )
3442 : 0 : return false;
3443 : :
3444 : 0 : if ( !query.isActive() || !query.isSelect() )
3445 : : {
3446 : 0 : QgsDebugMsg( QStringLiteral( "Verify password can decrypt configs FAILED, query not active or a select operation" ) );
3447 : 0 : return false;
3448 : : }
3449 : :
3450 : 0 : int checked = 0;
3451 : 0 : while ( query.next() )
3452 : : {
3453 : 0 : ++checked;
3454 : 0 : QString configstring( QgsAuthCrypto::decrypt( mMasterPass, masterPasswordCiv(), query.value( 1 ).toString() ) );
3455 : 0 : if ( configstring.isEmpty() )
3456 : : {
3457 : 0 : QgsDebugMsg( QStringLiteral( "Verify password can decrypt configs FAILED, could not decrypt a config (id: %1)" )
3458 : : .arg( query.value( 0 ).toString() ) );
3459 : 0 : return false;
3460 : : }
3461 : 0 : }
3462 : :
3463 : 0 : QgsDebugMsgLevel( QStringLiteral( "Verify password can decrypt configs SUCCESS (checked %1 configs)" ).arg( checked ), 2 );
3464 : 0 : return true;
3465 : 0 : }
3466 : :
3467 : 0 : bool QgsAuthManager::reencryptAllAuthenticationConfigs( const QString &prevpass, const QString &prevciv )
3468 : : {
3469 : 0 : if ( isDisabled() )
3470 : 0 : return false;
3471 : :
3472 : 0 : bool res = true;
3473 : 0 : const QStringList ids = configIds();
3474 : 0 : for ( const auto &configid : ids )
3475 : : {
3476 : 0 : res = res && reencryptAuthenticationConfig( configid, prevpass, prevciv );
3477 : : }
3478 : 0 : return res;
3479 : 0 : }
3480 : :
3481 : 0 : bool QgsAuthManager::reencryptAuthenticationConfig( const QString &authcfg, const QString &prevpass, const QString &prevciv )
3482 : : {
3483 : 0 : if ( isDisabled() )
3484 : 0 : return false;
3485 : :
3486 : : // no need to check for setMasterPassword, since this is private and it will be set
3487 : :
3488 : 0 : QSqlQuery query( authDatabaseConnection() );
3489 : :
3490 : 0 : query.prepare( QStringLiteral( "SELECT config FROM %1 "
3491 : 0 : "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
3492 : :
3493 : 0 : query.bindValue( QStringLiteral( ":id" ), authcfg );
3494 : :
3495 : 0 : if ( !authDbQuery( &query ) )
3496 : 0 : return false;
3497 : :
3498 : 0 : if ( !query.isActive() || !query.isSelect() )
3499 : : {
3500 : 0 : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for authcfg: %2" ).arg( authcfg ) );
3501 : 0 : return false;
3502 : : }
3503 : :
3504 : 0 : if ( query.first() )
3505 : : {
3506 : 0 : QString configstring( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3507 : :
3508 : 0 : if ( query.next() )
3509 : : {
3510 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one for authcfg: %1" ).arg( authcfg ) );
3511 : 0 : emit messageOut( tr( "Authentication database contains duplicate configuration IDs" ), authManTag(), WARNING );
3512 : 0 : return false;
3513 : : }
3514 : :
3515 : 0 : query.clear();
3516 : :
3517 : 0 : query.prepare( QStringLiteral( "UPDATE %1 "
3518 : : "SET config = :config "
3519 : 0 : "WHERE id = :id" ).arg( authDatabaseConfigTable() ) );
3520 : :
3521 : 0 : query.bindValue( QStringLiteral( ":id" ), authcfg );
3522 : 0 : query.bindValue( QStringLiteral( ":config" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), configstring ) );
3523 : :
3524 : 0 : if ( !authDbStartTransaction() )
3525 : 0 : return false;
3526 : :
3527 : 0 : if ( !authDbQuery( &query ) )
3528 : 0 : return false;
3529 : :
3530 : 0 : if ( !authDbCommit() )
3531 : 0 : return false;
3532 : :
3533 : 0 : QgsDebugMsgLevel( QStringLiteral( "Reencrypt SUCCESS for authcfg: %2" ).arg( authcfg ), 2 );
3534 : 0 : return true;
3535 : 0 : }
3536 : : else
3537 : : {
3538 : 0 : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db authcfg: %2" ).arg( authcfg ) );
3539 : 0 : return false;
3540 : : }
3541 : 0 : }
3542 : :
3543 : 0 : bool QgsAuthManager::reencryptAllAuthenticationSettings( const QString &prevpass, const QString &prevciv )
3544 : : {
3545 : : // TODO: start remove (when function is actually used)
3546 : 0 : Q_UNUSED( prevpass )
3547 : 0 : Q_UNUSED( prevciv )
3548 : 0 : return true;
3549 : : // end remove
3550 : :
3551 : : #if 0
3552 : : if ( isDisabled() )
3553 : : return false;
3554 : :
3555 : : ///////////////////////////////////////////////////////////////
3556 : : // When adding settings that require encryption, add to list //
3557 : : ///////////////////////////////////////////////////////////////
3558 : :
3559 : : QStringList encryptedsettings;
3560 : : encryptedsettings << "";
3561 : :
3562 : : for ( const auto & sett, std::as_const( encryptedsettings ) )
3563 : : {
3564 : : if ( sett.isEmpty() || !existsAuthSetting( sett ) )
3565 : : continue;
3566 : :
3567 : : // no need to check for setMasterPassword, since this is private and it will be set
3568 : :
3569 : : QSqlQuery query( authDbConnection() );
3570 : :
3571 : : query.prepare( QStringLiteral( "SELECT value FROM %1 "
3572 : : "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3573 : :
3574 : : query.bindValue( ":setting", sett );
3575 : :
3576 : : if ( !authDbQuery( &query ) )
3577 : : return false;
3578 : :
3579 : : if ( !query.isActive() || !query.isSelect() )
3580 : : {
3581 : : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for setting: %2" ).arg( sett ) );
3582 : : return false;
3583 : : }
3584 : :
3585 : : if ( query.first() )
3586 : : {
3587 : : QString settvalue( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3588 : :
3589 : : query.clear();
3590 : :
3591 : : query.prepare( QStringLiteral( "UPDATE %1 "
3592 : : "SET value = :value "
3593 : : "WHERE setting = :setting" ).arg( authDbSettingsTable() ) );
3594 : :
3595 : : query.bindValue( ":setting", sett );
3596 : : query.bindValue( ":value", QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), settvalue ) );
3597 : :
3598 : : if ( !authDbStartTransaction() )
3599 : : return false;
3600 : :
3601 : : if ( !authDbQuery( &query ) )
3602 : : return false;
3603 : :
3604 : : if ( !authDbCommit() )
3605 : : return false;
3606 : :
3607 : : QgsDebugMsg( QStringLiteral( "Reencrypt SUCCESS for setting: %2" ).arg( sett ) );
3608 : : return true;
3609 : : }
3610 : : else
3611 : : {
3612 : : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db setting: %2" ).arg( sett ) );
3613 : : return false;
3614 : : }
3615 : :
3616 : : if ( query.next() )
3617 : : {
3618 : : QgsDebugMsg( QStringLiteral( "Select contains more than one for setting: %1" ).arg( sett ) );
3619 : : emit messageOut( tr( "Authentication database contains duplicate setting keys" ), authManTag(), WARNING );
3620 : : }
3621 : :
3622 : : return false;
3623 : : }
3624 : :
3625 : : return true;
3626 : : #endif
3627 : : }
3628 : :
3629 : 0 : bool QgsAuthManager::reencryptAllAuthenticationIdentities( const QString &prevpass, const QString &prevciv )
3630 : : {
3631 : 0 : if ( isDisabled() )
3632 : 0 : return false;
3633 : :
3634 : 0 : bool res = true;
3635 : 0 : const QStringList ids = certIdentityIds();
3636 : 0 : for ( const auto &identid : ids )
3637 : : {
3638 : 0 : res = res && reencryptAuthenticationIdentity( identid, prevpass, prevciv );
3639 : : }
3640 : 0 : return res;
3641 : 0 : }
3642 : :
3643 : 0 : bool QgsAuthManager::reencryptAuthenticationIdentity(
3644 : : const QString &identid,
3645 : : const QString &prevpass,
3646 : : const QString &prevciv )
3647 : : {
3648 : 0 : if ( isDisabled() )
3649 : 0 : return false;
3650 : :
3651 : : // no need to check for setMasterPassword, since this is private and it will be set
3652 : :
3653 : 0 : QSqlQuery query( authDatabaseConnection() );
3654 : :
3655 : 0 : query.prepare( QStringLiteral( "SELECT key FROM %1 "
3656 : 0 : "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3657 : :
3658 : 0 : query.bindValue( QStringLiteral( ":id" ), identid );
3659 : :
3660 : 0 : if ( !authDbQuery( &query ) )
3661 : 0 : return false;
3662 : :
3663 : 0 : if ( !query.isActive() || !query.isSelect() )
3664 : : {
3665 : 0 : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, query not active or a select operation for identity id: %2" ).arg( identid ) );
3666 : 0 : return false;
3667 : : }
3668 : :
3669 : 0 : if ( query.first() )
3670 : : {
3671 : 0 : QString keystring( QgsAuthCrypto::decrypt( prevpass, prevciv, query.value( 0 ).toString() ) );
3672 : :
3673 : 0 : if ( query.next() )
3674 : : {
3675 : 0 : QgsDebugMsg( QStringLiteral( "Select contains more than one for identity id: %1" ).arg( identid ) );
3676 : 0 : emit messageOut( tr( "Authentication database contains duplicate identity IDs" ), authManTag(), WARNING );
3677 : 0 : return false;
3678 : : }
3679 : :
3680 : 0 : query.clear();
3681 : :
3682 : 0 : query.prepare( QStringLiteral( "UPDATE %1 "
3683 : : "SET key = :key "
3684 : 0 : "WHERE id = :id" ).arg( authDbIdentitiesTable() ) );
3685 : :
3686 : 0 : query.bindValue( QStringLiteral( ":id" ), identid );
3687 : 0 : query.bindValue( QStringLiteral( ":key" ), QgsAuthCrypto::encrypt( mMasterPass, masterPasswordCiv(), keystring ) );
3688 : :
3689 : 0 : if ( !authDbStartTransaction() )
3690 : 0 : return false;
3691 : :
3692 : 0 : if ( !authDbQuery( &query ) )
3693 : 0 : return false;
3694 : :
3695 : 0 : if ( !authDbCommit() )
3696 : 0 : return false;
3697 : :
3698 : 0 : QgsDebugMsgLevel( QStringLiteral( "Reencrypt SUCCESS for identity id: %2" ).arg( identid ), 2 );
3699 : 0 : return true;
3700 : 0 : }
3701 : : else
3702 : : {
3703 : 0 : QgsDebugMsg( QStringLiteral( "Reencrypt FAILED, could not find in db identity id: %2" ).arg( identid ) );
3704 : 0 : return false;
3705 : : }
3706 : 0 : }
3707 : :
3708 : 0 : bool QgsAuthManager::authDbOpen() const
3709 : : {
3710 : 0 : if ( isDisabled() )
3711 : 0 : return false;
3712 : :
3713 : 0 : QSqlDatabase authdb = authDatabaseConnection();
3714 : 0 : if ( !authdb.isOpen() )
3715 : : {
3716 : 0 : if ( !authdb.open() )
3717 : : {
3718 : 0 : QgsDebugMsg( QStringLiteral( "Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
3719 : : .arg( authenticationDatabasePath(),
3720 : : authdb.lastError().driverText(),
3721 : : authdb.lastError().databaseText() ) );
3722 : 0 : emit messageOut( tr( "Unable to establish authentication database connection" ), authManTag(), CRITICAL );
3723 : 0 : return false;
3724 : : }
3725 : 0 : }
3726 : 0 : return true;
3727 : 0 : }
3728 : :
3729 : 0 : bool QgsAuthManager::authDbQuery( QSqlQuery *query ) const
3730 : : {
3731 : 0 : if ( isDisabled() )
3732 : 0 : return false;
3733 : :
3734 : 0 : query->setForwardOnly( true );
3735 : 0 : if ( !query->exec() )
3736 : : {
3737 : 0 : const char *err = QT_TR_NOOP( "Auth db query exec() FAILED" );
3738 : 0 : QgsDebugMsg( err );
3739 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
3740 : 0 : return false;
3741 : : }
3742 : :
3743 : 0 : if ( query->lastError().isValid() )
3744 : : {
3745 : 0 : QgsDebugMsg( QStringLiteral( "Auth db query FAILED: %1\nError: %2" )
3746 : : .arg( query->executedQuery(),
3747 : : query->lastError().text() ) );
3748 : 0 : emit messageOut( tr( "Auth db query FAILED" ), authManTag(), WARNING );
3749 : 0 : return false;
3750 : : }
3751 : :
3752 : 0 : return true;
3753 : 0 : }
3754 : :
3755 : 0 : bool QgsAuthManager::authDbStartTransaction() const
3756 : : {
3757 : 0 : if ( isDisabled() )
3758 : 0 : return false;
3759 : :
3760 : 0 : if ( !authDatabaseConnection().transaction() )
3761 : : {
3762 : 0 : const char *err = QT_TR_NOOP( "Auth db FAILED to start transaction" );
3763 : 0 : QgsDebugMsg( err );
3764 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
3765 : 0 : return false;
3766 : : }
3767 : :
3768 : 0 : return true;
3769 : 0 : }
3770 : :
3771 : 0 : bool QgsAuthManager::authDbCommit() const
3772 : : {
3773 : 0 : if ( isDisabled() )
3774 : 0 : return false;
3775 : :
3776 : 0 : if ( !authDatabaseConnection().commit() )
3777 : : {
3778 : 0 : const char *err = QT_TR_NOOP( "Auth db FAILED to rollback changes" );
3779 : 0 : QgsDebugMsg( err );
3780 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
3781 : 0 : ( void )authDatabaseConnection().rollback();
3782 : 0 : return false;
3783 : : }
3784 : :
3785 : 0 : return true;
3786 : 0 : }
3787 : :
3788 : 0 : bool QgsAuthManager::authDbTransactionQuery( QSqlQuery *query ) const
3789 : : {
3790 : 0 : if ( isDisabled() )
3791 : 0 : return false;
3792 : :
3793 : 0 : if ( !authDatabaseConnection().transaction() )
3794 : : {
3795 : 0 : const char *err = QT_TR_NOOP( "Auth db FAILED to start transaction" );
3796 : 0 : QgsDebugMsg( err );
3797 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
3798 : 0 : return false;
3799 : : }
3800 : :
3801 : 0 : bool ok = authDbQuery( query );
3802 : :
3803 : 0 : if ( ok && !authDatabaseConnection().commit() )
3804 : : {
3805 : 0 : const char *err = QT_TR_NOOP( "Auth db FAILED to rollback changes" );
3806 : 0 : QgsDebugMsg( err );
3807 : 0 : emit messageOut( tr( err ), authManTag(), WARNING );
3808 : 0 : ( void )authDatabaseConnection().rollback();
3809 : 0 : return false;
3810 : : }
3811 : :
3812 : 0 : return ok;
3813 : 0 : }
3814 : :
3815 : 0 : void QgsAuthManager::insertCaCertInCache( QgsAuthCertUtils::CaCertSource source, const QList<QSslCertificate> &certs )
3816 : : {
3817 : 0 : for ( const auto &cert : certs )
3818 : : {
3819 : 0 : mCaCertsCache.insert( QgsAuthCertUtils::shaHexForCert( cert ),
3820 : 0 : QPair<QgsAuthCertUtils::CaCertSource, QSslCertificate>( source, cert ) );
3821 : : }
3822 : 0 : }
3823 : :
|