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