unified preauth key and custom server login views

Signed-off-by: Christian Müller <cmueller@trufflepig-forensics.com>
pull/520/head
Christian Müller 1 year ago
parent f26a828cbd
commit 46e28d20e3
No known key found for this signature in database
GPG Key ID: CE6F998A064CFD6D

@ -55,7 +55,6 @@ import com.tailscale.ipn.ui.view.ExitNodePicker
import com.tailscale.ipn.ui.view.HealthView import com.tailscale.ipn.ui.view.HealthView
import com.tailscale.ipn.ui.view.IntroView import com.tailscale.ipn.ui.view.IntroView
import com.tailscale.ipn.ui.view.LoginQRView import com.tailscale.ipn.ui.view.LoginQRView
import com.tailscale.ipn.ui.view.LoginWithAuthKeyView
import com.tailscale.ipn.ui.view.LoginWithCustomControlURLView import com.tailscale.ipn.ui.view.LoginWithCustomControlURLView
import com.tailscale.ipn.ui.view.MDMSettingsDebugView import com.tailscale.ipn.ui.view.MDMSettingsDebugView
import com.tailscale.ipn.ui.view.MainView import com.tailscale.ipn.ui.view.MainView
@ -249,9 +248,6 @@ class MainActivity : ComponentActivity() {
composable("intro", exitTransition = { fadeOut(animationSpec = tween(150)) }) { composable("intro", exitTransition = { fadeOut(animationSpec = tween(150)) }) {
IntroView(backTo("main")) IntroView(backTo("main"))
} }
composable("loginWithAuthKey") {
LoginWithAuthKeyView(onNavigateHome = backTo("main"), backTo("userSwitcher"))
}
composable("loginWithCustomControl") { composable("loginWithCustomControl") {
LoginWithCustomControlURLView( LoginWithCustomControlURLView(
onNavigateHome = backTo("main"), backTo("userSwitcher")) onNavigateHome = backTo("main"), backTo("userSwitcher"))

@ -31,7 +31,6 @@ import androidx.compose.ui.unit.dp
import com.tailscale.ipn.R import com.tailscale.ipn.R
import com.tailscale.ipn.ui.theme.listItem import com.tailscale.ipn.ui.theme.listItem
import com.tailscale.ipn.ui.util.set import com.tailscale.ipn.ui.util.set
import com.tailscale.ipn.ui.viewModel.LoginWithAuthKeyViewModel
import com.tailscale.ipn.ui.viewModel.LoginWithCustomControlURLViewModel import com.tailscale.ipn.ui.viewModel.LoginWithCustomControlURLViewModel
data class LoginViewStrings( data class LoginViewStrings(
@ -69,39 +68,8 @@ fun LoginWithCustomControlURLView(
LoginView( LoginView(
innerPadding = innerPadding, innerPadding = innerPadding,
strings = strings, strings = strings,
onSubmitAction = { viewModel.setControlURL(it, onNavigateHome) }) onSubmitAction = {it: String, it2: String -> viewModel.setControlURL(it, it2, onNavigateHome) }
}
}
@Composable
fun LoginWithAuthKeyView(
onNavigateHome: BackNavigation,
backToSettings: BackNavigation,
viewModel: LoginWithAuthKeyViewModel = LoginWithAuthKeyViewModel()
) {
Scaffold(
topBar = {
Header(
R.string.add_account,
onBack = backToSettings,
) )
}) { innerPadding ->
val error by viewModel.errorDialog.collectAsState()
val strings =
LoginViewStrings(
title = stringResource(id = R.string.auth_key_title),
explanation = stringResource(id = R.string.auth_key_explanation),
inputTitle = stringResource(id = R.string.auth_key_input_title),
placeholder = stringResource(id = R.string.auth_key_placeholder),
)
// Show the error overlay if need be
error?.let { ErrorDialog(type = it, action = { viewModel.errorDialog.set(null) }) }
LoginView(
innerPadding = innerPadding,
strings = strings,
onSubmitAction = { viewModel.setAuthKey(it, onNavigateHome) })
} }
} }
@ -109,10 +77,11 @@ fun LoginWithAuthKeyView(
fun LoginView( fun LoginView(
innerPadding: PaddingValues = PaddingValues(16.dp), innerPadding: PaddingValues = PaddingValues(16.dp),
strings: LoginViewStrings, strings: LoginViewStrings,
onSubmitAction: (String) -> Unit, onSubmitAction: (it: String, it2: String) -> Unit,
) { ) {
var textVal by remember { mutableStateOf("") } var textVal by remember { mutableStateOf("") }
var textVal2 by remember { mutableStateOf("") }
Column( Column(
modifier = modifier =
@ -144,12 +113,33 @@ fun LoginView(
) )
}) })
ListItem(
colors = MaterialTheme.colorScheme.listItem,
headlineContent = { Text(text = "Preauth Key") }, // TODO: clean this up
supportingContent = {
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.Transparent,
unfocusedContainerColor = Color.Transparent,
),
textStyle = MaterialTheme.typography.bodyMedium,
value = textVal2,
onValueChange = { textVal2 = it },
placeholder = {
Text("Key", style = MaterialTheme.typography.bodySmall) // TODO: clean this up
},
keyboardOptions = KeyboardOptions(capitalization = KeyboardCapitalization.None)
)
}
)
ListItem( ListItem(
colors = MaterialTheme.colorScheme.listItem, colors = MaterialTheme.colorScheme.listItem,
headlineContent = { headlineContent = {
Box(modifier = Modifier.fillMaxWidth()) { Box(modifier = Modifier.fillMaxWidth()) {
Button( Button(
onClick = { onSubmitAction(textVal) }, onClick = { onSubmitAction(textVal, textVal2) },
content = { Text(stringResource(id = R.string.add_account_short)) }) content = { Text(stringResource(id = R.string.add_account_short)) })
} }
}) })

