diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt index 2e2216eb5..91a8ee925 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/DiagnosticsViewModelTest.kt @@ -72,6 +72,7 @@ import java.nio.file.Files import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class DiagnosticsViewModelTest { @@ -115,6 +116,7 @@ class DiagnosticsViewModelTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt index 3ad2e86de..5131d00f0 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/MobileIdViewModelTest.kt @@ -71,6 +71,7 @@ import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.any import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.eq +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class MobileIdViewModelTest { @@ -137,6 +138,7 @@ class MobileIdViewModelTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt index 88901b8d3..ff892f353 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/SmartIdViewModelTest.kt @@ -72,6 +72,7 @@ import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.any import org.mockito.kotlin.atLeastOnce import org.mockito.kotlin.eq +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class SmartIdViewModelTest { @@ -144,6 +145,7 @@ class SmartIdViewModelTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt b/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt index 374cd88b4..7c57593e8 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/di/AppModules.kt @@ -26,6 +26,8 @@ import android.content.ContentResolver import android.content.Context import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.reflect.TypeToken import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -34,6 +36,8 @@ import dagger.hilt.components.SingletonComponent import ee.ria.DigiDoc.common.BuildVersionProvider import ee.ria.DigiDoc.common.BuildVersionProviderImpl import ee.ria.DigiDoc.common.certificate.CertificateService +import ee.ria.DigiDoc.configuration.deserializer.Cdoc2ConfDeserializer +import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider import ee.ria.DigiDoc.configuration.repository.ConfigurationRepository import ee.ria.DigiDoc.cryptolib.CDOC2Settings import ee.ria.DigiDoc.cryptolib.init.CryptoInitialization @@ -141,7 +145,14 @@ class AppModules { fun provideActivityManager(): ActivityManager = ActivityManagerImpl() @Provides - fun provideGson(): Gson = Gson() + @Singleton + fun provideGson(): Gson { + val type = object : TypeToken>() {}.type + + return GsonBuilder() + .registerTypeAdapter(type, Cdoc2ConfDeserializer()) + .create() + } @Provides fun provideRootChecker(): RootChecker = RootCheckerImpl() diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/domain/preferences/DataStore.kt b/app/src/main/kotlin/ee/ria/DigiDoc/domain/preferences/DataStore.kt index 1babbe195..ca29ff3a4 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/domain/preferences/DataStore.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/domain/preferences/DataStore.kt @@ -102,8 +102,7 @@ class DataStore } errorLog( logTag, - "Un" + - "able to read CAN", + "Unable to read CAN", ) return "" } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt index 10057617a..0f17003a5 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt @@ -62,7 +62,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -89,10 +88,10 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.asFlow +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import ee.ria.DigiDoc.R -import ee.ria.DigiDoc.common.Constant.Defaults.DEFAULT_UUID_VALUE import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider.CDOC2Conf import ee.ria.DigiDoc.domain.model.settings.CDOCSetting import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet @@ -111,6 +110,7 @@ import ee.ria.DigiDoc.utils.Route import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.isTalkBackEnabled import ee.ria.DigiDoc.utils.extensions.notAccessible import ee.ria.DigiDoc.utils.snackbar.SnackBarManager +import ee.ria.DigiDoc.viewmodel.EncryptionServicesViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedCertificateViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedSettingsViewModel @@ -120,6 +120,7 @@ import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.util.UUID @OptIn( ExperimentalLayoutApi::class, @@ -129,6 +130,7 @@ import kotlinx.coroutines.withContext @Composable fun EncryptionServicesSettingsScreen( modifier: Modifier = Modifier, + encryptionServicesViewModel: EncryptionServicesViewModel = hiltViewModel(), sharedSettingsViewModel: SharedSettingsViewModel, sharedMenuViewModel: SharedMenuViewModel, sharedCertificateViewModel: SharedCertificateViewModel, @@ -146,52 +148,50 @@ fun EncryptionServicesSettingsScreen( val configuration = sharedSettingsViewModel.updatedConfiguration.value - val getCdocSetting = sharedSettingsViewModel.dataStore::getCdocSetting - val setCdocSetting = sharedSettingsViewModel.dataStore::setCdocSetting - - val getUseOnlineEncryption = sharedSettingsViewModel.dataStore::getUseOnlineEncryption - val setUseOnlineEncryption = sharedSettingsViewModel.dataStore::setUseOnlineEncryption - - val getCDOC2SelectedService = sharedSettingsViewModel.dataStore::getCDOC2SelectedService - val setCDOC2SelectedService = sharedSettingsViewModel.dataStore::setCDOC2SelectedService - - val getCDOC2UUID = sharedSettingsViewModel.dataStore::getCDOC2UUID - val setCDOC2UUID = sharedSettingsViewModel.dataStore::setCDOC2UUID - - val getCDOC2FetchURL = sharedSettingsViewModel.dataStore::getCDOC2FetchURL - val setCDOC2FetchURL = sharedSettingsViewModel.dataStore::setCDOC2FetchURL - - val getCDOC2PostURL = sharedSettingsViewModel.dataStore::getCDOC2PostURL - val setCDOC2PostURL = sharedSettingsViewModel.dataStore::setCDOC2PostURL - - val cdoc2Default = configuration?.cdoc2Default ?: false - val cdoc2UseKeyServerDefault = configuration?.cdoc2UseKeyServer == true - val cdoc2DefaultKeyServer = configuration?.cdoc2DefaultKeyServer ?: DEFAULT_UUID_VALUE - val useKeyTransfer = rememberSaveable { mutableStateOf(getUseOnlineEncryption(cdoc2UseKeyServerDefault)) } - val useDefaultKeyTransferServer = rememberSaveable { mutableStateOf(true) } + val cdoc2Conf = configuration?.cdoc2Conf ?: emptyMap() - val settingsCdocServiceChoice = remember { mutableStateOf(getCdocSetting(cdoc2Default)) } + val cdocSetting by encryptionServicesViewModel.cdocSetting.collectAsStateWithLifecycle() + val useCDOC2SelectedService by encryptionServicesViewModel.useOnlineEncryption.collectAsStateWithLifecycle() + val selectedCDOC2Service by encryptionServicesViewModel.selectedCDOC2Service.collectAsStateWithLifecycle() + val cdoc2Uuid by encryptionServicesViewModel.cdoc2Uuid.collectAsStateWithLifecycle() + val cdoc2FetchUrl by encryptionServicesViewModel.cdoc2FetchUrl.collectAsStateWithLifecycle() + val cdoc2PostUrl by encryptionServicesViewModel.cdoc2PostUrl.collectAsStateWithLifecycle() + + val issuedTo by sharedSettingsViewModel.cryptoCertIssuedTo.collectAsState(null) + val validTo by sharedSettingsViewModel.cryptoCertValidTo.collectAsState(null) + val cryptoCertificate by sharedSettingsViewModel.cryptoCertificate.collectAsState(null) + + val useKeyTransfer = rememberSaveable { mutableStateOf(useCDOC2SelectedService) } + val useDefaultKeyTransferServer = + rememberSaveable { + mutableStateOf( + cdoc2Conf.values.any { it.uuid.toString() == selectedCDOC2Service }, + ) + } - val cdoc2Conf = configuration?.cdoc2Conf ?: emptyMap() + val keyTransferText = stringResource(R.string.option_key_transfer) + val manualKeyTransferText = stringResource(R.string.option_manual_key_transfer) - val settingsCDOC2SelectedService = - rememberSaveable { mutableStateOf(getCDOC2SelectedService(cdoc2DefaultKeyServer)) } + val customDefaultCDOC2UUID: UUID = + UUID.fromString( + "00000000-0000-0000-0000-00000000000" + (cdoc2Conf.size + 1).toString(), + ) + val customDefaultCDOC2FetchUrl = "https://cdoc2-keyserver-get" + val customDefaultCDOC2PostUrl = "https://cdoc2-keyserver-post" - val selectedCdoc2Conf = - cdoc2Conf[settingsCDOC2SelectedService.value] ?: CDOC2Conf( - name = stringResource(R.string.option_ria), - post = "https://cdoc2.id.ee:8443", - fetch = "https://cdoc2.id.ee:8444", + val cdoc2ConfManual = + CDOC2Conf( + uuid = customDefaultCDOC2UUID, + name = manualKeyTransferText, + post = customDefaultCDOC2FetchUrl, + fetch = customDefaultCDOC2PostUrl, ) - val settingsCDOC2UUID = rememberSaveable { mutableStateOf(getCDOC2UUID(settingsCDOC2SelectedService.value)) } - val settingsCDOC2FetchURL = rememberSaveable { mutableStateOf(getCDOC2FetchURL(selectedCdoc2Conf.fetch)) } - val settingsCDOC2PostURL = rememberSaveable { mutableStateOf(getCDOC2PostURL(selectedCdoc2Conf.post)) } + val allConfs = cdoc2Conf + (cdoc2ConfManual.uuid.toString() to cdoc2ConfManual) - val settingsCdocNameChoice = rememberSaveable { mutableStateOf(selectedCdoc2Conf.name) } + val configurationNames: List = allConfs.values.map { it.name } - val keyTransferText = stringResource(R.string.option_key_transfer) - val manualKeyTransferText = stringResource(R.string.option_manual_key_transfer) + val selectedCdoc2Conf = allConfs[selectedCDOC2Service] ?: cdoc2ConfManual val useCDOC1Label = stringResource(R.string.main_settings_crypto_use_cdoc1) val useCDOC2Label = stringResource(R.string.main_settings_crypto_use_cdoc2) @@ -201,33 +201,10 @@ fun EncryptionServicesSettingsScreen( val fetchUrlLabel = stringResource(R.string.main_settings_crypto_fetch_url) val postUrlLabel = stringResource(R.string.main_settings_crypto_post_url) - val nameChoices = arrayListOf() - - var index = 0 - val settingsCdocNameChoiceInt = rememberSaveable { mutableIntStateOf(0) } - - for ((_, value) in cdoc2Conf) { - nameChoices.add(value.name) - if (value.name == settingsCdocNameChoice.value) { - settingsCdocNameChoiceInt.intValue = index - } - - index++ - } - nameChoices.add(manualKeyTransferText) - val customDefaultCDOC2UUID = "00000000-0000-0000-0000-00000000000" + (index + 1).toString() - val customDefaultCDOC2FetchUrl = "https://cdoc2-keyserver-get" - val customDefaultCDOC2PostUrl = "https://cdoc2-keyserver-post" - var uuidText by rememberSaveable(stateSaver = textFieldValueSaver) { mutableStateOf( TextFieldValue( - text = - if (nameChoices[settingsCdocNameChoiceInt.intValue] == manualKeyTransferText) { - getCDOC2UUID(customDefaultCDOC2UUID) - } else { - settingsCDOC2UUID.value - }, + text = cdoc2Uuid, selection = TextRange.Zero, ), ) @@ -237,10 +214,10 @@ fun EncryptionServicesSettingsScreen( mutableStateOf( TextFieldValue( text = - if (nameChoices[settingsCdocNameChoiceInt.intValue] == manualKeyTransferText) { - getCDOC2FetchURL(customDefaultCDOC2FetchUrl) + if (!cdoc2FetchUrl.isEmpty()) { + cdoc2FetchUrl } else { - settingsCDOC2FetchURL.value + selectedCdoc2Conf.fetch }, selection = TextRange.Zero, ), @@ -251,10 +228,10 @@ fun EncryptionServicesSettingsScreen( mutableStateOf( TextFieldValue( text = - if (nameChoices[settingsCdocNameChoiceInt.intValue] == manualKeyTransferText) { - getCDOC2PostURL(customDefaultCDOC2PostUrl) + if (!cdoc2PostUrl.isEmpty()) { + cdoc2PostUrl } else { - settingsCDOC2PostURL.value + selectedCdoc2Conf.post }, selection = TextRange.Zero, ), @@ -262,27 +239,29 @@ fun EncryptionServicesSettingsScreen( } val saveParameters = { - setCdocSetting(settingsCdocServiceChoice.value) - setUseOnlineEncryption(useKeyTransfer.value) - var valueCDOC2UUID = customDefaultCDOC2UUID - var valueCDOC2FetchUrl = customDefaultCDOC2FetchUrl - var valueCDOC2PostUrl = customDefaultCDOC2PostUrl - - for ((key, value) in cdoc2Conf) { - if (value.name == nameChoices[settingsCdocNameChoiceInt.intValue]) { - settingsCdocNameChoice.value = value.name - settingsCDOC2SelectedService.value = key - useDefaultKeyTransferServer.value = true - valueCDOC2UUID = key - valueCDOC2FetchUrl = value.fetch - valueCDOC2PostUrl = value.post - } + encryptionServicesViewModel.setCdocSetting(cdocSetting) + encryptionServicesViewModel.setUseOnlineEncryption(useKeyTransfer.value) + var valueCDOC2UUID: String + var valueCDOC2FetchUrl: String + var valueCDOC2PostUrl: String + + val cdoc2Service = encryptionServicesViewModel.selectedCDOC2Service.value + if (cdoc2Service == customDefaultCDOC2UUID.toString()) { + useDefaultKeyTransferServer.value = false + valueCDOC2UUID = cdoc2Uuid + valueCDOC2FetchUrl = cdoc2FetchUrl + valueCDOC2PostUrl = cdoc2PostUrl + } else { + val conf = allConfs[cdoc2Service] ?: cdoc2ConfManual + useDefaultKeyTransferServer.value = true + valueCDOC2UUID = cdoc2Service + valueCDOC2FetchUrl = conf.fetch + valueCDOC2PostUrl = conf.post } - setCDOC2SelectedService(settingsCDOC2SelectedService.value) - setCDOC2UUID(valueCDOC2UUID) - setCDOC2FetchURL(valueCDOC2FetchUrl) - setCDOC2PostURL(valueCDOC2PostUrl) + encryptionServicesViewModel.setCdoc2Uuid(valueCDOC2UUID) + encryptionServicesViewModel.setCdoc2FetchUrl(valueCDOC2FetchUrl) + encryptionServicesViewModel.setCdoc2PostUrl(valueCDOC2PostUrl) uuidText = TextFieldValue( @@ -302,15 +281,6 @@ fun EncryptionServicesSettingsScreen( } sharedSettingsViewModel.updateCryptoCertData(context) - val issuedTo by sharedSettingsViewModel.cryptoCertIssuedTo.asFlow().collectAsState( - "", - ) - val validTo by sharedSettingsViewModel.cryptoCertValidTo.asFlow().collectAsState( - "", - ) - val cryptoCertificate by sharedSettingsViewModel.cryptoCertificate.asFlow().collectAsState( - null, - ) val filePicker = rememberLauncherForActivityResult( @@ -414,8 +384,7 @@ fun EncryptionServicesSettingsScreen( .fillMaxWidth() .padding(SPadding) .clickable { - settingsCdocServiceChoice.value = CDOCSetting.CDOC1 - setCdocSetting(settingsCdocServiceChoice.value) + encryptionServicesViewModel.setCdocSetting(CDOCSetting.CDOC1) }, verticalAlignment = Alignment.CenterVertically, ) { @@ -432,10 +401,9 @@ fun EncryptionServicesSettingsScreen( .semantics { contentDescription = useCDOC1Label }, - selected = settingsCdocServiceChoice.value == CDOCSetting.CDOC1, + selected = cdocSetting == CDOCSetting.CDOC1, onClick = { - settingsCdocServiceChoice.value = CDOCSetting.CDOC1 - setCdocSetting(CDOCSetting.CDOC1) + encryptionServicesViewModel.setCdocSetting(CDOCSetting.CDOC1) }, ) } @@ -465,8 +433,7 @@ fun EncryptionServicesSettingsScreen( modifier = modifier .clickable { - settingsCdocServiceChoice.value = CDOCSetting.CDOC2 - setCdocSetting(settingsCdocServiceChoice.value) + encryptionServicesViewModel.setCdocSetting(CDOCSetting.CDOC2) }, verticalAlignment = Alignment.CenterVertically, ) { @@ -484,15 +451,14 @@ fun EncryptionServicesSettingsScreen( .semantics { contentDescription = useCDOC2Label }, - selected = settingsCdocServiceChoice.value == CDOCSetting.CDOC2, + selected = cdocSetting == CDOCSetting.CDOC2, onClick = { - settingsCdocServiceChoice.value = CDOCSetting.CDOC2 - setCdocSetting(settingsCdocServiceChoice.value) + encryptionServicesViewModel.setCdocSetting(CDOCSetting.CDOC2) }, ) } - if (settingsCdocServiceChoice.value == CDOCSetting.CDOC2) { + if (cdocSetting == CDOCSetting.CDOC2) { Spacer(modifier = modifier.height(LPadding)) SettingsSwitchItem( @@ -520,7 +486,7 @@ fun EncryptionServicesSettingsScreen( label = { Text(serverLabel) }, - value = nameChoices[settingsCdocNameChoiceInt.intValue], + value = selectedCdoc2Conf.name, onValueChange = {}, readOnly = true, singleLine = true, @@ -581,15 +547,22 @@ fun EncryptionServicesSettingsScreen( OptionChooserDialog( modifier = modifier, title = R.string.choose_server_option, - choices = nameChoices, - selectedChoice = settingsCdocNameChoiceInt.intValue, + choices = configurationNames, + selectedChoice = + allConfs.keys.indexOf( + selectedCdoc2Conf.uuid.toString(), + ), cancelButtonClick = { openOptionChooserDialog = false }, okButtonClick = { selectedIndex -> - settingsCdocNameChoiceInt.intValue = selectedIndex + val entries = allConfs.entries.toList() + val entry = entries[selectedIndex] + encryptionServicesViewModel.setSelectedCDOC2Service( + entry.value.uuid.toString(), + ) useDefaultKeyTransferServer.value = - nameChoices[selectedIndex] == settingsCdocNameChoice.value + selectedCDOC2Service != customDefaultCDOC2UUID.toString() saveParameters() openOptionChooserDialog = false }, @@ -611,14 +584,14 @@ fun EncryptionServicesSettingsScreen( ) { OutlinedTextField( enabled = - settingsCdocServiceChoice.value == CDOCSetting.CDOC2 && + cdocSetting == CDOCSetting.CDOC2 && useKeyTransfer.value && !useDefaultKeyTransferServer.value, value = uuidText, singleLine = true, onValueChange = { uuidText = it.copy(selection = TextRange(it.text.length)) - setCDOC2UUID(it.text) + encryptionServicesViewModel.setCdoc2Uuid(it.text) }, shape = RectangleShape, label = { Text(uuidLabel) }, @@ -691,7 +664,7 @@ fun EncryptionServicesSettingsScreen( ) { OutlinedTextField( enabled = - settingsCdocServiceChoice.value == CDOCSetting.CDOC2 && + cdocSetting == CDOCSetting.CDOC2 && useKeyTransfer.value && !useDefaultKeyTransferServer.value, value = fetchUrlText, @@ -699,7 +672,7 @@ fun EncryptionServicesSettingsScreen( onValueChange = { fetchUrlText = it.copy(selection = TextRange(it.text.length)) - setCDOC2FetchURL(it.text) + encryptionServicesViewModel.setCdoc2FetchUrl(it.text) }, shape = RectangleShape, label = { Text(fetchUrlLabel) }, @@ -772,14 +745,14 @@ fun EncryptionServicesSettingsScreen( ) { OutlinedTextField( enabled = - settingsCdocServiceChoice.value == CDOCSetting.CDOC2 && + cdocSetting == CDOCSetting.CDOC2 && useKeyTransfer.value && !useDefaultKeyTransferServer.value, value = postUrlText, singleLine = true, onValueChange = { postUrlText = it.copy(selection = TextRange(it.text.length)) - setCDOC2PostURL(it.text) + encryptionServicesViewModel.setCdoc2PostUrl(it.text) }, shape = RectangleShape, label = { Text(postUrlLabel) }, @@ -840,7 +813,7 @@ fun EncryptionServicesSettingsScreen( } } } - if (settingsCdocServiceChoice.value == CDOCSetting.CDOC2 && + if (cdocSetting == CDOCSetting.CDOC2 && useKeyTransfer.value && !useDefaultKeyTransferServer.value ) { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt index 4f3fbfc8f..4ed2e6571 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt @@ -83,7 +83,6 @@ import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.text.style.TextAlign -import androidx.lifecycle.asFlow import androidx.navigation.NavHostController import ee.ria.DigiDoc.R import ee.ria.DigiDoc.network.proxy.ManualProxy @@ -214,7 +213,7 @@ fun ProxyServicesSettingsScreen( } LaunchedEffect(sharedSettingsViewModel.errorState) { - sharedSettingsViewModel.errorState.asFlow().collect { errorState -> + sharedSettingsViewModel.errorState.collect { errorState -> errorState?.let { withContext(Main) { showMessage(context, errorState) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ValidationServicesSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ValidationServicesSettingsScreen.kt index f9cbf4dad..45d0a9438 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ValidationServicesSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ValidationServicesSettingsScreen.kt @@ -82,7 +82,6 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.asFlow import androidx.navigation.NavHostController import ee.ria.DigiDoc.R import ee.ria.DigiDoc.network.siva.SivaSetting @@ -148,14 +147,14 @@ fun ValidationServicesSettingsScreen( ) } sharedSettingsViewModel.updateSivaData(settingsSivaServiceUrl.text, context) - val issuedTo by sharedSettingsViewModel.sivaIssuedTo.asFlow().collectAsState( + val issuedTo by sharedSettingsViewModel.sivaIssuedTo.collectAsState( "", ) - val validTo by sharedSettingsViewModel.sivaValidTo.asFlow().collectAsState( + val validTo by sharedSettingsViewModel.sivaValidTo.collectAsState( "", ) - val sivaCertificate by sharedSettingsViewModel.sivaCertificate.asFlow().collectAsState( + val sivaCertificate by sharedSettingsViewModel.sivaCertificate.collectAsState( null, ) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/TimestampServicesComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/TimestampServicesComponent.kt index e0b4c0946..dd4040f7b 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/TimestampServicesComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/TimestampServicesComponent.kt @@ -76,7 +76,6 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue -import androidx.lifecycle.asFlow import androidx.navigation.NavHostController import ee.ria.DigiDoc.R import ee.ria.DigiDoc.domain.model.settings.TSASetting @@ -128,14 +127,14 @@ fun TimestampServicesComponent( ) } sharedSettingsViewModel.updateTsaData(settingsTsaServiceUrl.text, context) - val issuedTo by sharedSettingsViewModel.tsaIssuedTo.asFlow().collectAsState( + val issuedTo by sharedSettingsViewModel.tsaIssuedTo.collectAsState( "", ) - val validTo by sharedSettingsViewModel.tsaValidTo.asFlow().collectAsState( + val validTo by sharedSettingsViewModel.tsaValidTo.collectAsState( "", ) - val tsaCertificate by sharedSettingsViewModel.tsaCertificate.asFlow().collectAsState( + val tsaCertificate by sharedSettingsViewModel.tsaCertificate.collectAsState( null, ) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptionServicesViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptionServicesViewModel.kt new file mode 100644 index 000000000..061d68564 --- /dev/null +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptionServicesViewModel.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2017 - 2025 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.viewmodel + +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import ee.ria.DigiDoc.common.Constant.Defaults.DEFAULT_UUID_VALUE +import ee.ria.DigiDoc.cryptolib.CDOC2Settings +import ee.ria.DigiDoc.domain.model.settings.CDOCSetting +import ee.ria.DigiDoc.domain.preferences.DataStore +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class EncryptionServicesViewModel + @Inject + constructor( + private val cdoc2Settings: CDOC2Settings, + private val dataStore: DataStore, + ) : ViewModel() { + private val _useOnlineEncryption = MutableStateFlow(cdoc2Settings.getUseOnlineEncryption()) + val useOnlineEncryption = _useOnlineEncryption.asStateFlow() + + private val _selectedCDOC2Service = MutableStateFlow(dataStore.getCDOC2SelectedService(DEFAULT_UUID_VALUE)) + val selectedCDOC2Service = _selectedCDOC2Service.asStateFlow() + + private val _cdocSetting = MutableStateFlow(dataStore.getCdocSetting(false)) + val cdocSetting = _cdocSetting.asStateFlow() + + private val _cdoc2Uuid = MutableStateFlow(dataStore.getCDOC2UUID(selectedCDOC2Service.toString())) + val cdoc2Uuid = _cdoc2Uuid.asStateFlow() + + private val _cdoc2FetchUrl = MutableStateFlow(dataStore.getCDOC2FetchURL("")) + val cdoc2FetchUrl = _cdoc2FetchUrl.asStateFlow() + + private val _cdoc2PostUrl = MutableStateFlow(dataStore.getCDOC2PostURL("")) + val cdoc2PostUrl = _cdoc2PostUrl.asStateFlow() + + fun setUseOnlineEncryption(value: Boolean) { + dataStore.setUseOnlineEncryption(value) + _useOnlineEncryption.value = value + } + + fun setSelectedCDOC2Service(value: String) { + dataStore.setCDOC2SelectedService(value) + _selectedCDOC2Service.value = value + } + + fun setCdocSetting(value: CDOCSetting) { + dataStore.setCdocSetting(value) + _cdocSetting.value = value + } + + fun setCdoc2Uuid(value: String) { + dataStore.setCDOC2UUID(value) + _cdoc2Uuid.value = value + } + + fun setCdoc2FetchUrl(value: String) { + dataStore.setCDOC2FetchURL(value) + _cdoc2FetchUrl.value = value + } + + fun setCdoc2PostUrl(value: String) { + dataStore.setCDOC2PostURL(value) + _cdoc2PostUrl.value = value + } + } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModel.kt index fafa8c80d..6c21e2f2c 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModel.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/shared/SharedSettingsViewModel.kt @@ -57,6 +57,8 @@ import ee.ria.DigiDoc.utilsLib.signing.CertificateUtil import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import okhttp3.OkHttpClient import okhttp3.Request @@ -90,41 +92,41 @@ class SharedSettingsViewModel private val _updatedConfiguration = MutableLiveData() val updatedConfiguration: LiveData = _updatedConfiguration - private val _sivaIssuedTo = MutableLiveData() - val sivaIssuedTo: LiveData = _sivaIssuedTo + private val _sivaIssuedTo = MutableStateFlow(null) + val sivaIssuedTo: StateFlow = _sivaIssuedTo - private val _sivaValidTo = MutableLiveData() - val sivaValidTo: LiveData = _sivaValidTo + private val _sivaValidTo = MutableStateFlow(null) + val sivaValidTo: StateFlow = _sivaValidTo - private val _tsaIssuedTo = MutableLiveData() - val tsaIssuedTo: LiveData = _tsaIssuedTo + private val _tsaIssuedTo = MutableStateFlow(null) + val tsaIssuedTo: StateFlow = _tsaIssuedTo - private val _tsaValidTo = MutableLiveData() - val tsaValidTo: LiveData = _tsaValidTo + private val _tsaValidTo = MutableStateFlow(null) + val tsaValidTo: StateFlow = _tsaValidTo - private val _cryptoCertIssuedTo = MutableLiveData() - val cryptoCertIssuedTo: LiveData = _cryptoCertIssuedTo + private val _cryptoCertIssuedTo = MutableStateFlow(null) + val cryptoCertIssuedTo: StateFlow = _cryptoCertIssuedTo - private val _cryptoCertValidTo = MutableLiveData() - val cryptoCertValidTo: LiveData = _cryptoCertValidTo + private val _cryptoCertValidTo = MutableStateFlow(null) + val cryptoCertValidTo: StateFlow = _cryptoCertValidTo - private val _previousSivaUrl = MutableLiveData() - val previousSivaUrl: LiveData = _previousSivaUrl + private val _previousSivaUrl = MutableStateFlow(null) + val previousSivaUrl: StateFlow = _previousSivaUrl - private val _previousTsaUrl = MutableLiveData() - val previousTsaUrl: LiveData = _previousTsaUrl + private val _previousTsaUrl = MutableStateFlow(null) + val previousTsaUrl: StateFlow = _previousTsaUrl - private val _sivaCertificate = MutableLiveData() - val sivaCertificate: LiveData = _sivaCertificate + private val _sivaCertificate = MutableStateFlow(null) + val sivaCertificate: StateFlow = _sivaCertificate - private val _tsaCertificate = MutableLiveData() - val tsaCertificate: LiveData = _tsaCertificate + private val _tsaCertificate = MutableStateFlow(null) + val tsaCertificate: StateFlow = _tsaCertificate - private val _cryptoCertificate = MutableLiveData() - val cryptoCertificate: LiveData = _cryptoCertificate + private val _cryptoCertificate = MutableStateFlow(null) + val cryptoCertificate: StateFlow = _cryptoCertificate - private val _errorState = MutableLiveData(null) - val errorState: LiveData = _errorState + private val _errorState = MutableStateFlow(null) + val errorState: StateFlow = _errorState init { CoroutineScope(Main).launch { @@ -140,6 +142,9 @@ class SharedSettingsViewModel resetSivaSettings() resetProxySettings() resetCryptoSettings() + + resetCertificateInfo() + resetErrors() } private fun resetProxySettings() { @@ -176,7 +181,6 @@ class SharedSettingsViewModel dataStore.setCDOC2UUID("00000000-0000-0000-0000-000000000002") dataStore.setCDOC2FetchURL("https://cdoc2-keyserver-get") dataStore.setCDOC2PostURL("https://cdoc2-keyserver-post") - dataStore.setCryptoCertName(null) removeCryptoCert() } @@ -299,7 +303,7 @@ class SharedSettingsViewModel sivaServiceUrl: String, context: Context, ) { - _previousSivaUrl.postValue(sivaServiceUrl) + _previousSivaUrl.value = sivaServiceUrl val sivaCertName: String = dataStore.getSettingsSivaCertName() val sivaFile = FileUtil.getCertFile(context, sivaCertName, DIR_SIVA_CERT) @@ -308,16 +312,16 @@ class SharedSettingsViewModel val fileContents: String = FileUtil.readFileContent(sivaFile.path) try { val sivaCert = CertificateUtil.x509Certificate(fileContents) - _sivaCertificate.postValue(sivaCert) + _sivaCertificate.value = sivaCert val certificateHolder: X509CertificateHolder = JcaX509CertificateHolder(sivaCert) val issuer: String = getSubject(certificateHolder) - _sivaIssuedTo.postValue(issuer) + _sivaIssuedTo.value = issuer val notAfter: Date = certificateHolder.notAfter if (notAfter.before(Date())) { val expiredText = context.getString(R.string.main_settings_siva_certificate_expired) - _sivaValidTo.postValue("${getFormattedDateTime(notAfter)} ($expiredText)") + _sivaValidTo.value = "${getFormattedDateTime(notAfter)} ($expiredText)" } else { - _sivaValidTo.postValue(getFormattedDateTime(notAfter)) + _sivaValidTo.value = getFormattedDateTime(notAfter) } } catch (e: CertificateException) { errorLog(logTag, "Unable to get SiVa certificate", e) @@ -326,6 +330,10 @@ class SharedSettingsViewModel removeSivaCert() resetCertificateInfo() } + } else { + _sivaIssuedTo.value = null + _sivaValidTo.value = null + _sivaCertificate.value = null } } @@ -333,7 +341,7 @@ class SharedSettingsViewModel tsaServiceUrl: String, context: Context, ) { - _previousTsaUrl.postValue(tsaServiceUrl) + _previousTsaUrl.value = tsaServiceUrl val tsaCertName: String = dataStore.getTSACertName() val tsaFile = FileUtil.getCertFile(context, tsaCertName, DIR_TSA_CERT) @@ -342,16 +350,16 @@ class SharedSettingsViewModel val fileContents: String = FileUtil.readFileContent(tsaFile.path) try { val tsaCert = CertificateUtil.x509Certificate(fileContents) - _tsaCertificate.postValue(tsaCert) + _tsaCertificate.value = tsaCert val certificateHolder: X509CertificateHolder = JcaX509CertificateHolder(tsaCert) val issuer: String = getSubject(certificateHolder) - _tsaIssuedTo.postValue(issuer) + _tsaIssuedTo.value = issuer val notAfter: Date = certificateHolder.notAfter if (notAfter.before(Date())) { val expiredText = context.getString(R.string.main_settings_siva_certificate_expired) - _tsaValidTo.postValue("${getFormattedDateTime(notAfter)} ($expiredText)") + _tsaValidTo.value = "${getFormattedDateTime(notAfter)} ($expiredText)" } else { - _tsaValidTo.postValue(getFormattedDateTime(notAfter)) + _tsaValidTo.value = getFormattedDateTime(notAfter) } } catch (e: CertificateException) { errorLog(logTag, "Unable to get TSA certificate", e) @@ -360,6 +368,10 @@ class SharedSettingsViewModel removeTsaCert() resetCertificateInfo() } + } else { + _tsaIssuedTo.value = null + _tsaIssuedTo.value = null + _tsaCertificate.value = null } } @@ -371,16 +383,16 @@ class SharedSettingsViewModel val fileContents: String = FileUtil.readFileContent(cryptoCertFile.path) try { val cryptoCert = CertificateUtil.x509Certificate(fileContents) - _cryptoCertificate.postValue(cryptoCert) + _cryptoCertificate.value = cryptoCert val certificateHolder: X509CertificateHolder = JcaX509CertificateHolder(cryptoCert) val issuer: String = getSubject(certificateHolder) - _cryptoCertIssuedTo.postValue(issuer) + _cryptoCertIssuedTo.value = issuer val notAfter: Date = certificateHolder.notAfter if (notAfter.before(Date())) { val expiredText = context.getString(R.string.main_settings_siva_certificate_expired) - _cryptoCertValidTo.postValue("${getFormattedDateTime(notAfter)} ($expiredText)") + _cryptoCertValidTo.value = "${getFormattedDateTime(notAfter)} ($expiredText)" } else { - _cryptoCertValidTo.postValue(getFormattedDateTime(notAfter)) + _cryptoCertValidTo.value = getFormattedDateTime(notAfter) } } catch (e: CertificateException) { errorLog(logTag, "Unable to get Crypto certificate", e) @@ -389,6 +401,10 @@ class SharedSettingsViewModel removeCryptoCert() resetCertificateInfo() } + } else { + _cryptoCertIssuedTo.value = null + _cryptoCertValidTo.value = null + _cryptoCertificate.value = null } } @@ -480,12 +496,19 @@ class SharedSettingsViewModel } private fun resetCertificateInfo() { - _sivaIssuedTo.postValue(null) - _sivaValidTo.postValue(null) - _tsaIssuedTo.postValue(null) - _tsaValidTo.postValue(null) - _cryptoCertIssuedTo.postValue(null) - _cryptoCertValidTo.postValue(null) + _sivaIssuedTo.value = null + _sivaValidTo.value = null + _sivaCertificate.value = null + _tsaIssuedTo.value = null + _tsaValidTo.value = null + _tsaCertificate.value = null + _cryptoCertIssuedTo.value = null + _cryptoCertValidTo.value = null + _cryptoCertificate.value = null + } + + private fun resetErrors() { + _errorState.value = null } fun checkConnection(manualProxySettings: ManualProxy) { @@ -514,15 +537,15 @@ class SharedSettingsViewModel val response = call.execute() if (response.code == 403) { debugLog(logTag, "Forbidden error with proxy configuration") - _errorState.postValue(R.string.main_settings_proxy_check_username_and_password) + _errorState.value = R.string.main_settings_proxy_check_username_and_password } if (response.code != 200) { debugLog(logTag, "No Internet connection detected") - _errorState.postValue(R.string.main_settings_proxy_check_connection_unsuccessful) + _errorState.value = R.string.main_settings_proxy_check_connection_unsuccessful } else { debugLog(logTag, "Internet connection detected successfully") - _errorState.postValue(R.string.main_settings_proxy_check_connection_success) + _errorState.value = R.string.main_settings_proxy_check_connection_success } } catch (e: IOException) { val message = e.message @@ -537,10 +560,10 @@ class SharedSettingsViewModel "Received HTTP status 403 or failed to authenticate. " + "Unable to connect with proxy configuration", ) - _errorState.postValue(R.string.main_settings_proxy_check_connection_unsuccessful) + _errorState.value = R.string.main_settings_proxy_check_connection_unsuccessful } errorLog(logTag, "Unable to check Internet connection", e) - _errorState.postValue(R.string.main_settings_proxy_check_connection_unsuccessful) + _errorState.value = R.string.main_settings_proxy_check_connection_unsuccessful } } } diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt index 174fa2083..88a5c4cb3 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/domain/model/ConfigurationViewModelTest.kt @@ -42,6 +42,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import java.util.Date +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class ConfigurationViewModelTest { @@ -118,6 +119,7 @@ class ConfigurationViewModelTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt index d4cc9a6ba..758279cd1 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/loader/ConfigurationLoaderTest.kt @@ -64,6 +64,7 @@ import java.time.LocalDateTime import java.time.ZoneId import java.util.Base64 import java.util.Date +import java.util.UUID @ExperimentalCoroutinesApi @RunWith(MockitoJUnitRunner::class) @@ -430,6 +431,7 @@ class ConfigurationLoaderTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt index 94b61d406..22ee43838 100644 --- a/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt +++ b/config-lib/src/androidTest/kotlin/ee/ria/DigiDoc/configuration/repository/ConfigurationRepositoryTest.kt @@ -47,6 +47,7 @@ import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnitRunner +import java.util.UUID @OptIn(ExperimentalCoroutinesApi::class) @RunWith(MockitoJUnitRunner::class) @@ -205,6 +206,7 @@ class ConfigurationRepositoryTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/deserializer/Cdoc2ConfDeserializer.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/deserializer/Cdoc2ConfDeserializer.kt new file mode 100644 index 000000000..19fb0ae66 --- /dev/null +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/deserializer/Cdoc2ConfDeserializer.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2017 - 2025 Riigi Infosüsteemi Amet + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +@file:Suppress("PackageName") + +package ee.ria.DigiDoc.configuration.deserializer + +import com.google.gson.JsonDeserializationContext +import com.google.gson.JsonDeserializer +import com.google.gson.JsonElement +import ee.ria.DigiDoc.configuration.provider.ConfigurationProvider +import java.lang.reflect.Type +import java.util.UUID + +class Cdoc2ConfDeserializer : JsonDeserializer> { + override fun deserialize( + json: JsonElement, + typeOfT: Type, + context: JsonDeserializationContext, + ): Map { + val jsonObject = json.asJsonObject + + return jsonObject.entrySet().associate { (key, value) -> + val conf = + context.deserialize( + value, + ConfigurationProvider.CDOC2Conf::class.java, + ) + key to conf.copy(uuid = UUID.fromString(key)) + } + } +} diff --git a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/provider/ConfigurationProvider.kt b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/provider/ConfigurationProvider.kt index 0b5630b94..dc687a140 100644 --- a/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/provider/ConfigurationProvider.kt +++ b/config-lib/src/main/kotlin/ee/ria/DigiDoc/configuration/provider/ConfigurationProvider.kt @@ -21,13 +21,18 @@ package ee.ria.DigiDoc.configuration.provider +import com.google.gson.annotations.JsonAdapter import com.google.gson.annotations.SerializedName +import ee.ria.DigiDoc.configuration.deserializer.Cdoc2ConfDeserializer import java.util.Date +import java.util.UUID data class ConfigurationProvider( @SerializedName("META-INF") val metaInf: MetaInf, @SerializedName("SIVA-URL") val sivaUrl: String, - @SerializedName("CDOC2-CONF") val cdoc2Conf: Map, + @SerializedName("CDOC2-CONF") + @JsonAdapter(Cdoc2ConfDeserializer::class) + val cdoc2Conf: Map, @SerializedName("CDOC2-DEFAULT") val cdoc2Default: Boolean?, @SerializedName("CDOC2-USE-KEYSERVER") val cdoc2UseKeyServer: Boolean, @SerializedName("CDOC2-DEFAULT-KEYSERVER") val cdoc2DefaultKeyServer: String, @@ -54,6 +59,7 @@ data class ConfigurationProvider( ) data class CDOC2Conf( + val uuid: UUID, @SerializedName("NAME") val name: String, @SerializedName("POST") val post: String, @SerializedName("FETCH") val fetch: String, diff --git a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt index 5002fa9dd..b4be584bd 100644 --- a/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt +++ b/crypto-lib/src/androidTest/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainerTest.kt @@ -82,6 +82,7 @@ import java.io.File import java.nio.charset.Charset import java.nio.file.Files import java.util.Base64 +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class CryptoContainerTest { @@ -141,6 +142,7 @@ class CryptoContainerTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444", diff --git a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CDOC2Settings.kt b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CDOC2Settings.kt index 3ec043863..45c67b97d 100644 --- a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CDOC2Settings.kt +++ b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CDOC2Settings.kt @@ -70,7 +70,11 @@ class CDOC2Settings fun getCDOC2PostURL(domain: String): String { val configurationProvider = configurationRepository.getConfiguration() val configPostUrl = - configurationProvider?.cdoc2Conf?.get(domain)?.post + configurationProvider + ?.cdoc2Conf + ?.values + ?.firstOrNull { it.post == domain } + ?.post ?: return preferences.getString( resources.getString(R.string.crypto_settings_use_cdoc2_post_url), "", @@ -82,7 +86,11 @@ class CDOC2Settings fun getCDOC2FetchURL(domain: String): String { val configurationProvider = configurationRepository.getConfiguration() val configFetchUrl = - configurationProvider?.cdoc2Conf?.get(domain)?.fetch + configurationProvider + ?.cdoc2Conf + ?.values + ?.firstOrNull { it.fetch == domain } + ?.fetch ?: return preferences.getString( resources.getString(R.string.crypto_settings_use_cdoc2_fetch_url), "", diff --git a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/init/InitializationTest.kt b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/init/InitializationTest.kt index 05e4b8d08..5e36d09ef 100644 --- a/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/init/InitializationTest.kt +++ b/libdigidoc-lib/src/androidTest/kotlin/ee/ria/DigiDoc/libdigidoclib/init/InitializationTest.kt @@ -41,6 +41,7 @@ import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnitRunner import java.lang.reflect.Field +import java.util.UUID @RunWith(MockitoJUnitRunner::class) class InitializationTest { @@ -75,6 +76,7 @@ class InitializationTest { mapOf( DEFAULT_UUID_VALUE to ConfigurationProvider.CDOC2Conf( + uuid = UUID.randomUUID(), name = "RIA", post = "https://cdoc2.id.ee:8443", fetch = "https://cdoc2.id.ee:8444",