QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsauthconfigurationstoragedb.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsauthconfigurationstoragedb.cpp - QgsAuthConfigurationStorageDb
3
4 ---------------------
5 begin : 20.6.2024
6 copyright : (C) 2024 by ale
7 email : [your-email-here]
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 ***************************************************************************/
17#include "moc_qgsauthconfigurationstoragedb.cpp"
18
19#include "qgslogger.h"
20#include "qgsauthcertutils.h"
21#include "qurl.h"
22
23#include <QSqlError>
24#include <QSqlDriver>
25#include <QSqlQuery>
26#include <QThread>
27#include <QCoreApplication>
28#include <QUrlQuery>
29
30
31QgsAuthConfigurationStorageDb::QgsAuthConfigurationStorageDb( const QMap<QString, QVariant> &settings )
32 : QgsAuthConfigurationStorage( settings )
33{
34 // Parse settings
35 mDriver = mConfiguration.value( QStringLiteral( "driver" ), QStringLiteral( "QSQLITE" ) ).toString();
36 mDatabase = mConfiguration.value( QStringLiteral( "database" ) ).toString();
37 mHost = mConfiguration.value( QStringLiteral( "host" ) ).toString();
38 mPort = mConfiguration.value( QStringLiteral( "port" ) ).toInt();
39 mUser = mConfiguration.value( QStringLiteral( "user" ) ).toString();
40 mPassword = mConfiguration.value( QStringLiteral( "password" ) ).toString();
41 mConnectOptions = mConfiguration.value( QStringLiteral( "options" ) ).toString();
42 // Debug print all connection settings
43 QgsDebugMsgLevel( QStringLiteral( "Auth db connection settings: driver=%1, database='%2', host=%3, port=%4, user='%5', schema=%6, options=%7" )
44 .arg( mDriver, mDatabase, mHost, QString::number( mPort ), mUser, mConnectOptions, mConfiguration.value( QStringLiteral( "schema" ) ).toString() ), 2 );
45
46}
47
52
54{
55 QMutexLocker locker( &mMutex );
56
57 QMapIterator<QThread *, QMetaObject::Connection> iterator( mConnectedThreads );
58 while ( iterator.hasNext() )
59 {
60 iterator.next();
61 QThread::disconnect( iterator.value() );
62 }
63}
64
66{
67 QSqlDatabase authdb;
68
69 QMutexLocker locker( &mMutex );
70
71 const QString connectionName = QStringLiteral( "authentication.configs:0x%1" ).arg( reinterpret_cast<quintptr>( QThread::currentThread() ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) );
72 QgsDebugMsgLevel( QStringLiteral( "Using auth db connection name: %1 " ).arg( connectionName ), 3 );
73 if ( !QSqlDatabase::contains( connectionName ) )
74 {
75 QgsDebugMsgLevel( QStringLiteral( "No existing connection, creating a new one" ), 3 );
76 authdb = QSqlDatabase::addDatabase( mDriver, connectionName );
77
78
79 // Check that the driver is available
80 if ( !QSqlDatabase::isDriverAvailable( mDriver ) )
81 {
82 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Driver '%1' is not available" ).arg( mDriver ) );
83 return authdb;
84 }
85
86 authdb.setDatabaseName( mDatabase );
87 authdb.setHostName( mHost );
88 authdb.setPort( mPort );
89 authdb.setUserName( mUser );
90 authdb.setPassword( mPassword );
91 authdb.setConnectOptions( mConnectOptions );
92
93 // Return if not valid
94 if ( !authdb.isValid() )
95 {
96 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db connection is not valid" ) );
97 return authdb;
98 }
99
100 // for background threads, remove database when current thread finishes
101 if ( QCoreApplication::instance() && QThread::currentThread() != QCoreApplication::instance()->thread() )
102 {
103 QgsDebugMsgLevel( QStringLiteral( "Scheduled auth db remove on thread close" ), 4 );
104
105 // IMPORTANT - we use a direct connection here, because the database removal must happen immediately
106 // when the thread finishes, and we cannot let this get queued on the main thread's event loop (where
107 // QgsAuthManager lives).
108 // Otherwise, the QSqlDatabase's private data's thread gets reset immediately the QThread::finished,
109 // and a subsequent call to QSqlDatabase::database with the same thread address (yep it happens, actually a lot)
110 // triggers a condition in QSqlDatabase which detects the nullptr private thread data and returns an invalid database instead.
111 // QSqlDatabase::removeDatabase is thread safe, so this is ok to do.
112 // Right about now is a good time to re-evaluate your selected career ;)
113 // I've done that and I decided to become a musician. I'll probably be a better musician than a software developer.
114 QMetaObject::Connection connection = connect( QThread::currentThread(), &QThread::finished, QThread::currentThread(), [connectionName, this ]
115 {
116 QMutexLocker locker( &mMutex );
117 QSqlDatabase::removeDatabase( connectionName );
118 mConnectedThreads.remove( QThread::currentThread() ); // NOLINT(clang-analyzer-core.CallAndMessage)
119 }, Qt::DirectConnection );
120
121 mConnectedThreads.insert( QThread::currentThread(), connection );
122 }
123 }
124 else
125 {
126 QgsDebugMsgLevel( QStringLiteral( "Reusing existing connection" ), 4 );
127 authdb = QSqlDatabase::database( connectionName, false );
128 }
129
130 locker.unlock();
131
132 if ( !authdb.isOpen() )
133 {
134 if ( !authdb.open() )
135 {
136 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Opening of authentication db FAILED : %1" ).arg( authdb.lastError().text() ) );
137 }
138 }
139
140 return authdb;
141}
142
144{
145 QMutexLocker locker( &mMutex );
146 QSqlDatabase authdb = authDatabaseConnection();
147
148 if ( !authdb.isOpen() )
149 {
150 if ( !authdb.open() )
151 {
152 const QString err = tr( "Unable to establish database connection\nDatabase: %1\nDriver error: %2\nDatabase error: %3" )
153 .arg( mDatabase,
154 authdb.lastError().driverText(),
155 authdb.lastError().databaseText() );
156
157 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( err );
158 return false;
159 }
160 }
161 return true;
162}
163
164
165bool QgsAuthConfigurationStorageDb::authDbQuery( QSqlQuery *query, const QString &sql ) const
166{
167 QMutexLocker locker( &mMutex );
168 query->setForwardOnly( true );
169 const bool result { sql.isEmpty() ? query->exec() : query->exec( sql ) };
170
171 auto boundQuery = []( const QSqlQuery * query ) -> QString
172 {
173 QString str = query->lastQuery();
174#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
175 QMapIterator<QString, QVariant> it( query->boundValues() );
176#else
177 const QStringList keys = query->boundValueNames();
178 const QVariantList values = query->boundValues();
179 QMap<QString, QVariant> boundValues;
180 for ( int i = 0; i < keys.count(); i++ )
181 {
182 boundValues.insert( keys.at( i ), values.at( i ).toString() );
183 }
184 QMapIterator<QString, QVariant> it = QMapIterator<QString, QVariant>( boundValues );
185#endif
186 while ( it.hasNext() )
187 {
188 it.next();
189 str.replace( it.key(), it.value().toString() );
190 }
191 return str;
192 };
193
194 if ( !result )
195 {
196 if ( query->lastError().isValid() )
197 {
198 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db query FAILED: %1\nError: %2" )
199 .arg( sql.isEmpty() ? boundQuery( query ) : sql,
200 query->lastError().text() ), Qgis::MessageLevel::Warning );
201 QgsDebugMsgLevel( QStringLiteral( "Auth db query FAILED: %1" ).arg( sql.isEmpty() ? boundQuery( query ) : sql ), 2 );
202 return false;
203 }
204 else
205 {
206 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db query exec() FAILED: %1" ).arg( sql.isEmpty() ? boundQuery( query ) : sql ), Qgis::MessageLevel::Warning );
207 QgsDebugMsgLevel( QStringLiteral( "Auth db query FAILED: %1" ).arg( sql.isEmpty() ? boundQuery( query ) : sql ), 2 );
208 return false;
209 }
210 }
211 return true;
212}
213
214
216{
217 QMutexLocker locker( &mMutex );
218 if ( !authDatabaseConnection().transaction() )
219 {
220 setError( tr( "Auth db FAILED to start transaction" ), Qgis::MessageLevel::Warning );
221 return false;
222 }
223
224 bool ok = authDbQuery( query );
225
226 if ( ok && !authDatabaseConnection().commit() )
227 {
228 setError( tr( "Auth db FAILED to rollback changes" ), Qgis::MessageLevel::Warning );
229 ( void )authDatabaseConnection().rollback();
230 return false;
231 }
232
233 return ok;
234}
235
237{
238 QMutexLocker locker( &mMutex );
239 QSqlDatabase authdb = authDatabaseConnection();
240 if ( !authdb.isValid() || !authdb.isOpen() )
241 {
242 setError( tr( "Auth db could not be opened" ) );
243 return false;
244 }
245 else
246 {
247 if ( !isReadOnly() && ( !createCertTables() || !createConfigTables() ) )
248 {
249 setError( tr( "Auth db initialization FAILED: %1" ).arg( lastError() ), Qgis::MessageLevel::Critical );
250 mIsReady = false;
251 return false;
252 }
253
254 mIsReady = true;
256
257 // Recompute capabilities if needed
258 connect( this, &QgsAuthConfigurationStorageDb::readOnlyChanged, this, [this]( bool )
259 {
261 } );
262
263 return true;
264 }
265}
266
267QList<QgsAuthConfigurationStorage::SettingParameter> QgsAuthConfigurationStorageDb::settingsParameters() const
268{
269 return
270 {
271 { QStringLiteral( "driver" ), tr( "SQL Driver (see https://doc.qt.io/qt/sql-driver.html)" ), QVariant::String },
272 { QStringLiteral( "database" ), tr( "Database" ), QVariant::String },
273 { QStringLiteral( "schema" ), tr( "Schema for all tables" ), QVariant::String },
274 { QStringLiteral( "host" ), tr( "Host" ), QVariant::String },
275 { QStringLiteral( "port" ), tr( "Port" ), QVariant::Int },
276 { QStringLiteral( "user" ), tr( "User" ), QVariant::String },
277 { QStringLiteral( "password" ), tr( "Password" ), QVariant::String },
278 { QStringLiteral( "options" ), tr( "Connection options" ), QVariant::String },
279 };
280}
281
282#ifndef QT_NO_SSL
283
284bool QgsAuthConfigurationStorageDb::storeCertIdentity( const QSslCertificate &cert, const QString &keyPem )
285{
286 QMutexLocker locker( &mMutex );
287
288 const QString id{ QgsAuthCertUtils::shaHexForCert( cert ) };
289
290 if ( certIdentityExists( id ) )
291 {
293 removeCertIdentity( id );
294 }
295 else
296 {
298 }
299
300 if ( !authDbOpen() )
301 {
302 setError( tr( "Auth db could not be opened" ) );
303 return false;
304 }
305
306 if ( cert.isNull() )
307 {
308 setError( tr( "Certificate is NULL" ) );
309 return false;
310 }
311
312 QSqlQuery query( authDatabaseConnection() );
313 const QString certPem{ cert.toPem() };
314
315 query.prepare( QStringLiteral( "INSERT INTO %1 (id, key, cert) VALUES (:id, :key, :cert)" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
316 query.bindValue( QStringLiteral( ":id" ), id );
317 query.bindValue( QStringLiteral( ":key" ), keyPem );
318 query.bindValue( QStringLiteral( ":cert" ), certPem );
319
320 if ( !authDbQuery( &query ) )
321 return false;
322
323 emit certIdentityChanged();
324
325 return true;
326}
327
328bool QgsAuthConfigurationStorageDb::removeCertIdentity( const QSslCertificate &cert )
329{
330 QMutexLocker locker( &mMutex );
331
333
334 if ( !authDbOpen() )
335 {
336 setError( tr( "Auth db could not be opened" ) );
337 return false;
338 }
339
340 QSqlQuery query( authDatabaseConnection() );
341
342 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
343 query.bindValue( QStringLiteral( ":id" ), cert.digest().toHex() );
344
345 if ( !authDbQuery( &query ) )
346 {
347 setError( tr( "Failed to remove cert identity '%1'" ).arg( QString( cert.digest().toHex() ) ), Qgis::MessageLevel::Critical );
348 return false;
349 }
350
351 emit certIdentityChanged();
352
353 return true;
354}
355
356const QSslCertificate QgsAuthConfigurationStorageDb::loadCertIdentity( const QString &id ) const
357{
358 QMutexLocker locker( &mMutex );
359
361
362 QSslCertificate emptycert;
363
364 if ( id.isEmpty() )
365 return emptycert;
366
367 if ( !authDbOpen() )
368 {
369 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
370 return emptycert;
371 }
372
373 QSqlQuery query( authDatabaseConnection() );
374
375 query.prepare( QStringLiteral( "SELECT cert FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
376 query.bindValue( QStringLiteral( ":id" ), id );
377
378 if ( !authDbQuery( &query ) )
379 return emptycert;
380
381 QSslCertificate cert;
382
383 if ( query.isActive() && query.isSelect() )
384 {
385 if ( query.first() )
386 {
387 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
388 QgsDebugMsgLevel( QStringLiteral( "Certificate identity retrieved for id: %1" ).arg( id ), 2 );
389 if ( cert.isNull() )
390 {
391 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to retrieve certificate identity for id: %1: certificate is NULL" ).arg( id ), Qgis::MessageLevel::Warning );
392 return emptycert;
393 }
394 }
395 if ( query.next() )
396 {
397 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Authentication database contains more than one certificate identity for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
398 return emptycert;
399 }
400 }
401 return cert;
402}
403
404const QPair<QSslCertificate, QString> QgsAuthConfigurationStorageDb::loadCertIdentityBundle( const QString &id ) const
405{
406 QMutexLocker locker( &mMutex );
407
409
410 QPair<QSslCertificate, QString> bundle;
411
412 if ( id.isEmpty() )
413 return bundle;
414
415 if ( !authDbOpen() )
416 {
417 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
418 return bundle;
419 }
420
421 QSqlQuery query( authDatabaseConnection() );
422
423 query.prepare( QStringLiteral( "SELECT key, cert FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
424
425 query.bindValue( QStringLiteral( ":id" ), id );
426
427 if ( !authDbQuery( &query ) )
428 return bundle;
429
430 if ( query.isActive() && query.isSelect() )
431 {
432 QSslCertificate cert;
433 QString key;
434 if ( query.first() )
435 {
436 key = query.value( 0 ).toString();
437 if ( key.isEmpty() )
438 {
439 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieve certificate identity bundle: FAILED to create private key" ), Qgis::MessageLevel::Warning );
440 return bundle;
441 }
442 cert = QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem );
443 if ( cert.isNull() )
444 {
445 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieve certificate identity bundle: FAILED to create certificate" ), Qgis::MessageLevel::Warning );
446 return bundle;
447 }
448 QgsDebugMsgLevel( QStringLiteral( "Certificate identity bundle retrieved for id: %1" ).arg( id ), 2 );
449 }
450 if ( query.next() )
451 {
452 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieved more than one certificate identity for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
453 return bundle;
454 }
455 bundle = qMakePair( cert, key );
456 }
457 return bundle;
458}
459
460const QList<QSslCertificate> QgsAuthConfigurationStorageDb::certIdentities() const
461{
462 QMutexLocker locker( &mMutex );
463
465
466 QList<QSslCertificate> certs;
467
468 if ( !authDbOpen() )
469 {
470 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
471 return certs;
472 }
473
474 QSqlQuery query( authDatabaseConnection() );
475
476 query.prepare( QStringLiteral( "SELECT cert FROM %1" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
477
478 if ( !authDbQuery( &query ) )
479 return certs;
480
481 if ( query.isActive() && query.isSelect() )
482 {
483 while ( query.next() )
484 {
485 QSslCertificate cert( query.value( 0 ).toByteArray(), QSsl::Pem );
486 if ( !cert.isNull() )
487 {
488 certs.append( cert );
489 }
490 }
491 }
492 return certs;
493}
494
496{
497 QMutexLocker locker( &mMutex );
498
500
501 if ( !authDbOpen() )
502 {
503 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
504 return {};
505 }
506
507 QSqlQuery query( authDatabaseConnection() );
508
509 query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
510
511 if ( !authDbQuery( &query ) )
512 return {};
513
514 QStringList ids;
515 if ( query.isActive() && query.isSelect() )
516 {
517 while ( query.next() )
518 {
519 ids.append( query.value( 0 ).toString() );
520 }
521 }
522 return ids;
523}
524
526{
527 QMutexLocker locker( &mMutex );
528
530
531 if ( id.isEmpty() )
532 return false;
533
534 if ( !authDbOpen() )
535 {
536 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
537 return {};
538 }
539
540 QSqlQuery query( authDatabaseConnection() );
541
542 query.prepare( QStringLiteral( "SELECT cert FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
543 query.bindValue( QStringLiteral( ":id" ), id );
544
545 bool ret { false };
546
547 if ( !authDbQuery( &query ) )
548 return false;
549
550 if ( query.isActive() && query.isSelect() )
551 {
552 if ( query.first() )
553 {
554 ret = true;
555 }
556 if ( query.next() )
557 {
558 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Authentication database contains more than one certificate bundles for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
559 return false;
560 }
561 }
562 return ret;
563}
564
566{
567 QMutexLocker locker( &mMutex );
568
570
571 if ( !authDbOpen() )
572 {
573 setError( tr( "Auth db could not be opened" ) );
574 return {};
575 }
576
577 QSqlQuery query( authDatabaseConnection() );
578
579 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) ) );
580 query.bindValue( QStringLiteral( ":id" ), id );
581
582 if ( !authDbQuery( &query ) )
583 {
584 setError( tr( "Failed to remove certificate identity '%1'" ).arg( id ), Qgis::MessageLevel::Critical );
585 return false;
586 }
587
588 if ( query.numRowsAffected() == 0 )
589 {
590 setError( tr( "No certificate identity found for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
591 return false;
592 }
593
594 emit certIdentityChanged();
595
596 return true;
597}
598
600{
601 QMutexLocker locker( &mMutex );
602
603 QSslCertificate cert( config.sslCertificate() );
604 QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
605
606 if ( sslCertCustomConfigExists( id, config.sslHostPort() ) )
607 {
609 removeSslCertCustomConfig( id, config.sslHostPort().trimmed() );
610 }
611 else
612 {
614 }
615
617 {
618 setError( tr( "Storage does not support creating SSL certificate custom configs" ), Qgis::MessageLevel::Critical );
619 return false;
620 }
621
622 if ( !authDbOpen() )
623 {
624 setError( tr( "Auth db could not be opened" ) );
625 return false;
626 }
627
628 QString certpem( cert.toPem() );
629 QSqlQuery query( authDatabaseConnection() );
630
631 query.prepare( QStringLiteral( "INSERT INTO %1 (id, host, cert, config) VALUES (:id, :host, :cert, :config)" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
632
633 query.bindValue( QStringLiteral( ":id" ), id );
634 query.bindValue( QStringLiteral( ":host" ), config.sslHostPort().trimmed() );
635 query.bindValue( QStringLiteral( ":cert" ), certpem );
636 query.bindValue( QStringLiteral( ":config" ), config.configString() );
637
638 if ( !authDbQuery( &query ) )
639 return false;
640
641 QgsDebugMsgLevel( QStringLiteral( "Store SSL cert custom config SUCCESS for host:port, id: %1, %2" )
642 .arg( config.sslHostPort().trimmed(), id ), 2 );
643
645
646 return true;
647}
648
650{
651 QMutexLocker locker( &mMutex );
652
654
655 if ( !authDbOpen() )
656 {
657 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
658 return {};
659 }
660
661 QSqlQuery query( authDatabaseConnection() );
662
663 query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
664
665 if ( !authDbQuery( &query ) )
666 return {};
667
668 QStringList ids;
669 if ( query.isActive() && query.isSelect() )
670 {
671 while ( query.next() )
672 {
673 ids.append( query.value( 0 ).toString() );
674 }
675 }
676 return ids;
677
678}
679
680const QgsAuthConfigSslServer QgsAuthConfigurationStorageDb::loadSslCertCustomConfig( const QString &id, const QString &hostport ) const
681{
682 QMutexLocker locker( &mMutex );
683
685
687
688 if ( id.isEmpty() || hostport.isEmpty() )
689 {
690 QgsDebugError( QStringLiteral( "Passed config ID or host:port is empty" ) );
691 return config;
692 }
693
694 if ( !authDbOpen() )
695 {
696 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
697 return config;
698 }
699
700 QSqlQuery query( authDatabaseConnection() );
701 query.prepare( QStringLiteral( "SELECT host, cert, config FROM %1 WHERE id = :id AND host = :host" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
702 query.bindValue( QStringLiteral( ":id" ), id );
703 query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
704
705 if ( !authDbQuery( &query ) )
706 return config;
707
708 if ( query.isActive() && query.isSelect() )
709 {
710 if ( query.first() )
711 {
712 config.setSslCertificate( QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem ) );
713 config.setSslHostPort( query.value( 0 ).toString().trimmed() );
714 config.loadConfigString( query.value( 2 ).toString() );
715 QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
716 }
717 if ( query.next() )
718 {
719 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieved more than one SSL cert custom config for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
720 return QgsAuthConfigSslServer();
721 }
722 }
723 return config;
724}
725
727{
728 // TODO: implement a flag to skip the query in case there are no custom
729 // certs in the storage, to avoid the overhead of checking the db
730 QMutexLocker locker( &mMutex );
731
733
735
736 if ( !authDbOpen() )
737 {
738 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
739 return config;
740 }
741
742 QSqlQuery query( authDatabaseConnection() );
743
744 query.prepare( QStringLiteral( "SELECT id, cert, config FROM %1 WHERE host = :host" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
745
746 query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
747
748 if ( !authDbQuery( &query ) )
749 return config;
750
751 if ( query.isActive() && query.isSelect() )
752 {
753 if ( query.first() )
754 {
755 config.setSslCertificate( QSslCertificate( query.value( 1 ).toByteArray(), QSsl::Pem ) );
756 config.setSslHostPort( hostport );
757 config.loadConfigString( query.value( 2 ).toString() );
758 QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config retrieved for host:port %1" ).arg( hostport ), 2 );
759 }
760 if ( query.next() )
761 {
762 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Authentication database contains more than one SSL cert custom config" ), Qgis::MessageLevel::Warning );
763 return QgsAuthConfigSslServer();
764 }
765 }
766
767 return config;
768}
769
770const QList<QgsAuthConfigSslServer> QgsAuthConfigurationStorageDb::sslCertCustomConfigs() const
771{
772 QMutexLocker locker( &mMutex );
773
775
776 QList<QgsAuthConfigSslServer> configs;
777
778 if ( !authDbOpen() )
779 {
780 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
781 return configs;
782 }
783
784 QSqlQuery query( authDatabaseConnection() );
785
786 query.prepare( QStringLiteral( "SELECT id, host, cert, config FROM %1" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
787
788 if ( !authDbQuery( &query ) )
789 return configs;
790
791 if ( query.isActive() && query.isSelect() )
792 {
793 while ( query.next() )
794 {
796 config.setSslCertificate( QSslCertificate( query.value( 2 ).toByteArray(), QSsl::Pem ) );
797 config.setSslHostPort( query.value( 1 ).toString().trimmed() );
798 config.loadConfigString( query.value( 3 ).toString() );
799 configs.append( config );
800 }
801 }
802 return configs;
803}
804
805bool QgsAuthConfigurationStorageDb::sslCertCustomConfigExists( const QString &id, const QString &hostport )
806{
807 QMutexLocker locker( &mMutex );
808
810
811 if ( id.isEmpty() || hostport.isEmpty() )
812 {
813 QgsDebugError( QStringLiteral( "Passed config ID or host:port is empty" ) );
814 return false;
815 }
816
817 if ( !authDbOpen() )
818 {
819 setError( tr( "Auth db could not be opened" ) );
820 return false;
821 }
822
823 QSqlQuery query( authDatabaseConnection() );
824
825 query.prepare( QStringLiteral( "SELECT id FROM %1 WHERE id = :id AND host = :host" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
826 query.bindValue( QStringLiteral( ":id" ), id );
827 query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
828
829 if ( !authDbQuery( &query ) )
830 return false;
831
832 bool res = false;
833 if ( query.isActive() && query.isSelect() )
834 {
835 if ( query.first() )
836 {
837 QgsDebugMsgLevel( QStringLiteral( "SSL cert custom config exists for host:port, id: %1, %2" ).arg( hostport, id ), 2 );
838 res = true;
839 }
840 if ( query.next() )
841 {
842 QgsDebugError( QStringLiteral( "Retrieved more than one SSL cert custom config for host:port, id: %1, %2" ).arg( hostport, id ) );
843 emit messageLog( tr( "Authentication database contains more than one SSL cert custom configs for host:port, id: %1, %2" )
844 .arg( hostport, id ), loggerTag(), Qgis::MessageLevel::Warning );
845 return false;
846 }
847 }
848 return res;
849}
850
851bool QgsAuthConfigurationStorageDb::removeSslCertCustomConfig( const QString &id, const QString &hostport )
852{
853 QMutexLocker locker( &mMutex );
854
856
857 if ( id.isEmpty() || hostport.isEmpty() )
858 {
859 QgsDebugError( QStringLiteral( "Passed config ID or host:port is empty" ) );
860 return false;
861 }
862
863 // TODO: check the flag to skip the query in case there are no custom certs config by host in the storage
864
865 if ( !authDbOpen() )
866 {
867 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
868 return false;
869 }
870
871 QSqlQuery query( authDatabaseConnection() );
872
873 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id AND host = :host" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) ) );
874 query.bindValue( QStringLiteral( ":id" ), id );
875 query.bindValue( QStringLiteral( ":host" ), hostport.trimmed() );
876
877 if ( !authDbQuery( &query ) )
878 {
879 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to remove SSL certificate custom config for host:port, id: %1, %2" ).arg( hostport, id ), Qgis::MessageLevel::Critical );
880 return false;
881 }
882
883 if ( query.numRowsAffected() == 0 )
884 {
885 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "No SSL certificate custom config found for host:port, id: %1, %2" ).arg( hostport, id ), Qgis::MessageLevel::Warning );
886 return false;
887 }
888
890
891 return true;
892}
893
895{
896 QMutexLocker locker( &mMutex );
897
899
900 if ( !authDbOpen() )
901 {
902 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
903 return {};
904 }
905
906 QSqlQuery query( authDatabaseConnection() );
907
908 query.prepare( QStringLiteral( "SELECT id FROM %1" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
909
910 if ( !authDbQuery( &query ) )
911 return {};
912
913 QStringList ids;
914 if ( query.isActive() && query.isSelect() )
915 {
916 while ( query.next() )
917 {
918 ids.append( query.value( 0 ).toString() );
919 }
920 }
921 return ids;
922}
923
924bool QgsAuthConfigurationStorageDb::storeCertAuthority( const QSslCertificate &cert )
925{
926 QMutexLocker locker( &mMutex );
927
928 if ( certAuthorityExists( cert ) )
929 {
931 removeCertAuthority( cert );
932 }
933 else
934 {
936 }
937
938 // don't refuse !cert.isValid() (actually just expired) CAs,
939 // as user may want to ignore that SSL connection error
940 if ( cert.isNull() )
941 {
942 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
943 return false;
944 }
945
946 if ( !authDbOpen() )
947 {
948 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
949 return false;
950 }
951
952 const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
953 const QString pem( cert.toPem() );
954
955 QSqlQuery query( authDatabaseConnection() );
956
957 query.prepare( QStringLiteral( "INSERT INTO %1 (id, cert) VALUES (:id, :cert)" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
958
959 query.bindValue( QStringLiteral( ":id" ), id );
960 query.bindValue( QStringLiteral( ":cert" ), pem );
961
962 if ( !authDbQuery( &query ) )
963 return false;
964
965 QgsDebugMsgLevel( QStringLiteral( "Store certificate authority SUCCESS for id: %1" ).arg( id ), 2 );
967
968 return true;
969}
970
971const QSslCertificate QgsAuthConfigurationStorageDb::loadCertAuthority( const QString &id ) const
972{
973 QMutexLocker locker( &mMutex );
974
976
977 QSslCertificate emptycert;
978
979 if ( id.isEmpty() )
980 return emptycert;
981
982 if ( !authDbOpen() )
983 {
984 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
985 return emptycert;
986 }
987
988 QSqlQuery query( authDatabaseConnection() );
989
990 query.prepare( QStringLiteral( "SELECT cert FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
991 query.bindValue( QStringLiteral( ":id" ), id );
992
993 if ( !authDbQuery( &query ) )
994 return emptycert;
995
996 QSslCertificate cert;
997
998 if ( query.isActive() && query.isSelect() )
999 {
1000 if ( query.first() )
1001 {
1002 cert = QSslCertificate( query.value( 0 ).toByteArray(), QSsl::Pem );
1003 QgsDebugMsgLevel( QStringLiteral( "Certificate authority retrieved for id: %1" ).arg( id ), 2 );
1004 }
1005 if ( query.next() )
1006 {
1007 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieved more than one certificate authority for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
1008 return emptycert;
1009 }
1010 }
1011 return cert;
1012}
1013
1014bool QgsAuthConfigurationStorageDb::certAuthorityExists( const QSslCertificate &cert ) const
1015{
1016 QMutexLocker locker( &mMutex );
1017
1019
1020 if ( cert.isNull() )
1021 {
1022 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1023 return false;
1024 }
1025
1026 if ( !authDbOpen() )
1027 {
1028 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1029 return false;
1030 }
1031
1032 const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1033
1034 QSqlQuery query( authDatabaseConnection() );
1035
1036 query.prepare( QStringLiteral( "SELECT id FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
1037 query.bindValue( QStringLiteral( ":id" ), id );
1038
1039 if ( !authDbQuery( &query ) )
1040 return false;
1041
1042 bool res = false;
1043 if ( query.isActive() && query.isSelect() )
1044 {
1045 if ( query.first() )
1046 {
1047 QgsDebugMsgLevel( QStringLiteral( "Certificate authority exists for id: %1" ).arg( id ), 2 );
1048 res = true;
1049 }
1050 if ( query.next() )
1051 {
1052 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieved more than one certificate authority for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
1053 // TODO: check whether it makes sense to return false here (and in other similar cases)
1054 return false;
1055 }
1056 }
1057 return res;
1058}
1059
1061{
1062 QMutexLocker locker( &mMutex );
1063
1065
1066 if ( cert.isNull() )
1067 {
1068 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1069 return false;
1070 }
1071
1072 if ( !authDbOpen() )
1073 {
1074 setError( tr( "Auth db could not be opened" ) );
1075 return false;
1076 }
1077
1078 const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1079
1080 QSqlQuery query( authDatabaseConnection() );
1081
1082 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
1083
1084 query.bindValue( QStringLiteral( ":id" ), id );
1085
1086 if ( !authDbQuery( &query ) )
1087 {
1088 setError( tr( "Failed to remove certificate authority '%1'" ).arg( id ), Qgis::MessageLevel::Critical );
1089 return false;
1090 }
1091
1092 if ( query.numRowsAffected() == 0 )
1093 {
1094 setError( tr( "No certificate authority found for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
1095 return false;
1096 }
1097
1098 emit certAuthorityChanged();
1099
1100 return true;
1101}
1102
1103const QMap<QString, QgsAuthCertUtils::CertTrustPolicy> QgsAuthConfigurationStorageDb::caCertsPolicy() const
1104{
1105 QMutexLocker locker( &mMutex );
1106
1108
1109 QMap<QString, QgsAuthCertUtils::CertTrustPolicy> trustedCerts;
1110
1111 if ( !authDbOpen() )
1112 {
1113 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1114 return trustedCerts;
1115 }
1116
1117 QSqlQuery query( authDatabaseConnection() );
1118
1119 query.prepare( QStringLiteral( "SELECT id, policy FROM %1" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) ) );
1120
1121 if ( !authDbQuery( &query ) )
1122 return trustedCerts;
1123
1124 if ( query.isActive() && query.isSelect() )
1125 {
1126 while ( query.next() )
1127 {
1128 QString id( query.value( 0 ).toString() );
1129 int policy = query.value( 1 ).toInt();
1130 QgsAuthCertUtils::CertTrustPolicy trustPolicy = static_cast< QgsAuthCertUtils::CertTrustPolicy >( policy );
1131 trustedCerts[ id ] = trustPolicy;
1132 }
1133 }
1134
1135 return trustedCerts;
1136}
1137
1138const QList<QSslCertificate> QgsAuthConfigurationStorageDb::caCerts() const
1139{
1140 QMutexLocker locker( &mMutex );
1141
1143
1144 QList<QSslCertificate> authorities;
1145
1146 if ( !authDbOpen() )
1147 {
1148 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1149 return authorities;
1150 }
1151
1152 QSqlQuery query( authDatabaseConnection() );
1153
1154 query.prepare( QStringLiteral( "SELECT id, cert FROM %1" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) ) );
1155
1156 if ( !authDbQuery( &query ) )
1157 return authorities;
1158
1159 if ( query.isActive() && query.isSelect() )
1160 {
1161 while ( query.next() )
1162 {
1163 const QSslCertificate cert( query.value( 1 ).toByteArray(), QSsl::Pem );
1164 if ( !cert.isNull() )
1165 {
1166 authorities.append( cert );
1167 }
1168 else
1169 {
1170 const QString id { query.value( 0 ).toString() };
1171 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Invalid CA found: %1" ).arg( id ), Qgis::MessageLevel::Warning );
1172 }
1173 }
1174 }
1175 return authorities;
1176}
1177
1179{
1180 QMutexLocker locker( &mMutex );
1181
1182 const bool policyExisted = certTrustPolicyExists( cert );
1183
1184 if ( policyExisted )
1185 {
1187 }
1188 else
1189 {
1191 }
1192
1193 if ( cert.isNull() )
1194 {
1195 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1196 return false;
1197 }
1198
1199 if ( !authDbOpen() )
1200 {
1201 setError( tr( "Auth db could not be opened" ) );
1202 return false;
1203 }
1204
1205 const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1206
1207 // Handle default trust case
1208 if ( policy == QgsAuthCertUtils::DefaultTrust )
1209 {
1210 if ( !policyExisted )
1211 {
1212 QgsDebugMsgLevel( QStringLiteral( "Passed policy was default, no cert records in database for id: %1" ).arg( id ), 2 );
1213 return true;
1214 }
1215
1216 if ( !removeCertTrustPolicy( cert ) )
1217 {
1218 setError( tr( "Failed to remove certificate trust policy for id: %1" ).arg( id ) );
1219 return false;
1220 }
1221
1222 QgsDebugMsgLevel( QStringLiteral( "Passed policy was default, all cert records in database were removed for id: %1" ).arg( id ), 2 );
1223
1224 emit certAuthorityChanged();
1225
1226 return true;
1227 }
1228
1229 // Handle other policies
1230 if ( policyExisted && !removeCertTrustPolicy( cert ) )
1231 {
1232 setError( tr( "Failed to remove certificate trust policy for id: %1" ).arg( id ) );
1233 return false;
1234 }
1235
1236 // Insert new policy
1237 QSqlQuery query( authDatabaseConnection() );
1238 query.prepare( QStringLiteral( "INSERT INTO %1 (id, policy) VALUES (:id, :policy)" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) ) );
1239
1240 query.bindValue( QStringLiteral( ":id" ), id );
1241 query.bindValue( QStringLiteral( ":policy" ), static_cast< int >( policy ) );
1242
1243 if ( !authDbQuery( &query ) )
1244 return false;
1245
1246 QgsDebugMsgLevel( QStringLiteral( "Store certificate trust policy SUCCESS for id: %1" ).arg( id ), 2 );
1247 emit certAuthorityChanged();
1248
1249 return true;
1250}
1251
1253{
1254 QMutexLocker locker( &mMutex );
1255
1257
1258 if ( cert.isNull() )
1259 {
1260 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1262 }
1263
1264 QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1265
1266 if ( !authDbOpen() )
1267 {
1268 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1270 }
1271
1272 QSqlQuery query( authDatabaseConnection() );
1273
1274 query.prepare( QStringLiteral( "SELECT policy FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) ) );
1275 query.bindValue( QStringLiteral( ":id" ), id );
1276
1277 if ( !authDbQuery( &query ) )
1279
1280 if ( query.isActive() && query.isSelect() )
1281 {
1282 if ( query.first() )
1283 {
1284 int policy = query.value( 0 ).toInt();
1285 QgsDebugMsgLevel( QStringLiteral( "Certificate trust policy retrieved for id: %1" ).arg( id ), 2 );
1286 return static_cast< QgsAuthCertUtils::CertTrustPolicy >( policy );
1287 }
1288 if ( query.next() )
1289 {
1290 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Retrieved more than one certificate trust policy for id: %1" ).arg( id ), Qgis::MessageLevel::Warning );
1292 }
1293 }
1295}
1296
1298{
1299 QMutexLocker locker( &mMutex );
1300
1302
1303 if ( cert.isNull() )
1304 {
1305 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1307 }
1308
1309 QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1310
1311 if ( !authDbOpen() )
1312 {
1313 setError( tr( "Auth db could not be opened" ) );
1315 }
1316
1317 QSqlQuery query( authDatabaseConnection() );
1318
1319 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) ) );
1320 query.bindValue( QStringLiteral( ":id" ), id );
1321
1322 if ( !authDbQuery( &query ) )
1323 {
1324 setError( tr( "Failed to remove certificate trust policy '%1'" ).arg( id ) );
1325 return false;
1326 }
1327
1328 if ( query.numRowsAffected() == 0 )
1329 {
1330 setError( tr( "No certificate trust policy found for id: %1" ).arg( id ) );
1331 return false;
1332 }
1333
1335
1336 return true;
1337}
1338
1339bool QgsAuthConfigurationStorageDb::certTrustPolicyExists( const QSslCertificate &cert ) const
1340{
1341 QMutexLocker locker( &mMutex );
1342
1344
1345 if ( cert.isNull() )
1346 {
1347 QgsDebugError( QStringLiteral( "Passed certificate is null" ) );
1349 }
1350
1351 QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
1352
1353 if ( !authDbOpen() )
1354 {
1355 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1357 }
1358
1359 QSqlQuery query( authDatabaseConnection() );
1360
1361 query.prepare( QStringLiteral( "SELECT COUNT(id) FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) ) );
1362 query.bindValue( QStringLiteral( ":id" ), id );
1363
1364 if ( !authDbQuery( &query ) )
1365 return false;
1366
1367 if ( query.isActive() && query.isSelect() )
1368 {
1369 if ( query.first() )
1370 {
1371 return query.value( 0 ).toInt() > 0;
1372 }
1373 }
1374 return false;
1375}
1376
1377#endif
1378
1379const QList<QgsAuthConfigurationStorage::MasterPasswordConfig> QgsAuthConfigurationStorageDb::masterPasswords() const
1380{
1381 QMutexLocker locker( &mMutex );
1382
1384
1385 QList<QgsAuthConfigurationStorage::MasterPasswordConfig> passwords;
1386
1387 if ( !authDbOpen() )
1388 {
1389 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1390 return passwords;
1391 }
1392
1393 QSqlQuery query( authDatabaseConnection() );
1394 query.prepare( QStringLiteral( "SELECT salt, civ, hash FROM %1" ).arg( quotedQualifiedIdentifier( masterPasswordTableName() ) ) );
1395
1396 if ( !authDbQuery( &query ) )
1397 return passwords;
1398
1399 if ( query.isActive() && query.isSelect() )
1400 {
1401 while ( query.next() )
1402 {
1403 const QString salt = query.value( 0 ).toString();
1404 const QString civ = query.value( 1 ).toString();
1405 const QString hash = query.value( 2 ).toString();
1406 passwords.append( { salt, civ, hash } );
1407 }
1408 }
1409 return passwords;
1410}
1411
1413{
1414 QMutexLocker locker( &mMutex );
1415
1417
1418 if ( !authDbOpen() )
1419 {
1420 setError( tr( "Auth db could not be opened" ) );
1421 return false;
1422 }
1423
1424 QSqlQuery query( authDatabaseConnection() );
1425 query.prepare( QStringLiteral( "INSERT INTO %1 (salt, civ, hash) VALUES (:salt, :civ, :hash)" ).arg( quotedQualifiedIdentifier( masterPasswordTableName() ) ) );
1426
1427 query.bindValue( QStringLiteral( ":salt" ), config.salt );
1428 query.bindValue( QStringLiteral( ":civ" ), config.civ );
1429 query.bindValue( QStringLiteral( ":hash" ), config.hash );
1430
1431 if ( !authDbQuery( &query ) )
1432 return false;
1433
1434 emit masterPasswordChanged();
1435
1436 return true;
1437}
1438
1440{
1441 QMutexLocker locker( &mMutex );
1442
1444
1445 const bool ret { clearTables( { masterPasswordTableName() } ) };
1446
1447 if ( ret )
1448 {
1449 emit masterPasswordChanged();
1450 }
1451
1452 return ret;
1453}
1454
1456{
1457 return QStringLiteral( "auth_configs" );
1458}
1459
1461{
1462 return QStringLiteral( "auth_settings" );
1463}
1464
1466{
1467 return QStringLiteral( "auth_identities" );
1468}
1469
1471{
1472 return QStringLiteral( "auth_servers" );
1473}
1474
1476{
1477 return QStringLiteral( "auth_authorities" );
1478}
1479
1481{
1482 return QStringLiteral( "auth_trust" );
1483}
1484
1486{
1487 return QStringLiteral( "auth_pass" );
1488}
1489
1490QString QgsAuthConfigurationStorageDb::quotedQualifiedIdentifier( const QString &name, bool isIndex ) const
1491{
1492 const QString schema { mConfiguration.value( QStringLiteral( "schema" ) ).toString() };
1493 if ( schema.isEmpty() )
1494 {
1495 return authDatabaseConnection().driver()->escapeIdentifier( name, QSqlDriver::TableName );
1496 }
1497 else
1498 {
1499 if ( isIndex )
1500 {
1501 return authDatabaseConnection().driver()->escapeIdentifier( schema + QStringLiteral( "_" ) + name, QSqlDriver::TableName );
1502 }
1503 else
1504 {
1505 return authDatabaseConnection().driver()->escapeIdentifier( schema, QSqlDriver::TableName ) + QStringLiteral( "." ) + authDatabaseConnection().driver()->escapeIdentifier( name, QSqlDriver::TableName );
1506 }
1507 }
1508}
1509
1511{
1512 return QStringLiteral( "%1:%2" ).arg( mDriver, mDatabase );
1513}
1514
1516{
1517 return QStringLiteral( "DB-%2" ).arg( mDriver );
1518}
1519
1521{
1522 return tr( "Store credentials in a %1 database" ).arg( name() );
1523}
1524
1526{
1527 QMutexLocker locker( &mMutex );
1528
1529 if ( mId.isEmpty() )
1530 {
1531 // Create a hash from the driver name, database name, port, hostname and username
1532 QCryptographicHash hash( QCryptographicHash::Sha256 );
1533 hash.addData( mDriver.toUtf8() );
1534 hash.addData( mDatabase.toUtf8() );
1535 hash.addData( QString::number( mPort ).toUtf8() );
1536 hash.addData( mHost.toUtf8() );
1537 hash.addData( mUser.toUtf8() );
1538 mId = QString( hash.result().toHex() );
1539 }
1540 return mId;
1541}
1542
1544{
1545 QMutexLocker locker( &mMutex );
1546
1547 if ( !authDbOpen() )
1548 {
1549 setError( tr( "Auth db could not be opened" ) );
1550 return false;
1551 }
1552
1553 QSqlQuery query( authDatabaseConnection() );
1554
1555 // create the tables
1556 QString qstr;
1557
1558 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1559 " salt TEXT NOT NULL,\n"
1560 " civ TEXT NOT NULL\n"
1561 ", hash TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( masterPasswordTableName() ) );
1562
1563 if ( !authDbQuery( &query, qstr ) )
1564 return false;
1565
1566 query.clear();
1567
1568 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1569 " id TEXT NOT NULL,\n"
1570 " name TEXT NOT NULL,\n"
1571 " uri TEXT,\n"
1572 " type TEXT NOT NULL,\n"
1573 " version INTEGER NOT NULL\n"
1574 ", config TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) );
1575 if ( !authDbQuery( &query, qstr ) )
1576 return false;
1577
1578 query.clear();
1579
1580 qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2 (id ASC);" ).arg( quotedQualifiedIdentifier( QStringLiteral( "config_id_index" ), true ), quotedQualifiedIdentifier( methodConfigTableName() ) );
1581
1582 if ( !authDbQuery( &query, qstr ) )
1583 return false;
1584
1585 query.clear();
1586
1587 qstr = QStringLiteral( "CREATE INDEX IF NOT EXISTS %1 ON %2 (uri ASC);" )
1588 .arg( quotedQualifiedIdentifier( QStringLiteral( "uri_index" ), true ),
1590
1591 if ( !authDbQuery( &query, qstr ) )
1592 return false;
1593
1594 return true;
1595}
1596
1598{
1599 QMutexLocker locker( &mMutex );
1600
1601 if ( !authDbOpen() )
1602 {
1603 setError( tr( "Auth db could not be opened" ) );
1604 return false;
1605 }
1606
1607 QgsDebugMsgLevel( QStringLiteral( "Creating cert tables in auth db" ), 2 );
1608
1609 QSqlQuery query( authDatabaseConnection() );
1610
1611 // create the tables
1612 QString qstr;
1613
1614 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1615 " setting TEXT NOT NULL\n"
1616 ", value TEXT);" ).arg( quotedQualifiedIdentifier( authSettingsTableName() ) );
1617
1618 if ( !authDbQuery( &query, qstr ) )
1619 return false;
1620
1621 query.clear();
1622
1623 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1624 " id TEXT NOT NULL,\n"
1625 " key TEXT NOT NULL\n"
1626 ", cert TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( certIdentityTableName() ) );
1627
1628
1629 if ( !authDbQuery( &query, qstr ) )
1630 return false;
1631
1632 query.clear();
1633
1634 qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2 (id ASC);" )
1635 .arg( quotedQualifiedIdentifier( QStringLiteral( "cert_ident_id_index" ), true ), quotedQualifiedIdentifier( certIdentityTableName() ) );
1636
1637 if ( !authDbQuery( &query, qstr ) )
1638 return false;
1639
1640 query.clear();
1641
1642
1643 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1644 " id TEXT NOT NULL,\n"
1645 " host TEXT NOT NULL,\n"
1646 " cert TEXT\n"
1647 ", config TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) );
1648
1649 if ( !authDbQuery( &query, qstr ) )
1650 return false;
1651
1652 query.clear();
1653
1654 qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2 (host ASC);" ).arg( quotedQualifiedIdentifier( QStringLiteral( "host_index" ), true ), quotedQualifiedIdentifier( sslCertCustomConfigTableName() ) );
1655
1656
1657 if ( !authDbQuery( &query, qstr ) )
1658 return false;
1659
1660 query.clear();
1661
1662
1663 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1664 " id TEXT NOT NULL\n"
1665 ", cert TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( certAuthorityTableName() ) );
1666
1667
1668 if ( !authDbQuery( &query, qstr ) )
1669 return false;
1670
1671 query.clear();
1672
1673 qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2 (id ASC);" )
1674 .arg( quotedQualifiedIdentifier( QStringLiteral( "ca_id_index" ), true ), quotedQualifiedIdentifier( certAuthorityTableName() ) );
1675
1676
1677 if ( !authDbQuery( &query, qstr ) )
1678 return false;
1679
1680 query.clear();
1681
1682 qstr = QStringLiteral( "CREATE TABLE IF NOT EXISTS %1 (\n"
1683 " id TEXT NOT NULL\n"
1684 ", policy TEXT NOT NULL);" ).arg( quotedQualifiedIdentifier( certTrustPolicyTableName() ) );
1685
1686 if ( !authDbQuery( &query, qstr ) )
1687 return false;
1688
1689 query.clear();
1690
1691 qstr = QStringLiteral( "CREATE UNIQUE INDEX IF NOT EXISTS %1 ON %2 (id ASC);" )
1692 .arg( quotedQualifiedIdentifier( QStringLiteral( "trust_id_index" ), true ), quotedQualifiedIdentifier( certTrustPolicyTableName() ) );
1693
1694
1695 if ( !authDbQuery( &query, qstr ) )
1696 return false;
1697
1698 query.clear();
1699
1700 return true;
1701}
1702
1704{
1705
1706 QMutexLocker locker( &mMutex );
1707
1709
1710 QgsAuthMethodConfigsMap baseConfigs;
1711
1712 if ( ! isEnabled() || !isReady() )
1713 return baseConfigs;
1714
1715 if ( !authDbOpen() )
1716 {
1717 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1718 return baseConfigs;
1719 }
1720
1721 QSqlQuery query( authDatabaseConnection() );
1722 query.prepare( QStringLiteral( "SELECT id, name, uri, type, version FROM %1" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
1723
1724 if ( !authDbQuery( &query ) )
1725 {
1726 return baseConfigs;
1727 }
1728
1729 if ( query.isActive() && query.isSelect() )
1730 {
1731 while ( query.next() )
1732 {
1733 QString authcfg = query.value( 0 ).toString();
1734 QgsAuthMethodConfig config;
1735 config.setId( authcfg );
1736 config.setName( query.value( 1 ).toString() );
1737 config.setUri( query.value( 2 ).toString() );
1738 config.setMethod( query.value( 3 ).toString() );
1739 config.setVersion( query.value( 4 ).toInt() );
1740
1741 if ( !allowedMethods.isEmpty() && !allowedMethods.contains( config.method() ) )
1742 {
1743 continue;
1744 }
1745
1746 baseConfigs.insert( authcfg, config );
1747 }
1748 }
1749 return baseConfigs;
1750
1751}
1752
1754{
1755
1756 QMutexLocker locker( &mMutex );
1757
1759
1760 QgsAuthMethodConfigsMap baseConfigs;
1761
1762 if ( ! isEnabled() || !isReady() )
1763 return baseConfigs;
1764
1765 if ( !authDbOpen() )
1766 {
1767 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
1768 return baseConfigs;
1769 }
1770
1771 QSqlQuery query( authDatabaseConnection() );
1772 query.prepare( QStringLiteral( "SELECT id, name, uri, type, version, config FROM %1" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
1773
1774 if ( !authDbQuery( &query ) )
1775 {
1776 return baseConfigs;
1777 }
1778
1779 if ( query.isActive() && query.isSelect() )
1780 {
1781 while ( query.next() )
1782 {
1783 QString authcfg = query.value( 0 ).toString();
1784 QgsAuthMethodConfig config;
1785 config.setId( authcfg );
1786 config.setName( query.value( 1 ).toString() );
1787 config.setUri( query.value( 2 ).toString() );
1788 config.setMethod( query.value( 3 ).toString() );
1789 config.setVersion( query.value( 4 ).toInt() );
1790 config.setConfig( QStringLiteral( "encrypted_payload" ), query.value( 5 ).toString() );
1791 baseConfigs.insert( authcfg, config );
1792 }
1793 }
1794 return baseConfigs;
1795}
1796
1798{
1799 QMutexLocker locker( &mMutex );
1800
1802
1803 if ( ! isEnabled() || !isReady() )
1804 return;
1805
1806 if ( !authDbOpen() )
1807 {
1808 setError( tr( "Auth db could not be opened" ) );
1809 return;
1810 }
1811
1812 // Check if each table exist and set capabilities
1813
1814 static const QStringList existingTables = authDatabaseConnection().tables();
1815 QString schema { mConfiguration.value( QStringLiteral( "schema" ) ).toString() };
1816 if ( ! schema.isEmpty() )
1817 {
1818 schema += '.';
1819 }
1820
1821 if ( existingTables.contains( schema.isEmpty() ? masterPasswordTableName( ) : schema + masterPasswordTableName( ) ) )
1822 {
1824 if ( !isReadOnly() )
1825 {
1829 }
1830 }
1831
1832 if ( existingTables.contains( schema.isEmpty() ? methodConfigTableName( ) : schema + methodConfigTableName( ) ) )
1833 {
1835 if ( !isReadOnly() )
1836 {
1840 }
1841 }
1842
1843 if ( existingTables.contains( schema.isEmpty() ? authSettingsTableName( ) : schema + authSettingsTableName( ) ) )
1844 {
1846 if ( !isReadOnly() )
1847 {
1851 }
1852 }
1853
1854 if ( existingTables.contains( schema.isEmpty() ? certIdentityTableName( ) : schema + certIdentityTableName( ) ) )
1855 {
1857 if ( !isReadOnly() )
1858 {
1862 }
1863 }
1864
1865 if ( existingTables.contains( schema.isEmpty() ? sslCertCustomConfigTableName( ) : schema + sslCertCustomConfigTableName( ) ) )
1866 {
1868 if ( !isReadOnly() )
1869 {
1873 }
1874 }
1875
1876 if ( existingTables.contains( schema.isEmpty() ? certAuthorityTableName( ) : schema + certAuthorityTableName( ) ) )
1877 {
1879 if ( !isReadOnly() )
1880 {
1884 }
1885 }
1886
1887 if ( existingTables.contains( schema.isEmpty() ? certTrustPolicyTableName( ) : schema + certTrustPolicyTableName( ) ) )
1888 {
1890 if ( !isReadOnly() )
1891 {
1895 }
1896 }
1897
1898 // Any delete capability will set ClearStorage
1906 {
1908 }
1909
1910}
1911
1912QgsAuthMethodConfig QgsAuthConfigurationStorageDb::loadMethodConfig( const QString &id, QString &payload, bool full ) const
1913{
1914 QMutexLocker locker( &mMutex );
1915
1917
1918 QgsAuthMethodConfig config;
1919
1920 if ( !authDbOpen() )
1921 {
1922 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ), Qgis::MessageLevel::Critical );
1923 return config;
1924 }
1925
1926 QSqlQuery query( authDatabaseConnection() );
1927
1928 if ( full )
1929 {
1930 query.prepare( QStringLiteral( "SELECT name, uri, type, version, config FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
1931 }
1932 else
1933 {
1934 query.prepare( QStringLiteral( "SELECT name, uri, type, version FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
1935 }
1936
1937 query.bindValue( QStringLiteral( ":id" ), id );
1938
1939 if ( !authDbQuery( &query ) )
1940 {
1941 return config;
1942 }
1943
1944 if ( query.isActive() && query.isSelect() )
1945 {
1946 if ( query.first() )
1947 {
1948 config.setId( id );
1949 config.setName( query.value( 0 ).toString() );
1950 config.setUri( query.value( 1 ).toString() );
1951 config.setMethod( query.value( 2 ).toString() );
1952 config.setVersion( query.value( 3 ).toInt() );
1953 if ( full )
1954 {
1955 payload = query.value( 4 ).toString();
1956 }
1957 }
1958 if ( query.next() )
1959 {
1960 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Authentication database contains more than one configuration IDs for '%1'" ).arg( id ), Qgis::MessageLevel::Warning );
1961 }
1962 }
1963
1964 return config;
1965}
1966
1968{
1969 QMutexLocker locker( &mMutex );
1970
1971 if ( methodConfigExists( config.id() ) )
1972 {
1974 }
1975 else
1976 {
1978 }
1979
1980 if ( !authDbOpen() )
1981 {
1982 setError( tr( "Auth db could not be opened" ) );
1983 return false;
1984 }
1985
1986 if ( payload.isEmpty() )
1987 {
1988 setError( tr( "Store config: FAILED because config string is empty" ), Qgis::MessageLevel::Warning );
1989 return false;
1990 }
1991
1992 if ( ! config.isValid( true ) )
1993 {
1994 setError( tr( "Store config: FAILED because config is invalid" ), Qgis::MessageLevel::Warning );
1995 return false;
1996 }
1997
1998 if ( methodConfigExists( config.id() ) )
1999 {
2000 removeMethodConfig( config.id() );
2001 }
2002
2003 QSqlQuery query( authDatabaseConnection() );
2004 query.prepare( QStringLiteral( "INSERT INTO %1 (id, name, uri, type, version, config) VALUES (:id, :name, :uri, :type, :version, :config)" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
2005 query.bindValue( QStringLiteral( ":id" ), config.id() );
2006 query.bindValue( QStringLiteral( ":name" ), config.name() );
2007 query.bindValue( QStringLiteral( ":uri" ), config.uri() );
2008 query.bindValue( QStringLiteral( ":type" ), config.method() );
2009 query.bindValue( QStringLiteral( ":version" ), config.version() );
2010 query.bindValue( QStringLiteral( ":config" ), payload );
2011
2012 if ( !authDbQuery( &query ) )
2013 {
2014 setError( tr( "Failed to store config '%1'" ).arg( config.id() ), Qgis::MessageLevel::Critical );
2015 return false;
2016 }
2017
2018 emit methodConfigChanged();
2019
2020 return true;
2021}
2022
2024{
2025 QMutexLocker locker( &mMutex );
2026
2028
2029 if ( !authDbOpen() )
2030 {
2031 setError( tr( "Auth db could not be opened" ) );
2032 return false;
2033 }
2034
2035 QSqlQuery query( authDatabaseConnection() );
2036
2037 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
2038 query.bindValue( QStringLiteral( ":id" ), id );
2039
2040 if ( !authDbQuery( &query ) )
2041 {
2042 setError( tr( "Failed to remove config '%1'" ).arg( id ), Qgis::MessageLevel::Critical );
2043 return false;
2044 }
2045
2046 if ( query.numRowsAffected() == 0 )
2047 {
2048 setError( tr( "Config '%1' does not exist" ).arg( id ), Qgis::MessageLevel::Warning );
2049 return false;
2050 }
2051
2052 emit methodConfigChanged();
2053
2054 return true;
2055}
2056
2058{
2059 QMutexLocker locker( &mMutex );
2060
2062
2063 if ( !authDbOpen() )
2064 {
2065 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
2066 return false;
2067 }
2068
2069 QSqlQuery query( authDatabaseConnection() );
2070
2071 query.prepare( QStringLiteral( "SELECT COUNT( id ) FROM %1 WHERE id = :id" ).arg( quotedQualifiedIdentifier( methodConfigTableName() ) ) );
2072 query.bindValue( QStringLiteral( ":id" ), id );
2073
2074 if ( !authDbQuery( &query ) )
2075 {
2076 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to query for config '%1'" ).arg( id ), Qgis::MessageLevel::Critical );
2077 return false;
2078 }
2079
2080 if ( query.next() )
2081 {
2082 return query.value( 0 ).toInt() > 0;
2083 }
2084
2085 return false;
2086}
2087
2088bool QgsAuthConfigurationStorageDb::storeAuthSetting( const QString &key, const QString &value )
2089{
2090 QMutexLocker locker( &mMutex );
2091
2092 if ( authSettingExists( key ) )
2093 {
2095 removeAuthSetting( key );
2096 }
2097 else
2098 {
2100 }
2101
2102 if ( !authDbOpen() )
2103 {
2104 setError( tr( "Auth db could not be opened" ) );
2105 return false;
2106 }
2107
2108 QSqlQuery query( authDatabaseConnection() );
2109
2110 query.prepare( QStringLiteral( "INSERT INTO %1 (setting, value) VALUES (:setting, :value)" )
2112 query.bindValue( QStringLiteral( ":setting" ), key );
2113 query.bindValue( QStringLiteral( ":value" ), value );
2114
2115 if ( !authDbQuery( &query ) )
2116 {
2117 setError( tr( "Failed to set setting '%1'" ).arg( key ), Qgis::MessageLevel::Critical );
2118 return false;
2119 }
2120 else
2121 {
2122 emit authSettingsChanged();
2123 return true;
2124 }
2125}
2126
2127QString QgsAuthConfigurationStorageDb::loadAuthSetting( const QString &key ) const
2128{
2129 QMutexLocker locker( &mMutex );
2130
2132
2133 if ( !authDbOpen() )
2134 {
2135 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
2136 return QString();
2137 }
2138
2139 QSqlQuery query( authDatabaseConnection() );
2140
2141 query.prepare( QStringLiteral( "SELECT value FROM %1 WHERE setting = :setting" ).arg( quotedQualifiedIdentifier( authSettingsTableName() ) ) );
2142 query.bindValue( QStringLiteral( ":setting" ), key );
2143
2144 if ( !authDbQuery( &query ) )
2145 {
2146 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to query for setting '%1'" ).arg( key ), Qgis::MessageLevel::Critical );
2147 return QString();
2148 }
2149
2150 if ( query.next() )
2151 {
2152 return query.value( 0 ).toString();
2153 }
2154
2155 // Not sure we need this warning, as it's not necessarily an error if the setting doesn't exist
2156 // const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Setting '%1' does not exist" ).arg( key ), Qgis::MessageLevel::Warning );
2157 QgsDebugMsgLevel( QStringLiteral( "Setting '%1' does not exist" ).arg( key ), 2 );
2158
2159 return QString();
2160}
2161
2162
2164{
2165 QMutexLocker locker( &mMutex );
2166
2168
2169 if ( !authDbOpen() )
2170 {
2171 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
2172 return false;
2173 }
2174
2175 QSqlQuery query( authDatabaseConnection() );
2176
2177 query.prepare( QStringLiteral( "DELETE FROM %1 WHERE setting = :setting" ).arg( quotedQualifiedIdentifier( authSettingsTableName() ) ) );
2178 query.bindValue( QStringLiteral( ":setting" ), key );
2179
2180 if ( !authDbQuery( &query ) )
2181 {
2182 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to remove setting '%1'" ).arg( key ), Qgis::MessageLevel::Critical );
2183 return false;
2184 }
2185
2186 if ( query.numRowsAffected() == 0 )
2187 {
2188 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Setting '%1' does not exist" ).arg( key ), Qgis::MessageLevel::Warning );
2189 return false;
2190 }
2191
2192 emit authSettingsChanged();
2193
2194 return true;
2195}
2196
2198{
2199 QMutexLocker locker( &mMutex );
2200
2202
2203 if ( !authDbOpen() )
2204 {
2205 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Auth db could not be opened" ) );
2206 return false;
2207 }
2208
2209 QSqlQuery query( authDatabaseConnection() );
2210
2211 query.prepare( QStringLiteral( "SELECT COUNT(value) FROM %1 WHERE setting = :setting" ).arg( quotedQualifiedIdentifier( authSettingsTableName() ) ) );
2212 query.bindValue( QStringLiteral( ":setting" ), key );
2213
2214 if ( !authDbQuery( &query ) )
2215 {
2216 const_cast< QgsAuthConfigurationStorageDb * >( this )->setError( tr( "Failed to query for setting '%1'" ).arg( key ), Qgis::MessageLevel::Critical );
2217 return false;
2218 }
2219
2220 if ( query.next() )
2221 {
2222 return query.value( 0 ).toInt() > 0;
2223 }
2224
2225 return false;
2226}
2227
2228
2229
2230bool QgsAuthConfigurationStorageDb::clearTables( const QStringList &tables )
2231{
2232 QMutexLocker locker( &mMutex );
2233
2234 if ( !authDbOpen() )
2235 {
2236 setError( tr( "Auth db could not be opened" ) );
2237 return false;
2238 }
2239
2240 QSqlQuery query( authDatabaseConnection() );
2241
2242 for ( const auto &table : std::as_const( tables ) )
2243 {
2244
2245 // Check if the table exists
2246 if ( ! tableExists( table ) )
2247 {
2248 setError( tr( "Failed to empty table '%1': table does not exist" ).arg( table ), Qgis::MessageLevel::Warning );
2249 continue;
2250 }
2251
2252 // Check whether the table supports deletion
2253 if ( table.compare( methodConfigTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2254 {
2256 }
2257 else if ( table.compare( authSettingsTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2258 {
2260 }
2261 else if ( table.compare( certIdentityTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2262 {
2264 }
2265 else if ( table.compare( sslCertCustomConfigTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2266 {
2268 }
2269 else if ( table.compare( certAuthorityTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2270 {
2272 }
2273 else if ( table.compare( certTrustPolicyTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2274 {
2276 }
2277 else if ( table.compare( masterPasswordTableName(), Qt::CaseSensitivity::CaseInsensitive ) )
2278 {
2280 }
2281 else
2282 {
2283 // Unsupported table: it should not happen!
2284 throw QgsNotSupportedException( tr( "Failed to empty table '%1': unsupported table" ).arg( table ) );
2285 }
2286
2287 query.prepare( QStringLiteral( "DELETE FROM %1" ).arg( quotedQualifiedIdentifier( table ) ) );
2288
2289 if ( !authDbQuery( &query ) )
2290 {
2291 setError( tr( "Failed to empty table '%1'" ).arg( quotedQualifiedIdentifier( table ) ) );
2292 return false;
2293 }
2294 }
2295 return true;
2296}
2297
2298bool QgsAuthConfigurationStorageDb::tableExists( const QString &table ) const
2299{
2300 QString schema { mConfiguration.value( QStringLiteral( "schema" ) ).toString() };
2301 if ( ! schema.isEmpty() )
2302 {
2303 schema += '.';
2304 }
2305 return authDatabaseConnection().tables().contains( schema + table );
2306}
2307
2308const QMap<QString, QVariant> QgsAuthConfigurationStorageDb::uriToSettings( const QString &uri )
2309{
2310 QUrl url( uri );
2311 QMap<QString, QVariant> settings;
2312
2313 if ( url.isValid() )
2314 {
2315 settings.insert( QStringLiteral( "driver" ), url.scheme().toUpper() );
2316 settings.insert( QStringLiteral( "host" ), url.host() );
2317 settings.insert( QStringLiteral( "port" ), QString::number( url.port() ) );
2318 QString path { url.path() };
2319 // Remove leading slash from the path unless the driver is QSQLITE or QSPATIALITE
2320 if ( path.startsWith( QLatin1Char( '/' ) ) &&
2321 !( settings.value( QStringLiteral( "driver" ) ) == QLatin1String( "QSQLITE" ) ||
2322 settings.value( QStringLiteral( "driver" ) ) == QLatin1String( "QSPATIALITE" ) ) )
2323 {
2324 path = path.mid( 1 );
2325 }
2326 settings.insert( QStringLiteral( "database" ), path );
2327 settings.insert( QStringLiteral( "user" ), url.userName() );
2328 settings.insert( QStringLiteral( "password" ), url.password() );
2329 QUrlQuery query{ url };
2330
2331 // Extract the schema from the query string
2332 QString schemaName { query.queryItemValue( QStringLiteral( "schema" ) ) };
2333 if ( schemaName.isEmpty() )
2334 {
2335 schemaName = query.queryItemValue( QStringLiteral( "SCHEMA" ) );
2336 }
2337
2338 if ( ! schemaName.isEmpty() )
2339 {
2340 settings.insert( QStringLiteral( "schema" ), schemaName );
2341 query.removeAllQueryItems( QStringLiteral( "schema" ) );
2342 query.removeAllQueryItems( QStringLiteral( "SCHEMA" ) );
2343 }
2344
2345 settings.insert( QStringLiteral( "options" ), query.toString() );
2346 }
2347 return settings;
2348}
2349
2351{
2352 if ( clearTables( {{ methodConfigTableName( ) }} ) )
2353 {
2354 emit methodConfigChanged();
2355 return true;
2356 }
2357 else
2358 {
2359 return false;
2360 }
2361}
2362
2364{
2365
2367
2368 if ( clearTables( {{
2376 }} ) )
2377 {
2378 emit storageChanged( id( ) );
2379 return true;
2380 }
2381 else
2382 {
2383 return false;
2384 }
2385}
2386
2388{
2389 QMutexLocker locker( &mMutex );
2390 return mIsReady;
2391}
QFlags< AuthConfigurationStorageCapability > AuthConfigurationStorageCapabilities
Authentication configuration storage capabilities.
Definition qgis.h:145
@ Warning
Warning message.
Definition qgis.h:156
@ Critical
Critical/error message.
Definition qgis.h:157
@ CreateSetting
Can create a new authentication setting.
@ UpdateSetting
Can update the authentication setting.
@ CreateConfiguration
Can create a new authentication configuration.
@ ClearStorage
Can clear all configurations from storage.
@ DeleteCertificateAuthority
Can delete a certificate authority.
@ DeleteSslCertificateCustomConfig
Can delete a SSL certificate custom config.
@ DeleteSetting
Can delete the authentication setting.
@ ReadSslCertificateCustomConfig
Can read a SSL certificate custom config.
@ DeleteMasterPassword
Can delete the master password.
@ CreateSslCertificateCustomConfig
Can create a new SSL certificate custom config.
@ ReadCertificateTrustPolicy
Can read a certificate trust policy.
@ ReadConfiguration
Can read an authentication configuration.
@ UpdateConfiguration
Can update an authentication configuration.
@ UpdateCertificateTrustPolicy
Can update a certificate trust policy.
@ ReadCertificateAuthority
Can read a certificate authority.
@ CreateCertificateAuthority
Can create a new certificate authority.
@ DeleteConfiguration
Can deleet an authentication configuration.
@ ReadSetting
Can read the authentication settings.
@ UpdateMasterPassword
Can update the master password.
@ UpdateCertificateAuthority
Can update a certificate authority.
@ CreateCertificateIdentity
Can create a new certificate identity.
@ ReadCertificateIdentity
Can read a certificate identity.
@ CreateCertificateTrustPolicy
Can create a new certificate trust policy.
@ ReadMasterPassword
Can read the master password.
@ CreateMasterPassword
Can create a new master password.
@ UpdateCertificateIdentity
Can update a certificate identity.
@ DeleteCertificateTrustPolicy
Can delete a certificate trust policy.
@ DeleteCertificateIdentity
Can delete a certificate identity.
@ UpdateSslCertificateCustomConfig
Can update a SSL certificate custom config.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
CertTrustPolicy
Type of certificate trust policy.
Configuration container for SSL server connection exceptions or overrides.
void setSslCertificate(const QSslCertificate &cert)
Sets server certificate object.
void setSslHostPort(const QString &hostport)
Sets server host:port string.
const QSslCertificate sslCertificate() const
Server certificate object.
const QString sslHostPort() const
Server host:port string.
const QString configString() const
Configuration as a concatenated string.
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
QSqlDatabase based implementation of QgsAuthConfigurationStorage.
QgsAuthConfigurationStorageDb(const QMap< QString, QVariant > &settings)
Creates a new QgsAuthConfigurationStorageDb instance from the specified settings.
bool removeCertTrustPolicy(const QSslCertificate &cert) override
Remove certificate trust policy.
bool authDbTransactionQuery(QSqlQuery *query)
Executes the specified query on the database using a transaction.
virtual bool tableExists(const QString &table) const
Returns true if the specified table exists in the database, false otherwise.
bool storeCertTrustPolicy(const QSslCertificate &cert, QgsAuthCertUtils::CertTrustPolicy policy) override
Store certificate trust policy.
QStringList certAuthorityIds() const override
Returns the list of certificate authority IDs in the storage.
const QgsAuthConfigSslServer loadSslCertCustomConfigByHost(const QString &hostport) const override
Loads an SSL certificate custom config by hostport (host:port)
QString loadAuthSetting(const QString &key) const override
Load an authentication setting from the storage.
bool removeAuthSetting(const QString &key) override
Remove an authentication setting from the storage.
const QMap< QString, QgsAuthCertUtils::CertTrustPolicy > caCertsPolicy() const override
Returns the map of CA certificates hashes in the storages and their trust policy.
QgsAuthCertUtils::CertTrustPolicy loadCertTrustPolicy(const QSslCertificate &cert) const override
Load certificate trust policy.
bool sslCertCustomConfigExists(const QString &id, const QString &hostport) override
Check if SSL certificate custom config exists.
virtual void checkCapabilities()
Checks the capabilities of the storage.
bool authDbQuery(QSqlQuery *query, const QString &sql=QString()) const
Runs the specified query on the database.
bool storeAuthSetting(const QString &key, const QString &value) override
Store an authentication setting in the storage.
bool removeCertIdentity(const QSslCertificate &cert) override
Remove a certificate identity from the storage.
const QPair< QSslCertificate, QString > loadCertIdentityBundle(const QString &id) const override
Returns a certificate identity bundle by id (sha hash).
const QList< QgsAuthConfigurationStorage::MasterPasswordConfig > masterPasswords() const override
Returns the list of (encrypted) master passwords stored in the database.
bool clearMethodConfigs() override
Remove all authentications configurations from the storage.
bool createCertTables()
Creates the certificate tables in the database.
bool storeSslCertCustomConfig(const QgsAuthConfigSslServer &config) override
Store an SSL certificate custom config.
virtual QString certIdentityTableName() const
Returns the name of the table used to store the certificate identities.
bool authDbOpen() const
Opens the connction to the database.
virtual QString quotedQualifiedIdentifier(const QString &identifier, bool isIndex=false) const
Returns the quoted identifier, prefixed with the schema (if not null), ready for the insertion into a...
bool methodConfigExists(const QString &id) const override
Check if an authentication configuration exists in the storage.
virtual QString methodConfigTableName() const
Returns the name of the table used to store the method configurations.
QSqlDatabase authDatabaseConnection() const
Returns the database connection used by this storage.
QList< QgsAuthConfigurationStorage::SettingParameter > settingsParameters() const override
Returns a list of the settings accepted by the storage.
QStringList certIdentityIds() const override
certIdentityIds get list of certificate identity ids from database
bool storeMasterPassword(const QgsAuthConfigurationStorage::MasterPasswordConfig &config) override
Store a master password in the database.
bool isReady() const override
Returns true is the storage is ready to be used.
bool initialize() override
Initializes the storage.
bool storeMethodConfig(const QgsAuthMethodConfig &mconfig, const QString &payload) override
Store an authentication config in the database.
bool removeCertAuthority(const QSslCertificate &cert) override
Remove a certificate authority.
const QSslCertificate loadCertIdentity(const QString &id) const override
certIdentity get a certificate identity by id (sha hash)
const QList< QgsAuthConfigSslServer > sslCertCustomConfigs() const override
sslCertCustomConfigs get SSL certificate custom configs
QgsAuthMethodConfigsMap authMethodConfigs(const QStringList &allowedMethods=QStringList()) const override
Returns a mapping of authentication configurations available from this storage.
const QList< QSslCertificate > caCerts() const override
Returns the list of CA certificates in the storage.
bool certTrustPolicyExists(const QSslCertificate &cert) const override
Check if certificate trust policy exists.
bool createConfigTables()
Creates the configuration tables in the database.
virtual QString authSettingsTableName() const
Returns the name of the table used to store the auth settings.
const QSslCertificate loadCertAuthority(const QString &id) const override
certAuthority get a certificate authority by id (sha hash)
virtual QString certTrustPolicyTableName() const
Returns the name of the table used to store the certificate trust policies.
bool removeMethodConfig(const QString &id) override
Removes the authentication configuration with the specified id.
QgsAuthMethodConfigsMap authMethodConfigsWithPayload() const override
Returns a mapping of authentication configurations available from this storage.
virtual QString sslCertCustomConfigTableName() const
Returns the name of the table used to store the SSL custom configurations.
QString id() const override
Returns the unique identifier of the storage object.
bool certIdentityExists(const QString &id) const override
Check if the certificate identity exists.
QString type() const override
Returns the type of the storage implementation.
bool certAuthorityExists(const QSslCertificate &cert) const override
Check if a certificate authority exists.
QgsAuthMethodConfig loadMethodConfig(const QString &id, QString &payload, bool full=false) const override
Load an authentication configuration from the database.
bool storeCertIdentity(const QSslCertificate &cert, const QString &keyPem) override
Store a certificate identity in the storage.
bool erase() override
Completely erase the storage removing all configurations/certs/settings etc.
virtual QString certAuthorityTableName() const
Returns the name of the table used to store the certificate authorities.
bool removeSslCertCustomConfig(const QString &id, const QString &hostport) override
Remove an SSL certificate custom config.
const QList< QSslCertificate > certIdentities() const override
certIdentities get certificate identities
bool clearMasterPasswords() override
Remove all master passwords from the database.
QString description() const override
Returns a human readable localized description of the storage implementation (e.g.
QString name() const override
Returns a human readable localized short name of the storage implementation (e.g "SQLite").
virtual QString masterPasswordTableName() const
Returns the name of the table used to store the master passwords.
bool authSettingExists(const QString &key) const override
Check if an authentication setting exists in the storage.
bool storeCertAuthority(const QSslCertificate &cert) override
Store a certificate authority.
const QgsAuthConfigSslServer loadSslCertCustomConfig(const QString &id, const QString &hostport) const override
Loads an SSL certificate custom config by id (sha hash) and hostport (host:port)
QStringList sslCertCustomConfigIds() const override
Returns the list of SSL certificate custom config ids.
Abstract class that defines the interface for all authentication configuration storage implementation...
QMap< QString, QVariant > mConfiguration
Store the implementation-specific configuration.
void readOnlyChanged(bool readOnly)
Emitted when the storage read-only status was changed.
void certIdentityChanged()
Emitted when the storage cert identity table was changed.
void methodConfigChanged()
Emitted when the storage method config table was changed.
void setError(const QString &error, Qgis::MessageLevel level=Qgis::MessageLevel::Critical)
Set the last error message to error with message level level.
Qgis::AuthConfigurationStorageCapabilities capabilities() const
Returns the capabilities of the storage.
virtual QString loggerTag() const
Returns the logger tag for the storage.
bool isEnabled() const
Returns true if the storage is enabled.
void sslCertTrustPolicyChanged()
Emitted when the storage ssl cert trust policy table was changed.
void masterPasswordChanged()
Emitted when the storage master password table was changed.
void certAuthorityChanged()
Emitted when the storage cert authority table was changed.
void messageLog(const QString &message, const QString &tag=QStringLiteral("Authentication"), Qgis::MessageLevel level=Qgis::MessageLevel::Info)
Custom logging signal to relay to console output and QgsMessageLog.
void sslCertCustomConfigChanged()
Emitted when the storage ssl cert custom config table was changed.
void authSettingsChanged()
Emitted when the storage auth settings table was changed.
void storageChanged(const QString &id)
Emitted when the storage was updated.
virtual QString lastError() const
Returns the last error message.
void checkCapability(Qgis::AuthConfigurationStorageCapability capability) const
Utility to check capability and throw QgsNotSupportedException if not supported.
virtual bool isReadOnly() const
Returns true if the storage is read-only, false otherwise.
QMap< QString, QVariant > settings() const
Returns the settings of the storage.
Qgis::AuthConfigurationStorageCapabilities mCapabilities
Store the capabilities of the storage.
Configuration storage class for authentication method configurations.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
QString method() const
Textual key of the associated authentication method.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
void setName(const QString &name)
Sets name of configuration.
void setVersion(int version)
Sets version of the configuration.
const QString name() const
Gets name of configuration.
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
void setConfig(const QString &key, const QString &value)
Set a single config value per key in the map.
int version() const
Gets version of the configuration.
void setMethod(const QString &method)
void setUri(const QString &uri)
void setId(const QString &id)
Sets auth config ID.
Custom exception class which is raised when an operation is not supported.
#define str(x)
Definition qgis.cpp:39
QHash< QString, QgsAuthMethodConfig > QgsAuthMethodConfigsMap
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
Structure that holds the (encrypted) master password elements.