QGIS API Documentation 3.99.0-Master (09f76ad7019)
Loading...
Searching...
No Matches
qgsauthsslconfigwidget.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsauthsslconfigwidget.cpp
3 ---------------------
4 begin : May 17, 2015
5 copyright : (C) 2015 by Boundless Spatial, Inc. USA
6 author : Larry Shaffer
7 email : lshaffer at boundlessgeo dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include "qgsapplication.h"
21#include "qgsauthguiutils.h"
22#include "qgsauthmanager.h"
23#include "qgslogger.h"
24
25#include <QDialogButtonBox>
26#include <QPushButton>
27#include <QSpinBox>
28#include <QString>
29#include <QUrl>
30
31#include "moc_qgsauthsslconfigwidget.cpp"
32
33using namespace Qt::StringLiterals;
34
35static const QString configFoundText_() { return QObject::tr( "Configuration loaded from database" ); }
36static const QString configNotFoundText_() { return QObject::tr( "Configuration not found in database" ); }
37
38QgsAuthSslConfigWidget::QgsAuthSslConfigWidget( QWidget *parent, const QSslCertificate &cert, const QString &hostport, const QList<QSslCertificate> &connectionCAs )
39 : QWidget( parent )
40 , mCert( nullptr )
41 , mConnectionCAs( connectionCAs )
42{
43 if ( QgsApplication::authManager()->isDisabled() )
44 {
45 mDisabled = true;
46 mAuthNotifyLayout = new QVBoxLayout;
47 this->setLayout( mAuthNotifyLayout );
48 mAuthNotify = new QLabel( QgsApplication::authManager()->disabledMessage(), this );
49 mAuthNotifyLayout->addWidget( mAuthNotify );
50 }
51 else
52 {
53 setupUi( this );
54 connect( btnCertInfo, &QToolButton::clicked, this, &QgsAuthSslConfigWidget::btnCertInfo_clicked );
55
56 connect( grpbxSslConfig, &QGroupBox::toggled, this, &QgsAuthSslConfigWidget::configEnabledChanged );
59
60 setUpSslConfigTree();
61
62 lblLoadedConfig->setVisible( false );
63 lblLoadedConfig->clear();
64
65 connect( leHost, &QLineEdit::textChanged, this, &QgsAuthSslConfigWidget::validateHostPortText );
66
67 if ( !cert.isNull() )
68 {
69 setSslCertificate( cert, hostport );
70 }
71 }
72}
73
75{
76 if ( mDisabled )
77 {
78 return nullptr;
79 }
80 return grpbxCert;
81}
82
84{
85 if ( mDisabled )
86 {
87 return nullptr;
88 }
89 return grpbxSslConfig;
90}
91
92// private
93QTreeWidgetItem *QgsAuthSslConfigWidget::addRootItem( const QString &label )
94{
95 QTreeWidgetItem *item = new QTreeWidgetItem(
96 QStringList() << label,
97 static_cast<int>( ConfigParent )
98 );
100 item->setTextAlignment( 0, Qt::AlignVCenter );
101 item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
102 treeSslConfig->insertTopLevelItem( treeSslConfig->topLevelItemCount(), item );
103
104 return item;
105}
106
107void QgsAuthSslConfigWidget::setUpSslConfigTree()
108{
109 treeSslConfig->setColumnCount( 1 );
110
111 // add config field names
112 mProtocolItem = addRootItem( tr( "Protocol" ) );
113 mProtocolCmbBx = new QComboBox( treeSslConfig );
114 mProtocolCmbBx->addItem( QgsAuthCertUtils::getSslProtocolName( QSsl::SecureProtocols ), static_cast<int>( QSsl::SecureProtocols ) );
115 mProtocolCmbBx->addItem( QgsAuthCertUtils::getSslProtocolName( QSsl::TlsV1_0 ), static_cast<int>( QSsl::TlsV1_0 ) );
116 mProtocolCmbBx->setMaximumWidth( 300 );
117 mProtocolCmbBx->setCurrentIndex( 0 );
118 QTreeWidgetItem *protocolitem = new QTreeWidgetItem(
119 mProtocolItem,
120 QStringList() << QString(),
121 static_cast<int>( ConfigItem )
122 );
123 protocolitem->setFlags( protocolitem->flags() & ~Qt::ItemIsSelectable );
124 treeSslConfig->setItemWidget( protocolitem, 0, mProtocolCmbBx );
125 mProtocolItem->setExpanded( true );
126
127 mVerifyModeItem = addRootItem( tr( "Peer verification" ) );
128 mVerifyPeerCmbBx = new QComboBox( treeSslConfig );
129 mVerifyPeerCmbBx->addItem( tr( "Verify Peer Certs" ), static_cast<int>( QSslSocket::VerifyPeer ) );
130 mVerifyPeerCmbBx->addItem( tr( "Do Not Verify Peer Certs" ), static_cast<int>( QSslSocket::VerifyNone ) );
131 mVerifyPeerCmbBx->setMaximumWidth( 300 );
132 mVerifyPeerCmbBx->setCurrentIndex( 0 );
133 QTreeWidgetItem *peerverifycmbxitem = new QTreeWidgetItem(
134 mVerifyModeItem,
135 QStringList() << QString(),
136 static_cast<int>( ConfigItem )
137 );
138 peerverifycmbxitem->setFlags( peerverifycmbxitem->flags() & ~Qt::ItemIsSelectable );
139 treeSslConfig->setItemWidget( peerverifycmbxitem, 0, mVerifyPeerCmbBx );
140 mVerifyModeItem->setExpanded( true );
141
142 mVerifyDepthItem = addRootItem( tr( "Peer verification depth (0 = complete cert chain)" ) );
143 mVerifyDepthSpnBx = new QSpinBox( treeSslConfig );
144 mVerifyDepthSpnBx->setMinimum( 0 );
145 mVerifyDepthSpnBx->setMaximum( 10 );
146 mVerifyDepthSpnBx->setMaximumWidth( 200 );
147 mVerifyDepthSpnBx->setAlignment( Qt::AlignHCenter );
148 QTreeWidgetItem *peerverifyspnbxitem = new QTreeWidgetItem(
149 mVerifyDepthItem,
150 QStringList() << QString(),
151 static_cast<int>( ConfigItem )
152 );
153 peerverifyspnbxitem->setFlags( peerverifyspnbxitem->flags() & ~Qt::ItemIsSelectable );
154 treeSslConfig->setItemWidget( peerverifyspnbxitem, 0, mVerifyDepthSpnBx );
155 mVerifyDepthItem->setExpanded( true );
156
157 mIgnoreErrorsItem = addRootItem( tr( "Ignore errors" ) );
158
159 const QList<QPair<QSslError::SslError, QString>> errenums = QgsAuthCertUtils::sslErrorEnumStrings();
160 for ( int i = 0; i < errenums.size(); i++ )
161 {
162 QTreeWidgetItem *item = new QTreeWidgetItem(
163 mIgnoreErrorsItem,
164 QStringList() << errenums.at( i ).second,
165 static_cast<int>( ConfigItem )
166 );
167 item->setCheckState( 0, Qt::Unchecked );
168 item->setTextAlignment( 0, Qt::AlignVCenter );
169 item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
170 item->setData( 0, Qt::UserRole, errenums.at( i ).first );
171 }
172 mIgnoreErrorsItem->setExpanded( true );
173}
174
176{
178 if ( mDisabled )
179 {
180 return config;
181 }
182 config.setSslCertificate( mCert );
183 config.setSslHostPort( leHost->text() );
184 config.setSslProtocol( sslProtocol() );
188 return config;
189}
190
192{
193 if ( mDisabled )
194 {
195 return QSslCertificate();
196 }
197 return mCert;
198}
199
201{
202 if ( mDisabled )
203 {
204 return QString();
205 }
206 return leHost->text();
207}
208
210{
211 if ( mDisabled )
212 {
213 return;
214 }
215 if ( grpbxSslConfig->isCheckable() )
216 {
217 grpbxSslConfig->setChecked( enable );
218 }
219}
220
221void QgsAuthSslConfigWidget::setSslCertificate( const QSslCertificate &cert, const QString &hostport )
222{
223 if ( mDisabled )
224 {
225 return;
226 }
227 if ( cert.isNull() )
228 {
229 return;
230 }
231 mCert = cert;
232
233 if ( !hostport.isEmpty() )
234 {
235 setSslHost( hostport );
236 }
237
238 const QString sha( QgsAuthCertUtils::shaHexForCert( cert ) );
239 const QgsAuthConfigSslServer config(
240 QgsApplication::authManager()->sslCertCustomConfig( sha, hostport.isEmpty() ? sslHost() : hostport )
241 );
242
243 emit certFoundInAuthDatabase( !config.isNull() );
244
245 lblLoadedConfig->setVisible( true );
246 if ( !config.isNull() )
247 {
248 loadSslCustomConfig( config );
249 leCommonName->setStyleSheet( QgsAuthGuiUtils::greenTextStyleSheet() );
250 }
251 else
252 {
253 lblLoadedConfig->setText( configNotFoundText_() );
254 leCommonName->setText( QgsAuthCertUtils::resolvedCertName( mCert ) );
255 leCommonName->setStyleSheet( QgsAuthGuiUtils::orangeTextStyleSheet() );
256 }
257 validateHostPortText( leHost->text() );
258}
259
261{
262 if ( mDisabled )
263 {
264 return;
265 }
267 if ( config.isNull() )
268 {
269 QgsDebugError( u"Passed-in SSL custom config is null"_s );
270 return;
271 }
272
273 const QSslCertificate cert( config.sslCertificate() );
274 if ( cert.isNull() )
275 {
276 QgsDebugError( u"SSL custom config's cert is null"_s );
277 return;
278 }
279
281 mCert = cert;
282 leCommonName->setText( QgsAuthCertUtils::resolvedCertName( cert ) );
283 leHost->setText( config.sslHostPort() );
285 setSslProtocol( config.sslProtocol() );
287
288 lblLoadedConfig->setVisible( true );
289 lblLoadedConfig->setText( configFoundText_() );
290}
291
293{
294 if ( mDisabled )
295 {
296 return;
297 }
298 if ( !QgsApplication::authManager()->storeSslCertCustomConfig( sslCustomConfig() ) )
299 {
300 QgsDebugError( u"SSL custom config FAILED to store in authentication storage"_s );
301 }
302}
303
305{
306 if ( mDisabled )
307 {
308 return;
309 }
310 mCert.clear();
311 mConnectionCAs.clear();
312 leCommonName->clear();
313 leCommonName->setStyleSheet( QString() );
314 leHost->clear();
315
316 lblLoadedConfig->setVisible( false );
317 lblLoadedConfig->clear();
321 enableSslCustomOptions( false );
322}
323
325{
326 if ( mDisabled )
327 {
328 return QSsl::UnknownProtocol;
329 }
330 return ( QSsl::SslProtocol ) mProtocolCmbBx->currentData().toInt();
331}
332
333void QgsAuthSslConfigWidget::setSslProtocol( QSsl::SslProtocol protocol )
334{
335 if ( mDisabled )
336 {
337 return;
338 }
339 const int indx( mProtocolCmbBx->findData( static_cast<int>( protocol ) ) );
340 mProtocolCmbBx->setCurrentIndex( indx );
341}
342
344{
345 if ( mDisabled )
346 {
347 return;
348 }
349 mProtocolCmbBx->setCurrentIndex( 0 );
350}
351
352void QgsAuthSslConfigWidget::appendSslIgnoreErrors( const QList<QSslError> &errors )
353{
354 if ( mDisabled )
355 {
356 return;
357 }
359
360 QList<QSslError::SslError> errenums;
361 const auto constErrors = errors;
362 for ( const QSslError &err : constErrors )
363 {
364 errenums << err.error();
365 }
366
367 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
368 {
369 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
370 if ( errenums.contains( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() ) )
371 {
372 item->setCheckState( 0, Qt::Checked );
373 }
374 }
375}
376
377void QgsAuthSslConfigWidget::setSslIgnoreErrorEnums( const QList<QSslError::SslError> &errorenums )
378{
379 if ( mDisabled )
380 {
381 return;
382 }
383 QList<QSslError> errors;
384 const auto constErrorenums = errorenums;
385 for ( const QSslError::SslError errorenum : constErrorenums )
386 {
387 errors << QSslError( errorenum );
388 }
389 setSslIgnoreErrors( errors );
390}
391
392void QgsAuthSslConfigWidget::setSslIgnoreErrors( const QList<QSslError> &errors )
393{
394 if ( mDisabled )
395 {
396 return;
397 }
398 if ( errors.isEmpty() )
399 {
400 return;
401 }
402
404
405 QList<QSslError::SslError> errenums;
406 const auto constErrors = errors;
407 for ( const QSslError &err : constErrors )
408 {
409 errenums << err.error();
410 }
411
412 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
413 {
414 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
415 const bool enable( errenums.contains( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() ) );
416 item->setCheckState( 0, enable ? Qt::Checked : Qt::Unchecked );
417 }
418}
419
421{
422 if ( mDisabled )
423 {
424 return;
425 }
426 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
427 {
428 mIgnoreErrorsItem->child( i )->setCheckState( 0, Qt::Unchecked );
429 }
430}
431
432const QList<QSslError::SslError> QgsAuthSslConfigWidget::sslIgnoreErrorEnums()
433{
434 QList<QSslError::SslError> errs;
435 if ( mDisabled )
436 {
437 return errs;
438 }
439 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
440 {
441 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
442 if ( item->checkState( 0 ) == Qt::Checked )
443 {
444 errs.append( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() );
445 }
446 }
447 return errs;
448}
449
451{
452 if ( mDisabled )
453 {
454 return QSslSocket::AutoVerifyPeer;
455 }
456 return ( QSslSocket::PeerVerifyMode ) mVerifyPeerCmbBx->currentData().toInt();
457}
458
460{
461 if ( mDisabled )
462 {
463 return 0;
464 }
465 return mVerifyDepthSpnBx->value();
466}
467
468void QgsAuthSslConfigWidget::setSslPeerVerify( QSslSocket::PeerVerifyMode mode, int modedepth )
469{
470 if ( mDisabled )
471 {
472 return;
473 }
475
476 const int indx( mVerifyPeerCmbBx->findData( static_cast<int>( mode ) ) );
477 mVerifyPeerCmbBx->setCurrentIndex( indx );
478
479 mVerifyDepthSpnBx->setValue( modedepth );
480}
481
483{
484 if ( mDisabled )
485 {
486 return;
487 }
488 mVerifyPeerCmbBx->setCurrentIndex( 0 );
489 mVerifyDepthSpnBx->setValue( 0 );
490}
491
493{
494 if ( mDisabled )
495 {
496 return false;
497 }
498 const bool cansave = ( isEnabled() && ( grpbxSslConfig->isCheckable() ? grpbxSslConfig->isChecked() : true ) && validateHostPort( leHost->text() ) );
499 if ( mCanSave != cansave )
500 {
501 mCanSave = cansave;
502 emit readyToSaveChanged( cansave );
503 }
504 return cansave;
505}
506
507void QgsAuthSslConfigWidget::setSslHost( const QString &host )
508{
509 if ( mDisabled )
510 {
511 return;
512 }
513 leHost->setText( host );
514}
515
516bool QgsAuthSslConfigWidget::validateHostPort( const QString &txt )
517{
518 const QString hostport( txt );
519 if ( hostport.isEmpty() )
520 {
521 return false;
522 }
523
524 // TODO: add QRegex checks against valid IP and domain.tld input
525 // i.e., currently accepts unlikely (though maybe valid) host:port combo, like 'a:1'
526 const QString urlbase( u"https://%1"_s.arg( hostport ) );
527 const QUrl url( urlbase );
528 return ( !url.host().isEmpty() && QString::number( url.port() ).size() > 0 && u"https://%1:%2"_s.arg( url.host() ).arg( url.port() ) == urlbase );
529}
530
532{
533 if ( mDisabled )
534 {
535 return;
536 }
537 const bool valid = validateHostPort( txt );
539 emit hostPortValidityChanged( valid );
540}
541
543{
544 if ( mDisabled )
545 {
546 return;
547 }
548 grpbxSslConfig->setCheckable( checkable );
549 if ( !checkable )
550 {
551 grpbxSslConfig->setEnabled( true );
552 }
553}
554
555void QgsAuthSslConfigWidget::btnCertInfo_clicked()
556{
557 if ( mCert.isNull() )
558 {
559 return;
560 }
561
562 QgsAuthCertInfoDialog *dlg = new QgsAuthCertInfoDialog( mCert, false, this, mConnectionCAs );
563 dlg->setWindowModality( Qt::WindowModal );
564 dlg->resize( 675, 500 );
565 dlg->exec();
566 dlg->deleteLater();
567}
568
569
571
572QgsAuthSslConfigDialog::QgsAuthSslConfigDialog( QWidget *parent, const QSslCertificate &cert, const QString &hostport )
573 : QDialog( parent )
574
575{
576 setWindowTitle( tr( "Custom Certificate Configuration" ) );
577 QVBoxLayout *layout = new QVBoxLayout( this );
578 layout->setContentsMargins( 6, 6, 6, 6 );
579
580 mSslConfigWdgt = new QgsAuthSslConfigWidget( this, cert, hostport );
581 connect( mSslConfigWdgt, &QgsAuthSslConfigWidget::readyToSaveChanged, this, &QgsAuthSslConfigDialog::checkCanSave );
582 layout->addWidget( mSslConfigWdgt );
583
584 QDialogButtonBox *buttonBox = new QDialogButtonBox(
585 QDialogButtonBox::Close | QDialogButtonBox::Save, Qt::Horizontal, this
586 );
587
588 buttonBox->button( QDialogButtonBox::Close )->setDefault( true );
589 mSaveButton = buttonBox->button( QDialogButtonBox::Save );
590 connect( buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close );
591 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsAuthSslConfigDialog::accept );
592 layout->addWidget( buttonBox );
593
594 setLayout( layout );
595 mSaveButton->setEnabled( mSslConfigWdgt->readyToSave() );
596}
597
599{
600 mSslConfigWdgt->saveSslCertConfig();
601 QDialog::accept();
602}
603
604void QgsAuthSslConfigDialog::checkCanSave( bool cansave )
605{
606 mSaveButton->setEnabled( cansave );
607}
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
Configuration container for SSL server connection exceptions or overrides.
void setSslProtocol(QSsl::SslProtocol protocol)
Sets SSL server protocol to use in connections.
void setSslCertificate(const QSslCertificate &cert)
Sets server certificate object.
void setSslHostPort(const QString &hostport)
Sets server host:port string.
QSsl::SslProtocol sslProtocol() const
SSL server protocol to use in connections.
void setSslPeerVerifyMode(QSslSocket::PeerVerifyMode mode)
Sets SSL client's peer verify mode to use in connections.
void setSslPeerVerifyDepth(int depth)
Set number or SSL client's peer to verify in connections.
int sslPeerVerifyDepth() const
Number or SSL client's peer to verify in connections.
bool isNull() const
Whether configuration is null (missing components).
void setSslIgnoredErrorEnums(const QList< QSslError::SslError > &errors)
Sets SSL server errors (as enum list) to ignore in connections.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
QSslSocket::PeerVerifyMode sslPeerVerifyMode() const
SSL client's peer verify mode to use in connections.
const QSslCertificate sslCertificate() const
Server certificate object.
const QString sslHostPort() const
Server host:port string.
static QString greenTextStyleSheet(const QString &selector="*")
Green text stylesheet representing valid, trusted, etc. certificate.
static QString redTextStyleSheet(const QString &selector="*")
Red text stylesheet representing invalid, untrusted, etc. certificate.
static QString orangeTextStyleSheet(const QString &selector="*")
Orange text stylesheet representing loaded component, but not stored in database.
static void setItemBold(QTreeWidgetItem *item)
Call setFirstColumnSpanned(true) on the item and make its font bold.
QgsAuthSslConfigDialog(QWidget *parent=nullptr, const QSslCertificate &cert=QSslCertificate(), const QString &hostport=QString())
Construct wrapper dialog for the SSL config widget.
Widget for editing an SSL server configuration.
QgsAuthSslConfigWidget(QWidget *parent=nullptr, const QSslCertificate &cert=QSslCertificate(), const QString &hostport=QString(), const QList< QSslCertificate > &connectionCAs=QList< QSslCertificate >())
Construct a widget for editing an SSL server certificate configuration.
const QSslCertificate sslCertificate()
Gets the SSL server certificate.
void validateHostPortText(const QString &txt)
Parse string for host:port.
void setSslPeerVerify(QSslSocket::PeerVerifyMode mode, int modedepth)
Sets the client's peer verify mode for connections.
QSslSocket::PeerVerifyMode sslPeerVerifyMode()
Gets the client's peer verify mode for connections.
QSsl::SslProtocol sslProtocol()
Gets the SSL protocol used for connections.
QGroupBox * sslConfigGroupBox()
Access to the SSL configuration's group box widget.
const QString sslHost()
Gets the host:port to associate with the server certificate.
void resetSslCertConfig()
Clear the current SSL server configuration and disabled it.
void setSslCertificate(const QSslCertificate &cert, const QString &hostport=QString())
Sets SSl certificate and any associated host:port.
void saveSslCertConfig()
Save the current SSL server configuration to the authentication database.
void certFoundInAuthDatabase(bool found)
Emitted when an certificate of same SHA hash is found in authentication database.
void resetSslProtocol()
Reset the SSL protocol to use in connections to the default.
void setSslHost(const QString &host)
Sets the host of the server.
void appendSslIgnoreErrors(const QList< QSslError > &errors)
Add to SSL errors to ignore for the connection.
void hostPortValidityChanged(bool valid)
Emitted when the validity of the host:port changes.
void setSslIgnoreErrorEnums(const QList< QSslError::SslError > &errorenums)
Sets the SSL errors (as enums) to ignore for the connection.
void readyToSaveChanged(bool cansave)
Emitted when the configuration can be saved changes.
void resetSslIgnoreErrors()
Clear the SSL errors to ignore for the connection.
void configEnabledChanged(bool enabled)
Emitted when the enabled state of the configuration changes.
void setSslIgnoreErrors(const QList< QSslError > &errors)
Sets the SSL errors to ignore for the connection.
int sslPeerVerifyDepth()
Gets the client's peer verify depth for connections.
void loadSslCustomConfig(const QgsAuthConfigSslServer &config=QgsAuthConfigSslServer())
Load an existing SSL server configuration.
const QList< QSslError::SslError > sslIgnoreErrorEnums()
Gets list of the SSL errors (as enums) to be ignored for connections.
bool readyToSave()
Verify if the configuration if ready to save.
void setSslProtocol(QSsl::SslProtocol protocol)
Sets the SSL protocol to use in connections.
QGroupBox * certificateGroupBox()
Access to the certificate's group box widget.
void enableSslCustomOptions(bool enable)
Enable or disable the custom options widget.
void resetSslPeerVerify()
Reset the client's peer verify mode for connections to default.
void setConfigCheckable(bool checkable)
Sets whether the config group box is checkable.
const QgsAuthConfigSslServer sslCustomConfig()
Gets the SSL configuration.
#define QgsDebugError(str)
Definition qgslogger.h:59