QGIS API Documentation 3.29.0-Master (8c80f25a4f)
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"
26#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( 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 ( !QgsVariantUtils::isNull( sortbyval ) )
71 btnGroupByOrg->setChecked( sortbyval.toBool() );
72
73 populateIdentitiesView();
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
86void 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
107static void removeChildren_( QTreeWidgetItem *item )
108{
109 const auto constTakeChildren = item->takeChildren();
110 for ( QTreeWidgetItem *child : constTakeChildren )
111 {
112 delete child;
113 }
114}
115
116void QgsAuthIdentitiesEditor::populateIdentitiesView()
117{
118 removeChildren_( mRootCertIdentItem );
119
120 populateIdentitiesSection( mRootCertIdentItem,
121 QgsApplication::authManager()->certIdentities(),
122 QgsAuthIdentitiesEditor::CertIdentity );
123}
124
125void QgsAuthIdentitiesEditor::refreshIdentitiesView()
126{
127 populateIdentitiesView();
128}
129
130void 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
143void 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
182void 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
222void 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
244void QgsAuthIdentitiesEditor::selectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
245{
246 Q_UNUSED( selected )
247 Q_UNUSED( deselected )
248 checkSelection();
249}
250
251void 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
272void 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
295void 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
317void 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
364void QgsAuthIdentitiesEditor::btnInfoIdentity_clicked()
365{
366 if ( treeIdentities->selectionModel()->selection().length() > 0 )
367 {
368 QTreeWidgetItem *item( treeIdentities->currentItem() );
369 handleDoubleClick( item, 0 );
370 }
371}
372
373void 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
384void 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
399QgsMessageBar *QgsAuthIdentitiesEditor::messageBar()
400{
401 return msgBar;
402}
403
404int 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:115
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.
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.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38