Skip to content
Merged
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 @@ -60,7 +60,7 @@ import com.wire.android.ui.theme.wireDimensions
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.kalium.logic.feature.debug.MIN_DEBUG_E2EI_CERTIFICATE_EXPIRATION_SECONDS
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult

@Composable
fun DebugDataOptions(
Expand Down Expand Up @@ -108,7 +108,7 @@ fun DebugDataOptionsContent(
onForceUpdateApiVersions: () -> Unit,
enrollE2EICertificate: () -> Unit,
e2eiCertificateExpirationInputState: TextFieldState,
handleE2EIEnrollmentResult: (FinalizeEnrollmentResult) -> Unit,
handleE2EIEnrollmentResult: (EnrollE2EIResult) -> Unit,
dismissCertificateDialog: () -> Unit,
checkCrlRevocationList: () -> Unit,
forceCRLExpirationAfterOneMinute: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
import com.wire.kalium.logic.feature.debug.SetDebugE2EICertificateExpirationUseCase
import com.wire.kalium.logic.feature.debug.TargetedRepairParam
import com.wire.kalium.logic.feature.e2ei.CheckCrlRevocationListUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountResult
import com.wire.kalium.logic.feature.keypackage.MLSKeyPackageCountUseCase
import com.wire.kalium.logic.feature.notificationToken.SendFCMTokenError
Expand Down Expand Up @@ -81,7 +81,7 @@
fun enrollE2EICertificate() {}
fun updateE2EICertificateExpiration(seconds: Long) {}
fun updateE2EICertificateExpirationInput(minutes: String) {}
fun handleE2EIEnrollmentResult(result: FinalizeEnrollmentResult) {}
fun handleE2EIEnrollmentResult(result: EnrollE2EIResult) {}

Check warning on line 84 in app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt#L84

Added line #L84 was not covered by tests
fun dismissCertificateDialog() {}
fun forceUpdateApiVersions() {}
fun disableEventProcessing(disabled: Boolean) {}
Expand Down Expand Up @@ -227,25 +227,17 @@
}
}

