QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgsauthconfig.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsauthconfig.cpp
3  ---------------------
4  begin : October 5, 2014
5  copyright : (C) 2014 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 "qgsauthconfig.h"
18 
19 #include <QtCrypto>
20 
21 #include <QFile>
22 #include <QObject>
23 #include <QCryptographicHash>
24 #include <QUrl>
25 
26 #include "qgsauthcertutils.h"
27 
28 
30 // QgsAuthMethodConfig
32 
33 const QString QgsAuthMethodConfig::CONFIG_SEP = QStringLiteral( "|||" );
34 const QString QgsAuthMethodConfig::CONFIG_KEY_SEP = QStringLiteral( ":::" );
35 const QString QgsAuthMethodConfig::CONFIG_LIST_SEP = QStringLiteral( "```" );
36 
37 const int QgsAuthMethodConfig::CONFIG_VERSION = 1;
38 
39 // get uniqueConfigId only on save
40 QgsAuthMethodConfig::QgsAuthMethodConfig( const QString &method, int version )
41  : mId( QString() )
42  , mName( QString() )
43  , mUri( QString() )
44  , mMethod( method )
45  , mVersion( version )
46  , mConfigMap( QgsStringMap() )
47 {
48 }
49 
51 {
52  return ( other.id() == id()
53  && other.name() == name()
54  && other.uri() == uri()
55  && other.method() == method()
56  && other.version() == version()
57  && other.configMap() == configMap() );
58 }
59 
61 {
62  return !( *this == other );
63 }
64 
65 bool QgsAuthMethodConfig::isValid( bool validateid ) const
66 {
67  bool idvalid = validateid ? !mId.isEmpty() : true;
68 
69  return (
70  idvalid
71  && !mName.isEmpty()
72  && !mMethod.isEmpty()
73  );
74 }
75 
76 const QString QgsAuthMethodConfig::configString() const
77 {
78  QStringList confstrs;
79  QgsStringMap::const_iterator i = mConfigMap.constBegin();
80  while ( i != mConfigMap.constEnd() )
81  {
82  confstrs << i.key() + CONFIG_KEY_SEP + i.value();
83  ++i;
84  }
85  return confstrs.join( CONFIG_SEP );
86 }
87 
88 void QgsAuthMethodConfig::loadConfigString( const QString &configstr )
89 {
91  if ( configstr.isEmpty() )
92  {
93  return;
94  }
95 
96  const QStringList confs( configstr.split( CONFIG_SEP ) );
97 
98  for ( const auto &conf : confs )
99  {
100  if ( conf.contains( CONFIG_KEY_SEP ) )
101  {
102  QStringList keyval( conf.split( CONFIG_KEY_SEP ) );
103  setConfig( keyval.at( 0 ), keyval.at( 1 ) );
104  }
105  }
106 
107  if ( configMap().empty() )
108  {
109  setConfig( QStringLiteral( "oldconfigstyle" ), configstr );
110  }
111 }
112 
113 void QgsAuthMethodConfig::setConfig( const QString &key, const QString &value )
114 {
115  mConfigMap.insert( key, value );
116 }
117 
118 void QgsAuthMethodConfig::setConfigList( const QString &key, const QStringList &value )
119 {
120  setConfig( key, value.join( CONFIG_LIST_SEP ) );
121 }
122 
123 int QgsAuthMethodConfig::removeConfig( const QString &key )
124 {
125  return mConfigMap.remove( key );
126 }
127 
128 QString QgsAuthMethodConfig::config( const QString &key, const QString &defaultvalue ) const
129 {
130  return mConfigMap.value( key, defaultvalue );
131 }
132 
133 QStringList QgsAuthMethodConfig::configList( const QString &key ) const
134 {
135  return config( key ).split( CONFIG_LIST_SEP );
136 }
137 
138 bool QgsAuthMethodConfig::hasConfig( const QString &key ) const
139 {
140  return mConfigMap.contains( key );
141 }
142 
143 bool QgsAuthMethodConfig::uriToResource( const QString &accessurl, QString *resource, bool withpath )
144 {
145  QString res = QString();
146  if ( !accessurl.isEmpty() )
147  {
148  QUrl url( accessurl );
149  if ( url.isValid() )
150  {
151  res = QStringLiteral( "%1://%2:%3%4" ).arg( url.scheme(), url.host() )
152  .arg( url.port() ).arg( withpath ? url.path() : QString() );
153  }
154  }
155  *resource = res;
156  return ( !res.isEmpty() );
157 }
158 
159 
160 #ifndef QT_NO_SSL
161 
163 // QgsPkiBundle
165 
166 QgsPkiBundle::QgsPkiBundle( const QSslCertificate &clientCert,
167  const QSslKey &clientKey,
168  const QList<QSslCertificate> &caChain )
169  : mCert( QSslCertificate() )
170  , mCertKey( QSslKey() )
171  , mCaChain( caChain )
172 {
173  setClientCert( clientCert );
174  setClientKey( clientKey );
175 }
176 
177 const QgsPkiBundle QgsPkiBundle::fromPemPaths( const QString &certPath,
178  const QString &keyPath,
179  const QString &keyPass,
180  const QList<QSslCertificate> &caChain )
181 {
182  QgsPkiBundle pkibundle;
183  if ( !certPath.isEmpty() && !keyPath.isEmpty()
184  && ( certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive )
185  || certPath.endsWith( QLatin1String( ".der" ), Qt::CaseInsensitive ) )
186  && QFile::exists( certPath ) && QFile::exists( keyPath )
187  )
188  {
189  // client cert
190  bool pem = certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive );
191  QSslCertificate clientcert( QgsAuthCertUtils::fileData( certPath ), pem ? QSsl::Pem : QSsl::Der );
192  pkibundle.setClientCert( clientcert );
193 
194  QSslKey clientkey;
195  clientkey = QgsAuthCertUtils::keyFromFile( keyPath, keyPass );
196  pkibundle.setClientKey( clientkey );
197  if ( !caChain.isEmpty() )
198  {
199  pkibundle.setCaChain( caChain );
200  }
201  }
202  return pkibundle;
203 }
204 
205 const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath,
206  const QString &bundlepass )
207 {
208  QgsPkiBundle pkibundle;
209  if ( QCA::isSupported( "pkcs12" )
210  && !bundlepath.isEmpty()
211  && ( bundlepath.endsWith( QLatin1String( ".p12" ), Qt::CaseInsensitive )
212  || bundlepath.endsWith( QLatin1String( ".pfx" ), Qt::CaseInsensitive ) )
213  && QFile::exists( bundlepath ) )
214  {
215  QCA::SecureArray passarray;
216  if ( !bundlepass.isNull() )
217  passarray = QCA::SecureArray( bundlepass.toUtf8() );
218  QCA::ConvertResult res;
219  QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, QStringLiteral( "qca-ossl" ) ) );
220  if ( res == QCA::ConvertGood && !bundle.isNull() )
221  {
222  const QCA::CertificateChain cert_chain( bundle.certificateChain() );
223  QSslCertificate cert( cert_chain.primary().toPEM().toLatin1() );
224  if ( !cert.isNull() )
225  {
226  pkibundle.setClientCert( cert );
227  }
228  QSslKey cert_key( bundle.privateKey().toPEM().toLatin1(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray() );
229  if ( !cert_key.isNull() )
230  {
231  pkibundle.setClientKey( cert_key );
232  }
233 
234  if ( cert_chain.size() > 1 )
235  {
236  QList<QSslCertificate> ca_chain;
237  for ( const auto &ca_cert : cert_chain )
238  {
239  if ( ca_cert != cert_chain.primary() )
240  {
241  ca_chain << QSslCertificate( ca_cert.toPEM().toLatin1() );
242  }
243  }
244  pkibundle.setCaChain( ca_chain );
245  }
246 
247  }
248  }
249  return pkibundle;
250 }
251 
253 {
254  return ( mCert.isNull() || mCertKey.isNull() );
255 }
256 
258 {
259  return ( !isNull() && QgsAuthCertUtils::certIsViable( mCert ) );
260 }
261 
262 const QString QgsPkiBundle::certId() const
263 {
264  if ( mCert.isNull() )
265  {
266  return QString();
267  }
268  return QString( mCert.digest( QCryptographicHash::Sha1 ).toHex() );
269 }
270 
271 void QgsPkiBundle::setClientCert( const QSslCertificate &cert )
272 {
273  mCert.clear();
274  if ( !cert.isNull() )
275  {
276  mCert = cert;
277  }
278 }
279 
280 void QgsPkiBundle::setClientKey( const QSslKey &certkey )
281 {
282  mCertKey.clear();
283  if ( !certkey.isNull() && certkey.type() == QSsl::PrivateKey )
284  {
285  mCertKey = certkey;
286  }
287 }
288 
289 
291 // QgsPkiConfigBundle
293 
295  const QSslCertificate &cert,
296  const QSslKey &certkey,
297  const QList<QSslCertificate> &cachain )
298  : mConfig( config )
299  , mCert( cert )
300  , mCertKey( certkey )
301  , mCaChain( cachain )
302 {
303 }
304 
306 {
307  return ( !mCert.isNull() && !mCertKey.isNull() );
308 }
309 
310 
312 // QgsAuthConfigSslServer
314 
315 const QString QgsAuthConfigSslServer::CONF_SEP = QStringLiteral( "|||" );
316 
318  : mSslHostPort( QString() )
319  , mSslCert( QSslCertificate() )
320  , mSslIgnoredErrors( QList<QSslError::SslError>() )
321 {
322  // TODO: figure out if Qt 5 has changed yet again, e.g. TLS-only
323  mQtVersion = 480;
324  // Qt 4.8 defaults to SecureProtocols, i.e. TlsV1SslV3
325  // http://qt-project.org/doc/qt-4.8/qssl.html#SslProtocol-enum
326  mSslProtocol = QSsl::SecureProtocols;
327 }
328 
329 const QList<QSslError> QgsAuthConfigSslServer::sslIgnoredErrors() const
330 {
331  QList<QSslError> errors;
332  const QList<QSslError::SslError> ignoredErrors = sslIgnoredErrorEnums();
333  for ( QSslError::SslError errenum : ignoredErrors )
334  {
335  errors << QSslError( errenum );
336  }
337  return errors;
338 }
339 
341 {
342  QStringList configlist;
343  configlist << QString::number( mVersion ) << QString::number( mQtVersion );
344 
345  configlist << QString::number( static_cast< int >( mSslProtocol ) );
346 
347  QStringList errs;
348  for ( auto err : mSslIgnoredErrors )
349  {
350  errs << QString::number( static_cast< int >( err ) );
351  }
352  configlist << errs.join( QStringLiteral( "~~" ) );
353 
354  configlist << QStringLiteral( "%1~~%2" ).arg( static_cast< int >( mSslPeerVerifyMode ) ).arg( mSslPeerVerifyDepth );
355 
356  return configlist.join( CONF_SEP );
357 }
358 
359 void QgsAuthConfigSslServer::loadConfigString( const QString &config )
360 {
361  if ( config.isEmpty() )
362  {
363  return;
364  }
365  QStringList configlist( config.split( CONF_SEP ) );
366 
367  mVersion = configlist.at( 0 ).toInt();
368  mQtVersion = configlist.at( 1 ).toInt();
369 
370  // TODO: Conversion between 4.7 -> 4.8 protocol enum differences (and reverse?).
371  // This is necessary for users upgrading from 4.7 to 4.8
372  mSslProtocol = static_cast< QSsl::SslProtocol >( configlist.at( 2 ).toInt() );
373 
374  mSslIgnoredErrors.clear();
375  const QStringList errs( configlist.at( 3 ).split( QStringLiteral( "~~" ) ) );
376  for ( const auto &err : errs )
377  {
378  mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) );
379  }
380 
381  QStringList peerverify( configlist.at( 4 ).split( QStringLiteral( "~~" ) ) );
382  mSslPeerVerifyMode = static_cast< QSslSocket::PeerVerifyMode >( peerverify.at( 0 ).toInt() );
383  mSslPeerVerifyDepth = peerverify.at( 1 ).toInt();
384 }
385 
387 {
388  return mSslCert.isNull() && mSslHostPort.isEmpty();
389 }
390 
391 #endif
bool isValid() const
Whether the bundle is valid.
bool isNull() const
Whether configuration is null (missing components)
QgsStringMap configMap() const
Gets extended configuration, mapped to key/value pairs of QStrings.
bool isNull() const
Whether the bundle, either its certificate or private key, is null.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
void setConfig(const QString &key, const QString &value)
Set a single config value per key in the map.
QString config(const QString &key, const QString &defaultvalue=QString()) const
Returns a config&#39;s value.
void setCaChain(const QList< QSslCertificate > &cachain)
Sets chain of Certificate Authorities for client certificate.
static QSslKey keyFromFile(const QString &keypath, const QString &keypass=QString(), QString *algtype=nullptr)
Returns non-encrypted key from a PEM or DER formatted file.
void setConfigList(const QString &key, const QStringList &value)
Set a multiple config values per key in the map.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
QStringList configList(const QString &key) const
Returns a config&#39;s list of values.
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static const QgsPkiBundle fromPkcs12Paths(const QString &bundlepath, const QString &bundlepass=QString())
Construct a bundle of PKI components from a PKCS#12 file path.
static QByteArray fileData(const QString &path)
Returns data from a local file via a read-only operation.
void setClientCert(const QSslCertificate &cert)
Sets client certificate object.
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:38
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.
const QString configString() const
Configuration as a concatenated string.
const QString certId() const
The sha hash of the client certificate.
QgsAuthConfigSslServer()
Construct a default SSL server configuration.
void loadConfigString(const QString &configstr)
Load existing extended configuration.
const QString name() const
Gets name of configuration.
Definition: qgsauthconfig.h:64
bool isValid()
Whether the bundle is valid.
QgsPkiConfigBundle(const QgsAuthMethodConfig &config, const QSslCertificate &cert, const QSslKey &certkey, const QList< QSslCertificate > &cachain=QList< QSslCertificate >())
Construct a bundle from existing PKI components and authentication method configuration.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
int version() const
Gets version of the configuration.
Definition: qgsauthconfig.h:77
void setClientKey(const QSslKey &certkey)
Sets private key object.
QgsAuthMethodConfig(const QString &method=QString(), int version=0)
Construct a configuration for an authentication method.
bool operator!=(const QgsAuthMethodConfig &other) const
Operator used to compare configs&#39; inequality.
static bool uriToResource(const QString &accessurl, QString *resource, bool withpath=false)
A utility function for generating a resource from a URL to be compared against the config&#39;s uri() for...
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
bool hasConfig(const QString &key) const
Whether a config key exists in config map.
QString method() const
Textual key of the associated authentication method.
Definition: qgsauthconfig.h:73
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
int removeConfig(const QString &key)
Remove a config from map.
static const QgsPkiBundle fromPemPaths(const QString &certPath, const QString &keyPath, const QString &keyPass=QString(), const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle of PKI components from PEM-formatted file paths.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:69
void clearConfigMap()
Clear all configs.
bool operator==(const QgsAuthMethodConfig &other) const
Operator used to compare configs&#39; equality.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
QgsPkiBundle(const QSslCertificate &clientCert=QSslCertificate(), const QSslKey &clientKey=QSslKey(), const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle from existing PKI components.
const QString id() const
Gets &#39;authcfg&#39; 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:59