QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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_()
36{
37 return QObject::tr( "Configuration loaded from database" );
38}
39static const QString configNotFoundText_()
40{
41 return QObject::tr( "Configuration not found in database" );
42}
43
44QgsAuthSslConfigWidget::QgsAuthSslConfigWidget( QWidget *parent, const QSslCertificate &cert, const QString &hostport, const QList<QSslCertificate> &connectionCAs )
45 : QWidget( parent )
46 , mCert( nullptr )
47 , mConnectionCAs( connectionCAs )
48{
49 if ( QgsApplication::authManager()->isDisabled() )
50 {
51 mDisabled = true;
52 mAuthNotifyLayout = new QVBoxLayout;
53 this->setLayout( mAuthNotifyLayout );
54 mAuthNotify = new QLabel( QgsApplication::authManager()->disabledMessage(), this );
55 mAuthNotifyLayout->addWidget( mAuthNotify );
56 }
57 else
58 {
59 setupUi( this );
60 connect( btnCertInfo, &QToolButton::clicked, this, &QgsAuthSslConfigWidget::btnCertInfo_clicked );
61
62 connect( grpbxSslConfig, &QGroupBox::toggled, this, &QgsAuthSslConfigWidget::configEnabledChanged );
65
66 setUpSslConfigTree();
67
68 lblLoadedConfig->setVisible( false );
69 lblLoadedConfig->clear();
70
71 connect( leHost, &QLineEdit::textChanged, this, &QgsAuthSslConfigWidget::validateHostPortText );
72
73 if ( !cert.isNull() )
74 {
75 setSslCertificate( cert, hostport );
76 }
77 }
78}
79
81{
82 if ( mDisabled )
83 {
84 return nullptr;
85 }
86 return grpbxCert;
87}
88
90{
91 if ( mDisabled )
92 {
93 return nullptr;
94 }
95 return grpbxSslConfig;
96}
97
98// private
99QTreeWidgetItem *QgsAuthSslConfigWidget::addRootItem( const QString &label )
100{
101 QTreeWidgetItem *item = new QTreeWidgetItem( QStringList() << label, static_cast<int>( ConfigParent ) );
103 item->setTextAlignment( 0, Qt::AlignVCenter );
104 item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
105 treeSslConfig->insertTopLevelItem( treeSslConfig->topLevelItemCount(), item );
106
107 return item;
108}
109
110void QgsAuthSslConfigWidget::setUpSslConfigTree()
111{
112 treeSslConfig->setColumnCount( 1 );
113
114 // add config field names
115 mProtocolItem = addRootItem( tr( "Protocol" ) );
116 mProtocolCmbBx = new QComboBox( treeSslConfig );
117 mProtocolCmbBx->addItem( QgsAuthCertUtils::getSslProtocolName( QSsl::SecureProtocols ), static_cast<int>( QSsl::SecureProtocols ) );
118 mProtocolCmbBx->addItem( QgsAuthCertUtils::getSslProtocolName( QSsl::TlsV1_0 ), static_cast<int>( QSsl::TlsV1_0 ) );
119 mProtocolCmbBx->setMaximumWidth( 300 );
120 mProtocolCmbBx->setCurrentIndex( 0 );
121 QTreeWidgetItem *protocolitem = new QTreeWidgetItem( mProtocolItem, QStringList() << QString(), static_cast<int>( ConfigItem ) );
122 protocolitem->setFlags( protocolitem->flags() & ~Qt::ItemIsSelectable );
123 treeSslConfig->setItemWidget( protocolitem, 0, mProtocolCmbBx );
124 mProtocolItem->setExpanded( true );
125
126 mVerifyModeItem = addRootItem( tr( "Peer verification" ) );
127 mVerifyPeerCmbBx = new QComboBox( treeSslConfig );
128 mVerifyPeerCmbBx->addItem( tr( "Verify Peer Certs" ), static_cast<int>( QSslSocket::VerifyPeer ) );
129 mVerifyPeerCmbBx->addItem( tr( "Do Not Verify Peer Certs" ), static_cast<int>( QSslSocket::VerifyNone ) );
130 mVerifyPeerCmbBx->setMaximumWidth( 300 );
131 mVerifyPeerCmbBx->setCurrentIndex( 0 );
132 QTreeWidgetItem *peerverifycmbxitem = new QTreeWidgetItem( mVerifyModeItem, QStringList() << QString(), static_cast<int>( ConfigItem ) );
133 peerverifycmbxitem->setFlags( peerverifycmbxitem->flags() & ~Qt::ItemIsSelectable );
134 treeSslConfig->setItemWidget( peerverifycmbxitem, 0, mVerifyPeerCmbBx );
135 mVerifyModeItem->setExpanded( true );
136
137 mVerifyDepthItem = addRootItem( tr( "Peer verification depth (0 = complete cert chain)" ) );
138 mVerifyDepthSpnBx = new QSpinBox( treeSslConfig );
139 mVerifyDepthSpnBx->setMinimum( 0 );
140 mVerifyDepthSpnBx->setMaximum( 10 );
141 mVerifyDepthSpnBx->setMaximumWidth( 200 );
142 mVerifyDepthSpnBx->setAlignment( Qt::AlignHCenter );
143 QTreeWidgetItem *peerverifyspnbxitem = new QTreeWidgetItem( mVerifyDepthItem, QStringList() << QString(), static_cast<int>( ConfigItem ) );
144 peerverifyspnbxitem->setFlags( peerverifyspnbxitem->flags() & ~Qt::ItemIsSelectable );
145 treeSslConfig->setItemWidget( peerverifyspnbxitem, 0, mVerifyDepthSpnBx );
146 mVerifyDepthItem->setExpanded( true );
147
148 mIgnoreErrorsItem = addRootItem( tr( "Ignore errors" ) );
149
150 const QList<QPair<QSslError::SslError, QString>> errenums = QgsAuthCertUtils::sslErrorEnumStrings();
151 for ( int i = 0; i < errenums.size(); i++ )
152 {
153 QTreeWidgetItem *item = new QTreeWidgetItem( mIgnoreErrorsItem, QStringList() << errenums.at( i ).second, static_cast<int>( ConfigItem ) );
154 item->setCheckState( 0, Qt::Unchecked );
155 item->setTextAlignment( 0, Qt::AlignVCenter );
156 item->setFlags( item->flags() & ~Qt::ItemIsSelectable );
157 item->setData( 0, Qt::UserRole, errenums.at( i ).first );
158 }
159 mIgnoreErrorsItem->setExpanded( true );
160}
161
163{
165 if ( mDisabled )
166 {
167 return config;
168 }
169 config.setSslCertificate( mCert );
170 config.setSslHostPort( leHost->text() );
171 config.setSslProtocol( sslProtocol() );
175 return config;
176}
177
179{
180 if ( mDisabled )
181 {
182 return QSslCertificate();
183 }
184 return mCert;
185}
186
188{
189 if ( mDisabled )
190 {
191 return QString();
192 }
193 return leHost->text();
194}
195
197{
198 if ( mDisabled )
199 {
200 return;
201 }
202 if ( grpbxSslConfig->isCheckable() )
203 {
204 grpbxSslConfig->setChecked( enable );
205 }
206}
207
208void QgsAuthSslConfigWidget::setSslCertificate( const QSslCertificate &cert, const QString &hostport )
209{
210 if ( mDisabled )
211 {
212 return;
213 }
214 if ( cert.isNull() )
215 {
216 return;
217 }
218 mCert = cert;
219
220 if ( !hostport.isEmpty() )
221 {
222 setSslHost( hostport );
223 }
224
225 const QString sha( QgsAuthCertUtils::shaHexForCert( cert ) );
226 const QgsAuthConfigSslServer config( QgsApplication::authManager()->sslCertCustomConfig( sha, hostport.isEmpty() ? sslHost() : hostport ) );
227
228 emit certFoundInAuthDatabase( !config.isNull() );
229
230 lblLoadedConfig->setVisible( true );
231 if ( !config.isNull() )
232 {
233 loadSslCustomConfig( config );
234 leCommonName->setStyleSheet( QgsAuthGuiUtils::greenTextStyleSheet() );
235 }
236 else
237 {
238 lblLoadedConfig->setText( configNotFoundText_() );
239 leCommonName->setText( QgsAuthCertUtils::resolvedCertName( mCert ) );
240 leCommonName->setStyleSheet( QgsAuthGuiUtils::orangeTextStyleSheet() );
241 }
242 validateHostPortText( leHost->text() );
243}
244
246{
247 if ( mDisabled )
248 {
249 return;
250 }
252 if ( config.isNull() )
253 {
254 QgsDebugError( u"Passed-in SSL custom config is null"_s );
255 return;
256 }
257
258 const QSslCertificate cert( config.sslCertificate() );
259 if ( cert.isNull() )
260 {
261 QgsDebugError( u"SSL custom config's cert is null"_s );
262 return;
263 }
264
266 mCert = cert;
267 leCommonName->setText( QgsAuthCertUtils::resolvedCertName( cert ) );
268 leHost->setText( config.sslHostPort() );
270 setSslProtocol( config.sslProtocol() );
272
273 lblLoadedConfig->setVisible( true );
274 lblLoadedConfig->setText( configFoundText_() );
275}
276
278{
279 if ( mDisabled )
280 {
281 return;
282 }
283 if ( !QgsApplication::authManager()->storeSslCertCustomConfig( sslCustomConfig() ) )
284 {
285 QgsDebugError( u"SSL custom config FAILED to store in authentication storage"_s );
286 }
287}
288
290{
291 if ( mDisabled )
292 {
293 return;
294 }
295 mCert.clear();
296 mConnectionCAs.clear();
297 leCommonName->clear();
298 leCommonName->setStyleSheet( QString() );
299 leHost->clear();
300
301 lblLoadedConfig->setVisible( false );
302 lblLoadedConfig->clear();
306 enableSslCustomOptions( false );
307}
308
310{
311 if ( mDisabled )
312 {
313 return QSsl::UnknownProtocol;
314 }
315 return ( QSsl::SslProtocol ) mProtocolCmbBx->currentData().toInt();
316}
317
318void QgsAuthSslConfigWidget::setSslProtocol( QSsl::SslProtocol protocol )
319{
320 if ( mDisabled )
321 {
322 return;
323 }
324 const int indx( mProtocolCmbBx->findData( static_cast<int>( protocol ) ) );
325 mProtocolCmbBx->setCurrentIndex( indx );
326}
327
329{
330 if ( mDisabled )
331 {
332 return;
333 }
334 mProtocolCmbBx->setCurrentIndex( 0 );
335}
336
337void QgsAuthSslConfigWidget::appendSslIgnoreErrors( const QList<QSslError> &errors )
338{
339 if ( mDisabled )
340 {
341 return;
342 }
344
345 QList<QSslError::SslError> errenums;
346 const auto constErrors = errors;
347 for ( const QSslError &err : constErrors )
348 {
349 errenums << err.error();
350 }
351
352 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
353 {
354 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
355 if ( errenums.contains( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() ) )
356 {
357 item->setCheckState( 0, Qt::Checked );
358 }
359 }
360}
361
362void QgsAuthSslConfigWidget::setSslIgnoreErrorEnums( const QList<QSslError::SslError> &errorenums )
363{
364 if ( mDisabled )
365 {
366 return;
367 }
368 QList<QSslError> errors;
369 const auto constErrorenums = errorenums;
370 for ( const QSslError::SslError errorenum : constErrorenums )
371 {
372 errors << QSslError( errorenum );
373 }
374 setSslIgnoreErrors( errors );
375}
376
377void QgsAuthSslConfigWidget::setSslIgnoreErrors( const QList<QSslError> &errors )
378{
379 if ( mDisabled )
380 {
381 return;
382 }
383 if ( errors.isEmpty() )
384 {
385 return;
386 }
387
389
390 QList<QSslError::SslError> errenums;
391 const auto constErrors = errors;
392 for ( const QSslError &err : constErrors )
393 {
394 errenums << err.error();
395 }
396
397 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
398 {
399 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
400 const bool enable( errenums.contains( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() ) );
401 item->setCheckState( 0, enable ? Qt::Checked : Qt::Unchecked );
402 }
403}
404
406{
407 if ( mDisabled )
408 {
409 return;
410 }
411 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
412 {
413 mIgnoreErrorsItem->child( i )->setCheckState( 0, Qt::Unchecked );
414 }
415}
416
417const QList<QSslError::SslError> QgsAuthSslConfigWidget::sslIgnoreErrorEnums()
418{
419 QList<QSslError::SslError> errs;
420 if ( mDisabled )
421 {
422 return errs;
423 }
424 for ( int i = 0; i < mIgnoreErrorsItem->childCount(); i++ )
425 {
426 QTreeWidgetItem *item( mIgnoreErrorsItem->child( i ) );
427 if ( item->checkState( 0 ) == Qt::Checked )
428 {
429 errs.append( ( QSslError::SslError ) item->data( 0, Qt::UserRole ).toInt() );
430 }
431 }
432 return errs;
433}
434
436{
437 if ( mDisabled )
438 {
439 return QSslSocket::AutoVerifyPeer;
440 }
441 return ( QSslSocket::PeerVerifyMode ) mVerifyPeerCmbBx->currentData().toInt();
442}
443
445{
446 if ( mDisabled )
447 {
448 return 0;
449 }
450 return mVerifyDepthSpnBx->value();
451}
452
453void QgsAuthSslConfigWidget::setSslPeerVerify( QSslSocket::PeerVerifyMode mode, int modedepth )
454{
455 if ( mDisabled )
456 {
457 return;
458 }
460
461 const int indx( mVerifyPeerCmbBx->findData( static_cast<int>( mode ) ) );
462 mVerifyPeerCmbBx->setCurrentIndex( indx );
463
464 mVerifyDepthSpnBx->setValue( modedepth );
465}
466
468{
469 if ( mDisabled )
470 {
471 return;
472 }
473 mVerifyPeerCmbBx->setCurrentIndex( 0 );
474 mVerifyDepthSpnBx->setValue( 0 );
475}
476
478{
479 if ( mDisabled )
480 {
481 return false;
482 }
483 const bool cansave = ( isEnabled() && ( grpbxSslConfig->isCheckable() ? grpbxSslConfig->isChecked() : true ) && validateHostPort( leHost->text() ) );
484 if ( mCanSave != cansave )
485 {
486 mCanSave = cansave;
487 emit readyToSaveChanged( cansave );
488 }
489 return cansave;
490}
491
492void QgsAuthSslConfigWidget::setSslHost( const QString &host )
493{
494 if ( mDisabled )
495 {
496 return;
497 }
498 leHost->setText( host );
499}
500
501bool QgsAuthSslConfigWidget::validateHostPort( const QString &txt )
502{
503 const QString hostport( txt );
504 if ( hostport.isEmpty() )
505 {
506 return false;
507 }
508
509 // TODO: add QRegex checks against valid IP and domain.tld input
510 // i.e., currently accepts unlikely (though maybe valid) host:port combo, like 'a:1'
511 const QString urlbase( u"https://%1"_s.arg( hostport ) );
512 const QUrl url( urlbase );
513 return ( !url.host().isEmpty() && QString::number( url.port() ).size() > 0 && u"https://%1:%2"_s.arg( url.host() ).arg( url.port() ) == urlbase );
514}
515
517{
518 if ( mDisabled )
519 {
520 return;
521 }
522 const bool valid = validateHostPort( txt );
524 emit hostPortValidityChanged( valid );
525}
526
528{
529 if ( mDisabled )
530 {
531 return;
532 }
533 grpbxSslConfig->setCheckable( checkable );
534 if ( !checkable )
535 {
536 grpbxSslConfig->setEnabled( true );
537 }
538}
539
540void QgsAuthSslConfigWidget::btnCertInfo_clicked()
541{
542 if ( mCert.isNull() )
543 {
544 return;
545 }
546
547 QgsAuthCertInfoDialog *dlg = new QgsAuthCertInfoDialog( mCert, false, this, mConnectionCAs );
548 dlg->setWindowModality( Qt::WindowModal );
549 dlg->resize( 675, 500 );
550 dlg->exec();
551 dlg->deleteLater();
552}
553
554
556
557QgsAuthSslConfigDialog::QgsAuthSslConfigDialog( QWidget *parent, const QSslCertificate &cert, const QString &hostport )
558 : QDialog( parent )
559
560{
561 setWindowTitle( tr( "Custom Certificate Configuration" ) );
562 QVBoxLayout *layout = new QVBoxLayout( this );
563 layout->setContentsMargins( 6, 6, 6, 6 );
564
565 mSslConfigWdgt = new QgsAuthSslConfigWidget( this, cert, hostport );
566 connect( mSslConfigWdgt, &QgsAuthSslConfigWidget::readyToSaveChanged, this, &QgsAuthSslConfigDialog::checkCanSave );
567 layout->addWidget( mSslConfigWdgt );
568
569 QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Close | QDialogButtonBox::Save, Qt::Horizontal, this );
570
571 buttonBox->button( QDialogButtonBox::Close )->setDefault( true );
572 mSaveButton = buttonBox->button( QDialogButtonBox::Save );
573 connect( buttonBox, &QDialogButtonBox::rejected, this, &QWidget::close );
574 connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsAuthSslConfigDialog::accept );
575 layout->addWidget( buttonBox );
576
577 setLayout( layout );
578 mSaveButton->setEnabled( mSslConfigWdgt->readyToSave() );
579}
580
582{
583 mSslConfigWdgt->saveSslCertConfig();
584 QDialog::accept();
585}
586
587void QgsAuthSslConfigDialog::checkCanSave( bool cansave )
588{
589 mSaveButton->setEnabled( cansave );
590}
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