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