Skip to content

Commit

Permalink
BIT-2411: Add logic for managed device pre-configured URLs (#3358)
Browse files Browse the repository at this point in the history
  • Loading branch information
david-livefront authored Jun 25, 2024
1 parent 63e7465 commit d9b1809
Show file tree
Hide file tree
Showing 8 changed files with 411 additions and 0 deletions.
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,11 @@
android:name="autoStoreLocales"
android:value="true" />
</service>

<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/app_restrictions" />

</application>

<queries>
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/x8bit/bitwarden/BitwardenApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.manager.CrashLogsManager
import com.x8bit.bitwarden.data.platform.manager.NetworkConfigManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

Expand All @@ -28,4 +29,7 @@ class BitwardenApplication : Application() {

@Inject
lateinit var organizationEventManager: OrganizationEventManager

@Inject
lateinit var restrictionManager: RestrictionManager
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.platform.manager.di

import android.app.Application
import android.content.Context
import androidx.core.content.getSystemService
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.platform.datasource.disk.EventDiskSource
Expand Down Expand Up @@ -41,6 +42,8 @@ import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManager
import com.x8bit.bitwarden.data.platform.manager.event.OrganizationEventManagerImpl
import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManager
import com.x8bit.bitwarden.data.platform.manager.garbage.GarbageCollectionManagerImpl
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManagerImpl
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.SettingsRepository
import com.x8bit.bitwarden.data.vault.datasource.sdk.BitwardenFeatureFlagManager
Expand Down Expand Up @@ -211,4 +214,19 @@ object PlatformManagerModule {
context = context,
dispatcherManager = dispatcherManager,
)

@Provides
@Singleton
fun provideRestrictionManager(
@ApplicationContext context: Context,
appForegroundManager: AppForegroundManager,
dispatcherManager: DispatcherManager,
environmentRepository: EnvironmentRepository,
): RestrictionManager = RestrictionManagerImpl(
appForegroundManager = appForegroundManager,
dispatcherManager = dispatcherManager,
context = context,
environmentRepository = environmentRepository,
restrictionsManager = requireNotNull(context.getSystemService()),
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.x8bit.bitwarden.data.platform.manager.restriction

/**
* A manager for handling restrictions.
*/
interface RestrictionManager
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.x8bit.bitwarden.data.platform.manager.restriction

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.RestrictionsManager
import android.os.Bundle
import com.x8bit.bitwarden.data.platform.manager.AppForegroundManager
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
import com.x8bit.bitwarden.data.platform.manager.model.AppForegroundState
import com.x8bit.bitwarden.data.platform.repository.EnvironmentRepository
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach

/**
* The default implementation of the [RestrictionManager].
*/
class RestrictionManagerImpl(
appForegroundManager: AppForegroundManager,
dispatcherManager: DispatcherManager,
private val context: Context,
private val environmentRepository: EnvironmentRepository,
private val restrictionsManager: RestrictionsManager,
) : RestrictionManager {
private val mainScope = CoroutineScope(dispatcherManager.main)
private val intentFilter = IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED)
private val restrictionsChangedReceiver = RestrictionsChangedReceiver()
private var isReceiverRegistered = false

init {
appForegroundManager
.appForegroundStateFlow
.onEach {
when (it) {
AppForegroundState.BACKGROUNDED -> handleBackground()
AppForegroundState.FOREGROUNDED -> handleForeground()
}
}
.launchIn(mainScope)
}

private fun handleBackground() {
if (isReceiverRegistered) {
context.unregisterReceiver(restrictionsChangedReceiver)
}
isReceiverRegistered = false
}

private fun handleForeground() {
context.registerReceiver(restrictionsChangedReceiver, intentFilter)
isReceiverRegistered = true
updatePreconfiguredRestrictionSettings()
}

private fun updatePreconfiguredRestrictionSettings() {
restrictionsManager
.applicationRestrictions
?.takeUnless { it.isEmpty }
?.let { setPreconfiguredSettings(it) }
}

private fun setPreconfiguredSettings(bundle: Bundle) {
bundle
.getString(BASE_ENVIRONMENT_URL_RESTRICTION_KEY)
?.let { url -> setPreconfiguredUrl(baseEnvironmentUrl = url) }
}

private fun setPreconfiguredUrl(baseEnvironmentUrl: String) {
environmentRepository.environment = when (val current = environmentRepository.environment) {
Environment.Us -> {
when (baseEnvironmentUrl) {
// If the base matches the predefined US environment, leave it alone
Environment.Us.environmentUrlData.base -> current
// If the base does not match the predefined US environment, create a
// self-hosted environment with the new base
else -> current.toSelfHosted(base = baseEnvironmentUrl)
}
}

Environment.Eu -> {
when (baseEnvironmentUrl) {
// If the base matches the predefined EU environment, leave it alone
Environment.Eu.environmentUrlData.base -> current
// If the base does not match the predefined EU environment, create a
// self-hosted environment with the new base
else -> current.toSelfHosted(base = baseEnvironmentUrl)
}
}

is Environment.SelfHosted -> current.toSelfHosted(base = baseEnvironmentUrl)
}
}

/**
* A [BroadcastReceiver] used to listen for [Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED]
* updates.
*
* Note: The `Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED` will only be received if the
* `BroadcastReceiver` is dynamically registered, so this cannot be registered in the manifest.
*/
private inner class RestrictionsChangedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED) {
updatePreconfiguredRestrictionSettings()
}
}
}
}

private const val BASE_ENVIRONMENT_URL_RESTRICTION_KEY: String = "baseEnvironmentUrl"

/**
* Helper method for creating a new [Environment.SelfHosted] with a new base.
*/
private fun Environment.toSelfHosted(
base: String,
): Environment.SelfHosted =
Environment.SelfHosted(
environmentUrlData = environmentUrlData.copy(base = base),
)
1 change: 1 addition & 0 deletions app/src/main/res/values/strings_non_localized.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
<string name="continue_to_complete_web_authn_verfication" translatable="false">Continue to complete WebAuthn verification.</string>
<string name="launch_web_authn" translatable="false">Launch WebAuthn</string>
<string name="there_was_an_error_starting_web_authn_two_factor_authentication" translatable="false">There was an error starting WebAuthn two factor authentication</string>
<string name="self_hosted_server_url" translatable="false">Self-hosted server URL</string>
</resources>
9 changes: 9 additions & 0 deletions app/src/main/res/xml/app_restrictions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
<restriction
android:defaultValue="@null"
android:description="@string/server_url"
android:key="baseEnvironmentUrl"
android:restrictionType="string"
android:title="@string/self_hosted_server_url" />
</restrictions>
Loading

0 comments on commit d9b1809

Please sign in to comment.