mdm: implement initial data structure to read from Android RestrictionsManager (#197)

updates tailscale/corp#18202
updates ENG-2849

Implements the basic data model for supporting MDM to allow us to add the hooks in the UI.

Signed-off-by: Jonathan Nobels <jonathan@tailscale.com>
Co-authored-by: Andrea Gottardo <andrea@gottardo.me>
pull/198/head
Jonathan Nobels 2 months ago committed by GitHub
parent bf0e56469f
commit 0d867aedce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -164,7 +164,7 @@ public class App extends Application {
return getEncryptedPrefs().getString(prefKey, null);
}
private SharedPreferences getEncryptedPrefs() throws IOException, GeneralSecurityException {
public SharedPreferences getEncryptedPrefs() throws IOException, GeneralSecurityException {
MasterKey key = new MasterKey.Builder(this)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build();

@ -1,12 +1,12 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com.tailscale.ipn
import android.content.Intent
import android.net.Uri
import android.content.Context
import android.content.RestrictionsManager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@ -16,6 +16,7 @@ import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.service.IpnManager
import com.tailscale.ipn.ui.theme.AppTheme
import com.tailscale.ipn.ui.view.AboutView
@ -102,5 +103,12 @@ class MainActivity : ComponentActivity() {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(browserIntent)
}
override fun onResume() {
super.onResume()
val restrictionsManager = this.getSystemService(Context.RESTRICTIONS_SERVICE) as RestrictionsManager
manager.mdmSettings = MDMSettings(restrictionsManager)
}
}

@ -3,12 +3,35 @@
package com.tailscale.ipn.mdm
enum class NetworkDevices(val value: String) {
currentUser("current-user"),
otherUsers("other-users"),
taggedDevices("tagged-devices"),
}
import android.content.RestrictionsManager
import com.tailscale.ipn.App
class MDMSettings(private val restrictionsManager: RestrictionsManager? = null) {
// TODO(angott): implement a typed enum string array type
val hiddenNetworkDevices: List<NetworkDevices> = emptyList()
fun get(setting: BooleanSetting): Boolean {
restrictionsManager?.let { restrictionsManager ->
restrictionsManager.applicationRestrictions.containsKey(setting.key)
return restrictionsManager.applicationRestrictions.getBoolean(setting.key)
} ?: run {
return App.getApplication().encryptedPrefs.getBoolean(setting.key, false)
}
}
fun get(setting: StringSetting): String? {
return App.getApplication().encryptedPrefs.getString(setting.key, null)
}
fun get(setting: AlwaysNeverUserDecidesSetting): AlwaysNeverUserDecidesValue {
val storedString = App.getApplication().encryptedPrefs.getString(setting.key, "user-decides")
?: "user-decides"
return AlwaysNeverUserDecidesValue.valueOf(storedString)
}
class MDMSettings {
val hiddenNetworkDevices: List<NetworkDevices> = emptyList()
fun get(setting: ShowHideSetting): ShowHideValue {
val storedString = App.getApplication().encryptedPrefs.getString(setting.key, "show")
?: "show"
return ShowHideValue.valueOf(storedString)
}
}

@ -0,0 +1,55 @@
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package com.tailscale.ipn.mdm
enum class BooleanSetting(val key: String, val localizedTitle: String) {
ForceEnabled("ForceEnabled", "Force Enabled Connection Toggle")
}
enum class StringSetting(val key: String, val localizedTitle: String) {
ExitNodeID("ExitNodeID", "Forced Exit Node: Stable ID"),
KeyExpirationNotice("KeyExpirationNotice", "Key Expiration Notice Period"),
LoginURL("LoginURL", "Custom control server URL"),
ManagedByCaption("ManagedByCaption", "Managed By - Caption"),
ManagedByOrganizationName("ManagedByOrganizationName", "Managed By - Organization Name"),
ManagedByURL("ManagedByURL", "Managed By - Support URL"),
Tailnet("Tailnet", "Recommended/Required Tailnet Name"),
}
// A setting representing a String value which is set to either `always`, `never` or `user-decides`.
enum class AlwaysNeverUserDecidesSetting(val key: String, val localizedTitle: String) {
AllowIncomingConnections("AllowIncomingConnections", "Allow Incoming Connections"),
DetectThirdPartyAppConflicts("DetectThirdPartyAppConflicts", "Detect potentially problematic third-party apps"),
ExitNodeAllowLANAccess("ExitNodeAllowLANAccess", "Allow LAN Access when using an exit node"),
PostureChecking("PostureChecking", "Enable Posture Checking"),
UseTailscaleDNSSettings("UseTailscaleDNSSettings", "Use Tailscale DNS Settings"),
UseTailscaleSubnets("UseTailscaleSubnets", "Use Tailscale Subnets")
}
enum class AlwaysNeverUserDecidesValue(val value: String) {
Always("always"),
Never("never"),
UserDecides("user-decides")
}
// A setting representing a String value which is set to either `show` or `hide`.
enum class ShowHideSetting(val key: String, val localizedTitle: String) {
ExitNodesPicker("ExitNodesPicker", "Exit Nodes Picker"),
ManageTailnetLock("ManageTailnetLock", "“Manage Tailnet lock” menu item"),
ResetToDefaults("ResetToDefaults", "“Reset to Defaults” menu item"),
RunExitNode("RunExitNode", "Run as Exit Node"),
TestMenu("TestMenu", "Show Debug Menu"),
UpdateMenu("UpdateMenu", "“Update Available” menu item"),
}
enum class ShowHideValue(val value: String) {
Show("show"),
Hide("hide")
}
enum class NetworkDevices(val value: String) {
currentUser("current-user"),
otherUsers("other-users"),
taggedDevices("tagged-devices"),
}

@ -7,6 +7,7 @@ package com.tailscale.ipn.ui.service
import android.content.Intent
import com.tailscale.ipn.App
import com.tailscale.ipn.IPNReceiver
import com.tailscale.ipn.mdm.MDMSettings
import com.tailscale.ipn.ui.localapi.LocalApiClient
import com.tailscale.ipn.ui.model.Ipn
import com.tailscale.ipn.ui.notifier.Notifier
@ -33,6 +34,7 @@ class IpnManager {
private var scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
var apiClient = LocalApiClient(scope)
var mdmSettings = MDMSettings()
val model = IpnModel(notifier, apiClient, scope)
val actions = IpnActions(

Loading…
Cancel
Save