QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsauthserverseditor.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsauthserverseditor.cpp
3 ---------------------
4 begin : April 26, 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#include "ui_qgsauthserverseditor.h"
20
21#include <QMenu>
22#include <QMessageBox>
23
24#include "qgssettings.h"
25#include "qgsapplication.h"
27#include "qgsauthcertutils.h"
28#include "qgsauthmanager.h"
29#include "qgsauthguiutils.h"
30#include "qgslogger.h"
31#include "qgsvariantutils.h"
32
34 : QWidget( parent )
35{
36 if ( QgsApplication::authManager()->isDisabled() )
37 {
38 mDisabled = true;
39 mAuthNotifyLayout = new QVBoxLayout;
40 this->setLayout( mAuthNotifyLayout );
41 mAuthNotify = new QLabel( QgsApplication::authManager()->disabledMessage(), this );
42 mAuthNotifyLayout->addWidget( mAuthNotify );
43 }
44 else
45 {
46 setupUi( this );
47 connect( btnAddServer, &QToolButton::clicked, this, &QgsAuthServersEditor::btnAddServer_clicked );
48 connect( btnRemoveServer, &QToolButton::clicked, this, &QgsAuthServersEditor::btnRemoveServer_clicked );
49 connect( btnEditServer, &QToolButton::clicked, this, &QgsAuthServersEditor::btnEditServer_clicked );
50 connect( btnGroupByOrg, &QToolButton::toggled, this, &QgsAuthServersEditor::btnGroupByOrg_toggled );
51
53 this, &QgsAuthServersEditor::authMessageOut );
54
56 this, &QgsAuthServersEditor::refreshSslConfigsView );
57
58 setupSslConfigsTree();
59
60 connect( treeServerConfigs->selectionModel(), &QItemSelectionModel::selectionChanged,
61 this, &QgsAuthServersEditor::selectionChanged );
62
63 connect( treeServerConfigs, &QTreeWidget::itemDoubleClicked,
64 this, &QgsAuthServersEditor::handleDoubleClick );
65
66 connect( btnViewRefresh, &QAbstractButton::clicked, this, &QgsAuthServersEditor::refreshSslConfigsView );
67
68 btnGroupByOrg->setChecked( false );
69 const QVariant sortbyval = QgsApplication::authManager()->authSetting( QStringLiteral( "serverssortby" ), QVariant( false ) );
70 if ( !QgsVariantUtils::isNull( sortbyval ) )
71 btnGroupByOrg->setChecked( sortbyval.toBool() );
72
73 populateSslConfigsView();
74 checkSelection();
75 }
76}
77
78static void setItemBold_( QTreeWidgetItem *item )
79{
80 item->setFirstColumnSpanned( true );
81 QFont secf( item->font( 0 ) );
82 secf.setBold( true );
83 item->setFont( 0, secf );
84}
85
86
87void QgsAuthServersEditor::setupSslConfigsTree()
88{
89 treeServerConfigs->setColumnCount( 3 );
90 treeServerConfigs->setHeaderLabels(
91 QStringList() << tr( "Common Name" )
92 << tr( "Host" )
93 << tr( "Expiry Date" ) );
94 treeServerConfigs->setColumnWidth( 0, 275 );
95 treeServerConfigs->setColumnWidth( 1, 200 );
96
97 // add root sections
98 mRootSslConfigItem = new QTreeWidgetItem(
99 treeServerConfigs,
100 QStringList( tr( "SSL Server Configurations" ) ),
101 static_cast<int>( QgsAuthServersEditor::Section ) );
102 setItemBold_( mRootSslConfigItem );
103 mRootSslConfigItem->setFlags( Qt::ItemIsEnabled );
104 mRootSslConfigItem->setExpanded( true );
105 treeServerConfigs->insertTopLevelItem( 0, mRootSslConfigItem );
106}
107
108static void removeChildren_( QTreeWidgetItem *item )
109{
110 const auto constTakeChildren = item->takeChildren();
111 for ( QTreeWidgetItem *child : constTakeChildren )
112 {
113 delete child;
114 }
115}
116
117void QgsAuthServersEditor::populateSslConfigsView()
118{
119 removeChildren_( mRootSslConfigItem );
120
121 populateSslConfigsSection( mRootSslConfigItem,
122 QgsApplication::authManager()->sslCertCustomConfigs(),
123 QgsAuthServersEditor::ServerConfig );
124}
125
126void QgsAuthServersEditor::refreshSslConfigsView()
127{
128 populateSslConfigsView();
129}
130
131void QgsAuthServersEditor::populateSslConfigsSection( QTreeWidgetItem *item,
132 const QList<QgsAuthConfigSslServer> &configs,
133 QgsAuthServersEditor::ConfigType conftype )
134{
135 if ( btnGroupByOrg->isChecked() )
136 {
137 appendSslConfigsToGroup( configs, conftype, item );
138 }
139 else
140 {
141 appendSslConfigsToItem( configs, conftype, item );
142 }
143}
144
145void QgsAuthServersEditor::appendSslConfigsToGroup( const QList<QgsAuthConfigSslServer> &configs,
146 QgsAuthServersEditor::ConfigType conftype,
147 QTreeWidgetItem *parent )
148{
149 if ( configs.empty() )
150 return;
151
152 if ( !parent )
153 {
154 parent = treeServerConfigs->currentItem();
155 }
156
157 // TODO: find all organizational name, sort and make subsections
158 const QMap< QString, QList<QgsAuthConfigSslServer> > orgconfigs(
160
161 QMap< QString, QList<QgsAuthConfigSslServer> >::const_iterator it = orgconfigs.constBegin();
162 for ( ; it != orgconfigs.constEnd(); ++it )
163 {
164 QTreeWidgetItem *grpitem( new QTreeWidgetItem( parent,
165 QStringList() << it.key(),
166 static_cast<int>( QgsAuthServersEditor::OrgName ) ) );
167 grpitem->setFirstColumnSpanned( true );
168 grpitem->setFlags( Qt::ItemIsEnabled );
169 grpitem->setExpanded( true );
170
171 QBrush orgb( grpitem->foreground( 0 ) );
172 orgb.setColor( QColor::fromRgb( 90, 90, 90 ) );
173 grpitem->setForeground( 0, orgb );
174 QFont grpf( grpitem->font( 0 ) );
175 grpf.setItalic( true );
176 grpitem->setFont( 0, grpf );
177
178 appendSslConfigsToItem( it.value(), conftype, grpitem );
179 }
180
181 parent->sortChildren( 0, Qt::AscendingOrder );
182}
183
184void QgsAuthServersEditor::appendSslConfigsToItem( const QList<QgsAuthConfigSslServer> &configs,
185 QgsAuthServersEditor::ConfigType conftype,
186 QTreeWidgetItem *parent )
187{
188 if ( configs.empty() )
189 return;
190
191 if ( !parent )
192 {
193 parent = treeServerConfigs->currentItem();
194 }
195
196 const QBrush redb( QgsAuthGuiUtils::redColor() );
197
198 // Columns: Common Name, Host, Expiry Date
199 const auto constConfigs = configs;
200 for ( const QgsAuthConfigSslServer &config : constConfigs )
201 {
202 const QSslCertificate cert( config.sslCertificate() );
203 const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
204
205 QStringList coltxts;
206 coltxts << QgsAuthCertUtils::resolvedCertName( cert );
207 coltxts << QString( config.sslHostPort() );
208 coltxts << cert.expiryDate().toString();
209
210 QTreeWidgetItem *item( new QTreeWidgetItem( parent, coltxts, static_cast<int>( conftype ) ) );
211
212 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificate.svg" ) ) );
213 if ( !QgsAuthCertUtils::certIsViable( cert ) )
214 {
215 item->setForeground( 2, redb );
216 item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
217 }
218
219 item->setData( 0, Qt::UserRole, id );
220 }
221
222 parent->sortChildren( 0, Qt::AscendingOrder );
223}
224
225void QgsAuthServersEditor::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
226{
227 Q_UNUSED( selected )
228 Q_UNUSED( deselected )
229 checkSelection();
230}
231
232void QgsAuthServersEditor::checkSelection()
233{
234 bool isconfig = false;
235 if ( treeServerConfigs->selectionModel()->selection().length() > 0 )
236 {
237 QTreeWidgetItem *item( treeServerConfigs->currentItem() );
238
239 switch ( ( QgsAuthServersEditor::ConfigType )item->type() )
240 {
241 case QgsAuthServersEditor::ServerConfig :
242 isconfig = true;
243 break;
244 default:
245 break;
246 }
247 }
248
249 btnRemoveServer->setEnabled( isconfig );
250 btnEditServer->setEnabled( isconfig );
251}
252
253void QgsAuthServersEditor::handleDoubleClick( QTreeWidgetItem *item, int col )
254{
255 Q_UNUSED( col )
256 bool isconfig = true;
257
258 switch ( ( QgsAuthServersEditor::ConfigType )item->type() )
259 {
260 case QgsAuthServersEditor::Section:
261 isconfig = false;
262 break;
263 case QgsAuthServersEditor::OrgName:
264 isconfig = false;
265 break;
266 default:
267 break;
268 }
269
270 if ( isconfig )
271 {
272 btnEditServer_clicked();
273 }
274}
275
276void QgsAuthServersEditor::btnAddServer_clicked()
277{
279 dlg->setWindowModality( Qt::WindowModal );
280 dlg->resize( 580, 512 );
281 if ( dlg->exec() )
282 {
283 refreshSslConfigsView();
284 }
285 dlg->deleteLater();
286}
287
288void QgsAuthServersEditor::btnRemoveServer_clicked()
289{
290 QTreeWidgetItem *item( treeServerConfigs->currentItem() );
291
292 if ( !item )
293 {
294 QgsDebugMsgLevel( QStringLiteral( "Current tree widget item not set" ), 2 );
295 return;
296 }
297
298 const QString digest( item->data( 0, Qt::UserRole ).toString() );
299 const QString hostport( item->text( 1 ) );
300
301 if ( digest.isEmpty() )
302 {
303 messageBar()->pushMessage( tr( "SSL custom config id missing" ),
304 Qgis::MessageLevel::Warning );
305 return;
306 }
307 if ( hostport.isEmpty() )
308 {
309 messageBar()->pushMessage( tr( "SSL custom config host:port missing" ),
310 Qgis::MessageLevel::Warning );
311 return;
312 }
313
314 if ( !QgsApplication::authManager()->existsSslCertCustomConfig( digest, hostport ) )
315 {
316 QgsDebugError( QStringLiteral( "SSL custom config does not exist in database for host:port, id %1:" )
317 .arg( hostport, digest ) );
318 return;
319 }
320
321 if ( QMessageBox::warning(
322 this, tr( "Remove SSL Custom Configuration" ),
323 tr( "Are you sure you want to remove the selected "
324 "SSL custom configuration from the database?\n\n"
325 "Operation can NOT be undone!" ),
326 QMessageBox::Ok | QMessageBox::Cancel,
327 QMessageBox::Cancel ) == QMessageBox::Cancel )
328 {
329 return;
330 }
331
332 if ( !QgsApplication::authManager()->removeSslCertCustomConfig( digest, hostport ) )
333 {
334 messageBar()->pushMessage( tr( "ERROR removing SSL custom config from authentication database for host:port, id %1:" )
335 .arg( hostport, digest ),
336 Qgis::MessageLevel::Critical );
337 return;
338 }
339
340 item->parent()->removeChild( item );
341 delete item;
342}
343
344void QgsAuthServersEditor::btnEditServer_clicked()
345{
346 QTreeWidgetItem *item( treeServerConfigs->currentItem() );
347
348 if ( !item )
349 {
350 QgsDebugMsgLevel( QStringLiteral( "Current tree widget item not set" ), 2 );
351 return;
352 }
353
354 const QString digest( item->data( 0, Qt::UserRole ).toString() );
355 const QString hostport( item->text( 1 ) );
356
357 if ( digest.isEmpty() )
358 {
359 messageBar()->pushMessage( tr( "SSL custom config id missing." ),
360 Qgis::MessageLevel::Warning );
361 return;
362 }
363 if ( hostport.isEmpty() )
364 {
365 messageBar()->pushMessage( tr( "SSL custom config host:port missing." ),
366 Qgis::MessageLevel::Warning );
367 return;
368 }
369
370 if ( !QgsApplication::authManager()->existsSslCertCustomConfig( digest, hostport ) )
371 {
372 QgsDebugError( QStringLiteral( "SSL custom config does not exist in database" ) );
373 return;
374 }
375
376 const QgsAuthConfigSslServer config( QgsApplication::authManager()->sslCertCustomConfig( digest, hostport ) );
377 const QSslCertificate cert( config.sslCertificate() );
378
379 QgsAuthSslConfigDialog *dlg = new QgsAuthSslConfigDialog( this, cert, hostport );
381 dlg->setWindowModality( Qt::WindowModal );
382 dlg->resize( 500, 500 );
383 if ( dlg->exec() )
384 {
385 refreshSslConfigsView();
386 }
387 dlg->deleteLater();
388}
389
390void QgsAuthServersEditor::btnGroupByOrg_toggled( bool checked )
391{
392 if ( !QgsApplication::authManager()->storeAuthSetting( QStringLiteral( "serverssortby" ), QVariant( checked ) ) )
393 {
394 authMessageOut( QObject::tr( "Could not store sort by preference." ),
395 QObject::tr( "Authentication SSL Configs" ),
397 }
398 populateSslConfigsView();
399}
400
401void QgsAuthServersEditor::authMessageOut( const QString &message, const QString &authtag, QgsAuthManager::MessageLevel level )
402{
403 const int levelint = static_cast<int>( level );
404 messageBar()->pushMessage( authtag, message, ( Qgis::MessageLevel )levelint, 7 );
405}
406
408{
409 if ( !mDisabled )
410 {
411 treeServerConfigs->setFocus();
412 }
413 QWidget::showEvent( e );
414}
415
416QgsMessageBar *QgsAuthServersEditor::messageBar()
417{
418 return msgBar;
419}
420
421int QgsAuthServersEditor::messageTimeout()
422{
423 const QgsSettings settings;
424 return settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
425}
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:99
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Gets the general name via RFC 5280 resolution.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Gets the sha1 hash for certificate.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QMap< QString, QList< QgsAuthConfigSslServer > > sslConfigsGroupedByOrg(const QList< QgsAuthConfigSslServer > &configs)
Map SSL custom configs' certificates to their oraganization.
Configuration container for SSL server connection exceptions or overrides.
static QColor redColor()
Red color representing invalid, untrusted, etc. certificate.
MessageLevel
Message log level (mirrors that of QgsMessageLog, so it can also output there)
void messageOut(const QString &message, const QString &tag=QgsAuthManager::AUTH_MAN_TAG, QgsAuthManager::MessageLevel level=QgsAuthManager::INFO) const
Custom logging signal to relay to console output and QgsMessageLog.
void authDatabaseChanged()
Emitted when the authentication db is significantly changed, e.g. large record removal,...
QVariant authSetting(const QString &key, const QVariant &defaultValue=QVariant(), bool decrypt=false)
authSetting get an authentication setting (retrieved as string and returned as QVariant( QString ))
QgsAuthServersEditor(QWidget *parent=nullptr)
Widget for editing authentication configurations directly in database.
void showEvent(QShowEvent *e) override
Dialog wrapper of widget for editing an SSL server configuration.
QgsAuthSslConfigWidget * sslCustomConfigWidget()
Access the embedded SSL server configuration widget.
void setConfigCheckable(bool checkable)
Sets whether the config group box is checkable.
Widget for importing an SSL server certificate exception into the authentication database.
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
void pushMessage(const QString &text, Qgis::MessageLevel level=Qgis::MessageLevel::Info, int duration=-1)
A convenience method for pushing a message with the specified text to the bar.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:64
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38