Compare commits

..

3 Commits

Author SHA1 Message Date
bw-ghapp[bot]
cada80b4b8 SDK Update - com.bitwarden:sdk-android 3.0.0-7360-56536238 2026-06-11 04:17:24 +00:00
bw-ghapp[bot]
b7a2691804 SDK Update - com.bitwarden:sdk-android 3.0.0-7351-75d69bfb 2026-06-10 14:32:19 +00:00
bw-ghapp[bot]
2c2c534bcd SDK Update - com.bitwarden:sdk-android 3.0.0-7350-9eb8c6eb 2026-06-10 14:24:30 +00:00
32 changed files with 1 additions and 426 deletions

View File

@@ -27,10 +27,4 @@ interface EnvironmentDiskSource {
* Stores the [urls] for the given [userEmail].
*/
fun storePreAuthEnvironmentUrlDataForEmail(userEmail: String, urls: EnvironmentUrlDataJson)
/**
* The fill-assist URL provided by the server config, or `null` if the server does not
* configure fill-assist targeting rules.
*/
var fillAssistRulesUrl: String?
}

View File

@@ -11,7 +11,6 @@ import kotlinx.serialization.json.Json
private const val PRE_AUTH_URLS_KEY = "preAuthEnvironmentUrls"
private const val EMAIL_VERIFICATION_URLS = "emailVerificationUrls"
private const val FILL_ASSIST_RULES_URL_KEY = "fillAssistRulesUrl"
/**
* Primary implementation of [EnvironmentDiskSource].
@@ -55,8 +54,4 @@ class EnvironmentDiskSourceImpl(
value = json.encodeToString(urls),
)
}
override var fillAssistRulesUrl: String?
get() = getString(key = FILL_ASSIST_RULES_URL_KEY)
set(value) = putString(key = FILL_ASSIST_RULES_URL_KEY, value = value)
}

View File

@@ -7,7 +7,6 @@ import com.bitwarden.network.interceptor.BaseUrlsProvider
import com.bitwarden.network.model.BitwardenServiceClientConfig
import com.bitwarden.network.service.ConfigService
import com.bitwarden.network.service.EventService
import com.bitwarden.network.service.FillAssistService
import com.bitwarden.network.service.PushService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.manager.AuthTokenManager
@@ -33,12 +32,6 @@ import javax.inject.Singleton
@InstallIn(SingletonComponent::class)
object PlatformNetworkModule {
@Provides
@Singleton
fun providesFillAssistService(
bitwardenServiceClient: BitwardenServiceClient,
): FillAssistService = bitwardenServiceClient.fillAssistService
@Provides
@Singleton
fun providesConfigService(

View File

@@ -31,6 +31,4 @@ class BaseUrlsProviderImpl(
.toEnvironmentUrlsOrDefault()
.environmentUrlData
.baseEventsUrl
override fun getBaseFillAssistUrl(): String? = environmentDiskSource.fillAssistRulesUrl
}

View File

@@ -7589,7 +7589,6 @@ class AuthRepositoryTest {
identityUrl = "mockIdentityUrl",
notificationsUrl = "mockNotificationsUrl",
ssoUrl = "mockSsoUrl",
fillAssistRulesUrl = null,
),
featureStates = emptyMap(),
communication = null,

View File

@@ -95,26 +95,11 @@ class EnvironmentDiskSourceTest {
json.parseToJsonElement(requireNotNull(actual)),
)
}
@Test
fun `fillAssistRulesUrl should pull from and update SharedPreferences`() {
assertNull(environmentDiskSource.fillAssistRulesUrl)
assertNull(fakeSharedPreferences.getString(FILL_ASSIST_RULES_URL_KEY, null))
environmentDiskSource.fillAssistRulesUrl = "https://fill-assist.example.com/"
assertEquals(
"https://fill-assist.example.com/",
fakeSharedPreferences.getString(FILL_ASSIST_RULES_URL_KEY, null),
)
environmentDiskSource.fillAssistRulesUrl = null
assertNull(fakeSharedPreferences.getString(FILL_ASSIST_RULES_URL_KEY, null))
}
}
private const val EMAIL = "email@example.com"
private const val EMAIL_VERIFICATION_URLS_KEY = "bwPreferencesStorage:emailVerificationUrls"
private const val PRE_AUTH_URLS_KEY = "bwPreferencesStorage:preAuthEnvironmentUrls"
private const val FILL_ASSIST_RULES_URL_KEY = "bwPreferencesStorage:fillAssistRulesUrl"
private const val ENVIRONMENT_URL_DATA_JSON = """
{

View File

@@ -29,8 +29,6 @@ class FakeEnvironmentDiskSource : EnvironmentDiskSource {
storedEmailVerificationUrls[userEmail] = urls
}
override var fillAssistRulesUrl: String? = null
private val mutablePreAuthEnvironmentUrlDataFlow =
bufferedMutableSharedFlow<EnvironmentUrlDataJson?>(replay = 1)
}

View File

@@ -62,7 +62,6 @@ class ServerCommunicationConfigRepositoryTest {
identityUrl = null,
notificationsUrl = null,
ssoUrl = null,
fillAssistRulesUrl = null,
),
featureStates = null,
communication = ConfigResponseJson.CommunicationJson(

View File

@@ -319,7 +319,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"dummy-boolean" to JsonPrimitive(true),

View File

@@ -4,7 +4,6 @@ import com.bitwarden.data.repository.model.Environment
import com.x8bit.bitwarden.data.platform.datasource.disk.FakeEnvironmentDiskSource
import com.x8bit.bitwarden.data.platform.provider.BaseUrlsProviderImpl
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
class BaseUrlsProviderTest {
@@ -67,16 +66,4 @@ class BaseUrlsProviderTest {
baseUrlsManager.getBaseEventsUrl(),
)
}
@Test
fun `getBaseFillAssistUrl should return url from disk source when present`() {
fakeEnvironmentDiskSource.fillAssistRulesUrl = "https://example.com/"
assertEquals("https://example.com/", baseUrlsManager.getBaseFillAssistUrl())
}
@Test
fun `getBaseFillAssistUrl should return null when not set`() {
fakeEnvironmentDiskSource.fillAssistRulesUrl = null
assertNull(baseUrlsManager.getBaseFillAssistUrl())
}
}

View File

@@ -36,7 +36,6 @@ class SdkRepositoryFactoryTests {
override fun getBaseApiUrl(): String = BASE_API_URL
override fun getBaseIdentityUrl(): String = BASE_IDENTITY_URL
override fun getBaseEventsUrl(): String = BASE_EVENTS_URL
override fun getBaseFillAssistUrl(): String? = null
},
authTokenProvider = mockk(),
certificateProvider = mockk(),

View File

@@ -48,7 +48,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"duo-redirect" to JsonPrimitive(true),

View File

@@ -20,6 +20,4 @@ object BaseUrlsProviderImpl : BaseUrlsProvider {
override fun getBaseEventsUrl(): String =
Environment.Us.environmentUrlData.baseEventsUrl
override fun getBaseFillAssistUrl(): String? = null
}

View File

@@ -262,7 +262,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"dummy-boolean" to JsonPrimitive(true),

View File

@@ -48,7 +48,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"duo-redirect" to JsonPrimitive(true),

View File

@@ -107,7 +107,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"duo-redirect" to JsonPrimitive(true),

View File

@@ -161,7 +161,6 @@ private val SERVER_CONFIG = ServerConfig(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"duo-redirect" to JsonPrimitive(true),
@@ -186,7 +185,6 @@ private val CONFIG_RESPONSE_JSON = ConfigResponseJson(
identityUrl = "http://localhost:33656",
notificationsUrl = "http://localhost:61840",
ssoUrl = "http://localhost:51822",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"duo-redirect" to JsonPrimitive(true),

View File

@@ -30,7 +30,7 @@ androidxRoom = "2.8.4"
androidxSecurityCrypto = "1.1.0"
androidxSplash = "1.2.0"
androidxWork = "2.11.2"
bitwardenSdk = "3.0.0-7338-5bdc976f"
bitwardenSdk = "3.0.0-7360-56536238"
crashlytics = "3.0.7"
detekt = "1.23.8"
firebaseBom = "34.14.0"

View File

@@ -16,7 +16,6 @@ import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.DigitalAssetLinkService
import com.bitwarden.network.service.DownloadService
import com.bitwarden.network.service.EventService
import com.bitwarden.network.service.FillAssistService
import com.bitwarden.network.service.FolderService
import com.bitwarden.network.service.HaveIBeenPwnedService
import com.bitwarden.network.service.IdentityService
@@ -108,11 +107,6 @@ interface BitwardenServiceClient {
*/
val eventService: EventService
/**
* Provides access to the Fill-Assist service.
*/
val fillAssistService: FillAssistService
/**
* Provides access to the Folder service.
*/

