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 @@ -6,13 +6,16 @@
package ee.ria.DigiDoc.utils

import android.net.Uri
import ee.ria.DigiDoc.BuildConfig
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test

class WebEidUriUtilTest {
private val appLinkHostUri = "https://${BuildConfig.APP_LINKS_HOST}"

@Test
fun isWebEidUri_customScheme_auth() {
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("web-eid-mobile://auth")))
Expand All @@ -30,27 +33,27 @@ class WebEidUriUtilTest {

@Test
fun isWebEidUri_appLinks_auth() {
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("https://id-test.eesti.ee/auth")))
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("$appLinkHostUri/auth")))
}

@Test
fun isWebEidUri_appLinks_cert() {
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("https://id-test.eesti.ee/cert")))
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("$appLinkHostUri/cert")))
}

@Test
fun isWebEidUri_appLinks_sign() {
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("https://id-test.eesti.ee/sign")))
assertTrue(WebEidUriUtil.isWebEidUri(Uri.parse("$appLinkHostUri/sign")))
}

@Test
fun isWebEidUri_appLinks_unknownOperation() {
assertFalse(WebEidUriUtil.isWebEidUri(Uri.parse("https://id-test.eesti.ee/unknown")))
assertFalse(WebEidUriUtil.isWebEidUri(Uri.parse("$appLinkHostUri/unknown")))
}

@Test
fun isWebEidUri_wrongHost() {
assertFalse(WebEidUriUtil.isWebEidUri(Uri.parse("https://evil.com/auth")))
assertFalse(WebEidUriUtil.isWebEidUri(Uri.parse("https://example.org/auth")))
}

@Test
Expand All @@ -68,6 +71,48 @@ class WebEidUriUtilTest {
assertFalse(WebEidUriUtil.isWebEidUri(Uri.parse("web-eid-mobile://unknown")))
}

@Test
fun browserPackageCandidate_applicationIdPreferredOverReferrer() {
assertEquals(
"com.example.browser",
WebEidUriUtil.browserPackageCandidate(
"com.example.browser",
Uri.parse("android-app://com.example.otherbrowser"),
),
)
}

@Test
fun browserPackageCandidate_emptyApplicationIdFallsBackToReferrer() {
assertEquals(
"com.example.otherbrowser",
WebEidUriUtil.browserPackageCandidate("", Uri.parse("android-app://com.example.otherbrowser")),
)
}

@Test
fun browserPackageCandidate_androidAppReferrer() {
assertEquals(
"com.example.otherbrowser",
WebEidUriUtil.browserPackageCandidate(null, Uri.parse("android-app://com.example.otherbrowser")),
)
}

@Test
fun browserPackageCandidate_httpsReferrerIsNotAPackage() {
assertNull(WebEidUriUtil.browserPackageCandidate(null, Uri.parse("https://example.com/auth")))
}

@Test
fun browserPackageCandidate_referrerWithoutAuthority() {
assertNull(WebEidUriUtil.browserPackageCandidate(null, Uri.parse("android-app://")))
}

@Test
fun browserPackageCandidate_noApplicationIdAndNoReferrer() {
assertNull(WebEidUriUtil.browserPackageCandidate(null, null))
}

