QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
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 #include "qgsauthcertutils.h"
19 #include "qgsxmlutils.h"
20 
21 #include <QtCrypto>
22 
23 #include <QFile>
24 #include <QObject>
25 #include <QCryptographicHash>
26 #include <QUrl>
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  const 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  const 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  const 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 bool QgsAuthMethodConfig::writeXml( QDomElement &parentElement, QDomDocument &document )
161 {
162  QDomElement element = document.createElement( QStringLiteral( "AuthMethodConfig" ) );
163  element.setAttribute( QStringLiteral( "method" ), mMethod );
164  element.setAttribute( QStringLiteral( "id" ), mId );
165  element.setAttribute( QStringLiteral( "name" ), mName );
166  element.setAttribute( QStringLiteral( "version" ), QString::number( mVersion ) );
167  element.setAttribute( QStringLiteral( "uri" ), mUri );
168 
169  QDomElement configElements = document.createElement( QStringLiteral( "Config" ) );
170  QgsStringMap::const_iterator i = mConfigMap.constBegin();
171  while ( i != mConfigMap.constEnd() )
172  {
173  configElements.setAttribute( i.key(), i.value() );
174  ++i;
175  }
176  element.appendChild( configElements );
177 
178  parentElement.appendChild( element );
179  return true;
180 }
181 
182 bool QgsAuthMethodConfig::readXml( const QDomElement &element )
183 {
184  if ( element.nodeName() != QLatin1String( "AuthMethodConfig" ) )
185  return false;
186 
187  mMethod = element.attribute( QStringLiteral( "method" ) );
188  mId = element.attribute( QStringLiteral( "id" ) );
189  mName = element.attribute( QStringLiteral( "name" ) );
190  mVersion = element.attribute( QStringLiteral( "version" ) ).toInt();
191  mUri = element.attribute( QStringLiteral( "uri" ) );
192 
193  clearConfigMap();
194  const QDomNamedNodeMap configAttributes = element.firstChildElement().attributes();
195  for ( int i = 0; i < configAttributes.length(); i++ )
196  {
197  const QDomAttr configAttribute = configAttributes.item( i ).toAttr();
198  setConfig( configAttribute.name(), configAttribute.value() );
199  }
200 
201  return true;
202 }
203 
204 #ifndef QT_NO_SSL
205 
207 // QgsPkiBundle
209 
210 QgsPkiBundle::QgsPkiBundle( const QSslCertificate &clientCert,
211  const QSslKey &clientKey,
212  const QList<QSslCertificate> &caChain )
213  : mCert( QSslCertificate() )
214  , mCertKey( QSslKey() )
215  , mCaChain( caChain )
216 {
219 }
220 
221 const QgsPkiBundle QgsPkiBundle::fromPemPaths( const QString &certPath,
222  const QString &keyPath,
223  const QString &keyPass,
224  const QList<QSslCertificate> &caChain )
225 {
226  QgsPkiBundle pkibundle;
227  if ( !certPath.isEmpty() && !keyPath.isEmpty()
228  && ( certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive )
229  || certPath.endsWith( QLatin1String( ".der" ), Qt::CaseInsensitive ) )
230  && QFile::exists( certPath ) && QFile::exists( keyPath )
231  )
232  {
233  // client cert
234  const bool pem = certPath.endsWith( QLatin1String( ".pem" ), Qt::CaseInsensitive );
235  const QSslCertificate clientcert( QgsAuthCertUtils::fileData( certPath ), pem ? QSsl::Pem : QSsl::Der );
236  pkibundle.setClientCert( clientcert );
237 
238  QSslKey clientkey;
239  clientkey = QgsAuthCertUtils::keyFromFile( keyPath, keyPass );
240  pkibundle.setClientKey( clientkey );
241  if ( !caChain.isEmpty() )
242  {
243  pkibundle.setCaChain( caChain );
244  }
245  }
246  return pkibundle;
247 }
248 
249 const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath,
250  const QString &bundlepass )
251 {
252  QgsPkiBundle pkibundle;
253  if ( QCA::isSupported( "pkcs12" )
254  && !bundlepath.isEmpty()
255  && ( bundlepath.endsWith( QLatin1String( ".p12" ), Qt::CaseInsensitive )
256  || bundlepath.endsWith( QLatin1String( ".pfx" ), Qt::CaseInsensitive ) )
257  && QFile::exists( bundlepath ) )
258  {
259  QCA::SecureArray passarray;
260  if ( !bundlepass.isNull() )
261  passarray = QCA::SecureArray( bundlepass.toUtf8() );
262  QCA::ConvertResult res;
263  const QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, QStringLiteral( "qca-ossl" ) ) );
264  if ( res == QCA::ConvertGood && !bundle.isNull() )
265  {
266  const QCA::CertificateChain cert_chain( bundle.certificateChain() );
267  const QSslCertificate cert( cert_chain.primary().toPEM().toLatin1() );
268  if ( !cert.isNull() )
269  {
270  pkibundle.setClientCert( cert );
271  }
272  const QSslKey cert_key( bundle.privateKey().toPEM().toLatin1(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray() );
273  if ( !cert_key.isNull() )
274  {
275  pkibundle.setClientKey( cert_key );
276  }
277 
278  if ( cert_chain.size() > 1 )
279  {
280  QList<QSslCertificate> ca_chain;
281  for ( const auto &ca_cert : cert_chain )
282  {
283  if ( ca_cert != cert_chain.primary() )
284  {
285  ca_chain << QSslCertificate( ca_cert.toPEM().toLatin1() );
286  }
287  }
288  pkibundle.setCaChain( ca_chain );
289  }
290 
291  }
292  }
293  return pkibundle;
294 }
295 
297 {
298  return ( mCert.isNull() || mCertKey.isNull() );
299 }
300 
302 {
303  return ( !isNull() && QgsAuthCertUtils::certIsViable( mCert ) );
304 }
305 
306 const QString QgsPkiBundle::certId() const
307 {
308  if ( mCert.isNull() )
309  {
310  return QString();
311  }
312  return QString( mCert.digest( QCryptographicHash::Sha1 ).toHex() );
313 }
314 
315 void QgsPkiBundle::setClientCert( const QSslCertificate &cert )
316 {
317  mCert.clear();
318  if ( !cert.isNull() )
319  {
320  mCert = cert;
321  }
322 }
323 
324 void QgsPkiBundle::setClientKey( const QSslKey &certkey )
325 {
326  mCertKey.clear();
327  if ( !certkey.isNull() && certkey.type() == QSsl::PrivateKey )
328  {
329  mCertKey = certkey;
330  }
331 }
332 
333 
335 // QgsPkiConfigBundle
337 
339  const QSslCertificate &cert,
340  const QSslKey &certkey,
341  const QList<QSslCertificate> &cachain )
342  : mConfig( config )
343  , mCert( cert )
344  , mCertKey( certkey )
345  , mCaChain( cachain )
346 {
347 }
348 
350 {
351  return ( !mCert.isNull() && !mCertKey.isNull() );
352 }
353 
354 
356 // QgsAuthConfigSslServer
358 
359 const QString QgsAuthConfigSslServer::CONF_SEP = QStringLiteral( "|||" );
360 
362  : mSslHostPort( QString() )
363  , mSslCert( QSslCertificate() )
364  , mSslIgnoredErrors( QList<QSslError::SslError>() )
365 {
366  // TODO: figure out if Qt 5 has changed yet again, e.g. TLS-only
367  mQtVersion = 480;
368  // Qt 4.8 defaults to SecureProtocols, i.e. TlsV1SslV3
369  // http://qt-project.org/doc/qt-4.8/qssl.html#SslProtocol-enum
370  mSslProtocol = QSsl::SecureProtocols;
371 }
372 
373 const QList<QSslError> QgsAuthConfigSslServer::sslIgnoredErrors() const
374 {
375  QList<QSslError> errors;
376  const QList<QSslError::SslError> ignoredErrors = sslIgnoredErrorEnums();
377  for ( const QSslError::SslError errenum : ignoredErrors )
378  {
379  errors << QSslError( errenum );
380  }
381  return errors;
382 }
383 
385 {
386  QStringList configlist;
387  configlist << QString::number( mVersion ) << QString::number( mQtVersion );
388 
389  configlist << QString::number( static_cast< int >( mSslProtocol ) );
390 
391  QStringList errs;
392  for ( const auto err : mSslIgnoredErrors )
393  {
394  errs << QString::number( static_cast< int >( err ) );
395  }
396  configlist << errs.join( QLatin1String( "~~" ) );
397 
398  configlist << QStringLiteral( "%1~~%2" ).arg( static_cast< int >( mSslPeerVerifyMode ) ).arg( mSslPeerVerifyDepth );
399 
400  return configlist.join( CONF_SEP );
401 }
402 
403 void QgsAuthConfigSslServer::loadConfigString( const QString &config )
404 {
405  if ( config.isEmpty() )
406  {
407  return;
408  }
409  const QStringList configlist( config.split( CONF_SEP ) );
410 
411  mVersion = configlist.at( 0 ).toInt();
412  mQtVersion = configlist.at( 1 ).toInt();
413 
414  // TODO: Conversion between 4.7 -> 4.8 protocol enum differences (and reverse?).
415  // This is necessary for users upgrading from 4.7 to 4.8
416  mSslProtocol = static_cast< QSsl::SslProtocol >( configlist.at( 2 ).toInt() );
417 
418  mSslIgnoredErrors.clear();
419  const QStringList errs( configlist.at( 3 ).split( QStringLiteral( "~~" ) ) );
420  for ( const auto &err : errs )
421  {
422  mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) );
423  }
424 
425  const QStringList peerverify( configlist.at( 4 ).split( QStringLiteral( "~~" ) ) );
426  mSslPeerVerifyMode = static_cast< QSslSocket::PeerVerifyMode >( peerverify.at( 0 ).toInt() );
427  mSslPeerVerifyDepth = peerverify.at( 1 ).toInt();
428 }
429 
431 {
432  return mSslCert.isNull() && mSslHostPort.isEmpty();
433 }
434 
435 #endif
QgsPkiBundle::setClientKey
void setClientKey(const QSslKey &certkey)
Sets private key object.
Definition: qgsauthconfig.cpp:324
qgsauthconfig.h
QgsAuthConfigSslServer::QgsAuthConfigSslServer
QgsAuthConfigSslServer()
Construct a default SSL server configuration.
Definition: qgsauthconfig.cpp:361
QgsAuthMethodConfig::operator!=
bool operator!=(const QgsAuthMethodConfig &other) const
Operator used to compare configs' inequality.
Definition: qgsauthconfig.cpp:60
QgsAuthMethodConfig::name
const QString name() const
Gets name of configuration.
Definition: qgsauthconfig.h:83
qgsauthcertutils.h
QgsAuthConfigSslServer::sslIgnoredErrors
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.
Definition: qgsauthconfig.cpp:373
QgsPkiBundle::setClientCert
void setClientCert(const QSslCertificate &cert)
Sets client certificate object.
Definition: qgsauthconfig.cpp:315
QgsPkiConfigBundle::isValid
bool isValid()
Whether the bundle is valid.
Definition: qgsauthconfig.cpp:349
QgsPkiBundle::isNull
bool isNull() const
Whether the bundle, either its certificate or private key, is null.
Definition: qgsauthconfig.cpp:296
QgsAuthMethodConfig::removeConfig
int removeConfig(const QString &key)
Remove a config from map.
Definition: qgsauthconfig.cpp:123
QgsAuthMethodConfig::readXml
bool readXml(const QDomElement &element)
from a DOM element.
Definition: qgsauthconfig.cpp:182
QgsPkiBundle::fromPkcs12Paths
static const QgsPkiBundle fromPkcs12Paths(const QString &bundlepath, const QString &bundlepass=QString())
Construct a bundle of PKI components from a PKCS#12 file path.
Definition: qgsauthconfig.cpp:249
QgsAuthMethodConfig::config
QString config(const QString &key, const QString &defaultvalue=QString()) const
Returns a config's value.
Definition: qgsauthconfig.cpp:128
QgsAuthMethodConfig::loadConfigString
void loadConfigString(const QString &configstr)
Load existing extended configuration.
Definition: qgsauthconfig.cpp:88
QgsAuthMethodConfig::operator==
bool operator==(const QgsAuthMethodConfig &other) const
Operator used to compare configs' equality.
Definition: qgsauthconfig.cpp:50
QgsPkiBundle
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
Definition: qgsauthconfig.h:210
QgsPkiBundle::clientKey
const QSslKey clientKey() const
Private key object.
Definition: qgsauthconfig.h:259
QgsAuthMethodConfig::hasConfig
bool hasConfig(const QString &key) const
Whether a config key exists in config map.
Definition: qgsauthconfig.cpp:138
QgsAuthConfigSslServer::sslIgnoredErrorEnums
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
Definition: qgsauthconfig.h:416
QgsAuthMethodConfig::writeXml
bool writeXml(QDomElement &parentElement, QDomDocument &document)
Stores the configuration in a DOM.
Definition: qgsauthconfig.cpp:160
QgsPkiBundle::clientCert
const QSslCertificate clientCert() const
Client certificate object.
Definition: qgsauthconfig.h:254
QgsAuthMethodConfig::QgsAuthMethodConfig
QgsAuthMethodConfig(const QString &method=QString(), int version=0)
Construct a configuration for an authentication method.
Definition: qgsauthconfig.cpp:40
QgsPkiBundle::setCaChain
void setCaChain(const QList< QSslCertificate > &cachain)
Sets chain of Certificate Authorities for client certificate.
Definition: qgsauthconfig.h:266
QgsAuthMethodConfig::version
int version() const
Gets version of the configuration.
Definition: qgsauthconfig.h:96
QgsPkiBundle::fromPemPaths
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.
Definition: qgsauthconfig.cpp:221
QgsPkiBundle::QgsPkiBundle
QgsPkiBundle(const QSslCertificate &clientCert=QSslCertificate(), const QSslKey &clientKey=QSslKey(), const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle from existing PKI components.
Definition: qgsauthconfig.cpp:210
QgsAuthMethodConfig::method
QString method() const
Textual key of the associated authentication method.
Definition: qgsauthconfig.h:92
QgsAuthMethodConfig::configList
QStringList configList(const QString &key) const
Returns a config's list of values.
Definition: qgsauthconfig.cpp:133
QgsPkiBundle::certId
const QString certId() const
The sha hash of the client certificate.
Definition: qgsauthconfig.cpp:306
QgsAuthCertUtils::keyFromFile
static QSslKey keyFromFile(const QString &keypath, const QString &keypass=QString(), QString *algtype=nullptr)
Returns non-encrypted key from a PEM or DER formatted file.
Definition: qgsauthcertutils.cpp:193
QgsPkiBundle::caChain
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
Definition: qgsauthconfig.h:264
qgsxmlutils.h
QgsPkiBundle::isValid
bool isValid() const
Whether the bundle is valid.
Definition: qgsauthconfig.cpp:301
QgsPkiConfigBundle::QgsPkiConfigBundle
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.
Definition: qgsauthconfig.cpp:338
QgsAuthMethodConfig::setConfigList
void setConfigList(const QString &key, const QStringList &value)
Set a multiple config values per key in the map.
Definition: qgsauthconfig.cpp:118
QgsAuthMethodConfig::configMap
QgsStringMap configMap() const
Gets extended configuration, mapped to key/value pairs of QStrings.
Definition: qgsauthconfig.h:119
QgsStringMap
QMap< QString, QString > QgsStringMap
Definition: qgis.h:2781
QgsAuthConfigSslServer::configString
const QString configString() const
Configuration as a concatenated string.
Definition: qgsauthconfig.cpp:384
QgsAuthMethodConfig::id
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:78
QgsAuthConfigSslServer::isNull
bool isNull() const
Whether configuration is null (missing components)
Definition: qgsauthconfig.cpp:430
QgsAuthMethodConfig::configString
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
Definition: qgsauthconfig.cpp:76
QgsAuthMethodConfig::isValid
bool isValid(bool validateid=false) const
Whether the configuration is valid.
Definition: qgsauthconfig.cpp:65
QgsAuthMethodConfig::setConfig
void setConfig(const QString &key, const QString &value)
Set a single config value per key in the map.
Definition: qgsauthconfig.cpp:113
QgsAuthMethodConfig::clearConfigMap
void clearConfigMap()
Clear all configs.
Definition: qgsauthconfig.h:170
QgsAuthCertUtils::certIsViable
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
Definition: qgsauthcertutils.cpp:1305
QgsAuthMethodConfig::uriToResource
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's uri() for...
Definition: qgsauthconfig.cpp:143
QgsAuthCertUtils::fileData
static QByteArray fileData(const QString &path)
Returns data from a local file via a read-only operation.
Definition: qgsauthcertutils.cpp:109
QgsAuthMethodConfig
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:41
QgsAuthConfigSslServer::loadConfigString
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
Definition: qgsauthconfig.cpp:403
QgsAuthMethodConfig::uri
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:88