QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
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 <QUrl>
24 
25 #include "qgsauthcertutils.h"
26 
27 
29 // QgsAuthMethodConfig
31 
32 const QString QgsAuthMethodConfig::mConfigSep = "|||";
33 const QString QgsAuthMethodConfig::mConfigKeySep = ":::";
34 const QString QgsAuthMethodConfig::mConfigListSep = "```";
35 
36 const int QgsAuthMethodConfig::mConfigVersion = 1;
37 
38 // get uniqueConfigId only on save
40  : mId( QString() )
41  , mName( QString() )
42  , mUri( QString() )
43  , mMethod( method )
44  , mVersion( version )
45  , mConfigMap( QgsStringMap() )
46 {
47 }
48 
50 {
51  return ( other.id() == id()
52  && other.name() == name()
53  && other.uri() == uri()
54  && other.method() == method()
55  && other.version() == version()
56  && other.configMap() == configMap() );
57 }
58 
60 {
61  return !( *this == other );
62 }
63 
64 bool QgsAuthMethodConfig::isValid( bool validateid ) const
65 {
66  bool idvalid = validateid ? !mId.isEmpty() : true;
67 
68  return (
69  idvalid
70  && !mName.isEmpty()
71  && !mMethod.isEmpty()
72  );
73 }
74 
76 {
77  QStringList confstrs;
79  while ( i != mConfigMap.constEnd() )
80  {
81  confstrs << i.key() + mConfigKeySep + i.value();
82  ++i;
83  }
84  return confstrs.join( mConfigSep );
85 }
86 
88 {
90  if ( configstr.isEmpty() )
91  {
92  return;
93  }
94 
95  QStringList confs( configstr.split( mConfigSep ) );
96 
97  Q_FOREACH ( const QString& conf, confs )
98  {
99  if ( conf.contains( mConfigKeySep ) )
100  {
101  QStringList keyval( conf.split( mConfigKeySep ) );
102  setConfig( keyval.at( 0 ), keyval.at( 1 ) );
103  }
104  }
105 
106  if ( configMap().empty() )
107  {
108  setConfig( "oldconfigstyle", configstr );
109  }
110 }
111 
112 void QgsAuthMethodConfig::setConfig( const QString &key, const QString &value )
113 {
114  mConfigMap.insert( key, value );
115 }
116 
118 {
119  setConfig( key, value.join( mConfigListSep ) );
120 }
121 
123 {
124  return mConfigMap.remove( key );
125 }
126 
127 QString QgsAuthMethodConfig::config( const QString &key, const QString& defaultvalue ) const
128 {
129  return mConfigMap.value( key, defaultvalue );
130 }
131 
133 {
134  return config( key ).split( mConfigListSep );
135 }
136 
137 bool QgsAuthMethodConfig::hasConfig( const QString &key ) const
138 {
139  return mConfigMap.contains( key );
140 }
141 
142 bool QgsAuthMethodConfig::uriToResource( const QString &accessurl, QString *resource, bool withpath )
143 {
144  QString res = QString();
145  if ( !accessurl.isEmpty() )
146  {
147  QUrl url( accessurl );
148  if ( url.isValid() )
149  {
150  res = QString( "%1://%2:%3%4" ).arg( url.scheme(), url.host() )
151  .arg( url.port() ).arg( withpath ? url.path() : "" );
152  }
153  }
154  *resource = res;
155  return ( !res.isEmpty() );
156 }
157 
158 
159 #ifndef QT_NO_OPENSSL
160 
162 // QgsPkiBundle
164 
166  const QSslKey &clientKey,
167  const QList<QSslCertificate> &caChain )
168  : mCert( QSslCertificate() )
169  , mCertKey( QSslKey() )
170  , mCaChain( caChain )
171 {
172  setClientCert( clientCert );
173  setClientKey( clientKey );
174 }
175 
176 static QByteArray fileData_( const QString& path, bool astext = false )
177 {
178  QByteArray data;
179  QFile file( path );
180  if ( file.exists() )
181  {
182  QFile::OpenMode openflags( QIODevice::ReadOnly );
183  if ( astext )
184  openflags |= QIODevice::Text;
185  bool ret = file.open( openflags );
186  if ( ret )
187  {
188  data = file.readAll();
189  }
190  file.close();
191  }
192  return data;
193 }
194 
196  const QString &keyPath,
197  const QString &keyPass,
199 {
200  QgsPkiBundle pkibundle;
201  if ( !certPath.isEmpty() && !keyPath.isEmpty()
202  && ( certPath.endsWith( ".pem", Qt::CaseInsensitive )
203  || certPath.endsWith( ".der", Qt::CaseInsensitive ) )
204  && ( keyPath.endsWith( ".pem", Qt::CaseInsensitive )
205  || keyPath.endsWith( ".der", Qt::CaseInsensitive ) )
206  && QFile::exists( certPath ) && QFile::exists( keyPath )
207  )
208  {
209  // client cert
210  bool pem = certPath.endsWith( ".pem", Qt::CaseInsensitive );
211  QSslCertificate clientcert( fileData_( certPath, pem ), pem ? QSsl::Pem : QSsl::Der );
212  pkibundle.setClientCert( clientcert );
213 
214  // client key
215  bool pem_key = keyPath.endsWith( ".pem", Qt::CaseInsensitive );
216  QByteArray keydata( fileData_( keyPath, pem_key ) );
217 
218  QSslKey clientkey;
219  clientkey = QSslKey( keydata,
220  QSsl::Rsa,
221  pem_key ? QSsl::Pem : QSsl::Der,
222  QSsl::PrivateKey,
223  !keyPass.isNull() ? keyPass.toUtf8() : QByteArray() );
224  if ( clientkey.isNull() )
225  {
226  // try DSA algorithm, since Qt can't seem to determine it otherwise
227  clientkey = QSslKey( keydata,
228  QSsl::Dsa,
229  pem_key ? QSsl::Pem : QSsl::Der,
230  QSsl::PrivateKey,
231  !keyPass.isNull() ? keyPass.toUtf8() : QByteArray() );
232  }
233  pkibundle.setClientKey( clientkey );
234  if ( !caChain.isEmpty() )
235  {
236  pkibundle.setCaChain( caChain );
237  }
238  }
239  return pkibundle;
240 }
241 
243  const QString &bundlepass )
244 {
245  QgsPkiBundle pkibundle;
246  if ( QCA::isSupported( "pkcs12" )
247  && !bundlepath.isEmpty()
248  && ( bundlepath.endsWith( ".p12", Qt::CaseInsensitive )
249  || bundlepath.endsWith( ".pfx", Qt::CaseInsensitive ) )
250  && QFile::exists( bundlepath ) )
251  {
252  QCA::SecureArray passarray;
253  if ( !bundlepass.isNull() )
254  passarray = QCA::SecureArray( bundlepass.toUtf8() );
255  QCA::ConvertResult res;
256  QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, QString( "qca-ossl" ) ) );
257  if ( res == QCA::ConvertGood && !bundle.isNull() )
258  {
259  QCA::CertificateChain cert_chain( bundle.certificateChain() );
260  QSslCertificate cert( cert_chain.primary().toPEM().toAscii() );
261  if ( !cert.isNull() )
262  {
263  pkibundle.setClientCert( cert );
264  }
265  QSslKey cert_key( bundle.privateKey().toPEM().toAscii(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray() );
266  if ( !cert_key.isNull() )
267  {
268  pkibundle.setClientKey( cert_key );
269  }
270 
271  if ( cert_chain.size() > 1 )
272  {
273  QList<QSslCertificate> ca_chain;
274  Q_FOREACH ( const QCA::Certificate& ca_cert, cert_chain )
275  {
276  if ( ca_cert != cert_chain.primary() )
277  {
278  ca_chain << QSslCertificate( ca_cert.toPEM().toAscii() );
279  }
280  }
281  pkibundle.setCaChain( ca_chain );
282  }
283 
284  }
285  }
286  return pkibundle;
287 }
288 
290 {
291  return ( mCert.isNull() || mCertKey.isNull() );
292 }
293 
295 {
296  return ( !isNull() && mCert.isValid() );
297 }
298 
300 {
301  if ( mCert.isNull() )
302  {
303  return QString::null;
304  }
305  return QgsAuthCertUtils::shaHexForCert( mCert );
306 }
307 
309 {
310  mCert.clear();
311  if ( !cert.isNull() )
312  {
313  mCert = cert;
314  }
315 }
316 
317 void QgsPkiBundle::setClientKey( const QSslKey &certkey )
318 {
319  mCertKey.clear();
320  if ( !certkey.isNull() && certkey.type() == QSsl::PrivateKey )
321  {
322  mCertKey = certkey;
323  }
324 }
325 
326 
328 // QgsPkiConfigBundle
330 
332  const QSslCertificate& cert,
333  const QSslKey& certkey )
334  : mConfig( config )
335  , mCert( cert )
336  , mCertKey( certkey )
337 {
338 }
339 
341 {
342  return ( !mCert.isNull() && !mCertKey.isNull() );
343 }
344 
345 
347 // QgsAuthConfigSslServer
349 
350 const QString QgsAuthConfigSslServer::mConfSep = "|||";
351 
353  : mSslHostPort( QString() )
354  , mSslCert( QSslCertificate() )
355  , mSslIgnoredErrors( QList<QSslError::SslError>() )
356  , mSslPeerVerifyMode( QSslSocket::VerifyPeer )
357  , mSslPeerVerifyDepth( 0 )
358  , mVersion( 1 )
359 {
360  // TODO: figure out if Qt 5 has changed yet again, e.g. TLS-only
361 #if QT_VERSION >= 0x040800
362  mQtVersion = 480;
363  // Qt 4.8 defaults to SecureProtocols, i.e. TlsV1SslV3
364  // http://qt-project.org/doc/qt-4.8/qssl.html#SslProtocol-enum
365  mSslProtocol = QSsl::SecureProtocols;
366 #else
367  mQtVersion = 470;
368  // older Qt 4.7 defaults to now-vulnerable SSLv3
369  // http://qt-project.org/doc/qt-4.7/qssl.html
370  // Default this to TlsV1 instead
371  mSslProtocol = QSsl::TlsV1;
372 #endif
373 }
374 
376 {
377  QList<QSslError> errors;
378  Q_FOREACH ( QSslError::SslError errenum, sslIgnoredErrorEnums() )
379  {
380  errors << QSslError( errenum );
381  }
382  return errors;
383 }
384 
386 {
387  QStringList configlist;
388  configlist << QString::number( mVersion ) << QString::number( mQtVersion );
389 
390  configlist << QString::number( static_cast< int >( mSslProtocol ) );
391 
392  QStringList errs;
393  Q_FOREACH ( const QSslError::SslError& err, mSslIgnoredErrors )
394  {
395  errs << QString::number( static_cast< int >( err ) );
396  }
397  configlist << errs.join( "~~" );
398 
399  configlist << QString( "%1~~%2" ).arg( static_cast< int >( mSslPeerVerifyMode ) ).arg( mSslPeerVerifyDepth );
400 
401  return configlist.join( mConfSep );
402 }
403 
405 {
406  if ( config.isEmpty() )
407  {
408  return;
409  }
410  QStringList configlist( config.split( mConfSep ) );
411 
412  mVersion = configlist.at( 0 ).toInt();
413  mQtVersion = configlist.at( 1 ).toInt();
414 
415  // TODO: Conversion between 4.7 -> 4.8 protocol enum differences (and reverse?).
416  // This is necessary for users upgrading from 4.7 to 4.8
417  mSslProtocol = static_cast< QSsl::SslProtocol >( configlist.at( 2 ).toInt() );
418 
419  mSslIgnoredErrors.clear();
420  QStringList errs( configlist.at( 3 ).split( "~~" ) );
421  Q_FOREACH ( const QString& err, errs )
422  {
423  mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) );
424  }
425 
426  QStringList peerverify( configlist.at( 4 ).split( "~~" ) );
427  mSslPeerVerifyMode = static_cast< QSslSocket::PeerVerifyMode >( peerverify.at( 0 ).toInt() );
428  mSslPeerVerifyDepth = peerverify.at( 1 ).toInt();
429 }
430 
432 {
433  return mSslCert.isNull() && mSslHostPort.isEmpty();
434 }
435 
436 #endif
typedef OpenMode
bool isValid() const
Whether the bundle is valid.
QSsl::KeyType type() const
bool isNull() const
Whether configuration is null (missing components)
void clear()
QgsStringMap configMap() const
Get extended configuration, mapped to key/value pairs of QStrings.
Definition: qgsauthconfig.h:97
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.
bool contains(const Key &key) const
QString config(const QString &key, const QString &defaultvalue=QString()) const
Return a config&#39;s value.
bool empty() const
bool isNull() const
bool isValid() const
void setCaChain(const QList< QSslCertificate > &cachain)
Set chain of Certificate Authorities for client certificate.
static const QgsPkiBundle fromPkcs12Paths(const QString &bundlepath, const QString &bundlepass=QString::null)
Construct a bundle of PKI components from a PKCS#12 file path.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
const_iterator constBegin() const
QString host() const
QString join(const QString &separator) const
void setConfigList(const QString &key, const QStringList &value)
Set a multiple config values per key in the map.
bool exists() const
int port() const
QStringList configList(const QString &key) const
Return a config&#39;s list of values.
bool isNull() const
Storage set for PKI bundle: SSL certificate, key, optional CA cert chain.
void clear()
static QByteArray fileData_(const QString &path, bool astext=false)
QString number(int n, int base)
void setClientCert(const QSslCertificate &cert)
Set client certificate object.
void append(const T &value)
const Key & key() const
bool isNull() const
Configuration storage class for authentication method configurations.
Definition: qgsauthconfig.h:36
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.
int toInt(bool *ok, int base) const
bool isEmpty() const
const QString configString() const
Configuration as a concatenated string.
bool isEmpty() const
const_iterator constEnd() const
const QString certId() const
The sha hash of the client certificate.
QByteArray readAll()
QgsAuthConfigSslServer()
Construct a default SSL server configuration.
void loadConfigString(const QString &configstr)
Load existing extended configuration.
QString path() const
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
const QString name() const
Get name of configuration.
Definition: qgsauthconfig.h:62
bool isValid()
Whether the bundle is valid.
const T & value() const
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QString scheme() const
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
int version() const
Get version of the configuration.
Definition: qgsauthconfig.h:75
bool contains(QChar ch, Qt::CaseSensitivity cs) const
virtual void close()
void setClientKey(const QSslKey &certkey)
Set private key object.
static QString shaHexForCert(const QSslCertificate &cert, bool formatted=false)
Get the sha1 hash for certificate.
QgsAuthMethodConfig(const QString &method=QString(), int version=0)
Construct a configuration for an authentication method.
bool isValid() const
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:71
void loadConfigString(const QString &config=QString())
Load concatenated string into configuration, e.g.
int removeConfig(const QString &key)
Remove a config from map.
QgsPkiConfigBundle(const QgsAuthMethodConfig &config, const QSslCertificate &cert, const QSslKey &certkey)
Construct a bundle from existing PKI components and authentication method configuration.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
Definition: qgsauthconfig.h:67
iterator insert(const Key &key, const T &value)
void clearConfigMap()
Clear all configs.
bool operator==(const QgsAuthMethodConfig &other) const
Operator used to compare configs&#39; equality.
static const QgsPkiBundle fromPemPaths(const QString &certPath, const QString &keyPath, const QString &keyPass=QString::null, const QList< QSslCertificate > &caChain=QList< QSslCertificate >())
Construct a bundle of PKI components from PEM-formatted file paths.
const QString configString() const
The extended configuration, as stored and retrieved from the authentication database.
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
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
Get &#39;authcfg&#39; 7-character alphanumeric ID of the config.
Definition: qgsauthconfig.h:57
const T value(const Key &key) const
int remove(const Key &key)
QByteArray toUtf8() const