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