@Test
fun getOperation_customScheme_auth() {
assertEquals(WebEidOperation.AUTH, WebEidUriUtil.getOperation(Uri.parse("web-eid-mobile://auth#dGVzdA")))
Expand All @@ -87,23 +132,23 @@ class WebEidUriUtilTest {
fun getOperation_appLinks_auth() {
assertEquals(
WebEidOperation.AUTH,
WebEidUriUtil.getOperation(Uri.parse("https://id-test.eesti.ee/auth#dGVzdA")),
WebEidUriUtil.getOperation(Uri.parse("$appLinkHostUri/auth#dGVzdA")),
)
}

@Test
fun getOperation_appLinks_cert() {
assertEquals(
WebEidOperation.CERT,
WebEidUriUtil.getOperation(Uri.parse("https://id-test.eesti.ee/cert#dGVzdA")),
WebEidUriUtil.getOperation(Uri.parse("$appLinkHostUri/cert#dGVzdA")),
)
}

@Test
fun getOperation_appLinks_sign() {
assertEquals(
WebEidOperation.SIGN,
WebEidUriUtil.getOperation(Uri.parse("https://id-test.eesti.ee/sign#dGVzdA")),
WebEidUriUtil.getOperation(Uri.parse("$appLinkHostUri/sign#dGVzdA")),
)
}

Expand All @@ -114,7 +159,7 @@ class WebEidUriUtilTest {

@Test
fun getOperation_appLinks_unknownOperation_returnsNull() {
assertNull(WebEidUriUtil.getOperation(Uri.parse("https://id-test.eesti.ee/unknown")))
assertNull(WebEidUriUtil.getOperation(Uri.parse("$appLinkHostUri/unknown")))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class WebEidViewModelTest {
@Test
fun webEidViewModel_secondResponseForSameFlow_isIgnored() {
runTest(UnconfinedTestDispatcher()) {
val emitted = mutableListOf<Uri>()
val emitted = mutableListOf<WebEidResponse>()
val job = launch { viewModel.relyingPartyResponseEvents.toList(emitted) }

viewModel.handleAuth(Uri.parse(validAuthUri))
Expand All @@ -164,7 +164,7 @@ class WebEidViewModelTest {
@Test
fun webEidViewModel_handleAuth_permitsOneResponsePerRequest() {
runTest(UnconfinedTestDispatcher()) {
val emitted = mutableListOf<Uri>()
val emitted = mutableListOf<WebEidResponse>()
val job = launch { viewModel.relyingPartyResponseEvents.toList(emitted) }

viewModel.handleAuth(Uri.parse(validAuthUri))
Expand All @@ -187,7 +187,7 @@ class WebEidViewModelTest {
val received = viewModel.relyingPartyResponseEvents.first()

assertNotNull(received)
assert(received.toString().startsWith("https://example.com/response#"))
assert(received.uri.toString().startsWith("https://example.com/response#"))
}
}

Expand Down Expand Up @@ -238,7 +238,7 @@ class WebEidViewModelTest {

viewModel.handleAuth(uri)

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand All @@ -263,7 +263,7 @@ class WebEidViewModelTest {

viewModel.handleAuth(uri)

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(
emittedUri.toString().startsWith(
"https://example.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.com/response#",
Expand Down Expand Up @@ -308,7 +308,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidAuthResult(cert, signingCert, signature)

verify(authService).buildAuthToken(cert, signingCert, signature)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -340,7 +340,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidAuthResult(cert, signingCert, signature)

verify(authService).buildAuthToken(cert, null, signature)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -372,7 +372,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidAuthResult(cert, signingCert, signature)

verify(authService).buildAuthToken(cert, signingCert, signature)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -407,7 +407,7 @@ class WebEidViewModelTest {

verify(authService).buildAuthToken(cert, null, signature)

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)

Expand Down Expand Up @@ -482,7 +482,7 @@ class WebEidViewModelTest {

viewModel.handleSign(uri)

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -540,7 +540,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidCertificateResult(signingCert)

verify(signService).buildCertificatePayload(signingCert)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -572,7 +572,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidCertificateResult(signingCert)

verify(signService).buildCertificatePayload(signingCert)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -603,7 +603,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidSignResult(signingCert, signature, responseUri)

verify(signService).buildSignPayload(signingCert, signature, hashFunction)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -633,7 +633,7 @@ class WebEidViewModelTest {
viewModel.handleWebEidSignResult(signingCert, signature, responseUri)

verify(signService).buildSignPayload(signingCert, signature, hashFunction)
val emittedUri = deferred.await()
val emittedUri = deferred.await().uri
assert(emittedUri.toString().startsWith("https://example.com/response#"))
assert(emittedUri.fragment != null)
val decodedPayload = String(decode(emittedUri.fragment, URL_SAFE))
Expand Down Expand Up @@ -661,7 +661,7 @@ class WebEidViewModelTest {

viewModel.handleUserCancelled()

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri

assert(emittedUri.toString().startsWith("https://example.com/response#"))
assertNotNull(emittedUri.fragment)
Expand Down Expand Up @@ -692,7 +692,7 @@ class WebEidViewModelTest {

viewModel.handleUserCancelled()

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri

assert(emittedUri.toString().startsWith("https://example.com/response#"))
assertNotNull(emittedUri.fragment)
Expand Down Expand Up @@ -720,7 +720,7 @@ class WebEidViewModelTest {

viewModel.handleUserCancelled()

val emittedUri = deferred.await()
val emittedUri = deferred.await().uri

assert(emittedUri.toString().startsWith("https://rp.example.com/sign/response#"))
assertNotNull(emittedUri.fragment)
Expand Down
45 changes: 38 additions & 7 deletions app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ package ee.ria.DigiDoc
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.Browser
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
Expand Down Expand Up @@ -40,6 +42,7 @@ import ee.ria.DigiDoc.utilsLib.file.FileUtil.getExternalFileUris
import ee.ria.DigiDoc.utilsLib.file.FileUtil.getLogsDirectory
import ee.ria.DigiDoc.utilsLib.locale.LocaleUtil.getLocale
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil
import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.debugLog
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import java.util.logging.Logger
Expand Down Expand Up @@ -163,6 +166,16 @@ class MainActivity :
isAppReady = true
}

if (webEidUri != null) {
try {
logWebEidLaunchSource(intent, browserPackage)
} catch (ce: CancellationException) {
throw ce
} catch (t: Throwable) {
LoggingUtil.errorLog(logTag, "Unable to log Web eID launch source", t)
}
}

setContent {
RIADigiDocTheme(darkTheme = useDarkMode) {
RIADigiDocAppScreen(
Expand Down Expand Up @@ -198,13 +211,11 @@ class MainActivity :
private fun isDarkModeEnabled(dataStore: DataStore): Boolean = dataStore.getThemeSetting() == ThemeSetting.DARK

private fun resolveBrowserPackage(intent: Intent): String? =
(
intent
.getStringExtra("com.android.browser.application_id")
?.takeIf { it.isNotEmpty() }
?: ActivityCompat.getReferrer(this)?.host
) // TODO: This needs testing with App Link
?.takeIf { pkg ->
WebEidUriUtil
.browserPackageCandidate(
intent.getStringExtra(Browser.EXTRA_APPLICATION_ID),
ActivityCompat.getReferrer(this),
)?.takeIf { pkg ->
val browseIntent =
Intent(Intent.ACTION_VIEW, "https://".toUri()).apply {
setPackage(pkg)
Expand All @@ -215,4 +226,24 @@ class MainActivity :
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY.toLong()),
) != null
}

private fun logWebEidLaunchSource(
intent: Intent,
browserPackage: String?,
) {
val referrer = ActivityCompat.getReferrer(this)
val initialCallerPackage =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
initialCaller.getPackage()
} else {
null
}
debugLog(
logTag,
"Web eID launch source. Application id ${intent.getStringExtra(Browser.EXTRA_APPLICATION_ID)}, " +
"referrer scheme ${referrer?.scheme}, referrer authority ${referrer?.authority}, " +
"initial caller $initialCallerPackage, extra keys ${intent.extras?.keySet()}, " +
"resolved browser $browserPackage, task $taskId, task root $isTaskRoot",
)
}
}
Loading
Loading