30
30
#include < QTimer>
31
31
#include < QUrlQuery>
32
32
#include < qgsapplication.h>
33
+ #include < qgsauthmanager.h>
33
34
#include < qgsmessagelog.h>
34
35
#include < qgsnetworkaccessmanager.h>
35
36
#include < qgssettings.h>
@@ -39,12 +40,20 @@ QFieldCloudConnection::QFieldCloudConnection()
39
40
: mUrl( QSettings().value( QStringLiteral( " /QFieldCloud/url" ), defaultUrl() ).toString() )
40
41
, mUsername( QSettings().value( QStringLiteral( " /QFieldCloud/username" ) ).toString() )
41
42
, mToken( QSettings().value( QStringLiteral( " /QFieldCloud/token" ) ).toByteArray() )
43
+ , mProvider( QSettings().value( QStringLiteral( " /QFieldCloud/provider" ) ).toString() )
44
+ , mProviderConfigId( QSettings().value( QStringLiteral( " /QFieldCloud/providerConfigId" ) ).toString() )
42
45
{
43
46
QgsNetworkAccessManager::instance ()->setTimeout ( 60 * 60 * 1000 );
44
47
QgsNetworkAccessManager::instance ()->setTransferTimeout ( 5 * 60 * 1000 );
45
48
// we cannot use "/" as separator, since QGIS puts a suffix QGIS/31700 anyway
46
49
const QString userAgent = QStringLiteral ( " qfield|%1|%2|%3|" ).arg ( qfield::appVersion, qfield::appVersionStr.normalized ( QString::NormalizationForm_KD ), qfield::gitRev );
47
50
QgsSettings ().setValue ( QStringLiteral ( " /qgis/networkAndProxy/userAgent" ), userAgent );
51
+
52
+ if ( !QgsApplication::authManager ()->availableAuthMethodConfigs ().contains ( mProviderConfigId ) )
53
+ {
54
+ mProviderConfigId .clear ();
55
+ QSettings ().remove ( " /QFieldCloud/providerConfigId" );
56
+ }
48
57
}
49
58
50
59
QMap<QString, QString> QFieldCloudConnection::sErrors = QMap<QString, QString>(
@@ -104,14 +113,30 @@ QStringList QFieldCloudConnection::urls() const
104
113
return savedUrls;
105
114
}
106
115
107
- QString QFieldCloudConnection::username () const
116
+ QString QFieldCloudConnection::avatarUrl () const
108
117
{
109
- return mUsername ;
118
+ return mAvatarUrl ;
110
119
}
111
120
112
- QString QFieldCloudConnection::avatarUrl () const
121
+ QString QFieldCloudConnection::provider () const
113
122
{
114
- return mAvatarUrl ;
123
+ return mProvider ;
124
+ }
125
+
126
+ void QFieldCloudConnection::setProvider ( const QString &provider )
127
+ {
128
+ if ( mProvider == provider )
129
+ return ;
130
+
131
+ mProvider = provider;
132
+ QSettings ().setValue ( QStringLiteral ( " /QFieldCloud/provider" ), provider );
133
+
134
+ emit providerChanged ();
135
+ }
136
+
137
+ QString QFieldCloudConnection::username () const
138
+ {
139
+ return mUsername ;
115
140
}
116
141
117
142
void QFieldCloudConnection::setUsername ( const QString &username )
@@ -149,9 +174,74 @@ CloudUserInformation QFieldCloudConnection::userInformation() const
149
174
return mUserInformation ;
150
175
}
151
176
177
+ bool QFieldCloudConnection::isFetchingAvailableProviders () const
178
+ {
179
+ return mIsFetchingAvailableProviders ;
180
+ }
181
+
182
+ QList<AuthenticationProvider> QFieldCloudConnection::availableProviders () const
183
+ {
184
+ return mAvailableProviders .values ();
185
+ }
186
+
187
+ void QFieldCloudConnection::getAuthenticationProviders ()
188
+ {
189
+ if ( !mAvailableProviders .isEmpty () )
190
+ {
191
+ mAvailableProviders .clear ();
192
+ emit availableProvidersChanged ();
193
+ }
194
+
195
+ mIsFetchingAvailableProviders = true ;
196
+ emit isFetchingAvailableProvidersChanged ();
197
+
198
+ QNetworkRequest request;
199
+ request.setHeader ( QNetworkRequest::ContentTypeHeader, " application/json" );
200
+ request.setAttribute ( QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy );
201
+ NetworkReply *reply = get ( request, " /api/v1/auth/providers/" );
202
+
203
+ connect ( reply, &NetworkReply::finished, this , [=]() {
204
+ QNetworkReply *rawReply = reply->currentRawReply ();
205
+
206
+ Q_ASSERT ( reply->isFinished () );
207
+ Q_ASSERT ( rawReply );
208
+
209
+ reply->deleteLater ();
210
+ rawReply->deleteLater ();
211
+
212
+ mIsFetchingAvailableProviders = false ;
213
+ emit isFetchingAvailableProvidersChanged ();
214
+
215
+ if ( rawReply->error () != QNetworkReply::NoError )
216
+ {
217
+ return ;
218
+ }
219
+
220
+ const QVariantList providers = QJsonDocument::fromJson ( rawReply->readAll () ).toVariant ().toList ();
221
+ for ( const QVariant &provider : providers )
222
+ {
223
+ const QVariantMap providerDetails = provider.toMap ();
224
+ const QString providerId = providerDetails.value ( QStringLiteral ( " id" ) ).toString ();
225
+ mAvailableProviders [providerId] = AuthenticationProvider ( providerId, providerDetails.value ( QStringLiteral ( " name" ) ).toString (), providerDetails );
226
+ }
227
+ emit availableProvidersChanged ();
228
+ } );
229
+ }
230
+
152
231
void QFieldCloudConnection::login ()
153
232
{
154
- const bool loginUsingToken = !mToken .isEmpty () && ( mPassword .isEmpty () || mUsername .isEmpty () );
233
+ if ( !mProvider .isEmpty () )
234
+ {
235
+ if ( mProviderConfigId .isEmpty () && !mAvailableProviders .contains ( mProvider ) )
236
+ {
237
+ emit loginFailed ( tr ( " Authentication provider missing" ) );
238
+ return ;
239
+ }
240
+ }
241
+
242
+ setStatus ( ConnectionStatus::Connecting );
243
+
244
+ const bool loginUsingToken = !mProvider .isEmpty () || ( !mToken .isEmpty () && ( mPassword .isEmpty () || mUsername .isEmpty () ) );
155
245
NetworkReply *reply = loginUsingToken
156
246
? get ( QStringLiteral ( " /api/v1/auth/user/" ) )
157
247
: post ( QStringLiteral ( " /api/v1/auth/token/" ), QVariantMap (
@@ -160,8 +250,6 @@ void QFieldCloudConnection::login()
160
250
{ " password" , mPassword },
161
251
} ) );
162
252
163
- setStatus ( ConnectionStatus::Connecting );
164
-
165
253
// Handle login redirect as an error state
166
254
connect ( reply, &NetworkReply::redirected, this , [=]() {
167
255
QNetworkReply *rawReply = reply->currentRawReply ();
@@ -272,6 +360,14 @@ void QFieldCloudConnection::logout()
272
360
mAvatarUrl .clear ();
273
361
emit avatarUrlChanged ();
274
362
363
+ if ( !mProviderConfigId .isEmpty () )
364
+ {
365
+ QgsApplication::instance ()->authManager ()->removeAuthenticationConfig ( mProviderConfigId );
366
+ mProviderConfigId .clear ();
367
+ QSettings ().remove ( " /QFieldCloud/providerConfigId" );
368
+ emit providerConfigurationChanged ();
369
+ }
370
+
275
371
setStatus ( ConnectionStatus::Disconnected );
276
372
}
277
373
@@ -469,6 +565,66 @@ void QFieldCloudConnection::setAuthenticationToken( QNetworkRequest &request )
469
565
{
470
566
request.setRawHeader ( " Authorization" , " Token " + mToken );
471
567
}
568
+
569
+ if ( !mProvider .isEmpty () )
570
+ {
571
+ if ( mProviderConfigId .isEmpty () && mAvailableProviders .contains ( mProvider ) )
572
+ {
573
+ const QVariantMap providerDetails = mAvailableProviders [mProvider ].details ();
574
+ const QVariantMap providerExtraTokens = providerDetails.value ( " extra_tokens" ).toMap ();
575
+ QStringList extraTokens;
576
+ for ( const QString &key : providerExtraTokens.keys () )
577
+ {
578
+ extraTokens << QStringLiteral ( " \" %1\" :\" %2\" " ).arg ( key, providerExtraTokens.value ( key ).toString () );
579
+ }
580
+
581
+ QgsAuthMethodConfig config;
582
+ config.setName ( " qfieldcloud-sso" );
583
+ config.setMethod ( " OAuth2" );
584
+ config.setConfig ( " oauth2config" , QStringLiteral ( " {\" accessMethod\" :0,"
585
+ " \" apiKey\" :null,"
586
+ " \" clientId\" :\" %1\" ,"
587
+ " \" clientSecret\" :\" %2\" ,"
588
+ " \" configType\" :1,"
589
+ " \" customHeader\" :null,"
590
+ " \" description\" :\"\" ,"
591
+ " \" grantFlow\" :0,"
592
+ " \" id\" :null,"
593
+ " \" name\" :null,"
594
+ " \" objectName\" :\"\" ,"
595
+ " \" password\" :null,"
596
+ " \" persistToken\" :true,"
597
+ " \" queryPairs\" :{\" 1\" :\" 1\" },"
598
+ " \" redirectHost\" :\" localhost\" ,"
599
+ " \" redirectPort\" :7070,"
600
+ " \" redirectUrl\" :null,"
601
+ " \" requestUrl\" :\" %3\" ,"
602
+ " \" tokenUrl\" :\" %4\" ,"
603
+ " \" refreshTokenUrl\" :\" %5\" ,"
604
+ " \" scope\" :\" %6\" ,"
605
+ " \" extraTokens\" :{%7},"
606
+ " \" requestTimeout\" :30,"
607
+ " \" username\" :null,"
608
+ " \" version\" :1"
609
+ " }" )
610
+ .arg ( providerDetails.value ( " client_id" ).toString (),
611
+ providerDetails.value ( " client_secret" ).toString (),
612
+ providerDetails.value ( " request_url" ).toString (),
613
+ providerDetails.value ( " token_url" ).toString (),
614
+ providerDetails.value ( " refresh_token_url" ).toString (),
615
+ providerDetails.value ( " scope" ).toString (),
616
+ extraTokens.join ( ' ,' ) ) );
617
+ qDebug () << config.config ( " oauth2config" );
618
+ QgsApplication::instance ()->authManager ()->storeAuthenticationConfig ( config, true );
619
+
620
+ mProviderConfigId = config.id ();
621
+ qDebug () << mProviderConfigId ;
622
+ QSettings ().setValue ( QStringLiteral ( " /QFieldCloud/providerConfigId" ), mProviderConfigId );
623
+ emit providerConfigurationChanged ();
624
+ }
625
+
626
+ QgsApplication::instance ()->authManager ()->updateNetworkRequest ( request, mProviderConfigId );
627
+ }
472
628
}
473
629
474
630
void QFieldCloudConnection::setClientHeaders ( QNetworkRequest &request )
0 commit comments