Skip to content

Commit e180584

Browse files
authored
Merge pull request #4000 from owncloud/feature/auth_webfinger_flow
Update WebFinger flow
2 parents ef3eefe + d7b6a36 commit e180584

File tree

35 files changed

+583
-383
lines changed

35 files changed

+583
-383
lines changed

CHANGELOG.md

+11
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Summary
1414
* Enhancement - Authenticated WebFinger: [#3943](https://github.com/owncloud/android/issues/3943)
1515
* Enhancement - Link in drawer menu: [#3949](https://github.com/owncloud/android/pull/3949)
1616
* Enhancement - Send language header in all requests: [#3980](https://github.com/owncloud/android/issues/3980)
17+
* Enhancement - Updated WebFinger flow: [#3998](https://github.com/owncloud/android/issues/3998)
1718

1819
Details
1920
-------
@@ -71,6 +72,16 @@ Details
7172
https://github.com/owncloud/android/pull/3982
7273
https://github.com/owncloud/android-library/pull/551
7374

75+
* Enhancement - Updated WebFinger flow: [#3998](https://github.com/owncloud/android/issues/3998)
76+
77+
WebFinger call won't follow redirections. WebFinger will be requested first and will skip
78+
status.php in case it's successful, and in case the lookup server is not directly accessible,
79+
we will continue the authentication flow with the regular status.php.
80+
81+
https://github.com/owncloud/android/issues/3998
82+
https://github.com/owncloud/android/pull/4000
83+
https://github.com/owncloud/android-library/pull/555
84+
7485
Changelog for ownCloud Android Client [3.0.4] (2023-03-07)
7586
=======================================
7687
The following sections list the changes in ownCloud Android Client 3.0.4 relevant to

changelog/unreleased/4000

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Enhancement: Updated WebFinger flow
2+
3+
WebFinger call won't follow redirections. WebFinger will be requested first and will skip status.php
4+
in case it's successful, and in case the lookup server is not directly accessible, we will continue
5+
the authentication flow with the regular status.php.
6+
7+
https://github.com/owncloud/android/issues/3998
8+
https://github.com/owncloud/android/pull/4000
9+
https://github.com/owncloud/android-library/pull/555

owncloudApp/src/androidTest/java/com/owncloud/android/authentication/LoginActivityTest.kt

+49-54
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
* ownCloud Android client application
33
*
44
* @author Abel García de Prada
5+
* @author Juan Carlos Garrote Gascón
56
*
6-
* Copyright (C) 2020 ownCloud GmbH.
7+
* Copyright (C) 2023 ownCloud GmbH.
78
*
89
* This program is free software: you can redistribute it and/or modify
910
* it under the terms of the GNU General Public License version 2,
@@ -23,6 +24,7 @@ package com.owncloud.android.authentication
2324
import android.accounts.AccountManager.KEY_ACCOUNT_NAME
2425
import android.accounts.AccountManager.KEY_ACCOUNT_TYPE
2526
import android.app.Activity.RESULT_OK
27+
import android.app.Instrumentation
2628
import android.content.Context
2729
import android.content.Intent
2830
import androidx.lifecycle.MutableLiveData
@@ -33,6 +35,7 @@ import androidx.test.espresso.Espresso.onView
3335
import androidx.test.espresso.action.ViewActions.click
3436
import androidx.test.espresso.intent.Intents
3537
import androidx.test.espresso.intent.Intents.intended
38+
import androidx.test.espresso.intent.matcher.IntentMatchers
3639
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
3740
import androidx.test.espresso.matcher.ViewMatchers.Visibility
3841
import androidx.test.espresso.matcher.ViewMatchers.withId
@@ -41,7 +44,6 @@ import com.owncloud.android.domain.exceptions.NoNetworkConnectionException
4144
import com.owncloud.android.domain.exceptions.OwncloudVersionNotSupportedException
4245
import com.owncloud.android.domain.exceptions.ServerNotReachableException
4346
import com.owncloud.android.domain.exceptions.UnauthorizedException
44-
import com.owncloud.android.domain.server.model.AuthenticationMethod
4547
import com.owncloud.android.domain.server.model.ServerInfo
4648
import com.owncloud.android.domain.utils.Event
4749
import com.owncloud.android.extensions.parseError
@@ -64,7 +66,9 @@ import com.owncloud.android.testutil.OC_ACCOUNT
6466
import com.owncloud.android.testutil.OC_AUTH_TOKEN_TYPE
6567
import com.owncloud.android.testutil.OC_BASIC_PASSWORD
6668
import com.owncloud.android.testutil.OC_BASIC_USERNAME
67-
import com.owncloud.android.testutil.OC_SERVER_INFO
69+
import com.owncloud.android.testutil.OC_INSECURE_SERVER_INFO_BASIC_AUTH
70+
import com.owncloud.android.testutil.OC_SECURE_SERVER_INFO_BASIC_AUTH
71+
import com.owncloud.android.testutil.OC_SECURE_SERVER_INFO_BEARER_AUTH
6872
import com.owncloud.android.utils.CONFIGURATION_SERVER_URL
6973
import com.owncloud.android.utils.CONFIGURATION_SERVER_URL_INPUT_VISIBILITY
7074
import com.owncloud.android.utils.NO_MDM_RESTRICTION_YET
@@ -81,6 +85,7 @@ import io.mockk.every
8185
import io.mockk.mockk
8286
import io.mockk.unmockkAll
8387
import io.mockk.verify
88+
import org.hamcrest.Matchers.allOf
8489
import org.junit.After
8590
import org.junit.Assert.assertEquals
8691
import org.junit.Assert.assertNotNull
@@ -199,14 +204,14 @@ class LoginActivityTest {
199204

200205
@Test
201206
fun initialViewStatus_brandedOptions_webfinger() {
202-
launchTest(webfingerLookupServer = OC_SERVER_INFO.baseUrl)
207+
launchTest(webfingerLookupServer = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
203208

204209
assertWebfingerFlowDisplayed(webfingerEnabled = true)
205210
}
206211

207212
@Test
208213
fun initialViewStatus_brandedOptions_serverInfoInSetup() {
209-
launchTest(showServerUrlInput = false, serverUrl = OC_SERVER_INFO.baseUrl)
214+
launchTest(showServerUrlInput = false, serverUrl = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
210215

211216
assertViewsDisplayed(
212217
showHostUrlFrame = false,
@@ -215,21 +220,21 @@ class LoginActivityTest {
215220
showEmbeddedCheckServerButton = false
216221
)
217222

218-
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
223+
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, true) }
219224
}
220225

221226
@Test
222227
fun initialViewStatus_brandedOptions_serverInfoInSetup_connectionFails() {
223228

224-
launchTest(showServerUrlInput = false, serverUrl = OC_SERVER_INFO.baseUrl)
229+
launchTest(showServerUrlInput = false, serverUrl = OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
225230

226231
serverInfoLiveData.postValue(Event(UIResult.Error(NoNetworkConnectionException())))
227232

228233
R.id.centeredRefreshButton.isDisplayed(true)
229234
R.id.centeredRefreshButton.scrollAndClick()
230235

231-
verify(exactly = 2) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
232-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC.copy(isSecureConnection = true))))
236+
verify(exactly = 2) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, true) }
237+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
233238

234239
R.id.centeredRefreshButton.isDisplayed(false)
235240
}
@@ -270,21 +275,21 @@ class LoginActivityTest {
270275
fun checkServerInfo_clickButton_callGetServerInfo() {
271276
launchTest()
272277

273-
R.id.hostUrlInput.typeText(OC_SERVER_INFO.baseUrl)
278+
R.id.hostUrlInput.typeText(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
274279

275280
R.id.embeddedCheckServerButton.scrollAndClick()
276281

277-
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
282+
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, true) }
278283
}
279284

280285
@Test
281286
fun checkServerInfo_clickLogo_callGetServerInfo() {
282287
launchTest()
283-
R.id.hostUrlInput.typeText(OC_SERVER_INFO.baseUrl)
288+
R.id.hostUrlInput.typeText(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
284289

285290
R.id.thumbnail.scrollAndClick()
286291

287-
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
292+
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl, true) }
288293
}
289294

290295
@Test
@@ -303,15 +308,15 @@ class LoginActivityTest {
303308
launchTest()
304309
R.id.hostUrlInput.typeText("demo.owncloud.com")
305310

306-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC.copy(isSecureConnection = true))))
311+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
307312

