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