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 @@ -28,6 +28,7 @@ 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.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
Expand All @@ -52,7 +53,9 @@ import ee.ria.DigiDoc.idcard.CardType
import ee.ria.DigiDoc.idcard.CodeType
import ee.ria.DigiDoc.idcard.DateOfBirthUtil
import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet
import ee.ria.DigiDoc.ui.component.myeid.mydata.MyEidDocumentStatus
import ee.ria.DigiDoc.ui.component.myeid.mydata.MyEidMyDataView
import ee.ria.DigiDoc.ui.component.myeid.mydata.certificateStatus
import ee.ria.DigiDoc.ui.component.myeid.pinandcertificate.MyEidPinAndCertificateView
import ee.ria.DigiDoc.ui.component.shared.HrefDynamicText
import ee.ria.DigiDoc.ui.component.shared.StatusSnackbarHost
Expand Down Expand Up @@ -90,6 +93,11 @@ fun MyEidScreen(
val isPin2Activated = idCardData?.pin2CodeChanged == true
val isCourierCard = idCardData?.personalData != null && idCardData?.pin1CodeChanged == false

val authCertificate = remember(idCardData) { idCardData?.authCertificate?.data?.x509Certificate() }
val signCertificate = remember(idCardData) { idCardData?.signCertificate?.data?.x509Certificate() }
val authCertificateStatus = idCardData?.let { certificateStatus(authCertificate) }
val signCertificateStatus = idCardData?.let { certificateStatus(signCertificate) }

val alphaForPin1BlockedState = getAlphaForBlockedState((isPin1Blocked && isPukBlocked) || isCourierCard)
val alphaForPin2BlockedState = getAlphaForBlockedState((isPin2Blocked && isPukBlocked) || isCourierCard)
val alphaForPukBlockedState = getAlphaForBlockedState(isPukBlocked)
Expand Down Expand Up @@ -310,15 +318,14 @@ fun MyEidScreen(
.alpha(alphaForPin1BlockedState),
title = stringResource(R.string.myeid_authentication_certificate_title),
subtitle =
stringResource(
R.string.myeid_certificate_valid_to,
sharedMyEidViewModel.getNotAfter(
idCardData?.authCertificate?.data?.x509Certificate(),
),
certificateSubtitle(
authCertificateStatus,
sharedMyEidViewModel.getNotAfter(authCertificate),
),
isPinBlocked = isPin1Blocked,
isPukBlocked = isPukBlocked,
isNotActivated = isCourierCard,
certificateStatus = authCertificateStatus,
forgotPinText =
if (isPin1Blocked) {
stringResource(
Expand All @@ -344,6 +351,14 @@ fun MyEidScreen(
},
)

if (authCertificateStatus == MyEidDocumentStatus.EXPIRED) {
ExpiredCertificateText(
modifier = modifier,
testTag = "myEidExpiredPin1DescriptionText",
codeType = CodeType.PIN1,
)
}

if (isPin1Blocked && !isPukBlocked) {
Text(
modifier =
Expand Down Expand Up @@ -398,15 +413,14 @@ fun MyEidScreen(
.alpha(alphaForPin2BlockedState),
title = stringResource(R.string.myeid_signing_certificate_title),
subtitle =
stringResource(
R.string.myeid_certificate_valid_to,
sharedMyEidViewModel.getNotAfter(
idCardData?.signCertificate?.data?.x509Certificate(),
),
certificateSubtitle(
signCertificateStatus,
sharedMyEidViewModel.getNotAfter(signCertificate),
),
isPinBlocked = isPin2Blocked,
isPukBlocked = isPukBlocked,
isNotActivated = isCourierCard,
certificateStatus = signCertificateStatus,
forgotPinText =
if (isPin2Blocked) {
stringResource(
Expand All @@ -432,12 +446,22 @@ fun MyEidScreen(
},
)

if (signCertificateStatus == MyEidDocumentStatus.EXPIRED) {
ExpiredCertificateText(
modifier = modifier,
testTag = "myEidExpiredPin2DescriptionText",
codeType = CodeType.PIN2,
)
}

if (isCourierCard) {
CourierCardWarningText(
modifier = modifier,
testTag = "myEidCourierCardPin2DescriptionText",
)
} else if (!isPin2Activated) {
} else if (!isPin2Activated &&
signCertificateStatus != MyEidDocumentStatus.EXPIRED
) {
Text(
modifier =
modifier
Expand Down Expand Up @@ -623,6 +647,36 @@ fun MyEidScreen(
)
}

@Composable
private fun certificateSubtitle(
status: MyEidDocumentStatus?,
validTo: String,
): String =
when (status) {
MyEidDocumentStatus.EXPIRED ->
stringResource(R.string.signature_update_signature_error_message_certificate_expired)
MyEidDocumentStatus.UNKNOWN -> stringResource(R.string.myeid_status_unknown)
else -> stringResource(R.string.myeid_certificate_valid_to, validTo)
}

@Composable
private fun ExpiredCertificateText(
modifier: Modifier,
testTag: String,
codeType: CodeType,
) {
Text(
modifier =
modifier
.fillMaxWidth()
.focusable(true)
.testTag(testTag),
text = stringResource(R.string.myeid_cert_expired, codeType),
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall,
)
}

@Composable
private fun CourierCardWarningText(
modifier: Modifier,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ package ee.ria.DigiDoc.ui.component.myeid.mydata

import android.content.Context
import ee.ria.DigiDoc.R
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import java.security.cert.X509Certificate

enum class MyEidDocumentStatus {
VALID,
Expand All @@ -20,3 +23,17 @@ fun MyEidDocumentStatus.getLocalized(context: Context): String =
MyEidDocumentStatus.EXPIRED -> context.getString(R.string.myeid_status_expired)
MyEidDocumentStatus.UNKNOWN -> context.getString(R.string.myeid_status_unknown)
}

fun certificateStatus(x509Certificate: X509Certificate?): MyEidDocumentStatus {
if (x509Certificate == null) {
return MyEidDocumentStatus.UNKNOWN
}
return try {
x509Certificate.checkValidity()
MyEidDocumentStatus.VALID
} catch (cee: CertificateExpiredException) {
MyEidDocumentStatus.EXPIRED
} catch (cnyve: CertificateNotYetValidException) {
MyEidDocumentStatus.UNKNOWN
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import ee.ria.DigiDoc.R
import ee.ria.DigiDoc.ui.component.myeid.mydata.MyEidDocumentStatus
import ee.ria.DigiDoc.ui.component.shared.HrefDynamicText
import ee.ria.DigiDoc.ui.theme.Dimensions.LINE_HEIGHT
import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding
Expand All @@ -64,6 +65,7 @@ fun MyEidPinAndCertificateView(
isPinBlocked: Boolean = false,
isPukBlocked: Boolean = false,
isNotActivated: Boolean = false,
certificateStatus: MyEidDocumentStatus? = null,
showForgotPin: Boolean = true,
forgotPinText: String = "",
onForgotPinClick: (() -> Unit)? = null,
Expand All @@ -72,6 +74,15 @@ fun MyEidPinAndCertificateView(
) {
val spokenTitleAndSubtitle = spokenDates("$title. $subtitle").lowercase()

val isCertificateUsable = certificateStatus == null || certificateStatus == MyEidDocumentStatus.VALID
val showErrorIcon = !isCertificateUsable || isPinBlocked
val subtitleColor =
if (isCertificateUsable) {
MaterialTheme.colorScheme.onSurface
} else {
MaterialTheme.colorScheme.error
}

Column(
modifier =
modifier
Expand Down Expand Up @@ -101,9 +112,21 @@ fun MyEidPinAndCertificateView(
horizontalArrangement = Arrangement.Start,
) {
Icon(
imageVector = ImageVector.vectorResource(R.drawable.ic_m3_check_circle_48dp_wght400),
imageVector =
ImageVector.vectorResource(
if (showErrorIcon) {
R.drawable.ic_m3_error_48dp_wght400
} else {
R.drawable.ic_m3_check_circle_48dp_wght400
},
),
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
tint =
if (showErrorIcon) {
MaterialTheme.colorScheme.error
} else {
MaterialTheme.colorScheme.primary
},
modifier =
modifier
.size(iconSizeM)
Expand Down Expand Up @@ -141,7 +164,7 @@ fun MyEidPinAndCertificateView(
newLineBeforeLink = true,
textStyle =
TextStyle(
color = MaterialTheme.colorScheme.onSurface,
color = subtitleColor,
fontSize = MaterialTheme.typography.bodyMedium.fontSize,
textAlign = TextAlign.Start,
lineHeight = TextUnit(LINE_HEIGHT, TextUnitType.Sp),
Expand All @@ -152,7 +175,7 @@ fun MyEidPinAndCertificateView(
modifier = modifier.notAccessible(),
text = subtitle,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
color = subtitleColor,
)
}
}
Expand Down Expand Up @@ -241,14 +264,25 @@ fun MyEidPinAndCertificateViewPreview() {
MyEidPinAndCertificateView(
title = "Identity certificate",
subtitle = "Certificate is valid until ${LocalDate.now()}",
certificateStatus = MyEidDocumentStatus.VALID,
forgotPinText = "Forgot PIN?",
onForgotPinClick = {},
changePinText = "Change PIN",
)
MyEidPinAndCertificateView(
title = "Signing certificate",
subtitle = "Certificate has expired",
certificateStatus = MyEidDocumentStatus.EXPIRED,
forgotPinText = "Forgot PIN?",
onForgotPinClick = {},
changePinText = "Change PIN",
)
MyEidPinAndCertificateView(
title = "Identity certificate",
subtitle = "Certificate is valid until ${LocalDate.now()}",
forgotPinText = "Forgot PIN?",
isPinBlocked = true,
certificateStatus = MyEidDocumentStatus.VALID,
forgotPinText = "Unblock PIN",
onForgotPinClick = {},
changePinText = "Change PIN",
)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values-et/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@
<string name="myeid_puk_blocked_url">https://www.id.ee/artikkel/id-kaardi-pin-ja-puk-koodide-muutmine/</string>
<string name="myeid_thales_puk_blocked">PUK on blokeeritud, kuna PUK-koodi on sisestatud 3 korda valesti. PUK-koodi ei saa ise lahti blokeerida.\n\nKuigi PUK-kood on blokeeritud, saab kõiki eID võimalusi kasutada, välja arvatud PUK-koodi vajavaid.\n\nUue PUK-koodi saamiseks tuleb taotleda uus dokument.</string>
<string name="myeid_thales_puk_blocked_url">https://www.id.ee/artikkel/id-kaardi-pin-ja-puk-koodide-muutmine/</string>
<string name="myeid_cert_expired">Sertifikaat on &lt;c>aegunud&lt;/c>! %1$s ei saa kasutada, kuna sertifikaat on aegunud.</string>
<string name="myeid_cert_expired">%1$s ei saa kasutada, kuna sertifikaat on aegunud.</string>
<string name="myeid_pin_part_personal_code_error">Uus %1$s-kood ei saa olla osa sinu isikukoodist</string>
<string name="myeid_pin_part_dob_error">Uus %1$s-kood ei saa olla sinu sünnikuupäev</string>
<string name="myeid_pin_too_easy_error">Uus %1$s-kood ei saa olla nii lihtne</string>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@
<string name="myeid_puk_blocked_url">https://www.id.ee/en/article/changing-id-card-pin-codes-and-puk-code/</string>
<string name="myeid_thales_puk_blocked">PUK has been blocked because PUK code has been entered incorrectly 3 times. You can not unblock the PUK code yourself.\n\nAs long as the PUK code is blocked, all eID options can be used, except transactions that need PUK code.\n\nA new document must be requested to receive the new PUK code.</string>
<string name="myeid_thales_puk_blocked_url">https://www.id.ee/en/article/changing-id-card-pin-codes-and-puk-code/</string>
<string name="myeid_cert_expired">Certificate has &lt;c>expired&lt;/c>! %1$s cannot be used because the certificate has expired.</string>
<string name="myeid_cert_expired">%1$s cannot be used because the certificate has expired.</string>
<string name="myeid_pin_part_personal_code_error">New %1$s code can\'t be part of your personal code</string>
<string name="myeid_pin_part_dob_error">New %1$s code can\'t be your date of birth</string>
<string name="myeid_pin_too_easy_error">New %1$s code can\'t be so easy</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: Estonian Information System Authority
// SPDX-License-Identifier: LGPL-2.1-or-later

@file:Suppress("PackageName")

package ee.ria.DigiDoc.ui.component.myeid.mydata

import org.junit.Assert.assertEquals
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import java.security.cert.CertificateExpiredException
import java.security.cert.CertificateNotYetValidException
import java.security.cert.X509Certificate

class MyEidDocumentStatusTest {
@Test
fun myEidDocumentStatus_certificateStatus_returnsUnknownWhenCertificateIsNull() {
assertEquals(MyEidDocumentStatus.UNKNOWN, certificateStatus(null))
}

@Test
fun myEidDocumentStatus_certificateStatus_returnsExpiredWhenCertificateHasExpired() {
val certificate = mock<X509Certificate>()
whenever(certificate.checkValidity()).thenThrow(CertificateExpiredException())

assertEquals(MyEidDocumentStatus.EXPIRED, certificateStatus(certificate))
}

@Test
fun myEidDocumentStatus_certificateStatus_returnsUnknownWhenCertificateIsNotYetValid() {
val certificate = mock<X509Certificate>()
whenever(certificate.checkValidity()).thenThrow(CertificateNotYetValidException())

assertEquals(MyEidDocumentStatus.UNKNOWN, certificateStatus(certificate))
}

@Test
fun myEidDocumentStatus_certificateStatus_returnsValidWhenCertificateIsWithinItsValidityPeriod() {
val certificate = mock<X509Certificate>()

assertEquals(MyEidDocumentStatus.VALID, certificateStatus(certificate))
}
}
Loading