308-
R.id.hostUrlInput.withText(SERVER_INFO_BASIC.baseUrl)
313+
R.id.hostUrlInput.withText(SECURE_SERVER_INFO_BASIC.baseUrl)
309314
}
310315

311316
@Test
312317
fun checkServerInfo_isSuccess_Secure() {
313318
launchTest()
314-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC.copy(isSecureConnection = true))))
319+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
315320

316321
with(R.id.server_status_text) {
317322
isDisplayed(true)
@@ -323,7 +328,7 @@ class LoginActivityTest {
323328
@Test
324329
fun checkServerInfo_isSuccess_NotSecure() {
325330
launchTest()
326-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC.copy(isSecureConnection = false))))
331+
serverInfoLiveData.postValue(Event(UIResult.Success(INSECURE_SERVER_INFO_BASIC)))
327332

328333
with(R.id.server_status_text) {
329334
isDisplayed(true)
@@ -336,34 +341,28 @@ class LoginActivityTest {
336341
fun checkServerInfo_isSuccess_Basic() {
337342
launchTest()
338343

339-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC)))
344+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
340345

341346
checkBasicFieldsVisibility(loginButtonShouldBeVisible = false)
342347
}
343348

344349
@Test
345350
fun checkServerInfo_isSuccess_Bearer() {
351+
Intents.init()
346352
launchTest()
353+
avoidOpeningChromeCustomTab()
347354

348-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BEARER)))
355+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BEARER)))
349356

