Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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 @@ -53,6 +53,7 @@ import com.wire.kalium.logic.feature.featureConfig.ObserveIsAppLockEditableUseCa
import com.wire.kalium.logic.feature.notificationToken.SaveNotificationTokenUseCase
import com.wire.kalium.logic.feature.notificationToken.SaveNotificationTokenUseCaseImpl
import com.wire.kalium.logic.feature.server.GetServerConfigUseCase
import com.wire.kalium.logic.feature.server.IsCrossBackendLoginBlockedUseCase
import com.wire.kalium.logic.feature.server.ServerConfigForAccountUseCase
import com.wire.kalium.logic.feature.server.UpdateApiVersionsUseCase
import com.wire.kalium.logic.feature.server.UpdateApiVersionsUseCaseImpl
Expand Down Expand Up @@ -196,6 +197,17 @@ public class GlobalKaliumScope internal constructor(
public val serverConfigForAccounts: ServerConfigForAccountUseCase
get() = ServerConfigForAccountUseCase(globalDatabase.serverConfigurationDAO)

public val isCrossBackendLoginBlocked: IsCrossBackendLoginBlockedUseCase
get() {
val currentSessionUseCase = session.currentSession
val serverConfigForAccountUseCase = serverConfigForAccounts
return IsCrossBackendLoginBlockedUseCase(
kaliumConfigs = kaliumConfigs,
currentSession = { currentSessionUseCase() },
serverConfigForAccount = { userId -> serverConfigForAccountUseCase(userId) },
)
}

public val authenticationScopeForConfigId: AuthenticationScopeForConfigIdUseCase
get() = AuthenticationScopeForConfigIdUseCase(
serverConfigurationDAO = globalDatabase.serverConfigurationDAO,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Wire
* Copyright (C) 2024 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.kalium.logic.feature.server

import com.wire.kalium.logic.configuration.server.ServerConfig
import com.wire.kalium.logic.data.auth.AccountInfo
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.featureFlags.KaliumConfigs

/**
* Checks whether a deep-link initiated login targets a different backend than
* the currently active session. When [KaliumConfigs.blockCrossBackendLogin] is
* disabled, or when there is no valid current session, this always returns false.
*/
public class IsCrossBackendLoginBlockedUseCase internal constructor(
private val kaliumConfigs: KaliumConfigs,
private val currentSession: suspend () -> CurrentSessionResult,
private val serverConfigForAccount: suspend (UserId) -> ServerConfigForAccountUseCase.Result,
) {
public sealed interface Target {
public data class Links(val links: ServerConfig.Links) : Target
public data class SsoConfigId(val id: String) : Target
}

@Suppress("ReturnCount")
public suspend operator fun invoke(target: Target): Boolean {
if (!kaliumConfigs.blockCrossBackendLogin) return false
val current = currentSessionConfig() ?: return false
return when (target) {
is Target.Links -> current.links != target.links
is Target.SsoConfigId -> current.id != target.id
}
}

@Suppress("ReturnCount")
private suspend fun currentSessionConfig(): ServerConfig? {
val session = currentSession() as? CurrentSessionResult.Success ?: return null
val valid = session.accountInfo as? AccountInfo.Valid ?: return null
val result = serverConfigForAccount(valid.userId) as? ServerConfigForAccountUseCase.Result.Success
return result?.config
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ public data class KaliumConfigs(
// Consumer-provided debug mode used by persistence and debug-only flows.
val isDebug: Boolean = false,

// When true, cross-backend login from deep links is blocked while another
// session is active on a different backend.
val blockCrossBackendLogin: Boolean = false,

/**
* Optional transformer for call configuration JSON.
* Applied before passing config to AVS. Used by CLI for testing purposes.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* Wire
* Copyright (C) 2024 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.kalium.logic.feature.server

import com.wire.kalium.common.error.StorageFailure
import com.wire.kalium.logic.data.auth.AccountInfo
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.featureFlags.KaliumConfigs
import com.wire.kalium.logic.util.stubs.newServerConfig
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class IsCrossBackendLoginBlockedUseCaseTest {

@Test
fun givenFlagDisabled_whenInvoked_thenReturnsFalse() = runTest {
val useCase = arrange(blockFlag = false)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(newServerConfig(2).links))

assertFalse(result)
}

@Test
fun givenNoCurrentSession_whenInvoked_thenReturnsFalse() = runTest {
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Failure.SessionNotFound,
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(newServerConfig(2).links))

assertFalse(result)
}

@Test
fun givenInvalidSession_whenInvoked_thenReturnsFalse() = runTest {
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(
AccountInfo.Invalid(USER_ID, com.wire.kalium.logic.data.logout.LogoutReason.SELF_HARD_LOGOUT)
),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(newServerConfig(2).links))

assertFalse(result)
}

@Test
fun givenServerConfigLookupFails_whenInvoked_thenReturnsFalse() = runTest {
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(AccountInfo.Valid(USER_ID)),
serverConfigResult = ServerConfigForAccountUseCase.Result.Failure(StorageFailure.DataNotFound),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(newServerConfig(2).links))

assertFalse(result)
}

@Test
fun givenMatchingLinks_whenInvoked_thenReturnsFalse() = runTest {
val current = newServerConfig(1)
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(AccountInfo.Valid(USER_ID)),
serverConfigResult = ServerConfigForAccountUseCase.Result.Success(current),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(current.links))

assertFalse(result)
}

@Test
fun givenDifferentLinks_whenInvoked_thenReturnsTrue() = runTest {
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(AccountInfo.Valid(USER_ID)),
serverConfigResult = ServerConfigForAccountUseCase.Result.Success(newServerConfig(1)),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.Links(newServerConfig(2).links))

assertTrue(result)
}

@Test
fun givenMatchingSsoConfigId_whenInvoked_thenReturnsFalse() = runTest {
val current = newServerConfig(1)
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(AccountInfo.Valid(USER_ID)),
serverConfigResult = ServerConfigForAccountUseCase.Result.Success(current),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.SsoConfigId(current.id))

assertFalse(result)
}

@Test
fun givenDifferentSsoConfigId_whenInvoked_thenReturnsTrue() = runTest {
val useCase = arrange(
blockFlag = true,
currentSession = CurrentSessionResult.Success(AccountInfo.Valid(USER_ID)),
serverConfigResult = ServerConfigForAccountUseCase.Result.Success(newServerConfig(1)),
)

val result = useCase(IsCrossBackendLoginBlockedUseCase.Target.SsoConfigId("some-other-config-id"))

assertTrue(result)
}

private fun arrange(
blockFlag: Boolean,
currentSession: CurrentSessionResult = CurrentSessionResult.Failure.SessionNotFound,
serverConfigResult: ServerConfigForAccountUseCase.Result =
ServerConfigForAccountUseCase.Result.Failure(StorageFailure.DataNotFound),
) = IsCrossBackendLoginBlockedUseCase(
kaliumConfigs = KaliumConfigs(blockCrossBackendLogin = blockFlag),
currentSession = { currentSession },
serverConfigForAccount = { serverConfigResult },
)

companion object {
private val USER_ID = UserId("user-id", "domain")
}
}
Loading