mirror of
https://github.com/bitwarden/android.git
synced 2025-03-15 18:58:59 +03:00
BIT-685: Add request headers to all network requests in the app (#300)
This commit is contained in:
parent
798fdf3e19
commit
9fcd2b1690
8 changed files with 149 additions and 15 deletions
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.datasource.network.di
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.RefreshAuthenticator
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.HeadersInterceptor
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.RetrofitsImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.LocalDateTimeSerializer
|
||||
|
@ -37,6 +38,10 @@ object PlatformNetworkModule {
|
|||
@Singleton
|
||||
fun providesAuthTokenInterceptor(): AuthTokenInterceptor = AuthTokenInterceptor()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesHeadersInterceptor(): HeadersInterceptor = HeadersInterceptor()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesRefreshAuthenticator(): RefreshAuthenticator = RefreshAuthenticator()
|
||||
|
@ -46,12 +51,14 @@ object PlatformNetworkModule {
|
|||
fun provideRetrofits(
|
||||
authTokenInterceptor: AuthTokenInterceptor,
|
||||
baseUrlInterceptors: BaseUrlInterceptors,
|
||||
headersInterceptor: HeadersInterceptor,
|
||||
refreshAuthenticator: RefreshAuthenticator,
|
||||
json: Json,
|
||||
): Retrofits =
|
||||
RetrofitsImpl(
|
||||
authTokenInterceptor = authTokenInterceptor,
|
||||
baseUrlInterceptors = baseUrlInterceptors,
|
||||
headersInterceptor = headersInterceptor,
|
||||
refreshAuthenticator = refreshAuthenticator,
|
||||
json = json,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.interceptor
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_CLIENT_NAME
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_CLIENT_VERSION
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_DEVICE_TYPE
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_USER_AGENT
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_NAME
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_CLIENT_VERSION
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_DEVICE_TYPE
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_VALUE_USER_AGENT
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
|
||||
/**
|
||||
* Interceptor responsible for adding various headers to all API requests.
|
||||
*/
|
||||
class HeadersInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response = chain.proceed(
|
||||
chain.request()
|
||||
.newBuilder()
|
||||
.header(HEADER_KEY_USER_AGENT, HEADER_VALUE_USER_AGENT)
|
||||
.header(HEADER_KEY_CLIENT_NAME, HEADER_VALUE_CLIENT_NAME)
|
||||
.header(HEADER_KEY_CLIENT_VERSION, HEADER_VALUE_CLIENT_VERSION)
|
||||
.header(HEADER_KEY_DEVICE_TYPE, HEADER_VALUE_DEVICE_TYPE)
|
||||
.build(),
|
||||
)
|
||||
}
|
|
@ -6,6 +6,7 @@ import com.x8bit.bitwarden.data.platform.datasource.network.core.ResultCallAdapt
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptor
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.HeadersInterceptor
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
|
@ -18,6 +19,7 @@ import retrofit2.Retrofit
|
|||
class RetrofitsImpl(
|
||||
authTokenInterceptor: AuthTokenInterceptor,
|
||||
baseUrlInterceptors: BaseUrlInterceptors,
|
||||
headersInterceptor: HeadersInterceptor,
|
||||
refreshAuthenticator: RefreshAuthenticator,
|
||||
json: Json,
|
||||
) : Retrofits {
|
||||
|
@ -70,6 +72,7 @@ class RetrofitsImpl(
|
|||
|
||||
private val baseOkHttpClient: OkHttpClient =
|
||||
OkHttpClient.Builder()
|
||||
.addInterceptor(headersInterceptor)
|
||||
.build()
|
||||
|
||||
private val authenticatedOkHttpClient: OkHttpClient by lazy {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.util
|
||||
|
||||
import android.os.Build
|
||||
import com.x8bit.bitwarden.BuildConfig
|
||||
|
||||
/**
|
||||
* The bearer prefix used for the 'authorization' headers value.
|
||||
*/
|
||||
|
@ -9,3 +12,45 @@ const val HEADER_VALUE_BEARER_PREFIX: String = "Bearer "
|
|||
* The key used for the 'authorization' headers.
|
||||
*/
|
||||
const val HEADER_KEY_AUTHORIZATION: String = "Authorization"
|
||||
|
||||
/**
|
||||
* The key used for the 'user-agent' headers.
|
||||
*/
|
||||
const val HEADER_KEY_USER_AGENT: String = "User-Agent"
|
||||
|
||||
/**
|
||||
* The value used for the 'user-agent' headers.
|
||||
*/
|
||||
@Suppress("MaxLineLength")
|
||||
val HEADER_VALUE_USER_AGENT: String =
|
||||
"Bitwarden_Mobile/${BuildConfig.VERSION_NAME} (Android ${Build.VERSION.RELEASE}; SDK ${Build.VERSION.SDK_INT}; Model ${Build.MODEL})"
|
||||
|
||||
/**
|
||||
* The key used for the 'bitwarden-client-name' headers.
|
||||
*/
|
||||
const val HEADER_KEY_CLIENT_NAME: String = "Bitwarden-Client-Name"
|
||||
|
||||
/**
|
||||
* The value used for the 'bitwarden-client-name' headers.
|
||||
*/
|
||||
const val HEADER_VALUE_CLIENT_NAME: String = "mobile"
|
||||
|
||||
/**
|
||||
* The key used for the 'bitwarden-client-version' headers.
|
||||
*/
|
||||
const val HEADER_KEY_CLIENT_VERSION: String = "Bitwarden-Client-Version"
|
||||
|
||||
/**
|
||||
* The value used for the 'bitwarden-client-version' headers.
|
||||
*/
|
||||
const val HEADER_VALUE_CLIENT_VERSION: String = BuildConfig.VERSION_NAME
|
||||
|
||||
/**
|
||||
* The key used for the 'device-type' headers.
|
||||
*/
|
||||
const val HEADER_KEY_DEVICE_TYPE: String = "Device-Type"
|
||||
|
||||
/**
|
||||
* The value used for the 'device-type' headers.
|
||||
*/
|
||||
const val HEADER_VALUE_DEVICE_TYPE: String = "0"
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.network.interceptor
|
||||
|
||||
import android.os.Build
|
||||
import com.x8bit.bitwarden.BuildConfig
|
||||
import com.x8bit.bitwarden.ui.platform.base.BaseRobolectricTest
|
||||
import okhttp3.Request
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class HeadersInterceptorTest : BaseRobolectricTest() {
|
||||
|
||||
private val headersInterceptors = HeadersInterceptor()
|
||||
|
||||
@Test
|
||||
fun `intercept should modify original request to include custom headers`() {
|
||||
// We reference the real BuildConfig here, since we don't want the test to break on every
|
||||
// version bump. We are also doing the same thing for Build when the SDK gets incremented.
|
||||
val versionName = BuildConfig.VERSION_NAME
|
||||
val release = Build.VERSION.RELEASE
|
||||
val sdk = Build.VERSION.SDK_INT
|
||||
val originalRequest = Request.Builder().url("http://www.fake.com/").build()
|
||||
val chain = FakeInterceptorChain(originalRequest)
|
||||
|
||||
val response = headersInterceptors.intercept(chain)
|
||||
|
||||
val request = response.request
|
||||
assertEquals(
|
||||
"Bitwarden_Mobile/$versionName (Android $release; SDK $sdk; Model robolectric)",
|
||||
request.header("User-Agent"),
|
||||
)
|
||||
assertEquals("mobile", request.header("Bitwarden-Client-Name"))
|
||||
assertEquals(versionName, request.header("Bitwarden-Client-Version"))
|
||||
assertEquals("0", request.header("Device-Type"))
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.platform.datasource.network.retrofit
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.authenticator.RefreshAuthenticator
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.AuthTokenInterceptor
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.BaseUrlInterceptors
|
||||
import com.x8bit.bitwarden.data.platform.datasource.network.interceptor.HeadersInterceptor
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
|
@ -37,6 +38,9 @@ class RetrofitsTest {
|
|||
mockIntercept { isEventsInterceptorCalled = true }
|
||||
}
|
||||
}
|
||||
private val headersInterceptors = mockk<HeadersInterceptor> {
|
||||
mockIntercept { isheadersInterceptorCalled = true }
|
||||
}
|
||||
private val refreshAuthenticator = mockk<RefreshAuthenticator> {
|
||||
mockAuthenticate { isRefreshAuthenticatorCalled = true }
|
||||
}
|
||||
|
@ -46,12 +50,14 @@ class RetrofitsTest {
|
|||
private val retrofits = RetrofitsImpl(
|
||||
authTokenInterceptor = authTokenInterceptor,
|
||||
baseUrlInterceptors = baseUrlInterceptors,
|
||||
headersInterceptor = headersInterceptors,
|
||||
refreshAuthenticator = refreshAuthenticator,
|
||||
json = json,
|
||||
)
|
||||
|
||||
private var isAuthInterceptorCalled = false
|
||||
private var isApiInterceptorCalled = false
|
||||
private var isheadersInterceptorCalled = false
|
||||
private var isIdentityInterceptorCalled = false
|
||||
private var isEventsInterceptorCalled = false
|
||||
private var isRefreshAuthenticatorCalled = false
|
||||
|
@ -122,6 +128,7 @@ class RetrofitsTest {
|
|||
|
||||
assertTrue(isAuthInterceptorCalled)
|
||||
assertTrue(isApiInterceptorCalled)
|
||||
assertTrue(isheadersInterceptorCalled)
|
||||
assertFalse(isIdentityInterceptorCalled)
|
||||
assertFalse(isEventsInterceptorCalled)
|
||||
}
|
||||
|
@ -139,6 +146,7 @@ class RetrofitsTest {
|
|||
|
||||
assertFalse(isAuthInterceptorCalled)
|
||||
assertTrue(isApiInterceptorCalled)
|
||||
assertTrue(isheadersInterceptorCalled)
|
||||
assertFalse(isIdentityInterceptorCalled)
|
||||
assertFalse(isEventsInterceptorCalled)
|
||||
}
|
||||
|
@ -156,6 +164,7 @@ class RetrofitsTest {
|
|||
|
||||
assertFalse(isAuthInterceptorCalled)
|
||||
assertFalse(isApiInterceptorCalled)
|
||||
assertTrue(isheadersInterceptorCalled)
|
||||
assertTrue(isIdentityInterceptorCalled)
|
||||
assertFalse(isEventsInterceptorCalled)
|
||||
}
|
||||
|
@ -175,6 +184,7 @@ class RetrofitsTest {
|
|||
|
||||
assertFalse(isAuthInterceptorCalled)
|
||||
assertFalse(isApiInterceptorCalled)
|
||||
assertTrue(isheadersInterceptorCalled)
|
||||
assertFalse(isIdentityInterceptorCalled)
|
||||
assertFalse(isEventsInterceptorCalled)
|
||||
}
|
||||
|
|
|
@ -3,30 +3,16 @@ package com.x8bit.bitwarden.ui.platform.base
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import com.x8bit.bitwarden.ui.platform.theme.BitwardenTheme
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import org.junit.Rule
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.shadows.ShadowLog
|
||||
|
||||
/**
|
||||
* A base class that can be used for performing Compose-layer testing using Robolectric, Compose
|
||||
* Testing, and JUnit 4.
|
||||
*/
|
||||
@Config(
|
||||
application = HiltTestApplication::class,
|
||||
sdk = [Config.NEWEST_SDK],
|
||||
)
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
abstract class BaseComposeTest {
|
||||
abstract class BaseComposeTest : BaseRobolectricTest() {
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
init {
|
||||
ShadowLog.stream = System.out
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for testing a basic Composable function that only requires a Composable environment
|
||||
* with the [BitwardenTheme].
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package com.x8bit.bitwarden.ui.platform.base
|
||||
|
||||
import dagger.hilt.android.testing.HiltTestApplication
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.shadows.ShadowLog
|
||||
|
||||
/**
|
||||
* A base class that can be used for performing tests that use Robolectric and JUnit 4.
|
||||
*/
|
||||
@Config(
|
||||
application = HiltTestApplication::class,
|
||||
sdk = [Config.NEWEST_SDK],
|
||||
)
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
abstract class BaseRobolectricTest {
|
||||
init {
|
||||
ShadowLog.stream = System.out
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue