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