@ -162,12 +162,6 @@ fun FusMenu(
viewModel.showHeaderMenu.set(false) viewModel.showHeaderMenu.set(false)
}, },
text = stringResource(id = R.string.custom_control_menu)) text = stringResource(id = R.string.custom_control_menu))
MenuItem(
onClick = {
onAuthKeyClick()
viewModel.showHeaderMenu.set(false)
},
text = stringResource(id = R.string.auth_key_menu))
} }
} }

@ -14,28 +14,17 @@ open class CustomLoginViewModel : IpnViewModel() {
val errorDialog: StateFlow<ErrorDialogType?> = MutableStateFlow(null) val errorDialog: StateFlow<ErrorDialogType?> = MutableStateFlow(null)
} }
class LoginWithAuthKeyViewModel : CustomLoginViewModel() {
// Sets the auth key and invokes the login flow
fun setAuthKey(authKey: String, onSuccess: () -> Unit) {
// The most basic of checks for auth key syntax
if (authKey.isEmpty()) {
errorDialog.set(ErrorDialogType.INVALID_AUTH_KEY)
return
}
loginWithAuthKey(authKey) {
it.onFailure { errorDialog.set(ErrorDialogType.ADD_PROFILE_FAILED) }
it.onSuccess { onSuccess() }
}
}
}
class LoginWithCustomControlURLViewModel : CustomLoginViewModel() { class LoginWithCustomControlURLViewModel : CustomLoginViewModel() {
// Sets the custom control URL and invokes the login flow // Sets the custom control URL and invokes the login flow
fun setControlURL(urlStr: String, onSuccess: () -> Unit) { fun setControlURL(urlStr: String, authKey: String, onSuccess: () -> Unit) {
// Some basic checks that the entered URL is "reasonable". The underlying // Some basic checks that the entered URL is "reasonable". The underlying
// localAPIClient will use the default server if we give it a broken URL, // localAPIClient will use the default server if we give it a broken URL,
// but we can make sure we can construct a URL from the input string and // but we can make sure we can construct a URL from the input string and
// ensure it has an http/https scheme // ensure it has an http/https scheme
if (authKey.isEmpty()) {
errorDialog.set(ErrorDialogType.INVALID_AUTH_KEY)
return
}
when (urlStr.startsWith("http", ignoreCase = true) && when (urlStr.startsWith("http", ignoreCase = true) &&
urlStr.contains("://") && urlStr.contains("://") &&
urlStr.length > 7) { urlStr.length > 7) {
@ -44,7 +33,7 @@ class LoginWithCustomControlURLViewModel : CustomLoginViewModel() {
return return
} }
true -> { true -> {
loginWithCustomControlURL(urlStr) { loginWithCustomControlURLAuthKey(urlStr, authKey) {
it.onFailure { errorDialog.set(ErrorDialogType.ADD_PROFILE_FAILED) } it.onFailure { errorDialog.set(ErrorDialogType.ADD_PROFILE_FAILED) }
it.onSuccess { onSuccess() } it.onSuccess { onSuccess() }
} }

@ -192,19 +192,15 @@ open class IpnViewModel : ViewModel() {
} ?: run { startAction() } } ?: run { startAction() }
} }
fun loginWithAuthKey(authKey: String, completionHandler: (Result<Unit>) -> Unit = {}) { fun loginWithCustomControlURLAuthKey(
val prefs = Ipn.MaskedPrefs()
prefs.WantRunning = true
login(prefs, authKey = authKey, completionHandler)
}
fun loginWithCustomControlURL(
controlURL: String, controlURL: String,
authKey: String,
completionHandler: (Result<Unit>) -> Unit = {} completionHandler: (Result<Unit>) -> Unit = {}
) { ) {
val prefs = Ipn.MaskedPrefs() val prefs = Ipn.MaskedPrefs()
prefs.ControlURL = controlURL prefs.ControlURL = controlURL
login(prefs, completionHandler = completionHandler) prefs.WantRunning = true
login(prefs, authKey = authKey, completionHandler = completionHandler)
} }
fun logout(completionHandler: (Result<String>) -> Unit = {}) { fun logout(completionHandler: (Result<String>) -> Unit = {}) {

Loading…
Cancel
Save