mirror of
https://github.com/bitwarden/android.git
synced 2026-08-26 22:04:07 -05:00
PM-35273: feat: Add support for SDK API calls by providing base urls (#6805)
This commit is contained in:
+24
-29
@@ -27,25 +27,21 @@ class AuthSdkSourceImpl(
|
||||
override suspend fun getNewAuthRequest(
|
||||
email: String,
|
||||
): Result<AuthRequestResponse> = runCatchingWithLogs {
|
||||
getClient()
|
||||
.auth()
|
||||
.newAuthRequest(
|
||||
email = email.lowercase(),
|
||||
)
|
||||
useClient { auth().newAuthRequest(email = email.lowercase()) }
|
||||
}
|
||||
|
||||
override suspend fun getUserFingerprint(
|
||||
email: String,
|
||||
publicKey: String,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient()
|
||||
.platform()
|
||||
.fingerprint(
|
||||
useClient {
|
||||
platform().fingerprint(
|
||||
req = FingerprintRequest(
|
||||
fingerprintMaterial = email.lowercase(),
|
||||
publicKey = publicKey,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun hashPassword(
|
||||
@@ -54,21 +50,19 @@ class AuthSdkSourceImpl(
|
||||
kdf: Kdf,
|
||||
purpose: HashPurpose,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient()
|
||||
.auth()
|
||||
.hashPassword(
|
||||
useClient {
|
||||
auth().hashPassword(
|
||||
email = email,
|
||||
password = password,
|
||||
kdfParams = kdf,
|
||||
purpose = purpose,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun makeKeyConnectorKeys(): Result<KeyConnectorResponse> =
|
||||
runCatchingWithLogs {
|
||||
getClient()
|
||||
.auth()
|
||||
.makeKeyConnectorKeys()
|
||||
useClient { auth().makeKeyConnectorKeys() }
|
||||
}
|
||||
|
||||
override suspend fun makeRegisterKeys(
|
||||
@@ -76,13 +70,13 @@ class AuthSdkSourceImpl(
|
||||
password: String,
|
||||
kdf: Kdf,
|
||||
): Result<RegisterKeyResponse> = runCatchingWithLogs {
|
||||
getClient()
|
||||
.auth()
|
||||
.makeRegisterKeys(
|
||||
useClient {
|
||||
auth().makeRegisterKeys(
|
||||
email = email,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun makeRegisterTdeKeysAndUnlockVault(
|
||||
@@ -105,15 +99,16 @@ class AuthSdkSourceImpl(
|
||||
password: String,
|
||||
additionalInputs: List<String>,
|
||||
): Result<PasswordStrength> = runCatchingWithLogs {
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
getClient()
|
||||
.auth()
|
||||
.passwordStrength(
|
||||
password = password,
|
||||
email = email,
|
||||
additionalInputs = additionalInputs,
|
||||
)
|
||||
.toPasswordStrengthOrNull()!!
|
||||
useClient {
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
auth()
|
||||
.passwordStrength(
|
||||
password = password,
|
||||
email = email,
|
||||
additionalInputs = additionalInputs,
|
||||
)
|
||||
.toPasswordStrengthOrNull()!!
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun satisfiesPolicy(
|
||||
@@ -121,12 +116,12 @@ class AuthSdkSourceImpl(
|
||||
passwordStrength: PasswordStrength,
|
||||
policy: MasterPasswordPolicyOptions,
|
||||
): Result<Boolean> = runCatchingWithLogs {
|
||||
getClient()
|
||||
.auth()
|
||||
.satisfiesPolicy(
|
||||
useClient {
|
||||
auth().satisfiesPolicy(
|
||||
password = password,
|
||||
strength = passwordStrength.toUByte(),
|
||||
policy = policy,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+21
-15
@@ -50,7 +50,7 @@ object PlatformNetworkModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBitwardenServiceClient(
|
||||
fun provideBitwardenServiceClientConfig(
|
||||
authTokenManager: AuthTokenManager,
|
||||
baseUrlsProvider: BaseUrlsProvider,
|
||||
authDiskSource: AuthDiskSource,
|
||||
@@ -58,20 +58,26 @@ object PlatformNetworkModule {
|
||||
buildInfoManager: BuildInfoManager,
|
||||
networkCookieManager: NetworkCookieManager,
|
||||
clock: Clock,
|
||||
): BitwardenServiceClient = bitwardenServiceClient(
|
||||
BitwardenServiceClientConfig(
|
||||
clock = clock,
|
||||
appIdProvider = authDiskSource,
|
||||
clientData = BitwardenServiceClientConfig.ClientData(
|
||||
userAgent = HEADER_VALUE_USER_AGENT,
|
||||
clientName = HEADER_VALUE_CLIENT_NAME,
|
||||
clientVersion = HEADER_VALUE_CLIENT_VERSION,
|
||||
),
|
||||
authTokenProvider = authTokenManager,
|
||||
baseUrlsProvider = baseUrlsProvider,
|
||||
certificateProvider = certificateManager,
|
||||
enableHttpBodyLogging = buildInfoManager.isDevBuild,
|
||||
cookieProvider = networkCookieManager,
|
||||
): BitwardenServiceClientConfig = BitwardenServiceClientConfig(
|
||||
clock = clock,
|
||||
appIdProvider = authDiskSource,
|
||||
clientData = BitwardenServiceClientConfig.ClientData(
|
||||
userAgent = HEADER_VALUE_USER_AGENT,
|
||||
clientName = HEADER_VALUE_CLIENT_NAME,
|
||||
clientVersion = HEADER_VALUE_CLIENT_VERSION,
|
||||
),
|
||||
authTokenProvider = authTokenManager,
|
||||
baseUrlsProvider = baseUrlsProvider,
|
||||
certificateProvider = certificateManager,
|
||||
enableHttpBodyLogging = buildInfoManager.isDevBuild,
|
||||
cookieProvider = networkCookieManager,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBitwardenServiceClient(
|
||||
serviceClientConfig: BitwardenServiceClientConfig,
|
||||
): BitwardenServiceClient = bitwardenServiceClient(
|
||||
config = serviceClientConfig,
|
||||
)
|
||||
}
|
||||
|
||||
+14
-1
@@ -15,9 +15,22 @@ abstract class BaseSdkSource(
|
||||
* Helper function to retrieve the [Client] associated with the given [userId].
|
||||
*/
|
||||
protected suspend fun getClient(
|
||||
userId: String? = null,
|
||||
userId: String,
|
||||
): Client = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
|
||||
/**
|
||||
* Helper function to retrieve a new [Client] and use it in the given [block].
|
||||
*/
|
||||
protected suspend fun <T> useClient(
|
||||
userId: String? = null,
|
||||
accessToken: String? = null,
|
||||
block: suspend Client.() -> T,
|
||||
): T = sdkClientManager.singleUseClient(
|
||||
userId = userId,
|
||||
accessToken = accessToken,
|
||||
block = block,
|
||||
)
|
||||
|
||||
/**
|
||||
* Invokes the [block] with `this` value as its receiver and returns its result if it was
|
||||
* successful and catches any exception that was thrown from the `block` and wrapping it as a
|
||||
|
||||
@@ -11,7 +11,20 @@ interface SdkClientManager {
|
||||
* Returns the cached [Client] instance for the given [userId], otherwise creates and caches
|
||||
* a new one and returns it.
|
||||
*/
|
||||
suspend fun getOrCreateClient(userId: String?): Client
|
||||
suspend fun getOrCreateClient(userId: String): Client
|
||||
|
||||
/**
|
||||
* Helper function to retrieve a new instance of the [Client] and use it in the given [block].
|
||||
* This client is never persisted after the [block] completes.
|
||||
*
|
||||
* @param userId The used to create the [Client]. If null, the SDK is unassociated with a user.
|
||||
* @param accessToken The access token used in network requests.
|
||||
*/
|
||||
suspend fun <T> singleUseClient(
|
||||
userId: String? = null,
|
||||
accessToken: String? = null,
|
||||
block: suspend Client.() -> T,
|
||||
): T
|
||||
|
||||
/**
|
||||
* Clears any resources from the [Client] associated with the given [userId] and removes it
|
||||
|
||||
+17
-5
@@ -15,10 +15,16 @@ class SdkClientManagerImpl(
|
||||
sdkRepoFactory: SdkRepositoryFactory,
|
||||
sdkPlatformApiFactory: SdkPlatformApiFactory,
|
||||
private val featureFlagManager: FeatureFlagManager,
|
||||
private val clientProvider: suspend (userId: String?) -> Client = { userId ->
|
||||
private val clientProvider: suspend (
|
||||
userId: String?,
|
||||
accessToken: String?,
|
||||
) -> Client = { userId, accessToken ->
|
||||
Client(
|
||||
tokenProvider = sdkRepoFactory.getClientManagedTokens(userId = userId),
|
||||
settings = null,
|
||||
tokenProvider = sdkRepoFactory.getClientManagedTokens(
|
||||
userId = userId,
|
||||
accessToken = accessToken,
|
||||
),
|
||||
settings = sdkRepoFactory.getClientSettings(),
|
||||
)
|
||||
.apply {
|
||||
platform().loadFlags(featureFlagManager.sdkFeatureFlags)
|
||||
@@ -32,7 +38,7 @@ class SdkClientManagerImpl(
|
||||
}
|
||||
},
|
||||
) : SdkClientManager {
|
||||
private val userIdToClientMap = mutableMapOf<String?, Client>()
|
||||
private val userIdToClientMap = mutableMapOf<String, Client>()
|
||||
|
||||
init {
|
||||
// The SDK requires access to Android APIs that were not made public until API 31. In order
|
||||
@@ -44,8 +50,14 @@ class SdkClientManagerImpl(
|
||||
}
|
||||
|
||||
override suspend fun getOrCreateClient(
|
||||
userId: String,
|
||||
): Client = userIdToClientMap.getOrPut(key = userId) { clientProvider(userId, null) }
|
||||
|
||||
override suspend fun <T> singleUseClient(
|
||||
userId: String?,
|
||||
): Client = userIdToClientMap.getOrPut(key = userId) { clientProvider(userId) }
|
||||
accessToken: String?,
|
||||
block: suspend Client.() -> T,
|
||||
): T = clientProvider(userId, accessToken).use { it.block() }
|
||||
|
||||
override fun destroyClient(
|
||||
userId: String?,
|
||||
|
||||
+3
@@ -15,6 +15,7 @@ import com.bitwarden.data.datasource.disk.ConfigDiskSource
|
||||
import com.bitwarden.data.manager.NativeLibraryManager
|
||||
import com.bitwarden.data.repository.ServerConfigRepository
|
||||
import com.bitwarden.network.BitwardenServiceClient
|
||||
import com.bitwarden.network.model.BitwardenServiceClientConfig
|
||||
import com.bitwarden.network.service.EventService
|
||||
import com.bitwarden.network.service.PushService
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
@@ -367,11 +368,13 @@ object PlatformManagerModule {
|
||||
cookieDiskSource: CookieDiskSource,
|
||||
configDiskSource: ConfigDiskSource,
|
||||
authDiskSource: AuthDiskSource,
|
||||
serviceClientConfig: BitwardenServiceClientConfig,
|
||||
): SdkRepositoryFactory = SdkRepositoryFactoryImpl(
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
cookieDiskSource = cookieDiskSource,
|
||||
configDiskSource = configDiskSource,
|
||||
authDiskSource = authDiskSource,
|
||||
serviceClientConfig = serviceClientConfig,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
||||
+10
-1
@@ -1,6 +1,7 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.sdk
|
||||
|
||||
import com.bitwarden.core.ClientManagedTokens
|
||||
import com.bitwarden.core.ClientSettings
|
||||
import com.bitwarden.sdk.Repositories
|
||||
import com.bitwarden.sdk.ServerCommunicationConfigRepository
|
||||
|
||||
@@ -16,7 +17,15 @@ interface SdkRepositoryFactory {
|
||||
/**
|
||||
* Retrieves or creates a [ClientManagedTokens] for use with the Bitwarden SDK.
|
||||
*/
|
||||
fun getClientManagedTokens(userId: String?): ClientManagedTokens
|
||||
fun getClientManagedTokens(
|
||||
userId: String?,
|
||||
accessToken: String?,
|
||||
): ClientManagedTokens
|
||||
|
||||
/**
|
||||
* Retrieves or creates a [ClientSettings] for use with the Bitwarden SDK.
|
||||
*/
|
||||
fun getClientSettings(): ClientSettings
|
||||
|
||||
/**
|
||||
* Retrieves or creates a [ServerCommunicationConfigRepository] for use with the Bitwarden SDK.
|
||||
|
||||
+17
@@ -1,7 +1,10 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.sdk
|
||||
|
||||
import com.bitwarden.core.ClientManagedTokens
|
||||
import com.bitwarden.core.ClientSettings
|
||||
import com.bitwarden.core.DeviceType
|
||||
import com.bitwarden.data.datasource.disk.ConfigDiskSource
|
||||
import com.bitwarden.network.model.BitwardenServiceClientConfig
|
||||
import com.bitwarden.sdk.Repositories
|
||||
import com.bitwarden.sdk.ServerCommunicationConfigRepository
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
@@ -20,6 +23,7 @@ class SdkRepositoryFactoryImpl(
|
||||
private val cookieDiskSource: CookieDiskSource,
|
||||
private val configDiskSource: ConfigDiskSource,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
private val serviceClientConfig: BitwardenServiceClientConfig,
|
||||
) : SdkRepositoryFactory {
|
||||
override fun getRepositories(userId: String?): Repositories =
|
||||
Repositories(
|
||||
@@ -34,12 +38,25 @@ class SdkRepositoryFactoryImpl(
|
||||
|
||||
override fun getClientManagedTokens(
|
||||
userId: String?,
|
||||
accessToken: String?,
|
||||
): ClientManagedTokens =
|
||||
SdkTokenRepository(
|
||||
userId = userId,
|
||||
accessToken = accessToken,
|
||||
authDiskSource = authDiskSource,
|
||||
)
|
||||
|
||||
override fun getClientSettings(): ClientSettings =
|
||||
ClientSettings(
|
||||
identityUrl = serviceClientConfig.baseUrlsProvider.getBaseIdentityUrl(),
|
||||
apiUrl = serviceClientConfig.baseUrlsProvider.getBaseApiUrl(),
|
||||
userAgent = serviceClientConfig.clientData.userAgent,
|
||||
deviceType = DeviceType.ANDROID,
|
||||
deviceIdentifier = serviceClientConfig.appIdProvider.uniqueAppId,
|
||||
bitwardenClientVersion = serviceClientConfig.clientData.clientVersion,
|
||||
bitwardenPackageType = null,
|
||||
)
|
||||
|
||||
override fun getServerCommunicationConfigRepository(): ServerCommunicationConfigRepository =
|
||||
ServerCommunicationConfigRepositoryImpl(
|
||||
cookieDiskSource = cookieDiskSource,
|
||||
|
||||
+2
-3
@@ -11,10 +11,9 @@ import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
*/
|
||||
class SdkTokenRepository(
|
||||
private val userId: String?,
|
||||
private val accessToken: String?,
|
||||
private val authDiskSource: AuthDiskSource,
|
||||
) : ClientManagedTokens {
|
||||
override suspend fun getAccessToken(): String? =
|
||||
userId?.let {
|
||||
authDiskSource.getAccountTokens(userId = it)?.accessToken
|
||||
}
|
||||
accessToken ?: userId?.let { authDiskSource.getAccountTokens(userId = it)?.accessToken }
|
||||
}
|
||||
|
||||
+6
-6
@@ -21,36 +21,36 @@ class GeneratorSdkSourceImpl(
|
||||
override suspend fun generatePassword(
|
||||
request: PasswordGeneratorRequest,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().password(request)
|
||||
useClient { generators().password(request) }
|
||||
}
|
||||
|
||||
override suspend fun generatePassphrase(
|
||||
request: PassphraseGeneratorRequest,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().passphrase(request)
|
||||
useClient { generators().passphrase(request) }
|
||||
}
|
||||
|
||||
override suspend fun generatePlusAddressedEmail(
|
||||
request: UsernameGeneratorRequest.Subaddress,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().username(request)
|
||||
useClient { generators().username(request) }
|
||||
}
|
||||
|
||||
override suspend fun generateCatchAllEmail(
|
||||
request: UsernameGeneratorRequest.Catchall,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().username(request)
|
||||
useClient { generators().username(request) }
|
||||
}
|
||||
|
||||
override suspend fun generateRandomWord(
|
||||
request: UsernameGeneratorRequest.Word,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().username(request)
|
||||
useClient { generators().username(request) }
|
||||
}
|
||||
|
||||
override suspend fun generateForwardedServiceEmail(
|
||||
request: UsernameGeneratorRequest.Forwarded,
|
||||
): Result<String> = runCatchingWithLogs {
|
||||
getClient().generators().username(request)
|
||||
useClient { generators().username(request) }
|
||||
}
|
||||
}
|
||||
|
||||
+30
-2
@@ -20,6 +20,7 @@ import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -34,7 +35,7 @@ class AuthSdkSourceTest {
|
||||
every { platform() } returns clientPlatform
|
||||
}
|
||||
private val sdkClientManager = mockk<SdkClientManager> {
|
||||
coEvery { getOrCreateClient(userId = null) } returns client
|
||||
coEvery { getOrCreateClient(userId = any()) } returns client
|
||||
}
|
||||
|
||||
private val authSkdSource: AuthSdkSource = AuthSdkSourceImpl(
|
||||
@@ -43,6 +44,10 @@ class AuthSdkSourceTest {
|
||||
|
||||
@Test
|
||||
fun `getNewAuthRequest should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val slot = slot<suspend Client.() -> AuthRequestResponse>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val email = "test@gmail.com"
|
||||
val expectedResult = mockk<AuthRequestResponse>()
|
||||
coEvery {
|
||||
@@ -62,6 +67,10 @@ class AuthSdkSourceTest {
|
||||
|
||||
@Test
|
||||
fun `getUserFingerprint should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val slot = slot<suspend Client.() -> String>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val email = "email@gmail.com"
|
||||
val publicKey = "publicKey"
|
||||
val expectedResult = "fingerprint"
|
||||
@@ -91,6 +100,10 @@ class AuthSdkSourceTest {
|
||||
|
||||
@Test
|
||||
fun `hashPassword should call SDK and return a Result with the correct data`() = runBlocking {
|
||||
val slot = slot<suspend Client.() -> String>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val email = "email"
|
||||
val password = "password"
|
||||
val kdf = mockk<Kdf>()
|
||||
@@ -128,6 +141,10 @@ class AuthSdkSourceTest {
|
||||
@Test
|
||||
fun `makeKeyConnectorKeys should call SDK and return a Result with the correct data`() =
|
||||
runBlocking {
|
||||
val slot = slot<suspend Client.() -> KeyConnectorResponse>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val expectedResult = mockk<KeyConnectorResponse>()
|
||||
coEvery { clientAuth.makeKeyConnectorKeys() } returns expectedResult
|
||||
|
||||
@@ -142,6 +159,10 @@ class AuthSdkSourceTest {
|
||||
@Test
|
||||
fun `makeRegisterKeys should call SDK and return a Result with the correct data`() =
|
||||
runBlocking {
|
||||
val slot = slot<suspend Client.() -> RegisterKeyResponse>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val email = "email"
|
||||
val password = "password"
|
||||
val kdf = mockk<Kdf>()
|
||||
@@ -181,7 +202,6 @@ class AuthSdkSourceTest {
|
||||
val orgPublicKey = "orgPublicKey"
|
||||
val rememberDevice = true
|
||||
val expectedResult = mockk<RegisterTdeKeyResponse>()
|
||||
coEvery { sdkClientManager.getOrCreateClient(userId = userId) } returns client
|
||||
coEvery {
|
||||
clientAuth.makeRegisterTdeKeys(
|
||||
email = email,
|
||||
@@ -209,6 +229,10 @@ class AuthSdkSourceTest {
|
||||
@Test
|
||||
fun `passwordStrength should call SDK and return a Result with the correct data`() =
|
||||
runBlocking {
|
||||
val slot = slot<suspend Client.() -> PasswordStrength>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val email = "email"
|
||||
val password = "password"
|
||||
val additionalInputs = listOf("test1", "test2")
|
||||
@@ -243,6 +267,10 @@ class AuthSdkSourceTest {
|
||||
@Test
|
||||
fun `satisfiesPolicy should call SDK and return a Result with the correct data`() =
|
||||
runBlocking {
|
||||
val slot = slot<suspend Client.() -> Boolean>()
|
||||
coEvery {
|
||||
sdkClientManager.singleUseClient(block = capture(slot))
|
||||
} coAnswers { slot.captured(client) }
|
||||
val password = "password"
|
||||
val passwordStrength = PasswordStrength.LEVEL_3
|
||||
val rawStrength = 3.toUByte()
|
||||
|
||||
+12
-1
@@ -72,6 +72,17 @@ class SdkClientManagerTest {
|
||||
assertNotEquals(firstClient, thirdClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `singleUseClient should create a new client everytime and run the lambda with it`() =
|
||||
runTest {
|
||||
val sdkClientManager = createSdkClientManager()
|
||||
val firstClient = sdkClientManager.singleUseClient { this }
|
||||
|
||||
// Additional calls should always create a new client
|
||||
val secondClient = sdkClientManager.singleUseClient { this }
|
||||
assertNotEquals(firstClient, secondClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `destroyClient should call close on the Client and remove it from the cache`() = runTest {
|
||||
val sdkClientManager = createSdkClientManager()
|
||||
@@ -88,7 +99,7 @@ class SdkClientManagerTest {
|
||||
}
|
||||
|
||||
private fun createSdkClientManager(): SdkClientManagerImpl = SdkClientManagerImpl(
|
||||
clientProvider = { mockk(relaxed = true) },
|
||||
clientProvider = { _, _ -> mockk(relaxed = true) },
|
||||
nativeLibraryManager = mockNativeLibraryManager,
|
||||
featureFlagManager = mockk(),
|
||||
sdkRepoFactory = sdkRepoFactory,
|
||||
|
||||
+70
-3
@@ -1,12 +1,21 @@
|
||||
package com.x8bit.bitwarden.data.platform.manager.sdk
|
||||
|
||||
import com.bitwarden.core.ClientSettings
|
||||
import com.bitwarden.core.DeviceType
|
||||
import com.bitwarden.data.datasource.disk.ConfigDiskSource
|
||||
import com.bitwarden.network.interceptor.BaseUrlsProvider
|
||||
import com.bitwarden.network.model.BitwardenServiceClientConfig
|
||||
import com.bitwarden.network.provider.AppIdProvider
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import io.mockk.mockk
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
|
||||
class SdkRepositoryFactoryTests {
|
||||
|
||||
@@ -14,12 +23,32 @@ class SdkRepositoryFactoryTests {
|
||||
private val cookieDiskSource: CookieDiskSource = mockk()
|
||||
private val configDiskSource: ConfigDiskSource = mockk()
|
||||
private val authDiskSource: AuthDiskSource = mockk()
|
||||
private val serviceClientConfig: BitwardenServiceClientConfig = BitwardenServiceClientConfig(
|
||||
clientData = BitwardenServiceClientConfig.ClientData(
|
||||
userAgent = USER_AGENT,
|
||||
clientName = CLIENT_NAME,
|
||||
clientVersion = CLIENT_VERSION,
|
||||
),
|
||||
appIdProvider = object : AppIdProvider {
|
||||
override val uniqueAppId: String get() = UNIQUE_APP_ID
|
||||
},
|
||||
baseUrlsProvider = object : BaseUrlsProvider {
|
||||
override fun getBaseApiUrl(): String = BASE_API_URL
|
||||
override fun getBaseIdentityUrl(): String = BASE_IDENTITY_URL
|
||||
override fun getBaseEventsUrl(): String = BASE_EVENTS_URL
|
||||
},
|
||||
authTokenProvider = mockk(),
|
||||
certificateProvider = mockk(),
|
||||
cookieProvider = mockk(),
|
||||
clock = FIXED_CLOCK,
|
||||
)
|
||||
|
||||
private val sdkRepoFactory: SdkRepositoryFactory = SdkRepositoryFactoryImpl(
|
||||
vaultDiskSource = vaultDiskSource,
|
||||
cookieDiskSource = cookieDiskSource,
|
||||
configDiskSource = configDiskSource,
|
||||
authDiskSource = authDiskSource,
|
||||
serviceClientConfig = serviceClientConfig,
|
||||
)
|
||||
|
||||
@Test
|
||||
@@ -45,18 +74,43 @@ class SdkRepositoryFactoryTests {
|
||||
@Test
|
||||
fun `getClientManagedTokens should create a new client`() {
|
||||
val userId = "userId"
|
||||
val firstClient = sdkRepoFactory.getClientManagedTokens(userId = userId)
|
||||
val firstClient = sdkRepoFactory.getClientManagedTokens(
|
||||
userId = userId,
|
||||
accessToken = null,
|
||||
)
|
||||
|
||||
// Additional calls for the same userId should create a repo
|
||||
val secondClient = sdkRepoFactory.getClientManagedTokens(userId = userId)
|
||||
val secondClient = sdkRepoFactory.getClientManagedTokens(
|
||||
userId = userId,
|
||||
accessToken = null,
|
||||
)
|
||||
assertNotEquals(firstClient, secondClient)
|
||||
|
||||
// Additional calls for different userIds should return a different repo
|
||||
val otherUserId = "otherUserId"
|
||||
val thirdClient = sdkRepoFactory.getClientManagedTokens(userId = otherUserId)
|
||||
val thirdClient = sdkRepoFactory.getClientManagedTokens(
|
||||
userId = otherUserId,
|
||||
accessToken = null,
|
||||
)
|
||||
assertNotEquals(firstClient, thirdClient)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getClientSettings should create correct getClientSettings`() {
|
||||
assertEquals(
|
||||
ClientSettings(
|
||||
identityUrl = BASE_IDENTITY_URL,
|
||||
apiUrl = BASE_API_URL,
|
||||
userAgent = USER_AGENT,
|
||||
deviceType = DeviceType.ANDROID,
|
||||
deviceIdentifier = UNIQUE_APP_ID,
|
||||
bitwardenClientVersion = CLIENT_VERSION,
|
||||
bitwardenPackageType = null,
|
||||
),
|
||||
sdkRepoFactory.getClientSettings(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getServerCommunicationConfigRepository should create a new repository`() {
|
||||
val firstRepo = sdkRepoFactory.getServerCommunicationConfigRepository()
|
||||
@@ -64,3 +118,16 @@ class SdkRepositoryFactoryTests {
|
||||
assertNotEquals(firstRepo, secondRepo)
|
||||
}
|
||||
}
|
||||
|
||||
private val FIXED_CLOCK: Clock = Clock.fixed(
|
||||
Instant.parse("2023-10-27T12:00:00Z"),
|
||||
ZoneOffset.UTC,
|
||||
)
|
||||
|
||||
private const val BASE_API_URL: String = "https://api.bitwarden.com"
|
||||
private const val BASE_EVENTS_URL: String = "https://events.bitwarden.com"
|
||||
private const val BASE_IDENTITY_URL: String = "https://identity.bitwarden.com"
|
||||
private const val CLIENT_NAME: String = "mobile"
|
||||
private const val CLIENT_VERSION: String = "2026.4.1"
|
||||
private const val UNIQUE_APP_ID: String = "app_id_12345"
|
||||
private const val USER_AGENT: String = "user (agent)"
|
||||
|
||||
+13
-1
@@ -34,7 +34,6 @@ class SdkTokenRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `getAccessToken should return null when userId is valid and accessToken is null`() =
|
||||
runTest {
|
||||
@@ -69,10 +68,23 @@ class SdkTokenRepositoryTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getAccessToken should return access token when accessToken is manually provided`() =
|
||||
runTest {
|
||||
val accessToken = "access_token"
|
||||
val repository = createSdkTokenRepository(accessToken = accessToken)
|
||||
assertEquals(accessToken, repository.getAccessToken())
|
||||
verify(exactly = 0) {
|
||||
authDiskSource.getAccountTokens(userId = USER_ID)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSdkTokenRepository(
|
||||
userId: String? = USER_ID,
|
||||
accessToken: String? = null,
|
||||
): SdkTokenRepository = SdkTokenRepository(
|
||||
userId = userId,
|
||||
accessToken = accessToken,
|
||||
authDiskSource = authDiskSource,
|
||||
)
|
||||
}
|
||||
|
||||
+3
-1
@@ -13,6 +13,7 @@ import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
@@ -23,7 +24,8 @@ class GeneratorSdkSourceTest {
|
||||
every { generators() } returns clientGenerators
|
||||
}
|
||||
private val sdkClientManager = mockk<SdkClientManager> {
|
||||
coEvery { getOrCreateClient(userId = null) } returns client
|
||||
val slot = slot<suspend Client.() -> String>()
|
||||
coEvery { singleUseClient(block = capture(slot)) } coAnswers { slot.captured(client) }
|
||||
}
|
||||
private val generatorSdkSource: GeneratorSdkSource = GeneratorSdkSourceImpl(sdkClientManager)
|
||||
|
||||
|
||||
+13
-5
@@ -42,11 +42,11 @@ object PlatformNetworkModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBitwardenServiceClient(
|
||||
fun provideBitwardenServiceClientConfig(
|
||||
baseUrlsProvider: BaseUrlsProvider,
|
||||
authDiskSource: AuthDiskSource,
|
||||
clock: Clock,
|
||||
): BitwardenServiceClient = bitwardenServiceClient(
|
||||
): BitwardenServiceClientConfig =
|
||||
BitwardenServiceClientConfig(
|
||||
clock = clock,
|
||||
appIdProvider = authDiskSource,
|
||||
@@ -59,6 +59,7 @@ object PlatformNetworkModule {
|
||||
enableHttpBodyLogging = BuildConfig.DEBUG,
|
||||
authTokenProvider = object : AuthTokenProvider {
|
||||
override fun getAuthTokenDataOrNull(): AuthTokenData? = null
|
||||
|
||||
override fun getAuthTokenDataOrNull(userId: String): AuthTokenData? = null
|
||||
},
|
||||
certificateProvider = object : CertificateProvider {
|
||||
@@ -66,7 +67,7 @@ object PlatformNetworkModule {
|
||||
keyType: Array<out String>?,
|
||||
issuers: Array<out Principal>?,
|
||||
socket: Socket?,
|
||||
) = ""
|
||||
): String = ""
|
||||
|
||||
override fun getCertificateChain(alias: String?): Array<X509Certificate>? = null
|
||||
|
||||
@@ -77,9 +78,16 @@ object PlatformNetworkModule {
|
||||
|
||||
override fun getCookies(hostname: String): List<NetworkCookie> = emptyList()
|
||||
|
||||
override fun acquireCookies(hostname: String) = Unit
|
||||
override fun acquireCookies(hostname: String): Unit = Unit
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBitwardenServiceClient(
|
||||
serviceClientConfig: BitwardenServiceClientConfig,
|
||||
): BitwardenServiceClient = bitwardenServiceClient(
|
||||
config = serviceClientConfig,
|
||||
)
|
||||
|
||||
@Provides
|
||||
|
||||
@@ -18,7 +18,7 @@ data class BitwardenServiceClientConfig(
|
||||
val authTokenProvider: AuthTokenProvider,
|
||||
val certificateProvider: CertificateProvider,
|
||||
val cookieProvider: CookieProvider,
|
||||
val clock: Clock = Clock.systemDefaultZone(),
|
||||
val clock: Clock,
|
||||
val enableHttpBodyLogging: Boolean = false,
|
||||
) {
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user