1 /***************************************************************************
2 begin : July 30, 2016
3 copyright : (C) 2016 by Monsanto Company, USA
4 author : Larry Shaffer, Boundless Spatial
5 email : lshaffer at boundlessgeo dot com
6 ***************************************************************************
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 ***************************************************************************/
14 #include <functional>
15
16 #include "qgsauthoauth2config.h"
17
18 #include <QDir>
19
20 #include "Json.h"
21
22 #include "qgsapplication.h"
23 #include "qgslogger.h"
24
25
QgsAuthOAuth2Config(QObject * parent)26 QgsAuthOAuth2Config::QgsAuthOAuth2Config( QObject *parent )
27 : QObject( parent )
28 , mQueryPairs( QVariantMap() )
29 {
30
31 // internal signal bounces
32 connect( this, &QgsAuthOAuth2Config::idChanged, this, &QgsAuthOAuth2Config::configChanged );
33 connect( this, &QgsAuthOAuth2Config::versionChanged, this, &QgsAuthOAuth2Config::configChanged );
34 connect( this, &QgsAuthOAuth2Config::configTypeChanged, this, &QgsAuthOAuth2Config::configChanged );
35 connect( this, &QgsAuthOAuth2Config::grantFlowChanged, this, &QgsAuthOAuth2Config::configChanged );
36 connect( this, &QgsAuthOAuth2Config::nameChanged, this, &QgsAuthOAuth2Config::configChanged );
37 connect( this, &QgsAuthOAuth2Config::descriptionChanged, this, &QgsAuthOAuth2Config::configChanged );
38 connect( this, &QgsAuthOAuth2Config::requestUrlChanged, this, &QgsAuthOAuth2Config::configChanged );
39 connect( this, &QgsAuthOAuth2Config::tokenUrlChanged, this, &QgsAuthOAuth2Config::configChanged );
40 connect( this, &QgsAuthOAuth2Config::refreshTokenUrlChanged, this, &QgsAuthOAuth2Config::configChanged );
41 connect( this, &QgsAuthOAuth2Config::redirectUrlChanged, this, &QgsAuthOAuth2Config::configChanged );
42 connect( this, &QgsAuthOAuth2Config::redirectPortChanged, this, &QgsAuthOAuth2Config::configChanged );
43 connect( this, &QgsAuthOAuth2Config::clientIdChanged, this, &QgsAuthOAuth2Config::configChanged );
44 connect( this, &QgsAuthOAuth2Config::clientSecretChanged, this, &QgsAuthOAuth2Config::configChanged );
45 connect( this, &QgsAuthOAuth2Config::usernameChanged, this, &QgsAuthOAuth2Config::configChanged );
46 connect( this, &QgsAuthOAuth2Config::passwordChanged, this, &QgsAuthOAuth2Config::configChanged );
47 connect( this, &QgsAuthOAuth2Config::scopeChanged, this, &QgsAuthOAuth2Config::configChanged );
48 connect( this, &QgsAuthOAuth2Config::apiKeyChanged, this, &QgsAuthOAuth2Config::configChanged );
49 connect( this, &QgsAuthOAuth2Config::persistTokenChanged, this, &QgsAuthOAuth2Config::configChanged );
50 connect( this, &QgsAuthOAuth2Config::accessMethodChanged, this, &QgsAuthOAuth2Config::configChanged );
51 connect( this, &QgsAuthOAuth2Config::requestTimeoutChanged, this, &QgsAuthOAuth2Config::configChanged );
52 connect( this, &QgsAuthOAuth2Config::queryPairsChanged, this, &QgsAuthOAuth2Config::configChanged );
53 connect( this, &QgsAuthOAuth2Config::customHeaderChanged, this, &QgsAuthOAuth2Config::configChanged );
54
55 // always recheck validity on any change
56 // this, in turn, may emit validityChanged( bool )
57 connect( this, &QgsAuthOAuth2Config::configChanged, this, &QgsAuthOAuth2Config::validateConfig );
58
59 validateConfig();
60 }
61
62
setId(const QString & value)63 void QgsAuthOAuth2Config::setId( const QString &value )
64 {
65 const QString preval( mId );
66 mId = value;
67 if ( preval != value )
68 emit idChanged( mId );
69 }
70
setVersion(int value)71 void QgsAuthOAuth2Config::setVersion( int value )
72 {
73 const int preval( mVersion );
74 mVersion = value;
75 if ( preval != value )
76 emit versionChanged( mVersion );
77 }
78
setConfigType(QgsAuthOAuth2Config::ConfigType value)79 void QgsAuthOAuth2Config::setConfigType( QgsAuthOAuth2Config::ConfigType value )
80 {
81 const ConfigType preval( mConfigType );
82 mConfigType = value;
83 if ( preval != value )
84 emit configTypeChanged( mConfigType );
85 }
86
setGrantFlow(QgsAuthOAuth2Config::GrantFlow value)87 void QgsAuthOAuth2Config::setGrantFlow( QgsAuthOAuth2Config::GrantFlow value )
88 {
89 const GrantFlow preval( mGrantFlow );
90 mGrantFlow = value;
91 if ( preval != value )
92 emit grantFlowChanged( mGrantFlow );
93 }
94
setName(const QString & value)95 void QgsAuthOAuth2Config::setName( const QString &value )
96 {
97 const QString preval( mName );
98 mName = value;
99 if ( preval != value )
100 emit nameChanged( mName );
101 }
102
setDescription(const QString & value)103 void QgsAuthOAuth2Config::setDescription( const QString &value )
104 {
105 const QString preval( mDescription );
106 mDescription = value;
107 if ( preval != value )
108 emit descriptionChanged( mDescription );
109 }
110
setRequestUrl(const QString & value)111 void QgsAuthOAuth2Config::setRequestUrl( const QString &value )
112 {
113 const QString preval( mRequestUrl );
114 mRequestUrl = value;
115 if ( preval != value )
116 emit requestUrlChanged( mRequestUrl );
117 }
118
setTokenUrl(const QString & value)119 void QgsAuthOAuth2Config::setTokenUrl( const QString &value )
120 {
121 const QString preval( mTokenUrl );
122 mTokenUrl = value;
123 if ( preval != value )
124 emit tokenUrlChanged( mTokenUrl );
125 }
126
setRefreshTokenUrl(const QString & value)127 void QgsAuthOAuth2Config::setRefreshTokenUrl( const QString &value )
128 {
129 const QString preval( mRefreshTokenUrl );
130 mRefreshTokenUrl = value;
131 if ( preval != value )
132 emit refreshTokenUrlChanged( mRefreshTokenUrl );
133 }
134
setRedirectUrl(const QString & value)135 void QgsAuthOAuth2Config::setRedirectUrl( const QString &value )
136 {
137 const QString preval( mRedirectURL );
138 mRedirectURL = value;
139 if ( preval != value )
140 emit redirectUrlChanged( mRedirectURL );
141 }
142
setRedirectPort(int value)143 void QgsAuthOAuth2Config::setRedirectPort( int value )
144 {
145 const int preval( mRedirectPort );
146 mRedirectPort = value;
147 if ( preval != value )
148 emit redirectPortChanged( mRedirectPort );
149 }
150
setClientId(const QString & value)151 void QgsAuthOAuth2Config::setClientId( const QString &value )
152 {
153 const QString preval( mClientId );
154 mClientId = value;
155 if ( preval != value )
156 emit clientIdChanged( mClientId );
157 }
158
setClientSecret(const QString & value)159 void QgsAuthOAuth2Config::setClientSecret( const QString &value )
160 {
161 const QString preval( mClientSecret );
162 mClientSecret = value;
163 if ( preval != value )
164 emit clientSecretChanged( mClientSecret );
165 }
166
setUsername(const QString & value)167 void QgsAuthOAuth2Config::setUsername( const QString &value )
168 {
169 const QString preval( mUsername );
170 mUsername = value;
171 if ( preval != value )
172 emit usernameChanged( mUsername );
173 }
174
setPassword(const QString & value)175 void QgsAuthOAuth2Config::setPassword( const QString &value )
176 {
177 const QString preval( mPassword );
178 mPassword = value;
179 if ( preval != value )
180 emit passwordChanged( mPassword );
181 }
182
setScope(const QString & value)183 void QgsAuthOAuth2Config::setScope( const QString &value )
184 {
185 const QString preval( mScope );
186 mScope = value;
187 if ( preval != value )
188 emit scopeChanged( mScope );
189 }
190
setApiKey(const QString & value)191 void QgsAuthOAuth2Config::setApiKey( const QString &value )
192 {
193 const QString preval( mApiKey );
194 mApiKey = value;
195 if ( preval != value )
196 emit apiKeyChanged( mApiKey );
197 }
198
setPersistToken(bool persist)199 void QgsAuthOAuth2Config::setPersistToken( bool persist )
200 {
201 const bool preval( mPersistToken );
202 mPersistToken = persist;
203 if ( preval != persist )
204 emit persistTokenChanged( mPersistToken );
205 }
206
setAccessMethod(QgsAuthOAuth2Config::AccessMethod value)207 void QgsAuthOAuth2Config::setAccessMethod( QgsAuthOAuth2Config::AccessMethod value )
208 {
209 const AccessMethod preval( mAccessMethod );
210 mAccessMethod = value;
211 if ( preval != value )
212 emit accessMethodChanged( mAccessMethod );
213 }
214
setCustomHeader(const QString & header)215 void QgsAuthOAuth2Config::setCustomHeader( const QString &header )
216 {
217 const QString preval( mCustomHeader );
218 mCustomHeader = header;
219 if ( preval != header )
220 emit customHeaderChanged( mCustomHeader );
221 }
222
setRequestTimeout(int value)223 void QgsAuthOAuth2Config::setRequestTimeout( int value )
224 {
225 const int preval( mRequestTimeout );
226 mRequestTimeout = value;
227 if ( preval != value )
228 emit requestTimeoutChanged( mRequestTimeout );
229 }
230
setQueryPairs(const QVariantMap & pairs)231 void QgsAuthOAuth2Config::setQueryPairs( const QVariantMap &pairs )
232 {
233 const QVariantMap preval( mQueryPairs );
234 mQueryPairs = pairs;
235 if ( preval != pairs )
236 emit queryPairsChanged( mQueryPairs );
237 }
238
setToDefaults()239 void QgsAuthOAuth2Config::setToDefaults()
240 {
241 setId( QString() );
242 setVersion( 1 );
243 setConfigType( QgsAuthOAuth2Config::Custom );
244 setGrantFlow( QgsAuthOAuth2Config::AuthCode );
245 setName( QString() );
246 setDescription( QString() );
247 setRequestUrl( QString() );
248 setTokenUrl( QString() );
249 setRefreshTokenUrl( QString() );
250 setRedirectUrl( QString() );
251 setRedirectPort( 7070 );
252 setClientId( QString() );
253 setClientSecret( QString() );
254 setUsername( QString() );
255 setPassword( QString() );
256 setScope( QString() );
257 setApiKey( QString() );
258 setPersistToken( false );
259 setAccessMethod( QgsAuthOAuth2Config::Header );
260 setCustomHeader( QString() );
261 setRequestTimeout( 30 ); // in seconds
262 setQueryPairs( QVariantMap() );
263 }
264
operator ==(const QgsAuthOAuth2Config & other) const265 bool QgsAuthOAuth2Config::operator==( const QgsAuthOAuth2Config &other ) const
266 {
267 return ( other.version() == this->version()
268 && other.configType() == this->configType()
269 && other.grantFlow() == this->grantFlow()
270 && other.name() == this->name()
271 && other.description() == this->description()
272 && other.requestUrl() == this->requestUrl()
273 && other.tokenUrl() == this->tokenUrl()
274 && other.refreshTokenUrl() == this->refreshTokenUrl()
275 && other.redirectUrl() == this->redirectUrl()
276 && other.redirectPort() == this->redirectPort()
277 && other.clientId() == this->clientId()
278 && other.clientSecret() == this->clientSecret()
279 && other.username() == this->username()
280 && other.password() == this->password()
281 && other.scope() == this->scope()
282 && other.apiKey() == this->apiKey()
283 && other.persistToken() == this->persistToken()
284 && other.accessMethod() == this->accessMethod()
285 && other.customHeader() == this->customHeader()
286 && other.requestTimeout() == this->requestTimeout()
287 && other.queryPairs() == this->queryPairs() );
288 }
289
operator !=(const QgsAuthOAuth2Config & other) const290 bool QgsAuthOAuth2Config::operator!=( const QgsAuthOAuth2Config &other ) const
291 {
292 return !( *this == other );
293 }
294
isValid() const295 bool QgsAuthOAuth2Config::isValid() const
296 {
297 return mValid;
298 }
299
300 // slot
validateConfig()301 void QgsAuthOAuth2Config::validateConfig()
302 {
303 validateConfigId( false );
304 }
305
306 // public
validateConfigId(bool needsId)307 void QgsAuthOAuth2Config::validateConfigId( bool needsId )
308 {
309 const bool oldvalid = mValid;
310
311 if ( mGrantFlow == AuthCode || mGrantFlow == Implicit )
312 {
313 mValid = ( !requestUrl().isEmpty()
314 && !tokenUrl().isEmpty()
315 && !clientId().isEmpty()
316 && ( mGrantFlow == AuthCode ? !clientSecret().isEmpty() : true )
317 && redirectPort() > 0
318 && ( needsId ? !id().isEmpty() : true ) );
319 }
320 else if ( mGrantFlow == ResourceOwner )
321 {
322 mValid = ( !tokenUrl().isEmpty()
323 && !username().isEmpty()
324 && !password().isEmpty()
325 && ( needsId ? !id().isEmpty() : true ) );
326 }
327
328 if ( mValid != oldvalid )
329 emit validityChanged( mValid );
330 }
331
loadConfigTxt(const QByteArray & configtxt,QgsAuthOAuth2Config::ConfigFormat format)332 bool QgsAuthOAuth2Config::loadConfigTxt(
333 const QByteArray &configtxt, QgsAuthOAuth2Config::ConfigFormat format )
334 {
335 QByteArray errStr;
336 bool res = false;
337
338 switch ( format )
339 {
340 case JSON:
341 {
342 const QVariant variant = QJsonWrapper::parseJson( configtxt, &res, &errStr );
343 if ( !res )
344 {
345 QgsDebugMsg( QStringLiteral( "Error parsing JSON: %1" ).arg( QString( errStr ) ) );
346 return res;
347 }
348 const QVariantMap variantMap = variant.toMap();
349 // safety check -- qvariant2qobject asserts if an non-matching property is found in the json
350 for ( QVariantMap::const_iterator iter = variantMap.constBegin(); iter != variantMap.constEnd(); ++iter )
351 {
352 const QVariant property = this->property( iter.key().toLatin1() );
353 if ( !property.isValid() ) // e.g. not a auth config json file
354 return false;
355 }
356
357 QJsonWrapper::qvariant2qobject( variantMap, this );
358 break;
359 }
360 default:
361 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
362 }
363 return true;
364 }
365
saveConfigTxt(QgsAuthOAuth2Config::ConfigFormat format,bool pretty,bool * ok) const366 QByteArray QgsAuthOAuth2Config::saveConfigTxt(
367 QgsAuthOAuth2Config::ConfigFormat format, bool pretty, bool *ok ) const
368 {
369 QByteArray out;
370 QByteArray errStr;
371 bool res = false;
372
373 if ( !isValid() )
374 {
375 QgsDebugMsg( QStringLiteral( "FAILED, config is not valid" ) );
376 if ( ok )
377 *ok = res;
378 return out;
379 }
380
381 switch ( format )
382 {
383 case JSON:
384 {
385 const QVariantMap variant = QJsonWrapper::qobject2qvariant( this );
386 out = QJsonWrapper::toJson( variant, &res, &errStr, pretty );
387 if ( !res )
388 {
389 QgsDebugMsg( QStringLiteral( "Error serializing JSON: %1" ).arg( QString( errStr ) ) );
390 }
391 break;
392 }
393 default:
394 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
395 }
396
397 if ( ok )
398 *ok = res;
399 return out;
400 }
401
mappedProperties() const402 QVariantMap QgsAuthOAuth2Config::mappedProperties() const
403 {
404 QVariantMap vmap;
405 vmap.insert( QStringLiteral( "apiKey" ), this->apiKey() );
406 vmap.insert( QStringLiteral( "clientId" ), this->clientId() );
407 vmap.insert( QStringLiteral( "clientSecret" ), this->clientSecret() );
408 vmap.insert( QStringLiteral( "configType" ), static_cast<int>( this->configType() ) );
409 vmap.insert( QStringLiteral( "description" ), this->description() );
410 vmap.insert( QStringLiteral( "grantFlow" ), static_cast<int>( this->grantFlow() ) );
411 vmap.insert( QStringLiteral( "id" ), this->id() );
412 vmap.insert( QStringLiteral( "name" ), this->name() );
413 vmap.insert( QStringLiteral( "password" ), this->password() );
414 vmap.insert( QStringLiteral( "persistToken" ), this->persistToken() );
415 vmap.insert( QStringLiteral( "queryPairs" ), this->queryPairs() );
416 vmap.insert( QStringLiteral( "redirectPort" ), this->redirectPort() );
417 vmap.insert( QStringLiteral( "redirectUrl" ), this->redirectUrl() );
418 vmap.insert( QStringLiteral( "refreshTokenUrl" ), this->refreshTokenUrl() );
419 vmap.insert( QStringLiteral( "accessMethod" ), static_cast<int>( this->accessMethod() ) );
420 vmap.insert( QStringLiteral( "customHeader" ), this->customHeader() );
421 vmap.insert( QStringLiteral( "requestTimeout" ), this->requestTimeout() );
422 vmap.insert( QStringLiteral( "requestUrl" ), this->requestUrl() );
423 vmap.insert( QStringLiteral( "scope" ), this->scope() );
424 vmap.insert( QStringLiteral( "tokenUrl" ), this->tokenUrl() );
425 vmap.insert( QStringLiteral( "username" ), this->username() );
426 vmap.insert( QStringLiteral( "version" ), this->version() );
427
428 return vmap;
429 }
430
431 // static
serializeFromVariant(const QVariantMap & variant,QgsAuthOAuth2Config::ConfigFormat format,bool pretty,bool * ok)432 QByteArray QgsAuthOAuth2Config::serializeFromVariant(
433 const QVariantMap &variant,
434 QgsAuthOAuth2Config::ConfigFormat format,
435 bool pretty,
436 bool *ok )
437 {
438 QByteArray out;
439 QByteArray errStr;
440 bool res = false;
441
442 switch ( format )
443 {
444 case JSON:
445 out = QJsonWrapper::toJson( variant, &res, &errStr, pretty );
446 if ( !res )
447 {
448 QgsDebugMsg( QStringLiteral( "Error serializing JSON: %1" ).arg( QString( errStr ) ) );
449 }
450 break;
451 default:
452 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
453 }
454
455 if ( ok )
456 *ok = res;
457 return out;
458 }
459
460 // static
variantFromSerialized(const QByteArray & serial,QgsAuthOAuth2Config::ConfigFormat format,bool * ok)461 QVariantMap QgsAuthOAuth2Config::variantFromSerialized(
462 const QByteArray &serial,
463 QgsAuthOAuth2Config::ConfigFormat format,
464 bool *ok )
465 {
466 QVariantMap vmap;
467 QByteArray errStr;
468 bool res = false;
469
470 switch ( format )
471 {
472 case JSON:
473 {
474 const QVariant var = QJsonWrapper::parseJson( serial, &res, &errStr );
475 if ( !res )
476 {
477 QgsDebugMsg( QStringLiteral( "Error parsing JSON to variant: %1" ).arg( QString( errStr ) ) );
478 if ( ok )
479 *ok = res;
480 return vmap;
481 }
482
483 if ( var.isNull() )
484 {
485 QgsDebugMsg( QStringLiteral( "Error parsing JSON to variant: %1" ).arg( "invalid or null" ) );
486 if ( ok )
487 *ok = res;
488 return vmap;
489 }
490 vmap = var.toMap();
491 if ( vmap.isEmpty() )
492 {
493 QgsDebugMsg( QStringLiteral( "Error parsing JSON to variantmap: %1" ).arg( "map empty" ) );
494 if ( ok )
495 *ok = res;
496 return vmap;
497 }
498 break;
499 }
500 default:
501 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
502 }
503
504 if ( ok )
505 *ok = res;
506 return vmap;
507 }
508
509 //static
writeOAuth2Config(const QString & filepath,QgsAuthOAuth2Config * config,QgsAuthOAuth2Config::ConfigFormat format,bool pretty)510 bool QgsAuthOAuth2Config::writeOAuth2Config(
511 const QString &filepath,
512 QgsAuthOAuth2Config *config,
513 QgsAuthOAuth2Config::ConfigFormat format,
514 bool pretty )
515 {
516 bool res = false;
517 const QByteArray configtxt = config->saveConfigTxt( format, pretty, &res );
518 if ( !res )
519 {
520 QgsDebugMsg( QStringLiteral( "FAILED to save config to text" ) );
521 return false;
522 }
523
524 QFile config_file( filepath );
525 const QString file_path( config_file.fileName() );
526
527 if ( config_file.open( QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text ) )
528 {
529 const qint64 bytesWritten = config_file.write( configtxt );
530 config_file.close();
531 if ( bytesWritten == -1 )
532 {
533 QgsDebugMsg( QStringLiteral( "FAILED to write config file: %1" ).arg( file_path ) );
534 return false;
535 }
536 }
537 else
538 {
539 QgsDebugMsg( QStringLiteral( "FAILED to open for writing config file: %1" ).arg( file_path ) );
540 return false;
541 }
542
543 if ( !config_file.setPermissions( QFile::ReadOwner | QFile::WriteOwner ) )
544 {
545 QgsDebugMsg( QStringLiteral( "FAILED to set permissions config file: %1" ).arg( file_path ) );
546 return false;
547 }
548
549 return true;
550 }
551
552 // static
loadOAuth2Configs(const QString & configdirectory,QObject * parent,QgsAuthOAuth2Config::ConfigFormat format,bool * ok)553 QList<QgsAuthOAuth2Config *> QgsAuthOAuth2Config::loadOAuth2Configs(
554 const QString &configdirectory,
555 QObject *parent,
556 QgsAuthOAuth2Config::ConfigFormat format,
557 bool *ok )
558 {
559 QList<QgsAuthOAuth2Config *> configs = QList<QgsAuthOAuth2Config *>();
560 const bool res = false;
561 QStringList namefilters;
562
563 switch ( format )
564 {
565 case JSON:
566 namefilters << QStringLiteral( "*.json" );
567 break;
568 default:
569 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
570 if ( ok )
571 *ok = res;
572 return configs;
573 }
574
575 QDir configdir( configdirectory );
576 configdir.setNameFilters( namefilters );
577 const QStringList configfiles = configdir.entryList( namefilters );
578
579 if ( configfiles.size() > 0 )
580 {
581 QgsDebugMsg( QStringLiteral( "Config files found in: %1...\n%2" )
582 .arg( configdir.path(), configfiles.join( QLatin1String( ", " ) ) ) );
583 }
584 else
585 {
586 QgsDebugMsg( QStringLiteral( "No config files found in: %1" ).arg( configdir.path() ) );
587 if ( ok ) *ok = res;
588 return configs;
589 }
590
591 // Add entries
592 for ( const auto &configfile : configfiles )
593 {
594 QByteArray configtxt;
595 QFile cfile( configdir.path() + '/' + configfile );
596 if ( cfile.exists() )
597 {
598 const bool ret = cfile.open( QIODevice::ReadOnly | QIODevice::Text );
599 if ( ret )
600 {
601 configtxt = cfile.readAll();
602 }
603 else
604 {
605 QgsDebugMsg( QStringLiteral( "FAILED to open config for reading: %1" ).arg( configfile ) );
606 }
607 cfile.close();
608 }
609
610 if ( configtxt.isEmpty() )
611 {
612 QgsDebugMsg( QStringLiteral( "EMPTY read of config: %1" ).arg( configfile ) );
613 continue;
614 }
615
616 QgsAuthOAuth2Config *config = new QgsAuthOAuth2Config( parent );
617 if ( !config->loadConfigTxt( configtxt, format ) )
618 {
619 QgsDebugMsg( QStringLiteral( "FAILED to load config: %1" ).arg( configfile ) );
620 config->deleteLater();
621 continue;
622 }
623 configs << config;
624 }
625
626 if ( ok ) *ok = true;
627 return configs;
628 }
629
630 // static
mapOAuth2Configs(const QString & configdirectory,QObject * parent,QgsAuthOAuth2Config::ConfigFormat format,bool * ok)631 QgsStringMap QgsAuthOAuth2Config::mapOAuth2Configs(
632 const QString &configdirectory,
633 QObject *parent,
634 QgsAuthOAuth2Config::ConfigFormat format,
635 bool *ok )
636 {
637 QgsStringMap configs = QgsStringMap();
638 const bool res = false;
639 QStringList namefilters;
640
641 switch ( format )
642 {
643 case JSON:
644 namefilters << QStringLiteral( "*.json" );
645 break;
646 default:
647 QgsDebugMsg( QStringLiteral( "Unsupported output format" ) );
648 if ( ok )
649 *ok = res;
650 return configs;
651 }
652
653 QDir configdir( configdirectory );
654 configdir.setNameFilters( namefilters );
655 const QStringList configfiles = configdir.entryList( namefilters );
656
657 if ( configfiles.size() > 0 )
658 {
659 QgsDebugMsg( QStringLiteral( "Config files found in: %1...\n%2" )
660 .arg( configdir.path(), configfiles.join( QLatin1String( ", " ) ) ) );
661 }
662 else
663 {
664 QgsDebugMsg( QStringLiteral( "No config files found in: %1" ).arg( configdir.path() ) );
665 if ( ok )
666 *ok = res;
667 return configs;
668 }
669
670 // Add entries
671 for ( const auto &configfile : configfiles )
672 {
673 QByteArray configtxt;
674 QFile cfile( configdir.path() + '/' + configfile );
675 if ( cfile.exists() )
676 {
677 const bool ret = cfile.open( QIODevice::ReadOnly | QIODevice::Text );
678 if ( ret )
679 {
680 configtxt = cfile.readAll();
681 }
682 else
683 {
684 QgsDebugMsg( QStringLiteral( "FAILED to open config for reading: %1" ).arg( configfile ) );
685 }
686 cfile.close();
687 }
688
689 if ( configtxt.isEmpty() )
690 {
691 QgsDebugMsg( QStringLiteral( "EMPTY read of config: %1" ).arg( configfile ) );
692 continue;
693 }
694
695 // validate the config before caching it
696 std::unique_ptr<QgsAuthOAuth2Config, std::function<void( QgsAuthOAuth2Config * )> > config( new QgsAuthOAuth2Config( parent ), []( QgsAuthOAuth2Config * cfg ) { cfg->deleteLater( );} );
697 if ( !config->loadConfigTxt( configtxt, format ) )
698 {
699 QgsDebugMsg( QStringLiteral( "FAILED to load config: %1" ).arg( configfile ) );
700 continue;
701 }
702 if ( config->id().isEmpty() )
703 {
704 QgsDebugMsg( QStringLiteral( "NO ID SET for config: %1" ).arg( configfile ) );
705 continue;
706 }
707 configs.insert( config->id(), configtxt );
708 }
709
710 if ( ok )
711 *ok = true;
712 return configs;
713 }
714
configLocations(const QString & extradir)715 QStringList QgsAuthOAuth2Config::configLocations( const QString &extradir )
716 {
717 QStringList dirs;
718 // in order of override preference, i.e. user over pkg dir
719 dirs << QgsAuthOAuth2Config::oauth2ConfigsPkgDataDir()
720 << QgsAuthOAuth2Config::oauth2ConfigsUserSettingsDir();
721
722 if ( !extradir.isEmpty() )
723 {
724 // configs of similar IDs in this dir will override existing in standard dirs
725 dirs << extradir;
726 }
727 return dirs;
728 }
729
mappedOAuth2ConfigsCache(QObject * parent,const QString & extradir)730 QgsStringMap QgsAuthOAuth2Config::mappedOAuth2ConfigsCache( QObject *parent, const QString &extradir )
731 {
732 QgsStringMap configs;
733 bool ok = false;
734
735 // Load from default locations
736 const QStringList configdirs = configLocations( extradir );
737 for ( const auto &configdir : configdirs )
738 {
739 const QFileInfo configdirinfo( configdir );
740 if ( !configdirinfo.exists() || !configdirinfo.isDir() )
741 {
742 continue;
743 }
744 const QgsStringMap newconfigs = QgsAuthOAuth2Config::mapOAuth2Configs(
745 configdirinfo.canonicalFilePath(), parent, QgsAuthOAuth2Config::JSON, &ok );
746 if ( ok )
747 {
748 QgsStringMap::const_iterator i = newconfigs.constBegin();
749 while ( i != newconfigs.constEnd() )
750 {
751 configs.insert( i.key(), i.value() );
752 ++i;
753 }
754 }
755 }
756 return configs;
757 }
758
759 // static
oauth2ConfigsPkgDataDir()760 QString QgsAuthOAuth2Config::oauth2ConfigsPkgDataDir()
761 {
762 return QgsApplication::pkgDataPath() + QStringLiteral( "/oauth2_configs" );
763 }
764
765 // static
oauth2ConfigsUserSettingsDir()766 QString QgsAuthOAuth2Config::oauth2ConfigsUserSettingsDir()
767 {
768 return QgsApplication::qgisSettingsDirPath() + QStringLiteral( "oauth2_configs" );
769 }
770
771 // static
configTypeString(QgsAuthOAuth2Config::ConfigType configtype)772 QString QgsAuthOAuth2Config::configTypeString( QgsAuthOAuth2Config::ConfigType configtype )
773 {
774 switch ( configtype )
775 {
776 case QgsAuthOAuth2Config::Custom:
777 return tr( "Custom" );
778 case QgsAuthOAuth2Config::Predefined:
779 default:
780 return tr( "Predefined" );
781 }
782 }
783
784 // static
grantFlowString(QgsAuthOAuth2Config::GrantFlow flow)785 QString QgsAuthOAuth2Config::grantFlowString( QgsAuthOAuth2Config::GrantFlow flow )
786 {
787 switch ( flow )
788 {
789 case QgsAuthOAuth2Config::AuthCode:
790 return tr( "Authorization Code" );
791 case QgsAuthOAuth2Config::Implicit:
792 return tr( "Implicit" );
793 case QgsAuthOAuth2Config::ResourceOwner:
794 default:
795 return tr( "Resource Owner" );
796 }
797 }
798
799 // static
accessMethodString(QgsAuthOAuth2Config::AccessMethod method)800 QString QgsAuthOAuth2Config::accessMethodString( QgsAuthOAuth2Config::AccessMethod method )
801 {
802 switch ( method )
803 {
804 case QgsAuthOAuth2Config::Header:
805 return tr( "Header" );
806 case QgsAuthOAuth2Config::Form:
807 return tr( "Form (POST only)" );
808 case QgsAuthOAuth2Config::Query:
809 default:
810 return tr( "URL Query" );
811 }
812 }
813
814 // static
tokenCacheDirectory(bool temporary)815 QString QgsAuthOAuth2Config::tokenCacheDirectory( bool temporary )
816 {
817 const QDir setdir( QgsApplication::qgisSettingsDirPath() );
818 return QStringLiteral( "%1/oauth2-cache" ).arg( temporary ? QDir::tempPath() : setdir.canonicalPath() );
819 }
820
821 // static
tokenCacheFile(const QString & suffix)822 QString QgsAuthOAuth2Config::tokenCacheFile( const QString &suffix )
823 {
824 return QStringLiteral( "authcfg-%1.ini" ).arg( !suffix.isEmpty() ? suffix : QStringLiteral( "cache" ) );
825 }
826
827 // static
tokenCachePath(const QString & suffix,bool temporary)828 QString QgsAuthOAuth2Config::tokenCachePath( const QString &suffix, bool temporary )
829 {
830 return QStringLiteral( "%1/%2" ).arg( QgsAuthOAuth2Config::tokenCacheDirectory( temporary ),
831 QgsAuthOAuth2Config::tokenCacheFile( suffix ) );
832 }
833