350357
checkBearerFieldsVisibility()
351-
}
352-
353-
@Test
354-
fun checkServerInfo_isSuccess_None() {
355-
launchTest()
356-
357-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC.copy(authenticationMethod = AuthenticationMethod.NONE))))
358-
359-
assertNoneFieldsVisibility()
358+
Intents.release()
360359
}
361360

362361
@Test
363362
fun checkServerInfo_isSuccess_basicModifyUrlInput() {
364363
launchTest()
365364

366-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC)))
365+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
367366

368367
checkBasicFieldsVisibility()
369368

@@ -388,15 +387,18 @@ class LoginActivityTest {
388387

389388
@Test
390389
fun checkServerInfo_isSuccess_bearerModifyUrlInput() {
390+
Intents.init()
391391
launchTest()
392+
avoidOpeningChromeCustomTab()
392393

393-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BEARER)))
394+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BEARER)))
394395

395396
checkBearerFieldsVisibility()
396397

397398
R.id.hostUrlInput.typeText("anything")
398399

399400
R.id.auth_status_text.assertVisibility(Visibility.GONE)
401+
Intents.release()
400402
}
401403

402404
@Test
@@ -459,7 +461,7 @@ class LoginActivityTest {
459461
fun loginBasic_callLoginBasic() {
460462
launchTest()
461463

462-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC)))
464+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
463465

464466
R.id.account_username.typeText(OC_BASIC_USERNAME)
465467

@@ -477,7 +479,7 @@ class LoginActivityTest {
477479
fun loginBasic_callLoginBasic_trimUsername() {
478480
launchTest()
479481

480-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC)))
482+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
481483

482484
R.id.account_username.typeText(" $OC_BASIC_USERNAME ")
483485

@@ -495,7 +497,7 @@ class LoginActivityTest {
495497
fun loginBasic_showOrHideFields() {
496498
launchTest()
497499

498-
serverInfoLiveData.postValue(Event(UIResult.Success(SERVER_INFO_BASIC)))
500+
serverInfoLiveData.postValue(Event(UIResult.Success(SECURE_SERVER_INFO_BASIC)))
499501

500502
R.id.account_username.typeText(OC_BASIC_USERNAME)
501503

@@ -664,17 +666,17 @@ class LoginActivityTest {
664666

665667
launchTest(intent = intentWithAccount)
666668

667-
baseUrlLiveData.postValue(Event(UIResult.Success(OC_SERVER_INFO.baseUrl)))
669+
baseUrlLiveData.postValue(Event(UIResult.Success(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)))
668670

669671
with(R.id.hostUrlInput) {
670-
withText(OC_SERVER_INFO.baseUrl)
672+
withText(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
671673
assertVisibility(Visibility.VISIBLE)
672674
isDisplayed(true)
673675
isEnabled(false)
674676
isFocusable(false)
675677
}
676678

677-
verify(exactly = 0) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
679+
verify(exactly = 0) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl) }
678680
}
679681

680682
@Test
@@ -686,17 +688,17 @@ class LoginActivityTest {
686688

687689
launchTest(intent = intentWithAccount)
688690

689-
baseUrlLiveData.postValue(Event(UIResult.Success(OC_SERVER_INFO.baseUrl)))
691+
baseUrlLiveData.postValue(Event(UIResult.Success(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)))
690692

691693
with(R.id.hostUrlInput) {
692-
withText(OC_SERVER_INFO.baseUrl)
694+
withText(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl)
693695
assertVisibility(Visibility.VISIBLE)
694696
isDisplayed(true)
695697
isEnabled(false)
696698
isFocusable(false)
697699
}
698700

699-
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SERVER_INFO.baseUrl) }
701+
verify(exactly = 1) { authenticationViewModel.getServerInfo(OC_SECURE_SERVER_INFO_BASIC_AUTH.baseUrl) }
700702
}
701703

