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