Skip to content

Commit

Permalink
Merge pull request #5380 from boundlessgeo/bd_2272_pki_parsed_ca
Browse files Browse the repository at this point in the history
[auth] Allow to optionally add CAs from PKI bundle to the trusted CAs for the configured connection
  • Loading branch information
elpaso committed Oct 18, 2017
2 parents df0d717 + b1c45d5 commit a7765ca
Show file tree
Hide file tree
Showing 20 changed files with 850 additions and 225 deletions.
45 changes: 45 additions & 0 deletions python/core/auth/qgsauthcertutils.sip
Expand Up @@ -87,12 +87,29 @@ Return list of concatenated certs from a PEM or DER formatted file
:rtype: list of QSslCertificate
%End

static QList<QSslCertificate> casFromFile( const QString &certspath );
%Docstring
Return list of concatenated CAs from a PEM or DER formatted file
:rtype: list of QSslCertificate
%End

static QSslCertificate certFromFile( const QString &certpath );
%Docstring
Return first cert from a PEM or DER formatted file
:rtype: QSslCertificate
%End

static QList<QSslCertificate> casMerge( const QList<QSslCertificate> &bundle1,
const QList<QSslCertificate> &bundle2 );
%Docstring
casMerge merges two certificate bundles in a single one removing duplicates, the certificates
from the ``bundle2`` are appended to ``bundle1`` if not already there
\param bundle1 first bundle
\param bundle2 second bundle
:return: a list of unique certificates
:rtype: list of QSslCertificate
%End

static QSslKey keyFromFile( const QString &keypath,
const QString &keypass = QString(),
QString *algtype = 0 );
Expand All @@ -110,6 +127,15 @@ Return list of concatenated certs from a PEM Base64 text block
:rtype: list of QSslCertificate
%End


static QList<QSslCertificate> casRemoveSelfSigned( const QList<QSslCertificate> &caList );
%Docstring
casRemoveSelfSigned remove self-signed CA certificates from ``caList``
\param caList list of CA certificates
:return: a list of non self-signed certificates
:rtype: list of QSslCertificate
%End

static QStringList certKeyBundleToPem( const QString &certpath,
const QString &keypath,
const QString &keypass = QString(),
Expand All @@ -136,6 +162,25 @@ Return list of concatenated certs from a PEM Base64 text block
:rtype: list of str
%End

static QList<QSslCertificate> pkcs12BundleCas( const QString &bundlepath,
const QString &bundlepass = QString() );
%Docstring
Return list of CA certificates (as QSslCertificate) for a PKCS#12 bundle
\param bundlepath File path to the PKCS bundle
\param bundlepass Passphrase for bundle
:return: list of certificate
:rtype: list of QSslCertificate
%End


static QByteArray certsToPemText( const QList<QSslCertificate> &certs );
%Docstring
certsToPemText dump a list of QSslCertificates to PEM text
\param certs list of certs
:return: a byte array of concatenated certificates as PEM text
:rtype: QByteArray
%End

static QString pemTextToTempFile( const QString &name, const QByteArray &pemtext );
%Docstring
Write a temporary file for a PEM text of cert/key/CAs bundle component
Expand Down
17 changes: 16 additions & 1 deletion python/core/auth/qgsauthconfig.sip
Expand Up @@ -302,12 +302,14 @@ class QgsPkiConfigBundle

QgsPkiConfigBundle( const QgsAuthMethodConfig &config,
const QSslCertificate &cert,
const QSslKey &certkey );
const QSslKey &certkey,
const QList<QSslCertificate> &cachain = QList<QSslCertificate>( ) );
%Docstring
Construct a bundle from existing PKI components and authentication method configuration
\param config Authentication method configuration
\param cert Certificate to store in bundle
\param certkey Private key to store in bundle
\param cachain list of CA certificates
%End

bool isValid();
Expand Down Expand Up @@ -346,6 +348,19 @@ Private key object
Set private key object
%End

QList<QSslCertificate> caChain() const;
%Docstring
caChain return the CA chain
:return: list of CA certificates
:rtype: list of QSslCertificate
%End

void setCaChain( const QList<QSslCertificate> &caChain );
%Docstring
setCaChain set the CA chain
\param caChain
%End

};


