QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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
void setConfig(const QString &key, const QString &value)
Set a single config value per key in the map.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
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.
bool isNull() const
Whether configuration is null (missing components)
bool isNull() const
Whether the bundle, either its certificate or private key, is null.
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:577
const QString name() const
Gets name of configuration.
Definition: qgsauthconfig.h:64
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.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:69
void setClientCert(const QSslCertificate &cert)
Sets client certificate object.
const QString configString() const
Configuration as a concatenated string.
QgsStringMap configMap() const
Gets extended configuration, mapped to key/value pairs of QStrings.
const QString certId() const
The sha hash of the client certificate.
bool operator!=(const QgsAuthMethodConfig &other) const
Operator used to compare configs&#39; inequality.
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:38
QgsAuthConfigSslServer()
Construct a default SSL server configuration.
void loadConfigString(const QString &configstr)
Load existing extended configuration.
bool isValid()
Whether the bundle is valid.
const QString id() const
Gets &#39;authcfg&#39; 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:59
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.
QStringList configList(const QString &key) const
Returns a config&#39;s list of values.
QString config(const QString &key, const QString &defaultvalue=QString()) const
Returns a config&#39;s value.
void setClientKey(const QSslKey &certkey)
Sets private key object.
QgsAuthMethodConfig(const QString &method=QString(), int version=0)
Construct a configuration for an authentication method.
const QList< QSslCertificate > caChain() const
Chain of Certificate Authorities for client certificate.
bool isValid() const
Whether the bundle is valid.
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...
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.
bool hasConfig(const QString &key) const
Whether a config key exists in config map.
bool isValid(bool validateid=false) const
Whether the configuration is valid.
void clearConfigMap()
Clear all configs.
bool operator==(const QgsAuthMethodConfig &other) const
Operator used to compare configs&#39; equality.
int version() const
Gets version of the configuration.
Definition: qgsauthconfig.h:77
QString method() const
Textual key of the associated authentication method.
Definition: qgsauthconfig.h:73
QgsPkiBundle(const QSslCertificate &clientCert=QSslCertificate(), const QSslKey &clientKey=QSslKey(), const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle from existing PKI components.
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.