mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Make SdkClientManager the single source of the Bitwarden SDK Client (#1242)
This commit is contained in:
parent
bf26db1d4f
commit
de39f76627
9 changed files with 94 additions and 114 deletions
|
@ -6,53 +6,43 @@ import com.bitwarden.core.MasterPasswordPolicyOptions
|
|||
import com.bitwarden.core.RegisterKeyResponse
|
||||
import com.bitwarden.crypto.HashPurpose
|
||||
import com.bitwarden.crypto.Kdf
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientAuth
|
||||
import com.bitwarden.sdk.ClientPlatform
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toPasswordStrengthOrNull
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.util.toUByte
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
|
||||
/**
|
||||
* Primary implementation of [AuthSdkSource] that serves as a convenience wrapper around a
|
||||
* [ClientAuth].
|
||||
*/
|
||||
class AuthSdkSourceImpl(
|
||||
private val clientAuth: ClientAuth,
|
||||
private val clientPlatform: ClientPlatform,
|
||||
dispatcherManager: DispatcherManager,
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
private val sdkClientManager: SdkClientManager,
|
||||
) : AuthSdkSource {
|
||||
|
||||
private val ioScope = CoroutineScope(dispatcherManager.io)
|
||||
|
||||
init {
|
||||
ioScope.launch {
|
||||
clientPlatform.loadFlags(featureFlagManager.featureFlags)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getNewAuthRequest(
|
||||
email: String,
|
||||
): Result<AuthRequestResponse> = runCatching {
|
||||
clientAuth.newAuthRequest(
|
||||
email = email,
|
||||
)
|
||||
getClient()
|
||||
.auth()
|
||||
.newAuthRequest(
|
||||
email = email,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getUserFingerprint(
|
||||
email: String,
|
||||
publicKey: String,
|
||||
): Result<String> = runCatching {
|
||||
clientPlatform.fingerprint(
|
||||
req = FingerprintRequest(
|
||||
fingerprintMaterial = email,
|
||||
publicKey = publicKey,
|
||||
),
|
||||
)
|
||||
getClient()
|
||||
.platform()
|
||||
.fingerprint(
|
||||
req = FingerprintRequest(
|
||||
fingerprintMaterial = email,
|
||||
publicKey = publicKey,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun hashPassword(
|
||||
|
@ -61,12 +51,14 @@ class AuthSdkSourceImpl(
|
|||
kdf: Kdf,
|
||||
purpose: HashPurpose,
|
||||
): Result<String> = runCatching {
|
||||
clientAuth.hashPassword(
|
||||
email = email,
|
||||
password = password,
|
||||
kdfParams = kdf,
|
||||
purpose = purpose,
|
||||
)
|
||||
getClient()
|
||||
.auth()
|
||||
.hashPassword(
|
||||
email = email,
|
||||
password = password,
|
||||
kdfParams = kdf,
|
||||
purpose = purpose,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun makeRegisterKeys(
|
||||
|
@ -74,11 +66,13 @@ class AuthSdkSourceImpl(
|
|||
password: String,
|
||||
kdf: Kdf,
|
||||
): Result<RegisterKeyResponse> = runCatching {
|
||||
clientAuth.makeRegisterKeys(
|
||||
email = email,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
)
|
||||
getClient()
|
||||
.auth()
|
||||
.makeRegisterKeys(
|
||||
email = email,
|
||||
password = password,
|
||||
kdf = kdf,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun passwordStrength(
|
||||
|
@ -87,7 +81,8 @@ class AuthSdkSourceImpl(
|
|||
additionalInputs: List<String>,
|
||||
): Result<PasswordStrength> = runCatching {
|
||||
@Suppress("UnsafeCallOnNullableType")
|
||||
clientAuth
|
||||
getClient()
|
||||
.auth()
|
||||
.passwordStrength(
|
||||
password = password,
|
||||
email = email,
|
||||
|
@ -101,10 +96,16 @@ class AuthSdkSourceImpl(
|
|||
passwordStrength: PasswordStrength,
|
||||
policy: MasterPasswordPolicyOptions,
|
||||
): Result<Boolean> = runCatching {
|
||||
clientAuth.satisfiesPolicy(
|
||||
password = password,
|
||||
strength = passwordStrength.toUByte(),
|
||||
policy = policy,
|
||||
)
|
||||
getClient()
|
||||
.auth()
|
||||
.satisfiesPolicy(
|
||||
password = password,
|
||||
strength = passwordStrength.toUByte(),
|
||||
policy = policy,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getClient(
|
||||
userId: String? = null,
|
||||
): Client = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
package com.x8bit.bitwarden.data.auth.datasource.sdk.di
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.AuthSdkSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -21,13 +19,8 @@ object AuthSdkModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideAuthSdkSource(
|
||||
client: Client,
|
||||
featureFlagManager: BitwardenFeatureFlagManager,
|
||||
dispatcherManager: DispatcherManager,
|
||||
sdkClientManager: SdkClientManager,
|
||||
): AuthSdkSource = AuthSdkSourceImpl(
|
||||
clientAuth = client.auth(),
|
||||
clientPlatform = client.platform(),
|
||||
featureFlagManager = featureFlagManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
sdkClientManager = sdkClientManager,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.di
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Provides dependencies related to encryption / decryption / secure generation.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object EncryptionModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideBitwardenClient(): Client {
|
||||
return Client(null)
|
||||
}
|
||||
}
|
|
@ -11,11 +11,11 @@ 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
|
||||
|
||||
/**
|
||||
* Clears any resources from the [Client] associated with the given [userId] and removes it
|
||||
* from the internal cache.
|
||||
*/
|
||||
fun destroyClient(userId: String)
|
||||
fun destroyClient(userId: String?)
|
||||
}
|
||||
|
|
|
@ -9,19 +9,19 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
|||
class SdkClientManagerImpl(
|
||||
private val featureFlagManager: BitwardenFeatureFlagManager,
|
||||
private val clientProvider: suspend () -> Client = {
|
||||
Client(null)
|
||||
.apply { platform().loadFlags(featureFlagManager.featureFlags) }
|
||||
Client(settings = null).apply {
|
||||
platform().loadFlags(featureFlagManager.featureFlags)
|
||||
}
|
||||
},
|
||||
) : SdkClientManager {
|
||||
private val userIdToClientMap = mutableMapOf<String, Client>()
|
||||
private val userIdToClientMap = mutableMapOf<String?, Client>()
|
||||
|
||||
override suspend fun getOrCreateClient(
|
||||
userId: String,
|
||||
): Client =
|
||||
userIdToClientMap.getOrPut(key = userId) { clientProvider() }
|
||||
userId: String?,
|
||||
): Client = userIdToClientMap.getOrPut(key = userId) { clientProvider() }
|
||||
|
||||
override fun destroyClient(
|
||||
userId: String,
|
||||
userId: String?,
|
||||
) {
|
||||
userIdToClientMap
|
||||
.remove(key = userId)
|
||||
|
|
|
@ -3,50 +3,57 @@ package com.x8bit.bitwarden.data.tools.generator.datasource.sdk
|
|||
import com.bitwarden.generators.PassphraseGeneratorRequest
|
||||
import com.bitwarden.generators.PasswordGeneratorRequest
|
||||
import com.bitwarden.generators.UsernameGeneratorRequest
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientGenerators
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
|
||||
/**
|
||||
* Implementation of [GeneratorSdkSource] that delegates password generation.
|
||||
*
|
||||
* @property clientGenerator An instance of [ClientGenerators] provided by the Bitwarden SDK.
|
||||
* @property sdkClientManager The [SdkClientManager] used to retrieve an instance of the
|
||||
* [ClientGenerators] provided by the Bitwarden SDK.
|
||||
*/
|
||||
class GeneratorSdkSourceImpl(
|
||||
private val clientGenerator: ClientGenerators,
|
||||
private val sdkClientManager: SdkClientManager,
|
||||
) : GeneratorSdkSource {
|
||||
|
||||
override suspend fun generatePassword(
|
||||
request: PasswordGeneratorRequest,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.password(request)
|
||||
getClient().generators().password(request)
|
||||
}
|
||||
|
||||
override suspend fun generatePassphrase(
|
||||
request: PassphraseGeneratorRequest,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.passphrase(request)
|
||||
getClient().generators().passphrase(request)
|
||||
}
|
||||
|
||||
override suspend fun generatePlusAddressedEmail(
|
||||
request: UsernameGeneratorRequest.Subaddress,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.username(request)
|
||||
getClient().generators().username(request)
|
||||
}
|
||||
|
||||
override suspend fun generateCatchAllEmail(
|
||||
request: UsernameGeneratorRequest.Catchall,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.username(request)
|
||||
getClient().generators().username(request)
|
||||
}
|
||||
|
||||
override suspend fun generateRandomWord(
|
||||
request: UsernameGeneratorRequest.Word,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.username(request)
|
||||
getClient().generators().username(request)
|
||||
}
|
||||
|
||||
override suspend fun generateForwardedServiceEmail(
|
||||
request: UsernameGeneratorRequest.Forwarded,
|
||||
): Result<String> = runCatching {
|
||||
clientGenerator.username(request)
|
||||
getClient().generators().username(request)
|
||||
}
|
||||
|
||||
private suspend fun getClient(
|
||||
userId: String? = null,
|
||||
): Client = sdkClientManager.getOrCreateClient(userId = userId)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.x8bit.bitwarden.data.tools.generator.datasource.sdk.di
|
||||
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.sdk.GeneratorSdkSourceImpl
|
||||
import dagger.Module
|
||||
|
@ -19,6 +19,6 @@ object GeneratorSdkModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGeneratorSdkSource(
|
||||
client: Client,
|
||||
): GeneratorSdkSource = GeneratorSdkSourceImpl(clientGenerator = client.generators())
|
||||
sdkClientManager: SdkClientManager,
|
||||
): GeneratorSdkSource = GeneratorSdkSourceImpl(sdkClientManager = sdkClientManager)
|
||||
}
|
||||
|
|
|
@ -6,20 +6,20 @@ import com.bitwarden.core.MasterPasswordPolicyOptions
|
|||
import com.bitwarden.core.RegisterKeyResponse
|
||||
import com.bitwarden.crypto.HashPurpose
|
||||
import com.bitwarden.crypto.Kdf
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientAuth
|
||||
import com.bitwarden.sdk.ClientPlatform
|
||||
import com.x8bit.bitwarden.data.auth.datasource.sdk.model.PasswordStrength
|
||||
import com.x8bit.bitwarden.data.platform.base.FakeDispatcherManager
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
|
@ -28,26 +28,18 @@ class AuthSdkSourceTest {
|
|||
private val clientPlatform = mockk<ClientPlatform> {
|
||||
coEvery { loadFlags(any()) } just runs
|
||||
}
|
||||
private val featureFlagManager = mockk<BitwardenFeatureFlagManager> {
|
||||
coEvery { featureFlags } returns emptyMap()
|
||||
private val client = mockk<Client> {
|
||||
every { auth() } returns clientAuth
|
||||
every { platform() } returns clientPlatform
|
||||
}
|
||||
private val sdkClientManager = mockk<SdkClientManager> {
|
||||
coEvery { getOrCreateClient(userId = null) } returns client
|
||||
}
|
||||
private val dispatcherManager = FakeDispatcherManager()
|
||||
|
||||
private val authSkdSource: AuthSdkSource = AuthSdkSourceImpl(
|
||||
clientAuth = clientAuth,
|
||||
clientPlatform = clientPlatform,
|
||||
featureFlagManager = featureFlagManager,
|
||||
dispatcherManager = dispatcherManager,
|
||||
sdkClientManager = sdkClientManager,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
coVerify(exactly = 1) {
|
||||
featureFlagManager.featureFlags
|
||||
clientPlatform.loadFlags(any())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getNewAuthRequest should call SDK and return a Result with correct data`() = runBlocking {
|
||||
val email = "test@gmail.com"
|
||||
|
|
|
@ -5,10 +5,13 @@ import com.bitwarden.generators.ForwarderServiceType
|
|||
import com.bitwarden.generators.PassphraseGeneratorRequest
|
||||
import com.bitwarden.generators.PasswordGeneratorRequest
|
||||
import com.bitwarden.generators.UsernameGeneratorRequest
|
||||
import com.bitwarden.sdk.Client
|
||||
import com.bitwarden.sdk.ClientGenerators
|
||||
import com.x8bit.bitwarden.data.platform.manager.SdkClientManager
|
||||
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
|
@ -16,7 +19,13 @@ import org.junit.Test
|
|||
|
||||
class GeneratorSdkSourceTest {
|
||||
private val clientGenerators = mockk<ClientGenerators>()
|
||||
private val generatorSdkSource: GeneratorSdkSource = GeneratorSdkSourceImpl(clientGenerators)
|
||||
private val client = mockk<Client> {
|
||||
every { generators() } returns clientGenerators
|
||||
}
|
||||
private val sdkClientManager = mockk<SdkClientManager> {
|
||||
coEvery { getOrCreateClient(userId = null) } returns client
|
||||
}
|
||||
private val generatorSdkSource: GeneratorSdkSource = GeneratorSdkSourceImpl(sdkClientManager)
|
||||
|
||||
@Test
|
||||
fun `generatePassword should call SDK and return a Result with the generated password`() =
|
||||
|
|
Loading…
Reference in a new issue