override fun handleE2EIEnrollmentResult(result: FinalizeEnrollmentResult) {
override fun handleE2EIEnrollmentResult(result: EnrollE2EIResult) {
state = when (result) {
is FinalizeEnrollmentResult.Failure.OAuthError -> {
state.copy(
certificate = result.reason,
showCertificate = true,
startGettingE2EICertificate = false
)
}

is FinalizeEnrollmentResult.Failure -> {
is EnrollE2EIResult.Failure -> {
state.copy(
certificate = result.toString(),
showCertificate = true,
startGettingE2EICertificate = false
)
}

is FinalizeEnrollmentResult.Success -> {
is EnrollE2EIResult.Success -> {

Check warning on line 240 in app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/debug/DebugDataOptionsViewModel.kt#L240

Added line #L240 was not covered by tests
state.copy(
certificate = result.certificate,
showCertificate = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.SupportPage
import com.wire.android.util.supportUrlResource
import com.wire.android.util.ui.PreviewMultipleThemes
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult

@Composable
internal fun E2EIEnrollmentRouteScreen(
Expand Down Expand Up @@ -91,7 +91,7 @@ private fun E2EIEnrollmentScreenContent(
dismissSuccess: () -> Unit,
dismissErrorDialog: () -> Unit,
enrollE2EICertificate: () -> Unit,
handleE2EIEnrollmentResult: (FinalizeEnrollmentResult) -> Unit,
handleE2EIEnrollmentResult: (EnrollE2EIResult) -> Unit,
openCertificateDetails: () -> Unit,
onBackButtonClicked: () -> Unit,
onCancelEnrollmentClicked: () -> Unit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.kalium.logic.feature.client.FinalizeMLSClientAfterE2EIEnrollmentUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import kotlinx.coroutines.launch

data class E2EIEnrollmentState(
Expand Down Expand Up @@ -56,16 +56,16 @@
state = state.copy(isLoading = true, startGettingE2EICertificate = true)
}

fun handleE2EIEnrollmentResult(result: FinalizeEnrollmentResult) {
fun handleE2EIEnrollmentResult(result: EnrollE2EIResult) {
state = when (result) {
is FinalizeEnrollmentResult.Failure -> {
is EnrollE2EIResult.Failure -> {
state.copy(
isLoading = false,
isCertificateEnrollError = true,
startGettingE2EICertificate = false
)
}
is FinalizeEnrollmentResult.Success -> {
is EnrollE2EIResult.Success -> {

Check warning on line 68 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/E2EIEnrollmentViewModel.kt#L68

Added line #L68 was not covered by tests
state.copy(
certificate = result.certificate,
isCertificateEnrollSuccess = true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,32 @@ import androidx.compose.ui.platform.LocalContext
import com.wire.android.feature.e2ei.OAuthUseCase
import com.wire.android.ui.getE2EICertificateViewModel
import com.wire.android.util.extension.getActivity
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.data.e2ei.E2EIAuthenticationRequest
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive

@Composable
fun GetE2EICertificateUI(
enrollmentResultHandler: (FinalizeEnrollmentResult) -> Unit,
enrollmentResultHandler: (EnrollE2EIResult) -> Unit,
isNewClient: Boolean,
viewModel: GetE2EICertificateViewModel = getE2EICertificateViewModel()
) {
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current

LaunchedEffect(Unit) {
viewModel.requestOAuthFlow.onEach {
OAuthUseCase(context, it.target, it.oAuthClaims).launch(
viewModel.requestOAuthFlow.onEach { request ->
OAuthUseCase(
context,
request.authenticationRequest.target,
request.authenticationRequest.toOAuthClaims(),
).launch(
context.getActivity()!!.activityResultRegistry,
forceLoginFlow = true
) { result -> viewModel.handleOAuthResult(result, it) }
) { result -> viewModel.handleOAuthResult(request.id, result) }
}.launchIn(coroutineScope)
}

Expand All @@ -53,3 +60,27 @@ fun GetE2EICertificateUI(
viewModel.getCertificate(isNewClient)
}
}

internal fun E2EIAuthenticationRequest.toOAuthClaims() = JsonObject(
mapOf(
ID_TOKEN to JsonObject(
mapOf(
KEY_AUTH to requiredClaim(keyAuth),
ACME_AUDIENCE to requiredClaim(acmeAudience),
)
)
)
)

private fun requiredClaim(value: String) = JsonObject(
mapOf(
ESSENTIAL to JsonPrimitive(true),
VALUE to JsonPrimitive(value),
)
)

private const val ID_TOKEN = "id_token"
private const val KEY_AUTH = "keyauth"
private const val ACME_AUDIENCE = "acme_aud"
private const val ESSENTIAL = "essential"
private const val VALUE = "value"
Original file line number Diff line number Diff line change
Expand Up @@ -21,84 +21,84 @@
import dev.zacsweers.metro.Inject

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.feature.e2ei.OAuthUseCase
import com.wire.android.util.dispatchers.DispatcherProvider
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.feature.e2ei.usecase.E2EIEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.InitialEnrollmentResult
import com.wire.kalium.logic.data.e2ei.E2EIAuthenticationRequest
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.session.CurrentSessionUseCase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicLong

internal data class E2EIOAuthRequest(
val id: Long,
val authenticationRequest: E2EIAuthenticationRequest,
)

internal class E2EIOAuthCoordinator {
private val nextRequestId = AtomicLong()
private val pendingRequests = ConcurrentHashMap<Long, CompletableDeferred<OAuthUseCase.OAuthResult>>()
private val requestChannel = Channel<E2EIOAuthRequest>(Channel.BUFFERED)

val requestFlow = requestChannel.receiveAsFlow()

suspend fun authenticate(request: E2EIAuthenticationRequest): String {
val requestId = nextRequestId.incrementAndGet()
val result = CompletableDeferred<OAuthUseCase.OAuthResult>()
pendingRequests[requestId] = result
return try {
requestChannel.send(E2EIOAuthRequest(requestId, request))
when (val oAuthResult = result.await()) {
is OAuthUseCase.OAuthResult.Success -> oAuthResult.idToken
is OAuthUseCase.OAuthResult.Failed -> throw E2EIAuthenticationException(oAuthResult.reason)
}
} finally {
pendingRequests.remove(requestId)?.cancel()
}
}

fun handleResult(requestId: Long, oAuthResult: OAuthUseCase.OAuthResult) {
pendingRequests[requestId]?.complete(oAuthResult)
}
}

internal class E2EIAuthenticationException(reason: String) : Exception(reason)

class GetE2EICertificateViewModel @Inject constructor(
@KaliumCoreLogic private val coreLogic: CoreLogic,
private val currentSession: CurrentSessionUseCase,
val dispatcherProvider: DispatcherProvider
) : ViewModel() {

private val scope = CoroutineScope(SupervisorJob() + dispatcherProvider.default())
private val oAuthCoordinator = E2EIOAuthCoordinator()

Check warning on line 80 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L80

Added line #L80 was not covered by tests

val requestOAuthFlow = MutableSharedFlow<E2EIEnrollmentResult.Initialized>()
val enrollmentResultFlow = MutableSharedFlow<FinalizeEnrollmentResult>()
internal val requestOAuthFlow = oAuthCoordinator.requestFlow
val enrollmentResultFlow = MutableSharedFlow<EnrollE2EIResult>()

Check warning on line 83 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L82-L83

Added lines #L82 - L83 were not covered by tests

fun handleOAuthResult(oAuthResult: OAuthUseCase.OAuthResult, initialEnrollmentResult: E2EIEnrollmentResult.Initialized) {
scope.launch {
when (oAuthResult) {
is OAuthUseCase.OAuthResult.Success -> finalizeEnrollment(oAuthResult, initialEnrollmentResult)

is OAuthUseCase.OAuthResult.Failed -> enrollmentResultFlow.emit(
FinalizeEnrollmentResult.Failure.OAuthError(oAuthResult.reason)
)
}
}
internal fun handleOAuthResult(requestId: Long, oAuthResult: OAuthUseCase.OAuthResult) {
oAuthCoordinator.handleResult(requestId, oAuthResult)

Check warning on line 86 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L86

Added line #L86 was not covered by tests
}

fun getCertificate(isNewClient: Boolean) {
scope.launch {
viewModelScope.launch(dispatcherProvider.default()) {

Check warning on line 90 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L90

Added line #L90 was not covered by tests
val currentSessionResult = currentSession()
if (currentSessionResult is CurrentSessionResult.Success && currentSessionResult.accountInfo.isValid()) {
val result = coreLogic.getSessionScope(currentSessionResult.accountInfo.userId)
.users
.enrollE2EI
.initialEnrollment(isNewClientRegistration = isNewClient)
when (result) {
is InitialEnrollmentResult.Failure -> {
enrollmentResultFlow.emit(FinalizeEnrollmentResult.Failure.Generic(result.toE2EIFailure()))
}

is InitialEnrollmentResult.Success -> {
requestOAuthFlow.emit(result.initializationResult)
}
}
.invoke(
isNewClientRegistration = isNewClient,
authenticate = oAuthCoordinator::authenticate,

Check warning on line 98 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L96-L98

Added lines #L96 - L98 were not covered by tests
)
enrollmentResultFlow.emit(result)

Check warning on line 100 in app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/e2eiEnrollment/GetE2EICertificateViewModel.kt#L100

Added line #L100 was not covered by tests
}
}
}

private suspend fun finalizeEnrollment(
oAuthResult: OAuthUseCase.OAuthResult.Success,
initialEnrollmentResult: E2EIEnrollmentResult.Initialized
) {
val currentSessionResult = currentSession()

if (currentSessionResult is CurrentSessionResult.Success && currentSessionResult.accountInfo.isValid()) {
val enrollmentResult = coreLogic.getSessionScope(currentSessionResult.accountInfo.userId)
.users
.enrollE2EI.finalizeEnrollment(
oAuthResult.idToken,
oAuthResult.authState,
initialEnrollmentResult
)
enrollmentResultFlow.emit(enrollmentResult)
}
}

private fun InitialEnrollmentResult.Failure.toE2EIFailure() = when (this) {
is InitialEnrollmentResult.Failure.E2EIDisabled -> com.wire.kalium.common.error.E2EIFailure.Disabled
is InitialEnrollmentResult.Failure.MissingTeamSettings -> com.wire.kalium.common.error.E2EIFailure.MissingTeamSettings
is InitialEnrollmentResult.Failure.Generic -> this.e2EIFailure
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import com.wire.kalium.logic.data.message.TeamSelfDeleteTimer
import com.wire.kalium.logic.data.sync.SyncState
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import com.wire.kalium.logic.feature.session.CurrentSessionFlowUseCase
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import com.wire.kalium.logic.feature.user.E2EIRequiredResult
Expand Down Expand Up @@ -286,10 +286,10 @@
featureFlagState = featureFlagState.copy(isE2EILoading = true, startGettingE2EICertificate = true)
}

fun handleE2EIEnrollmentResult(result: FinalizeEnrollmentResult) {
fun handleE2EIEnrollmentResult(result: EnrollE2EIResult) {
val e2eiRequired = featureFlagState.e2EIRequired
featureFlagState = when (result) {
is FinalizeEnrollmentResult.Failure -> {
is EnrollE2EIResult.Failure -> {
featureFlagState.copy(
isE2EILoading = false,
startGettingE2EICertificate = false,
Expand All @@ -298,7 +298,7 @@
)
}

is FinalizeEnrollmentResult.Success -> {
is EnrollE2EIResult.Success -> {

Check warning on line 301 in app/src/main/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/home/sync/FeatureFlagNotificationViewModel.kt#L301

Added line #L301 was not covered by tests
featureFlagState.copy(
isE2EILoading = false,
e2EIRequired = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ import com.wire.kalium.logic.feature.e2ei.MLSClientE2EIStatus
import com.wire.kalium.logic.feature.e2ei.MLSClientIdentity
import com.wire.kalium.logic.feature.e2ei.MLSCredentialsType
import com.wire.kalium.logic.feature.e2ei.X509Identity
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import kotlinx.datetime.Instant

@Composable
Expand Down Expand Up @@ -132,7 +132,7 @@ internal fun DeviceDetailsRouteScreen(
fun DeviceDetailsContent(
state: DeviceDetailsState,
passwordTextState: TextFieldState,
handleE2EIEnrollmentResult: (FinalizeEnrollmentResult) -> Unit,
handleE2EIEnrollmentResult: (EnrollE2EIResult) -> Unit,
modifier: Modifier = Modifier,
onDeleteDevice: () -> Unit = {},
onNavigateBack: () -> Unit = {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
import com.wire.kalium.logic.feature.client.Result
import com.wire.kalium.logic.feature.client.UpdateClientVerificationStatusUseCase
import com.wire.kalium.logic.feature.debug.BreakSessionUseCase
import com.wire.kalium.logic.feature.e2ei.usecase.FinalizeEnrollmentResult
import com.wire.kalium.logic.feature.e2ei.usecase.EnrollE2EIResult
import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityResult
import com.wire.kalium.logic.feature.e2ei.usecase.GetMLSClientIdentityUseCase
import com.wire.kalium.logic.feature.user.GetUserInfoResult
Expand Down Expand Up @@ -159,16 +159,16 @@
fun enrollE2EICertificate() {
state = state.copy(isLoadingCertificate = true, startGettingE2EICertificate = true)
}
fun handleE2EIEnrollmentResult(result: FinalizeEnrollmentResult) {
fun handleE2EIEnrollmentResult(result: EnrollE2EIResult) {
state = when (result) {
is FinalizeEnrollmentResult.Failure -> {
is EnrollE2EIResult.Failure -> {
state.copy(
isLoadingCertificate = false,
startGettingE2EICertificate = false,
isE2EICertificateEnrollError = true,
)
}
is FinalizeEnrollmentResult.Success -> {
is EnrollE2EIResult.Success -> {

Check warning on line 171 in app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt

View check run for this annotation

Codecov / codecov/patch

app/src/main/kotlin/com/wire/android/ui/settings/devices/DeviceDetailsViewModel.kt#L171

Added line #L171 was not covered by tests
getE2eiCertificate()
state.copy(
isE2EICertificateEnrollSuccess = true,
Expand Down
Loading
Loading