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