QGIS API Documentation  3.21.0-Master (5b68dc587e)
qgsauthidentitieseditor.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsauthidentitieseditor.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_qgsauthidentitieseditor.h"
19 
20 #include <QMenu>
21 #include <QMessageBox>
22 
23 #include "qgssettings.h"
24 #include "qgsapplication.h"
25 #include "qgsauthcertificateinfo.h"
26 #include "qgsauthcertutils.h"
28 #include "qgsauthmanager.h"
29 #include "qgsauthguiutils.h"
30 #include "qgslogger.h"
31 
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( btnAddIdentity, &QToolButton::clicked, this, &QgsAuthIdentitiesEditor::btnAddIdentity_clicked );
48  connect( btnRemoveIdentity, &QToolButton::clicked, this, &QgsAuthIdentitiesEditor::btnRemoveIdentity_clicked );
49  connect( btnInfoIdentity, &QToolButton::clicked, this, &QgsAuthIdentitiesEditor::btnInfoIdentity_clicked );
50  connect( btnGroupByOrg, &QToolButton::toggled, this, &QgsAuthIdentitiesEditor::btnGroupByOrg_toggled );
51 
53  this, &QgsAuthIdentitiesEditor::authMessageOut );
54 
56  this, &QgsAuthIdentitiesEditor::refreshIdentitiesView );
57 
58  setupIdentitiesTree();
59 
60  connect( treeIdentities->selectionModel(), &QItemSelectionModel::selectionChanged,
61  this, &QgsAuthIdentitiesEditor::selectionChanged );
62 
63  connect( treeIdentities, &QTreeWidget::itemDoubleClicked,
64  this, &QgsAuthIdentitiesEditor::handleDoubleClick );
65 
66  connect( btnViewRefresh, &QAbstractButton::clicked, this, &QgsAuthIdentitiesEditor::refreshIdentitiesView );
67 
68  btnGroupByOrg->setChecked( false );
69  const QVariant sortbyval = QgsApplication::authManager()->authSetting( QStringLiteral( "identitiessortby" ), QVariant( false ) );
70  if ( !sortbyval.isNull() )
71  btnGroupByOrg->setChecked( sortbyval.toBool() );
72 
73  populateIdentitiesView();
74  checkSelection();
75  }
76 }
77 
78 static 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 void QgsAuthIdentitiesEditor::setupIdentitiesTree()
87 {
88  treeIdentities->setColumnCount( 3 );
89  treeIdentities->setHeaderLabels(
90  QStringList() << tr( "Common Name" )
91  << tr( "Serial #" )
92  << tr( "Expiry Date" ) );
93  treeIdentities->setColumnWidth( 0, 300 );
94  treeIdentities->setColumnWidth( 1, 75 );
95 
96  // add root sections
97  mRootCertIdentItem = new QTreeWidgetItem(
98  treeIdentities,
99  QStringList( tr( "Certificate Bundles" ) ),
100  static_cast<int>( QgsAuthIdentitiesEditor::Section ) );
101  setItemBold_( mRootCertIdentItem );
102  mRootCertIdentItem->setFlags( Qt::ItemIsEnabled );
103  mRootCertIdentItem->setExpanded( true );
104  treeIdentities->insertTopLevelItem( 0, mRootCertIdentItem );
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 QgsAuthIdentitiesEditor::populateIdentitiesView()
117 {
118  removeChildren_( mRootCertIdentItem );
119 
120  populateIdentitiesSection( mRootCertIdentItem,
121  QgsApplication::authManager()->certIdentities(),
122  QgsAuthIdentitiesEditor::CertIdentity );
123 }
124 
125 void QgsAuthIdentitiesEditor::refreshIdentitiesView()
126 {
127  populateIdentitiesView();
128 }
129 
130 void QgsAuthIdentitiesEditor::populateIdentitiesSection( QTreeWidgetItem *item, const QList<QSslCertificate> &certs,
131  QgsAuthIdentitiesEditor::IdentityType identype )
132 {
133  if ( btnGroupByOrg->isChecked() )
134  {
135  appendIdentitiesToGroup( certs, identype, item );
136  }
137  else
138  {
139  appendIdentitiesToItem( certs, identype, item );
140  }
141 }
142 
143 void QgsAuthIdentitiesEditor::appendIdentitiesToGroup( const QList<QSslCertificate> &certs,
144  QgsAuthIdentitiesEditor::IdentityType identype,
145  QTreeWidgetItem *parent )
146 {
147  if ( certs.empty() )
148  return;
149 
150  if ( !parent )
151  {
152  parent = treeIdentities->currentItem();
153  }
154 
155  // TODO: find all organizational name, sort and make subsections
156  const QMap< QString, QList<QSslCertificate> > orgcerts(
158 
159  QMap< QString, QList<QSslCertificate> >::const_iterator it = orgcerts.constBegin();
160  for ( ; it != orgcerts.constEnd(); ++it )
161  {
162  QTreeWidgetItem *grpitem( new QTreeWidgetItem( parent,
163  QStringList() << it.key(),
164  static_cast<int>( QgsAuthIdentitiesEditor::OrgName ) ) );
165  grpitem->setFirstColumnSpanned( true );
166  grpitem->setFlags( Qt::ItemIsEnabled );
167  grpitem->setExpanded( true );
168 
169  QBrush orgb( grpitem->foreground( 0 ) );
170  orgb.setColor( QColor::fromRgb( 90, 90, 90 ) );
171  grpitem->setForeground( 0, orgb );
172  QFont grpf( grpitem->font( 0 ) );
173  grpf.setItalic( true );
174  grpitem->setFont( 0, grpf );
175 
176  appendIdentitiesToItem( it.value(), identype, grpitem );
177  }
178 
179  parent->sortChildren( 0, Qt::AscendingOrder );
180 }
181 
182 void QgsAuthIdentitiesEditor::appendIdentitiesToItem( const QList<QSslCertificate> &certs,
183  QgsAuthIdentitiesEditor::IdentityType identype,
184  QTreeWidgetItem *parent )
185 {
186  if ( certs.empty() )
187  return;
188 
189  if ( !parent )
190  {
191  parent = treeIdentities->currentItem();
192  }
193 
194  const QBrush redb( QgsAuthGuiUtils::redColor() );
195 
196  // Columns: Common Name, Serial #, Expiry Date
197  const auto constCerts = certs;
198  for ( const QSslCertificate &cert : constCerts )
199  {
200  const QString id( QgsAuthCertUtils::shaHexForCert( cert ) );
201 
202  QStringList coltxts;
203  coltxts << QgsAuthCertUtils::resolvedCertName( cert );
204  coltxts << QString( cert.serialNumber() );
205  coltxts << cert.expiryDate().toString();
206 
207  QTreeWidgetItem *item( new QTreeWidgetItem( parent, coltxts, static_cast<int>( identype ) ) );
208 
209  item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificate.svg" ) ) );
210  if ( !QgsAuthCertUtils::certIsViable( cert ) )
211  {
212  item->setForeground( 2, redb );
213  item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificateUntrusted.svg" ) ) );
214  }
215 
216  item->setData( 0, Qt::UserRole, id );
217  }
218 
219  parent->sortChildren( 0, Qt::AscendingOrder );
220 }
221 
222 void QgsAuthIdentitiesEditor::showCertInfo( QTreeWidgetItem *item )
223 {
224  if ( !item )
225  return;
226 
227  const QString digest( item->data( 0, Qt::UserRole ).toString() );
228 
229  if ( !QgsApplication::authManager()->existsCertIdentity( digest ) )
230  {
231  QgsDebugMsg( QStringLiteral( "Certificate identity does not exist in database" ) );
232  return;
233  }
234 
235  const QSslCertificate cert( QgsApplication::authManager()->certIdentity( digest ) );
236 
237  QgsAuthCertInfoDialog *dlg = new QgsAuthCertInfoDialog( cert, false, this );
238  dlg->setWindowModality( Qt::WindowModal );
239  dlg->resize( 675, 500 );
240  dlg->exec();
241  dlg->deleteLater();
242 }
243 
244 void QgsAuthIdentitiesEditor::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
245 {
246  Q_UNUSED( selected )
247  Q_UNUSED( deselected )
248  checkSelection();
249 }
250 
251 void QgsAuthIdentitiesEditor::checkSelection()
252 {
253  bool iscert = false;
254  if ( treeIdentities->selectionModel()->selection().length() > 0 )
255  {
256  QTreeWidgetItem *item( treeIdentities->currentItem() );
257 
258  switch ( ( QgsAuthIdentitiesEditor::IdentityType )item->type() )
259  {
260  case QgsAuthIdentitiesEditor::CertIdentity:
261  iscert = true;
262  break;
263  default:
264  break;
265  }
266  }
267 
268  btnRemoveIdentity->setEnabled( iscert );
269  btnInfoIdentity->setEnabled( iscert );
270 }
271 
272 void QgsAuthIdentitiesEditor::handleDoubleClick( QTreeWidgetItem *item, int col )
273 {
274  Q_UNUSED( col )
275  bool iscert = true;
276 
277  switch ( ( QgsAuthIdentitiesEditor::IdentityType )item->type() )
278  {
279  case QgsAuthIdentitiesEditor::Section:
280  iscert = false;
281  break;
282  case QgsAuthIdentitiesEditor::OrgName:
283  iscert = false;
284  break;
285  default:
286  break;
287  }
288 
289  if ( iscert )
290  {
291  showCertInfo( item );
292  }
293 }
294 
295 void QgsAuthIdentitiesEditor::btnAddIdentity_clicked()
296 {
298  dlg->setWindowModality( Qt::WindowModal );
299  dlg->resize( 400, dlg->height() );
300  if ( dlg->exec() )
301  {
303  {
304  const QPair<QSslCertificate, QSslKey> &bundle( dlg->certBundleToImport() );
305  if ( !QgsApplication::authManager()->storeCertIdentity( bundle.first, bundle.second ) )
306  {
307  messageBar()->pushMessage( tr( "ERROR storing identity bundle in authentication database." ),
308  Qgis::MessageLevel::Critical );
309  }
310  populateIdentitiesView();
311  mRootCertIdentItem->setExpanded( true );
312  }
313  }
314  dlg->deleteLater();
315 }
316 
317 void QgsAuthIdentitiesEditor::btnRemoveIdentity_clicked()
318 {
319  QTreeWidgetItem *item( treeIdentities->currentItem() );
320 
321  if ( !item )
322  {
323  QgsDebugMsg( QStringLiteral( "Current tree widget item not set" ) );
324  return;
325  }
326 
327  const QString digest( item->data( 0, Qt::UserRole ).toString() );
328 
329  if ( digest.isEmpty() )
330  {
331  messageBar()->pushMessage( tr( "Certificate id missing." ),
332  Qgis::MessageLevel::Warning );
333  return;
334  }
335 
336  if ( !QgsApplication::authManager()->existsCertIdentity( digest ) )
337  {
338  QgsDebugMsg( QStringLiteral( "Certificate identity does not exist in database" ) );
339  return;
340  }
341 
342  if ( QMessageBox::warning(
343  this, tr( "Remove Certificate Identity" ),
344  tr( "Are you sure you want to remove the selected "
345  "certificate identity from the database?\n\n"
346  "Operation can NOT be undone!" ),
347  QMessageBox::Ok | QMessageBox::Cancel,
348  QMessageBox::Cancel ) == QMessageBox::Cancel )
349  {
350  return;
351  }
352 
353  if ( !QgsApplication::authManager()->removeCertIdentity( digest ) )
354  {
355  messageBar()->pushMessage( tr( "ERROR removing cert identity from authentication database for id %1:" ).arg( digest ),
356  Qgis::MessageLevel::Critical );
357  return;
358  }
359 
360  item->parent()->removeChild( item );
361  delete item;
362 }
363 
364 void QgsAuthIdentitiesEditor::btnInfoIdentity_clicked()
365 {
366  if ( treeIdentities->selectionModel()->selection().length() > 0 )
367  {
368  QTreeWidgetItem *item( treeIdentities->currentItem() );
369  handleDoubleClick( item, 0 );
370  }
371 }
372 
373 void QgsAuthIdentitiesEditor::btnGroupByOrg_toggled( bool checked )
374 {
375  if ( !QgsApplication::authManager()->storeAuthSetting( QStringLiteral( "identitiessortby" ), QVariant( checked ) ) )
376  {
377  authMessageOut( QObject::tr( "Could not store sort by preference." ),
378  QObject::tr( "Authentication Identities" ),
380  }
381  populateIdentitiesView();
382 }
383 
384 void QgsAuthIdentitiesEditor::authMessageOut( const QString &message, const QString &authtag, QgsAuthManager::MessageLevel level )
385 {
386  const int levelint = static_cast<int>( level );
387  messageBar()->pushMessage( authtag, message, ( Qgis::MessageLevel )levelint, 7 );
388 }
389 
391 {
392  if ( !mDisabled )
393  {
394  treeIdentities->setFocus();
395  }
396  QWidget::showEvent( e );
397 }
398 
399 QgsMessageBar *QgsAuthIdentitiesEditor::messageBar()
400 {
401  return msgBar;
402 }
403 
404 int QgsAuthIdentitiesEditor::messageTimeout()
405 {
406  const QgsSettings settings;
407  return settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
408 }
MessageLevel
Level for messages This will be used both for message log and message bar in application.
Definition: qgis.h:106
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.
Dialog wrapper for widget displaying detailed info on a certificate and its hierarchical trust chain.
static QString resolvedCertName(const QSslCertificate &cert, bool issuer=false)
Gets the general name via RFC 5280 resolution.
static QMap< QString, QList< QSslCertificate > > certsGroupedByOrg(const QList< QSslCertificate > &certs)
Map certificates to their oraganization.
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 QColor redColor()
Red color representing invalid, untrusted, etc. certificate.
void showEvent(QShowEvent *e) override
Overridden show event of base widget.
QgsAuthIdentitiesEditor(QWidget *parent=nullptr)
Widget for editing authentication configurations directly in database.
Widget for importing an identity certificate/key bundle into the authentication database.
QgsAuthImportIdentityDialog::IdentityType identityType()
Gets identity type.
const QPair< QSslCertificate, QSslKey > certBundleToImport()
Gets certificate/key bundle to be imported.
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 ))
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38