702704
@Test
@@ -713,6 +715,11 @@ class LoginActivityTest {
713715
Intents.release()
714716
}
715717

718+
private fun avoidOpeningChromeCustomTab() {
719+
Intents.intending(allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW)))
720+
.respondWith(Instrumentation.ActivityResult(RESULT_OK, null))
721+
}
722+
716723
private fun checkBasicFieldsVisibility(
717724
fieldsShouldBeVisible: Boolean = true,
718725
loginButtonShouldBeVisible: Boolean = false
@@ -747,19 +754,6 @@ class LoginActivityTest {
747754
}
748755
}
749756

750-
private fun assertNoneFieldsVisibility() {
751-
with(R.id.server_status_text) {
752-
isDisplayed(true)
753-
assertVisibility(Visibility.VISIBLE)
754-
withText(R.string.auth_unsupported_auth_method)
755-
}
756-
757-
R.id.account_username.assertVisibility(Visibility.GONE)
758-
R.id.account_password.assertVisibility(Visibility.GONE)
759-
R.id.loginButton.assertVisibility(Visibility.GONE)
760-
R.id.auth_status_text.assertVisibility(Visibility.GONE)
761-
}
762-
763757
private fun assertWebfingerFlowDisplayed(
764758
webfingerEnabled: Boolean,
765759
) {
@@ -803,8 +797,9 @@ class LoginActivityTest {
803797
}
804798

805799
companion object {
806-
val SERVER_INFO_BASIC = OC_SERVER_INFO
807-
val SERVER_INFO_BEARER = OC_SERVER_INFO.copy(authenticationMethod = AuthenticationMethod.BEARER_TOKEN)
800+
val SECURE_SERVER_INFO_BASIC = OC_SECURE_SERVER_INFO_BASIC_AUTH
801+
val INSECURE_SERVER_INFO_BASIC = OC_INSECURE_SERVER_INFO_BASIC_AUTH
802+
val SECURE_SERVER_INFO_BEARER = OC_SECURE_SERVER_INFO_BEARER_AUTH
808803
private const val CUSTOM_WELCOME_TEXT = "Welcome to this test"
809804
private const val BRANDED_APP_NAME = "BrandedAppName"
810805
}

owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ val repositoryModule = module {
5252
factory<AuthenticationRepository> { OCAuthenticationRepository(get(), get()) }
5353
factory<CapabilityRepository> { OCCapabilityRepository(get(), get()) }
5454
factory<FileRepository> { OCFileRepository(get(), get(), get(), get()) }
55-
factory<ServerInfoRepository> { OCServerInfoRepository(get()) }
55+
factory<ServerInfoRepository> { OCServerInfoRepository(get(), get(), get()) }
5656
factory<ShareRepository> { OCShareRepository(get(), get()) }
5757
factory<ShareeRepository> { OCShareeRepository(get()) }
5858
factory<SpacesRepository> { OCSpacesRepository(get(), get()) }
5959
factory<UserRepository> { OCUserRepository(get(), get()) }
60-
factory<OAuthRepository> { OCOAuthRepository(get(), get()) }
60+
factory<OAuthRepository> { OCOAuthRepository(get()) }
6161
factory<FolderBackupRepository> { OCFolderBackupRepository(get()) }
6262
factory<WebFingerRepository> { OCWebFingerRepository(get()) }
6363
factory<TransferRepository> { OCTransferRepository(get()) }

0 commit comments

Comments
 (0)