Expand Down
51 changes: 51 additions & 0 deletions src/auth/pkipaths/qgsauthpkipathsedit.cpp
Expand Up @@ -36,6 +36,11 @@ QgsAuthPkiPathsEdit::QgsAuthPkiPathsEdit( QWidget *parent )
connect( chkPkiPathsPassShow, &QCheckBox::stateChanged, this, &QgsAuthPkiPathsEdit::chkPkiPathsPassShow_stateChanged );
connect( btnPkiPathsCert, &QToolButton::clicked, this, &QgsAuthPkiPathsEdit::btnPkiPathsCert_clicked );
connect( btnPkiPathsKey, &QToolButton::clicked, this, &QgsAuthPkiPathsEdit::btnPkiPathsKey_clicked );
connect( cbAddCas, &QCheckBox::stateChanged, this, [ = ]( int state ) { cbAddRootCa->setEnabled( state == Qt::Checked ); } );
lblCas->hide();
twCas->hide();
cbAddCas->hide();
cbAddRootCa->hide();
}

bool QgsAuthPkiPathsEdit::validateConfig()
Expand Down Expand Up @@ -100,6 +105,12 @@ bool QgsAuthPkiPathsEdit::validateConfig()
tr( "%1 thru %2" ).arg( startdate.toString(), enddate.toString() ),
( certvalid ? Valid : Invalid ) );

bool showCas( certvalid && populateCas() );
lblCas->setVisible( showCas );
twCas->setVisible( showCas );
cbAddCas->setVisible( showCas );
cbAddRootCa->setVisible( showCas );

return validityChange( certvalid );
}

Expand All @@ -109,6 +120,8 @@ QgsStringMap QgsAuthPkiPathsEdit::configMap() const
config.insert( QStringLiteral( "certpath" ), lePkiPathsCert->text() );
config.insert( QStringLiteral( "keypath" ), lePkiPathsKey->text() );
config.insert( QStringLiteral( "keypass" ), lePkiPathsKeyPass->text() );
config.insert( QStringLiteral( "addcas" ), cbAddCas->isChecked() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
config.insert( QStringLiteral( "addrootca" ), cbAddRootCa->isChecked() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );

return config;
}
Expand All @@ -121,6 +134,8 @@ void QgsAuthPkiPathsEdit::loadConfig( const QgsStringMap &configmap )
lePkiPathsCert->setText( configmap.value( QStringLiteral( "certpath" ) ) );
lePkiPathsKey->setText( configmap.value( QStringLiteral( "keypath" ) ) );
lePkiPathsKeyPass->setText( configmap.value( QStringLiteral( "keypass" ) ) );
cbAddCas->setChecked( configmap.value( QStringLiteral( "addcas" ), QStringLiteral( "false " ) ) == QStringLiteral( "true" ) );
cbAddRootCa->setChecked( configmap.value( QStringLiteral( "addrootca" ), QStringLiteral( "false " ) ) == QStringLiteral( "true" ) );

validateConfig();
}
Expand Down Expand Up @@ -224,3 +239,39 @@ bool QgsAuthPkiPathsEdit::validityChange( bool curvalid )
}
return curvalid;
}


bool QgsAuthPkiPathsEdit::populateCas()
{
twCas->clear();
const QList<QSslCertificate> cas( QgsAuthCertUtils::casFromFile( lePkiPathsCert->text() ) );
if ( cas.isEmpty() )
{
return false;
}

QTreeWidgetItem *prevItem( nullptr );
QList<QSslCertificate>::const_iterator it( cas.constEnd() );
while ( it != cas.constBegin() )
{
--it;
const QSslCertificate cert = static_cast<QSslCertificate>( *it );
QTreeWidgetItem *item;

if ( prevItem && cert.issuerInfo( QSslCertificate::SubjectInfo::CommonName ).contains( prevItem->text( 0 ) ) )
{
item = new QTreeWidgetItem( QStringList( cert.subjectInfo( QSslCertificate::SubjectInfo::CommonName ) ) );
prevItem->addChild( item );
}
else
{
item = new QTreeWidgetItem( twCas, QStringList( cert.subjectInfo( QSslCertificate::SubjectInfo::CommonName ) ) );
}
item->setIcon( 0, QgsApplication::getThemeIcon( QStringLiteral( "/mIconCertificate.svg" ) ) );
item->setToolTip( 0, tr( "<ul><li>Serial #: %1</li><li>Expiry date: %2</li></ul>" ).arg( cert.serialNumber( ), cert.expiryDate().toString( Qt::TextDate ) ) );
prevItem = item;
}
twCas->expandAll();

return true;
}
2 changes: 1 addition & 1 deletion src/auth/pkipaths/qgsauthpkipathsedit.h
Expand Up @@ -65,7 +65,7 @@ class QgsAuthPkiPathsEdit : public QgsAuthMethodEdit, private Ui::QgsAuthPkiPath

private:
bool validityChange( bool curvalid );

bool populateCas();
QgsStringMap mConfigMap;
bool mValid = 0;
};
Expand Down

0 comments on commit a7765ca

Please sign in to comment.