View File

@@ -29,8 +29,6 @@ import com.bitwarden.network.service.DownloadService
import com.bitwarden.network.service.DownloadServiceImpl
import com.bitwarden.network.service.EventService
import com.bitwarden.network.service.EventServiceImpl
import com.bitwarden.network.service.FillAssistService
import com.bitwarden.network.service.FillAssistServiceImpl
import com.bitwarden.network.service.FolderService
import com.bitwarden.network.service.FolderServiceImpl
import com.bitwarden.network.service.HaveIBeenPwnedService
@@ -157,10 +155,6 @@ internal class BitwardenServiceClientImpl(
)
}
override val fillAssistService: FillAssistService by lazy {
FillAssistServiceImpl(api = retrofits.fillAssistRetrofit.create())
}
override val haveIBeenPwnedService: HaveIBeenPwnedService by lazy {
HaveIBeenPwnedServiceImpl(
api = retrofits

View File

@@ -1,27 +0,0 @@
package com.bitwarden.network.api
import com.bitwarden.network.model.FillAssistFormsJson
import com.bitwarden.network.model.FillAssistManifestJson
import com.bitwarden.network.model.NetworkResult
import retrofit2.http.GET
import retrofit2.http.Path
/**
* Defines endpoints for retrieving fill-assist targeting rules. The base URL is set dynamically
* at runtime via [com.bitwarden.network.interceptor.BaseUrlInterceptors.fillAssistInterceptor].
*/
internal interface FillAssistApi {
/**
* Fetches the fill-assist manifest.
*/
@GET("manifest.json")
suspend fun getManifest(): NetworkResult<FillAssistManifestJson>
/**
* Fetches the forms rules file by [filename] (e.g. "forms.v1.json").
*/
@GET("{filename}")
suspend fun getForms(
@Path("filename") filename: String,
): NetworkResult<FillAssistFormsJson>
}

View File

@@ -29,11 +29,4 @@ internal class BaseUrlInterceptors(
val eventsInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
baseUrlsProvider.getBaseEventsUrl()
}
/**
* An interceptor for fill-assist calls.
*/
val fillAssistInterceptor: BaseUrlInterceptor = BaseUrlInterceptor {
baseUrlsProvider.getBaseFillAssistUrl()
}
}

View File

@@ -18,10 +18,4 @@ interface BaseUrlsProvider {
* Gets the base URL for "/events" calls.
*/
fun getBaseEventsUrl(): String
/**
* Gets the base URL for fill-assist calls, or null if the server does not provide
* fill-assist targeting rules.
*/
fun getBaseFillAssistUrl(): String?
}

View File

@@ -62,8 +62,6 @@ data class ConfigResponseJson(
* @param identityUrl The URL of the identity service in the environment.
* @param notificationsUrl The URL of the notifications service in the environment.
* @param ssoUrl The URL of the single sign-on (SSO) service in the environment.
* @param fillAssistRulesUrl The base URL of the fill-assist targeting rules, or null if
* the server does not provide fill-assist rules.
*/
@Serializable
data class EnvironmentJson(
@@ -84,9 +82,6 @@ data class ConfigResponseJson(
@SerialName("sso")
val ssoUrl: String?,
@SerialName("fillAssistRules")
val fillAssistRulesUrl: String?,
)
/**

View File

@@ -1,68 +0,0 @@
package com.bitwarden.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
/**
* Represents the fill-assist forms rules file.
*
* @property schemaVersion The semantic version string for this file (e.g. "1.0.0").
* @property hosts Map of hostname (optionally with port) to [HostEntryJson], or null if the host
* is explicitly excluded from fill-assist.
*/
@Serializable
data class FillAssistFormsJson(
@SerialName("schemaVersion")
val schemaVersion: String,
@SerialName("hosts")
val hosts: Map<String, HostEntryJson?>,
) {
/**
* Form descriptions and pathname-specific overrides for a single host.
*
* @property forms Site-wide fallback form descriptions.
* @property pathnames Pathname-specific overrides; a null value means that path is excluded.
*/
@Serializable
data class HostEntryJson(
@SerialName("forms")
val forms: List<FormJson>?,
@SerialName("pathnames")
val pathnames: Map<String, PathnameEntryJson?>?,
)
/**
* Form descriptions for a specific pathname.
*
* @property forms The form descriptions for this path.
*/
@Serializable
data class PathnameEntryJson(
@SerialName("forms")
val forms: List<FormJson>,
)
/**
* Describes one logical form on a page.
*
* @property category The categorical purpose of this form (e.g. "account-login").
* @property container Optional CSS selectors identifying the form's container element.
* @property fields Map of field key to [JsonElement] representing a compositeSelectorArray.
* Each array element is either a CSS selector string or an array of strings for composite
* multi-input fields. Unknown fields are gracefully ignored via [ignoreUnknownKeys].
*/
@Serializable
data class FormJson(
@SerialName("category")
val category: String,
@SerialName("container")
val container: List<String>?,
@SerialName("fields")
val fields: Map<String, JsonElement>,
)
}

View File

@@ -1,64 +0,0 @@
package com.bitwarden.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Represents the fill-assist manifest returned by the fill-assist service.
*
* @property buildId The unique identifier for this build.
* @property timestamp The ISO-8601 timestamp when this build was produced.
* @property gitSha The git commit SHA for this build.
* @property maps The map data entries keyed by map type.
*/
@Serializable
data class FillAssistManifestJson(
@SerialName("buildId")
val buildId: String,
@SerialName("timestamp")
val timestamp: String,
@SerialName("gitSha")
val gitSha: String,
@SerialName("maps")
val maps: MapsJson,
) {
/**
* Container for all available maps.
*
* @property forms Map of schema version string (e.g. "v1", "v2") to [FileEntryJson].
* Using a [Map] allows new versions to appear automatically without model changes.
*/
@Serializable
data class MapsJson(
@SerialName("forms")
val forms: Map<String, FileEntryJson>,
)
/**
* Metadata for a single versioned file in a map.
*
* @property filename The filename to fetch (e.g. "forms.v1.json").
* @property cid The SHA-256 content hash in "sha256:<hex>" format. Used as a staleness key
* to detect when the forms file has changed on the server, avoiding unnecessary re-downloads.
* @property schema The schema filename associated with this file version.
* @property deprecated When true, this version has entered its end-of-life support window.
* Consumers should plan migration but may continue using the version until it is removed.
*/
@Serializable
data class FileEntryJson(
@SerialName("filename")
val filename: String,
@SerialName("cid")
val cid: String,
@SerialName("schema")
val schema: String,
@SerialName("deprecated")
val deprecated: Boolean?,
)
}

View File

@@ -36,12 +36,6 @@ internal interface Retrofits {
*/
val unauthenticatedIdentityRetrofit: Retrofit
/**
* Allows access to fill-assist calls. The base URL is determined dynamically via the
* [BaseUrlInterceptors.fillAssistInterceptor].
*/
val fillAssistRetrofit: Retrofit
/**
* Allows access to static API calls (ex: external APIs).
*

View File

@@ -64,16 +64,6 @@ internal class RetrofitsImpl(
//endregion Unauthenticated Retrofits
//region Fill-Assist Retrofit
override val fillAssistRetrofit: Retrofit by lazy {
createUnauthenticatedRetrofit(
baseUrlInterceptor = baseUrlInterceptors.fillAssistInterceptor,
)
}
//endregion Fill-Assist Retrofit
//region Static Retrofit
override fun createStaticRetrofit(isAuthenticated: Boolean, baseUrl: String): Retrofit {

View File

@@ -1,21 +0,0 @@
package com.bitwarden.network.service
import com.bitwarden.network.model.FillAssistFormsJson
import com.bitwarden.network.model.FillAssistManifestJson
/**
* Provides access to the fill-assist targeting rules service.
*/
interface FillAssistService {
/**
* Fetches and parses the fill-assist manifest.
*/
suspend fun getManifest(): Result<FillAssistManifestJson>
/**
* Downloads and parses the forms rules file identified by [filename] (e.g. "forms.v1.json").
*
* Returns [Result.failure] if the network request fails or parsing fails.
*/
suspend fun getForms(filename: String): Result<FillAssistFormsJson>
}

View File

@@ -1,20 +0,0 @@
package com.bitwarden.network.service
import com.bitwarden.network.api.FillAssistApi
import com.bitwarden.network.model.FillAssistFormsJson
import com.bitwarden.network.model.FillAssistManifestJson
import com.bitwarden.network.util.toResult
/**
* Default implementation of [FillAssistService].
*/
internal class FillAssistServiceImpl(
private val api: FillAssistApi,
) : FillAssistService {
override suspend fun getManifest(): Result<FillAssistManifestJson> =
api.getManifest().toResult()
override suspend fun getForms(filename: String): Result<FillAssistFormsJson> =
api.getForms(filename = filename).toResult()
}

View File

@@ -68,7 +68,6 @@ private val CONFIG_RESPONSE = ConfigResponseJson(
notificationsUrl = "notificationsUrl",
identityUrl = "identityUrl",
ssoUrl = "ssoUrl",
fillAssistRulesUrl = null,
),
featureStates = mapOf(
"feature one" to JsonPrimitive(false),

View File

@@ -1,116 +0,0 @@
package com.bitwarden.network.service
import com.bitwarden.core.data.util.asSuccess
import com.bitwarden.network.api.FillAssistApi
import com.bitwarden.network.base.BaseServiceTest
import com.bitwarden.network.model.FillAssistFormsJson
import com.bitwarden.network.model.FillAssistManifestJson
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonPrimitive
import okhttp3.mockwebserver.MockResponse
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
import retrofit2.create
class FillAssistServiceTest : BaseServiceTest() {
private val api: FillAssistApi = retrofit.create()
private val service = FillAssistServiceImpl(api = api)
@Test
fun `getManifest should parse manifest response`() = runTest {
server.enqueue(MockResponse().setBody(MANIFEST_JSON))
assertEquals(MANIFEST.asSuccess(), service.getManifest())
}
@Test
fun `getManifest should return failure on server error`() = runTest {
server.enqueue(MockResponse().setResponseCode(500))
assertTrue(service.getManifest().isFailure)
}
@Test
fun `getForms should parse and return forms`() = runTest {
server.enqueue(MockResponse().setBody(FORMS_V1_JSON))
assertEquals(FORMS_V1.asSuccess(), service.getForms(filename = "forms.v1.json"))
}
@Test
fun `getForms should return failure on server error`() = runTest {
server.enqueue(MockResponse().setResponseCode(404))
assertTrue(service.getForms(filename = "forms.v1.json").isFailure)
}
}
private val MANIFEST = FillAssistManifestJson(
buildId = "local-build",
timestamp = "2026-05-20T15:01:02.956Z",
gitSha = "abc123",
maps = FillAssistManifestJson.MapsJson(
forms = mapOf(
"v1" to FillAssistManifestJson.FileEntryJson(
filename = "forms.v1.json",
cid = "sha256:5b8f688d24bb9c38b4094838fa2baacb3cc4ab302e3545adf016b05f6b6b96db",
schema = "forms.v1.schema.json",
deprecated = null,
),
),
),
)
private val FORMS_V1 = FillAssistFormsJson(
schemaVersion = "1.0.0",
hosts = mapOf(
"example.com" to FillAssistFormsJson.HostEntryJson(
forms = listOf(
FillAssistFormsJson.FormJson(
category = "account-login",
container = null,
fields = mapOf(
"username" to JsonArray(listOf(JsonPrimitive("input#user"))),
"password" to JsonArray(listOf(JsonPrimitive("input#pass"))),
),
),
),
pathnames = null,
),
),
)
private const val MANIFEST_JSON = """
{
"buildId": "local-build",
"timestamp": "2026-05-20T15:01:02.956Z",
"gitSha": "abc123",
"maps": {
"forms": {
"v1": {
"filename": "forms.v1.json",
"cid": "sha256:5b8f688d24bb9c38b4094838fa2baacb3cc4ab302e3545adf016b05f6b6b96db",
"schema": "forms.v1.schema.json"
}
}
}
}
"""
private const val FORMS_V1_JSON = """
{
"schemaVersion": "1.0.0",
"hosts": {
"example.com": {
"forms": [
{
"category": "account-login",
"fields": {
"username": ["input#user"],
"password": ["input#pass"]
}
}
]
}
}
}
"""