fix: guard global ABSDK session state
This commit is contained in:
+21
-10
@@ -208,12 +208,17 @@ class AbsdkBridge internal constructor(
|
||||
private inline fun <T> withActiveSession(
|
||||
profileId: ProviderProfileId,
|
||||
block: () -> DomainResult<T>,
|
||||
): DomainResult<T> = if (hasActiveSession(profileId)) {
|
||||
block()
|
||||
} else {
|
||||
DomainResult.Failure(
|
||||
DomainError.TokenInvalid(detail = "No active ABSDK session for this provider"),
|
||||
)
|
||||
): DomainResult<T> {
|
||||
if (!hasActiveSession(profileId)) {
|
||||
return DomainResult.Failure(
|
||||
DomainError.TokenInvalid(detail = "No active ABSDK session for this provider"),
|
||||
)
|
||||
}
|
||||
return block().also { result ->
|
||||
if (result is DomainResult.Failure && result.error is DomainError.TokenInvalid) {
|
||||
activeSession.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasActiveSession(profileId: ProviderProfileId): Boolean =
|
||||
@@ -299,10 +304,16 @@ class AbsdkBridge internal constructor(
|
||||
private const val DISABLED_STATUS = "0"
|
||||
private const val ENABLED_STATUS = "1"
|
||||
|
||||
fun create(): AbsdkBridge = AbsdkBridge(
|
||||
client = VendorAbsdkClient.create(),
|
||||
dispatcher = Dispatchers.IO,
|
||||
)
|
||||
@OptIn(CleartextAbsdkTransport::class)
|
||||
private val processInstance: AbsdkBridge by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
||||
AbsdkBridge(
|
||||
client = VendorAbsdkClient.create(),
|
||||
dispatcher = Dispatchers.IO,
|
||||
)
|
||||
}
|
||||
|
||||
@CleartextAbsdkTransport
|
||||
fun create(): AbsdkBridge = processInstance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package com.flagship.abox.manager.absdk.bridge
|
||||
|
||||
/**
|
||||
* Marks construction of the original ABSDK 0630 client, whose base endpoint is hardcoded to HTTP.
|
||||
*
|
||||
* Opting in acknowledges that transport confidentiality and integrity are not provided by the
|
||||
* vendor JAR. App integration must remain disabled until the deployment has an approved transport
|
||||
* control and the security limitation is presented to the responsible integrator.
|
||||
*/
|
||||
@RequiresOptIn(
|
||||
message = "ABSDK 0630 uses a hardcoded cleartext HTTP transport. " +
|
||||
"Do not enable it without an approved transport control.",
|
||||
level = RequiresOptIn.Level.ERROR,
|
||||
)
|
||||
@Retention(AnnotationRetention.BINARY)
|
||||
@Target(
|
||||
AnnotationTarget.CLASS,
|
||||
AnnotationTarget.CONSTRUCTOR,
|
||||
AnnotationTarget.FUNCTION,
|
||||
AnnotationTarget.PROPERTY,
|
||||
)
|
||||
annotation class CleartextAbsdkTransport
|
||||
+1
@@ -4,6 +4,7 @@ import com.dhc.absdk.ABRet
|
||||
import com.dhc.absdk.ABSDK
|
||||
import com.dhc.absdk.IrInfo
|
||||
|
||||
@CleartextAbsdkTransport
|
||||
internal class VendorAbsdkClient private constructor(
|
||||
private val sdk: ABSDK,
|
||||
) : AbsdkClient {
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.flagship.abox.manager.absdk.bridge
|
||||
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(CleartextAbsdkTransport::class)
|
||||
class AbsdkFactoryTest {
|
||||
@Test
|
||||
fun productionFactoryReturnsOneProcessInstance() {
|
||||
assertSame(AbsdkBridge.create(), AbsdkBridge.create())
|
||||
}
|
||||
}
|
||||
+19
@@ -91,6 +91,25 @@ class AbsdkSessionBridgeTest {
|
||||
assertNull(bridge.observeSession(profile).first())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deviceTokenErrorClearsSessionAndBlocksFurtherCalls() = runTest {
|
||||
val client = ScriptedAbsdkClient().apply {
|
||||
socketStatusResponses = ArrayDeque(
|
||||
listOf(ScriptedAbsdkClient.failure("10002", "expired")),
|
||||
)
|
||||
}
|
||||
val bridge = bridge(client)
|
||||
bridge.login(profile, credentials)
|
||||
|
||||
val first = bridge.getStatus(profile, DeviceName("socket"))
|
||||
|
||||
assertTrue((first as DomainResult.Failure).error is DomainError.TokenInvalid)
|
||||
assertNull(bridge.observeSession(profile).first())
|
||||
val second = bridge.getStatus(profile, DeviceName("socket"))
|
||||
assertTrue((second as DomainResult.Failure).error is DomainError.TokenInvalid)
|
||||
assertEquals(listOf("login:demo", "socket-status:socket"), client.calls)
|
||||
}
|
||||
|
||||
private fun bridge(client: ScriptedAbsdkClient): AbsdkBridge =
|
||||
AbsdkBridge(client, Dispatchers.Unconfined)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user