QGIS API Documentation  3.21.0-Master (5b68dc587e)
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
static QByteArray fileData(const QString &path)
Returns data from a local file via a read-only operation.
static bool certIsViable(const QSslCertificate &cert)
certIsViable checks for viability errors of cert and whether it is NULL
static QSslKey keyFromFile(const QString &keypath, const QString &keypass=QString(), QString *algtype=nullptr)
Returns non-encrypted key from a PEM or DER formatted file.
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
bool isNull() const
Whether configuration is null (missing components)
const QString configString() const
Configuration as a concatenated string.
QgsAuthConfigSslServer()
Construct a default SSL server configuration.
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g. from auth database.
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:42
QString config(const QString &key, const QString &defaultvalue=QString()) const
Returns a config's value.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
QString method() const
Textual key of the associated authentication method.
Definition: qgsauthconfig.h:78
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:74
int removeConfig(const QString &key)
Remove a config from map.
bool readXml(const QDomElement &element)
from a DOM element.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
const QString name() const
Gets name of configuration.
Definition: qgsauthconfig.h:69
bool operator==(const QgsAuthMethodConfig &other) const
Operator used to compare configs' equality.
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:64
void loadConfigString(const QString &configstr)
Load existing extended configuration.
void clearConfigMap()
Clear all configs.
bool writeXml(QDomElement &parentElement, QDomDocument &document)
Stores the configuration in a DOM.
void setConfig(const QString &key, const QString &value)
Set a single config value per key in the map.
QStringList configList(const QString &key) const
Returns a config's list of values.
int version() const
Gets version of the configuration.
Definition: qgsauthconfig.h:82
QgsStringMap configMap() const
Gets extended configuration, mapped to key/value pairs of QStrings.
bool operator!=(const QgsAuthMethodConfig &other) const
Operator used to compare configs' inequality.
bool hasConfig(const QString &key) const
Whether a config key exists in config map.
QgsAuthMethodConfig(const QString &method=QString(), int version=0)
Construct a configuration for an authentication method.
void setConfigList(const QString &key, const QStringList &value)
Set a multiple config values per key in the map.
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...
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
const QString certId() const
The sha hash of the client certificate.
bool isNull() const
Whether the bundle, either its certificate or private key, is null.
QgsPkiBundle(const QSslCertificate &clientCert=QSslCertificate(), const QSslKey &clientKey=QSslKey(), const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle from existing PKI components.
void setClientKey(const QSslKey &certkey)
Sets private key object.
void setClientCert(const QSslCertificate &cert)
Sets client certificate object.
static const QgsPkiBundle fromPkcs12Paths(const QString &bundlepath, const QString &bundlepass=QString())
Construct a bundle of PKI components from a PKCS#12 file path.
const QSslKey clientKey() const
Private key object.
void setCaChain(const QList< QSslCertificate > &cachain)
Sets chain of Certificate Authorities for client certificate.
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
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 QSslCertificate clientCert() const
Client certificate object.
bool isValid() const
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.
bool isValid()
Whether the bundle is valid.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:1691