Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class NomadLogoutReceiver : CoroutineReceiver() {

appLogger.i("$TAG Received logout broadcast")

if (!coreLogic.getGlobalScope().doesValidNomadAccountExist()) {
appLogger.i("$TAG Logout ignored: current session is not a nomad account")
return
}

@Suppress("TooGenericExceptionCaught")
try {
performLogout()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@

// Returns whether an intent was handled, or if there was nothing to do
@Suppress("ReturnCount")
fun handleIntentsThatAreNotDeepLinks(intent: Intent?): Boolean {

Check failure on line 392 in app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 16 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-android&issues=AZ0uxAx-B0YJ19oE0zJk&open=AZ0uxAx-B0YJ19oE0zJk&pullRequest=4682
val result = intentsProcessor.get().invoke(intent)
if (result != null) {
if (!nomadProfilesFeatureConfig.isEnabled()) {
Expand All @@ -404,6 +404,11 @@
return@launch
}

if (serverLinks?.isProductionApi() == true) {
appLogger.w("Nomad login ignored: resolved backend is Wire production")
return@launch
}

if (!isNomadProfilesFlowEnabled(serverLinks)) {
return@launch
}
Expand Down Expand Up @@ -803,3 +808,7 @@
internal data class OnOpenUserProfile(val result: DeepLinkResult.OpenOtherUserProfile) : WireActivityViewAction
internal data class OnSSOLogin(val result: DeepLinkResult.SSOLogin) : WireActivityViewAction
internal data class ShowToast(val messageResId: Int) : WireActivityViewAction

// TODO: replace with the kalium `ServerConfig.isProductionApi()` once it is made public and supports `Links`

Check warning on line 812 in app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this TODO comment.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-android&issues=AZ0uxAx-B0YJ19oE0zJj&open=AZ0uxAx-B0YJ19oE0zJj&pullRequest=4682
internal fun ServerConfig.Links.isProductionApi(): Boolean =
ServerConfig.PRODUCTION.api.contains(java.net.URI(api).host ?: "")
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.wire.kalium.logic.feature.auth.LogoutUseCase
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.session.CurrentSessionUseCase
import com.wire.kalium.logic.feature.session.DeleteSessionUseCase
import com.wire.kalium.logic.feature.session.DoesValidNomadAccountExistUseCase
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.coVerify
Expand Down Expand Up @@ -114,6 +115,26 @@ class NomadLogoutReceiverTest {
verify(exactly = 0) { arrangement.context.startActivity(any()) }
}

@Test
fun `when current session is not a nomad account then logout broadcast is ignored`() = runTest {
val userId = UserId("user", "domain")
val arrangement = Arrangement()
.withCurrentSession(CurrentSessionResult.Success(AccountInfo.Valid(userId)))
.withValidNomadAccountExists(false)
.arrange()

val intent = mockk<Intent> { every { action } returns NomadLogoutReceiver.ACTION_LOGOUT }
arrangement.receiver.receive(arrangement.context, intent)
advanceUntilIdle()

coVerify(exactly = 0) {
arrangement.currentSession()
arrangement.logoutUseCase(any(), any())
arrangement.deleteSession(any())
arrangement.accountSwitch(any())
}
}

private class Arrangement {

@MockK
Expand All @@ -140,6 +161,9 @@ class NomadLogoutReceiverTest {
@MockK
lateinit var nomadProfilesFeatureConfig: NomadProfilesFeatureConfig

@MockK
lateinit var doesValidNomadAccountExist: DoesValidNomadAccountExistUseCase

val context = mockk<Context>(relaxed = true)
val receiver = NomadLogoutReceiver()

Expand All @@ -154,6 +178,8 @@ class NomadLogoutReceiverTest {
coEvery { logoutUseCase(any(), any()) } returns Unit
coEvery { deleteSession(any()) } returns DeleteSessionUseCase.Result.Success
every { coreLogic.getGlobalScope().deleteSession } returns deleteSession
every { coreLogic.getGlobalScope().doesValidNomadAccountExist } returns doesValidNomadAccountExist
coEvery { doesValidNomadAccountExist() } returns true
coEvery { accountSwitch(any()) } returns SwitchAccountResult.NoOtherAccountToSwitch
every { coreLogic.getSessionScope(any()) } returns userSessionScope
every { nomadProfilesFeatureConfig.isEnabled() } returns true
Expand All @@ -175,5 +201,9 @@ class NomadLogoutReceiverTest {
fun withNomadProfilesEnabled(enabled: Boolean) = apply {
every { nomadProfilesFeatureConfig.isEnabled() } returns enabled
}

fun withValidNomadAccountExists(exists: Boolean) = apply {
coEvery { doesValidNomadAccountExist() } returns exists
}
}
}
68 changes: 68 additions & 0 deletions app/src/test/kotlin/com/wire/android/ui/IsProductionApiTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.ui

import com.wire.kalium.logic.configuration.server.ServerConfig
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class IsProductionApiTest {

@Test
fun `given production API URL, then returns true`() {
val links = serverLinks(api = ServerConfig.PRODUCTION.api)
assertTrue(links.isProductionApi())
}

@Test
fun `given production API URL with trailing path, then returns true`() {
val links = serverLinks(api = "https://prod-nginz-https.wire.com/some/path")
assertTrue(links.isProductionApi())
}

@Test
fun `given custom on-premises API URL, then returns false`() {
val links = serverLinks(api = "https://custom-backend.example.com")
assertFalse(links.isProductionApi())
}

@Test
fun `given staging API URL, then returns false`() {
val links = serverLinks(api = ServerConfig.STAGING.api)
assertFalse(links.isProductionApi())
}

@Test
fun `given URL with production host as substring, then returns false`() {
val links = serverLinks(api = "https://not-prod-nginz-https.wire.com.evil.com")
assertFalse(links.isProductionApi())
}

private fun serverLinks(api: String) = ServerConfig.Links(
api = api,
accounts = "https://accounts.example.com",
webSocket = "https://ws.example.com",
blackList = "https://blacklist.example.com",
teams = "https://teams.example.com",
website = "https://example.com",
title = "test",
isOnPremises = false,
apiProxy = null
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ import com.wire.kalium.logic.feature.client.IsProfileQRCodeEnabledUseCase
import com.wire.kalium.logic.feature.client.NewClientResult
import com.wire.kalium.logic.feature.client.ObserveNewClientsUseCase
import com.wire.kalium.logic.feature.conversation.CheckConversationInviteCodeUseCase
import com.wire.kalium.logic.configuration.server.ServerConfig
import com.wire.kalium.logic.feature.server.GetServerConfigResult
import com.wire.kalium.logic.feature.server.GetServerConfigUseCase
import com.wire.kalium.logic.feature.session.CurrentSessionFlowUseCase
Expand Down Expand Up @@ -899,6 +900,27 @@ class WireActivityViewModelTest {
}
}

@Test
fun `given resolved backend is Wire production, when handling nomad intent, then login is ignored`() = runTest {
val (arrangement, viewModel) = Arrangement()
.withAutomatedLoginIntent(
ssoCode = "wire-b6261497-5b7d-4a57-8f4d-3a94e936b2c0",
backendConfig = "url"
)
.withProductionServerConfig()
.arrange()

viewModel.actions.test {
val handled = viewModel.handleIntentsThatAreNotDeepLinks(mockedIntent())
advanceUntilIdle()

assertTrue(handled)
assertFalse(arrangement.automatedLoginManager.pendingMoveToBackgroundAfterSync)
coVerify(exactly = 0) { arrangement.isNomadProfilesEnabledUseCase.invoke() }
expectNoEvents()
}
}

@Test
fun `given nomad profiles disabled, when handling sharing intent, then import media screen is still shown`() = runTest {
val (_, viewModel) = Arrangement()
Expand Down Expand Up @@ -1298,6 +1320,10 @@ class WireActivityViewModelTest {
coEvery { loginTypeSelector.canUseNewLogin(any()) } returns canUseNewLogin
}

fun withProductionServerConfig(): Arrangement = apply {
coEvery { getServerConfigUseCase(any()) } returns GetServerConfigResult.Success(ServerConfig.PRODUCTION)
}

fun arrange() = this to viewModel
}

Expand Down
Loading