Handle null or blank auth urls for Duo 2FA (#1044)

This commit is contained in:
Caleb Derosier 2024-02-21 11:00:38 -07:00 committed by Álison Fernandes
parent 8eafb8e180
commit 2e2b80470c
4 changed files with 47 additions and 8 deletions

View file

@ -32,14 +32,13 @@ val GetTokenResponseJson.TwoFactorRequired?.preferredAuthMethod: TwoFactorAuthMe
/**
* If it exists, return the value of the Duo auth url.
*/
val GetTokenResponseJson.TwoFactorRequired?.twoFactorDuoAuthUrl: String
val GetTokenResponseJson.TwoFactorRequired?.twoFactorDuoAuthUrl: String?
get() = this
?.authMethodsData
?.duo
?.get("AuthUrl")
?.jsonPrimitive
?.contentOrNull
.orEmpty()
/**
* If it exists, return the value to display for the email used with two-factor authentication.

View file

@ -160,9 +160,18 @@ class TwoFactorLoginViewModel @Inject constructor(
*/
private fun handleContinueButtonClick() {
if (state.authMethod.isDuo) {
val authUrl = authRepository.twoFactorResponse.twoFactorDuoAuthUrl
// The url should not be empty unless the environment is somehow not supported.
sendEvent(
event = TwoFactorLoginEvent.NavigateToDuo(
uri = Uri.parse(authRepository.twoFactorResponse.twoFactorDuoAuthUrl),
event = authUrl
?.let {
TwoFactorLoginEvent.NavigateToDuo(
uri = Uri.parse(it),
)
}
?: TwoFactorLoginEvent.ShowToast(
// TODO BIT-1927 Update to use string resource
message = "Duo not yet supported".asText(),
),
)
} else {

View file

@ -6,6 +6,7 @@ import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class TwoFactorRequiredExtensionTest {
@ -62,7 +63,7 @@ class TwoFactorRequiredExtensionTest {
}
@Test
fun `twoFactorDuoAuthUrl returns empty string when no DUO AuthUrl is present`() {
fun `twoFactorDuoAuthUrl returns null when no DUO AuthUrl is present`() {
val subject = GetTokenResponseJson.TwoFactorRequired(
authMethodsData = mapOf(
TwoFactorAuthMethod.AUTHENTICATOR_APP to JsonObject(mapOf("AuthUrl" to JsonNull)),
@ -70,7 +71,7 @@ class TwoFactorRequiredExtensionTest {
captchaToken = null,
ssoToken = null,
)
assertEquals("", subject.twoFactorDuoAuthUrl)
assertNull(subject.twoFactorDuoAuthUrl)
}
@Test

View file

@ -249,8 +249,9 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `ContinueButtonClick login should emit NavigateToDuo when auth method is Duo`() = runTest {
fun `ContinueButtonClick login should emit NavigateToDuo when auth method is Duo and authUrl is non-null`() = runTest {
val authMethodsData = mapOf(
TwoFactorAuthMethod.DUO to JsonObject(
mapOf("AuthUrl" to JsonPrimitive("bitwarden.com")),
@ -281,6 +282,35 @@ class TwoFactorLoginViewModelTest : BaseViewModelTest() {
}
}
@Suppress("MaxLineLength")
@Test
fun `ContinueButtonClick login should emit ShowToast when auth method is Duo and authUrl is null`() = runTest {
val authMethodsData = mapOf(
TwoFactorAuthMethod.DUO to JsonObject(
mapOf("Nothing" to JsonPrimitive("Nothing")),
),
)
val response = GetTokenResponseJson.TwoFactorRequired(
authMethodsData = authMethodsData,
captchaToken = null,
ssoToken = null,
)
every { authRepository.twoFactorResponse } returns response
val mockkUri = mockk<Uri>()
val viewModel = createViewModel(
state = DEFAULT_STATE.copy(
authMethod = TwoFactorAuthMethod.DUO,
),
)
viewModel.eventFlow.test {
viewModel.actionChannel.trySend(TwoFactorLoginAction.ContinueButtonClick)
assertEquals(
TwoFactorLoginEvent.ShowToast("Duo not yet supported".asText()),
awaitItem(),
)
}
}
@Test
fun `ContinueButtonClick login returns CaptchaRequired should emit NavigateToCaptcha`() =
runTest {