QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
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 "qgsauthcertutils.h"
20#include "qgslogger.h"
21#include "qgsxmlutils.h"
22
23#include <QCryptographicHash>
24#include <QFile>
25#include <QObject>
26#include <QString>
27#include <QUrl>
28#include <QtCrypto>
29
30using namespace Qt::StringLiterals;
31
33// QgsAuthMethodConfig
35
36const QString QgsAuthMethodConfig::CONFIG_SEP = u"|||"_s;
37const QString QgsAuthMethodConfig::CONFIG_KEY_SEP = u":::"_s;
38const QString QgsAuthMethodConfig::CONFIG_LIST_SEP = u"```"_s;
39
40const int QgsAuthMethodConfig::CONFIG_VERSION = 1;
41
42// get uniqueConfigId only on save
44 : mId( QString() )
45 , mName( QString() )
46 , mUri( QString() )
47 , mMethod( method )
48 , mVersion( version )
49 , mConfigMap( QgsStringMap() )
50{}
51
53{
54 return ( other.id() == id() && other.name() == name() && other.uri() == uri() && other.method() == method() && other.version() == version() && other.configMap() == configMap() );
55}
56
58{
59 return !( *this == other );
60}
61
62bool QgsAuthMethodConfig::isValid( bool validateid ) const
63{
64 const bool idvalid = validateid ? !mId.isEmpty() : true;
65
66 return ( idvalid && !mName.isEmpty() && !mMethod.isEmpty() );
67}
68
70{
71 QStringList confstrs;
72 QgsStringMap::const_iterator i = mConfigMap.constBegin();
73 while ( i != mConfigMap.constEnd() )
74 {
75 confstrs << i.key() + CONFIG_KEY_SEP + i.value();
76 ++i;
77 }
78 return confstrs.join( CONFIG_SEP );
79}
80
81void QgsAuthMethodConfig::loadConfigString( const QString &configstr )
82{
84 if ( configstr.isEmpty() )
85 {
86 return;
87 }
88
89 const QStringList confs( configstr.split( CONFIG_SEP ) );
90
91 for ( const auto &conf : confs )
92 {
93 if ( conf.contains( CONFIG_KEY_SEP ) )
94 {
95 const QStringList keyval( conf.split( CONFIG_KEY_SEP ) );
96 setConfig( keyval.at( 0 ), keyval.at( 1 ) );
97 }
98 }
99
100 if ( configMap().empty() )
101 {
102 setConfig( u"oldconfigstyle"_s, configstr );
103 }
104}
105
106void QgsAuthMethodConfig::setConfig( const QString &key, const QString &value )
107{
108 mConfigMap.insert( key, value );
109}
110
111void QgsAuthMethodConfig::setConfigList( const QString &key, const QStringList &value )
112{
113 setConfig( key, value.join( CONFIG_LIST_SEP ) );
114}
115
116int QgsAuthMethodConfig::removeConfig( const QString &key )
117{
118 return mConfigMap.remove( key );
119}
120
121QString QgsAuthMethodConfig::config( const QString &key, const QString &defaultvalue ) const
122{
123 return mConfigMap.value( key, defaultvalue );
124}
125
126QStringList QgsAuthMethodConfig::configList( const QString &key ) const
127{
128 return config( key ).split( CONFIG_LIST_SEP );
129}
130
131bool QgsAuthMethodConfig::hasConfig( const QString &key ) const
132{
133 return mConfigMap.contains( key );
134}
135
136bool QgsAuthMethodConfig::uriToResource( const QString &accessurl, QString *resource, bool withpath )
137{
138 QString res = QString();
139 if ( !accessurl.isEmpty() )
140 {
141 const QUrl url( accessurl );
142 if ( url.isValid() )
143 {
144 res = u"%1://%2:%3%4"_s.arg( url.scheme(), url.host() ).arg( url.port() ).arg( withpath ? url.path() : QString() );
145 }
146 }
147 *resource = res;
148 return ( !res.isEmpty() );
149}
150
151
152bool QgsAuthMethodConfig::writeXml( QDomElement &parentElement, QDomDocument &document )
153{
154 QDomElement element = document.createElement( u"AuthMethodConfig"_s );
155 element.setAttribute( u"method"_s, mMethod );
156 element.setAttribute( u"id"_s, mId );
157 element.setAttribute( u"name"_s, mName );
158 element.setAttribute( u"version"_s, QString::number( mVersion ) );
159 element.setAttribute( u"uri"_s, mUri );
160
161 QDomElement configElements = document.createElement( u"Config"_s );
162 QgsStringMap::const_iterator i = mConfigMap.constBegin();
163 while ( i != mConfigMap.constEnd() )
164 {
165 configElements.setAttribute( i.key(), i.value() );
166 ++i;
167 }
168 element.appendChild( configElements );
169
170 parentElement.appendChild( element );
171 return true;
172}
173
174bool QgsAuthMethodConfig::readXml( const QDomElement &element )
175{
176 if ( element.nodeName() != "AuthMethodConfig"_L1 )
177 return false;
178
179 mMethod = element.attribute( u"method"_s );
180 mId = element.attribute( u"id"_s );
181 mName = element.attribute( u"name"_s );
182 mVersion = element.attribute( u"version"_s ).toInt();
183 mUri = element.attribute( u"uri"_s );
184
186 const QDomNamedNodeMap configAttributes = element.firstChildElement().attributes();
187 for ( int i = 0; i < configAttributes.length(); i++ )
188 {
189 const QDomAttr configAttribute = configAttributes.item( i ).toAttr();
190 setConfig( configAttribute.name(), configAttribute.value() );
191 }
192
193 return true;
194}
195
196#ifndef QT_NO_SSL
197
199// QgsPkiBundle
201
202QgsPkiBundle::QgsPkiBundle( const QSslCertificate &clientCert, const QSslKey &clientKey, const QList<QSslCertificate> &caChain )
203 : mCert( QSslCertificate() )
204 , mCertKey( QSslKey() )
205 , mCaChain( caChain )
206{
209}
210
211const QgsPkiBundle QgsPkiBundle::fromPemPaths( const QString &certPath, const QString &keyPath, const QString &keyPass, const QList<QSslCertificate> &caChain )
212{
213 QgsPkiBundle pkibundle;
214 if ( !certPath.isEmpty()
215 && !keyPath.isEmpty()
216 && ( certPath.endsWith( ".pem"_L1, Qt::CaseInsensitive ) || certPath.endsWith( ".der"_L1, Qt::CaseInsensitive ) )
217 && QFile::exists( certPath )
218 && QFile::exists( keyPath ) )
219 {
220 // client cert
221 const bool pem = certPath.endsWith( ".pem"_L1, Qt::CaseInsensitive );
222 const QSslCertificate clientcert( QgsAuthCertUtils::fileData( certPath ), pem ? QSsl::Pem : QSsl::Der );
223 pkibundle.setClientCert( clientcert );
224
225 QSslKey clientkey;
226 clientkey = QgsAuthCertUtils::keyFromFile( keyPath, keyPass );
227 pkibundle.setClientKey( clientkey );
228 if ( !caChain.isEmpty() )
229 {
230 pkibundle.setCaChain( caChain );
231 }
232 }
233 return pkibundle;
234}
235
236const QgsPkiBundle QgsPkiBundle::fromPkcs12Paths( const QString &bundlepath, const QString &bundlepass )
237{
238 QgsPkiBundle pkibundle;
239 if ( QCA::isSupported( "pkcs12" )
240 && !bundlepath.isEmpty()
241 && ( bundlepath.endsWith( ".p12"_L1, Qt::CaseInsensitive ) || bundlepath.endsWith( ".pfx"_L1, Qt::CaseInsensitive ) )
242 && QFile::exists( bundlepath ) )
243 {
244 QCA::SecureArray passarray;
245 if ( !bundlepass.isNull() )
246 passarray = QCA::SecureArray( bundlepass.toUtf8() );
247 QCA::ConvertResult res;
248 const QCA::KeyBundle bundle( QCA::KeyBundle::fromFile( bundlepath, passarray, &res, u"qca-ossl"_s ) );
249 if ( res == QCA::ConvertGood && !bundle.isNull() )
250 {
251 const QCA::CertificateChain cert_chain( bundle.certificateChain() );
252 const QSslCertificate cert( cert_chain.primary().toPEM().toLatin1() );
253 if ( !cert.isNull() )
254 {
255 pkibundle.setClientCert( cert );
256 }
257 const QSslKey cert_key( bundle.privateKey().toPEM().toLatin1(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, QByteArray() );
258 if ( !cert_key.isNull() )
259 {
260 pkibundle.setClientKey( cert_key );
261 }
262
263 if ( cert_chain.size() > 1 )
264 {
265 QList<QSslCertificate> ca_chain;
266 for ( const auto &ca_cert : cert_chain )
267 {
268 if ( ca_cert != cert_chain.primary() )
269 {
270 ca_chain << QSslCertificate( ca_cert.toPEM().toLatin1() );
271 }
272 }
273 pkibundle.setCaChain( ca_chain );
274 }
275 }
276 }
277 return pkibundle;
278}
279
281{
282 return ( mCert.isNull() || mCertKey.isNull() );
283}
284
286{
287 return ( !isNull() && QgsAuthCertUtils::certIsViable( mCert ) );
288}
289
290const QString QgsPkiBundle::certId() const
291{
292 if ( mCert.isNull() )
293 {
294 return QString();
295 }
296 return QString( mCert.digest( QCryptographicHash::Sha1 ).toHex() );
297}
298
299void QgsPkiBundle::setClientCert( const QSslCertificate &cert )
300{
301 mCert.clear();
302 if ( !cert.isNull() )
303 {
304 mCert = cert;
305 }
306}
307
308void QgsPkiBundle::setClientKey( const QSslKey &certkey )
309{
310 mCertKey.clear();
311 if ( !certkey.isNull() && certkey.type() == QSsl::PrivateKey )
312 {
313 mCertKey = certkey;
314 }
315}
316
317
319// QgsPkiConfigBundle
321
322QgsPkiConfigBundle::QgsPkiConfigBundle( const QgsAuthMethodConfig &config, const QSslCertificate &cert, const QSslKey &certkey, const QList<QSslCertificate> &cachain )
323 : mConfig( config )
324 , mCert( cert )
325 , mCertKey( certkey )
326 , mCaChain( cachain )
327{}
328
330{
331 return ( !mCert.isNull() && !mCertKey.isNull() );
332}
333
334
336// QgsAuthConfigSslServer
338
339const QString QgsAuthConfigSslServer::CONF_SEP = u"|||"_s;
340
342 : mSslHostPort( QString() )
343 , mSslCert( QSslCertificate() )
344 , mSslIgnoredErrors( QList<QSslError::SslError>() )
345{
346 // TODO: figure out if Qt 5 has changed yet again, e.g. TLS-only
347 mQtVersion = 480;
348 // Qt 4.8 defaults to SecureProtocols, i.e. TlsV1SslV3
349 // http://qt-project.org/doc/qt-4.8/qssl.html#SslProtocol-enum
350 mSslProtocol = QSsl::SecureProtocols;
351}
352
353const QList<QSslError> QgsAuthConfigSslServer::sslIgnoredErrors() const
354{
355 QList<QSslError> errors;
356 const QList<QSslError::SslError> ignoredErrors = sslIgnoredErrorEnums();
357 for ( const QSslError::SslError errenum : ignoredErrors )
358 {
359 errors << QSslError( errenum );
360 }
361 return errors;
362}
363
365{
366 QStringList configlist { QString::number( mVersion ), QString::number( mQtVersion ), encodeSslProtocol( mSslProtocol ) };
367
368 QStringList errs;
369 for ( const auto err : mSslIgnoredErrors )
370 {
371 errs << QString::number( static_cast< int >( err ) );
372 }
373 configlist << errs.join( "~~"_L1 );
374
375 configlist << u"%1~~%2"_s.arg( static_cast< int >( mSslPeerVerifyMode ) ).arg( mSslPeerVerifyDepth );
376
377 return configlist.join( CONF_SEP );
378}
379
380void QgsAuthConfigSslServer::loadConfigString( const QString &config )
381{
382 if ( config.isEmpty() )
383 {
384 return;
385 }
386 const QStringList configlist( config.split( CONF_SEP ) );
387
388 mVersion = configlist.at( 0 ).toInt();
389 mQtVersion = configlist.at( 1 ).toInt();
390 mSslProtocol = decodeSslProtocol( configlist.at( 2 ) );
391
392 mSslIgnoredErrors.clear();
393 const QStringList errs( configlist.at( 3 ).split( u"~~"_s ) );
394 for ( const auto &err : errs )
395 {
396 mSslIgnoredErrors.append( static_cast< QSslError::SslError >( err.toInt() ) );
397 }
398
399 const QStringList peerverify( configlist.at( 4 ).split( u"~~"_s ) );
400 mSslPeerVerifyMode = static_cast< QSslSocket::PeerVerifyMode >( peerverify.at( 0 ).toInt() );
401 mSslPeerVerifyDepth = peerverify.at( 1 ).toInt();
402}
403
405{
406 return mSslCert.isNull() && mSslHostPort.isEmpty();
407}
408
409QSsl::SslProtocol QgsAuthConfigSslServer::decodeSslProtocol( const QString &protocol )
410{
411 bool ok = false;
412 const int qt5EnumInt = protocol.toInt( &ok );
413 if ( ok )
414 {
415 // old qt5 enum value. We can't directly cast this to QSsl::SslProtocol, as those values
416 // have changed in qt6!
417 switch ( qt5EnumInt )
418 {
419 case 0: // SslV3
420 return QSsl::SslProtocol::UnknownProtocol;
421
422 case 1: // SslV2
423 case 2:
424 return QSsl::SslProtocol::TlsV1_0;
425 case 3:
426 return QSsl::SslProtocol::TlsV1_1;
427 case 4:
428 return QSsl::SslProtocol::TlsV1_2;
429 case 5:
430 return QSsl::SslProtocol::AnyProtocol;
431 case 6:
432 return QSsl::SslProtocol::TlsV1_3;
433 case 7:
434 return QSsl::SslProtocol::SecureProtocols;
435 case 8:
436 return QSsl::SslProtocol::TlsV1_0OrLater;
437 case 9:
438 return QSsl::SslProtocol::TlsV1_1OrLater;
439 case 10:
440 return QSsl::SslProtocol::TlsV1_2OrLater;
441 case 11:
442 return QSsl::SslProtocol::DtlsV1_0;
443 case 12:
444 return QSsl::SslProtocol::DtlsV1_0OrLater;
445 case 13:
446 return QSsl::SslProtocol::DtlsV1_2;
447 case 14:
448 return QSsl::SslProtocol::DtlsV1_2OrLater;
449 case 15:
450 return QSsl::SslProtocol::TlsV1_3;
451 case 16:
452 return QSsl::SslProtocol::TlsV1_3OrLater;
453 default:
454 return QSsl::SslProtocol::UnknownProtocol;
455 }
456 }
457
458 if ( protocol == "TlsV1_0"_L1 )
459 return QSsl::SslProtocol::TlsV1_0;
460 else if ( protocol == "TlsV1_1"_L1 )
461 return QSsl::SslProtocol::TlsV1_1;
462 else if ( protocol == "TlsV1_2"_L1 )
463 return QSsl::SslProtocol::TlsV1_2;
464 else if ( protocol == "AnyProtocol"_L1 )
465 return QSsl::SslProtocol::AnyProtocol;
466 else if ( protocol == "SecureProtocols"_L1 )
467 return QSsl::SslProtocol::SecureProtocols;
468 else if ( protocol == "TlsV1_0OrLater"_L1 )
469 return QSsl::SslProtocol::TlsV1_0OrLater;
470 else if ( protocol == "TlsV1_1OrLater"_L1 )
471 return QSsl::SslProtocol::TlsV1_1OrLater;
472 else if ( protocol == "TlsV1_2OrLater"_L1 )
473 return QSsl::SslProtocol::TlsV1_2OrLater;
474 else if ( protocol == "DtlsV1_0"_L1 )
475 return QSsl::SslProtocol::DtlsV1_0;
476 else if ( protocol == "DtlsV1_0OrLater"_L1 )
477 return QSsl::SslProtocol::DtlsV1_0OrLater;
478 else if ( protocol == "DtlsV1_2"_L1 )
479 return QSsl::SslProtocol::DtlsV1_2;
480 else if ( protocol == "DtlsV1_2OrLater"_L1 )
481 return QSsl::SslProtocol::DtlsV1_2OrLater;
482 else if ( protocol == "TlsV1_3"_L1 )
483 return QSsl::SslProtocol::TlsV1_3;
484 else if ( protocol == "TlsV1_3OrLater"_L1 )
485 return QSsl::SslProtocol::TlsV1_3OrLater;
486
487 QgsDebugError( u"Can't decode protocol \"%1\""_s.arg( protocol ) );
488
489 return QSsl::SslProtocol::UnknownProtocol;
490}
491
492QString QgsAuthConfigSslServer::encodeSslProtocol( QSsl::SslProtocol protocol )
493{
494 switch ( protocol )
495 {
496 case QSsl::TlsV1_0:
497 return u"TlsV1_0"_s;
498 case QSsl::TlsV1_1:
499 return u"TlsV1_1"_s;
500 case QSsl::TlsV1_2:
501 return u"TlsV1_2"_s;
502 case QSsl::AnyProtocol:
503 return u"AnyProtocol"_s;
504 case QSsl::SecureProtocols:
505 return u"SecureProtocols"_s;
506 case QSsl::TlsV1_0OrLater:
507 return u"TlsV1_0OrLater"_s;
508 case QSsl::TlsV1_1OrLater:
509 return u"TlsV1_1OrLater"_s;
510 case QSsl::TlsV1_2OrLater:
511 return u"TlsV1_2OrLater"_s;
512 case QSsl::DtlsV1_0:
513 return u"DtlsV1_0"_s;
514 case QSsl::DtlsV1_0OrLater:
515 return u"DtlsV1_0OrLater"_s;
516 case QSsl::DtlsV1_2:
517 return u"DtlsV1_2"_s;
518 case QSsl::DtlsV1_2OrLater:
519 return u"DtlsV1_2OrLater"_s;
520 case QSsl::TlsV1_3:
521 return u"TlsV1_3"_s;
522 case QSsl::TlsV1_3OrLater:
523 return u"TlsV1_3OrLater"_s;
524 case QSsl::UnknownProtocol:
525 return u"UnknownProtocol"_s;
526 }
527
528 QgsDebugError( u"Can't encode protocol: %1"_s.arg( protocol ) );
529
530 return u"UnknownProtocol"_s;
531}
532
533#endif
const QList< QSslError > sslIgnoredErrors() const
SSL server errors to ignore in connections.
bool isNull() const
Whether configuration is null (missing components).
const QList< QSslError::SslError > sslIgnoredErrorEnums() const
SSL server errors (as enum list) to ignore in connections.
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.
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.
const QString uri() const
A URI to auto-select a config when connecting to a resource.
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.
bool operator==(const QgsAuthMethodConfig &other) const
const QString id() const
Gets 'authcfg' 7-character alphanumeric ID of the config.
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.
QgsStringMap configMap() const
Gets extended configuration, mapped to key/value pairs of QStrings.
bool operator!=(const QgsAuthMethodConfig &other) const
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...
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.
const QgsAuthMethodConfig config() const
Authentication method configuration.
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:7475
#define QgsDebugError(str)
Definition qgslogger.h:59