Cross-validation: Ugly but necessary
This commit is contained in:
parent
f958998771
commit
92385e03cd
5 changed files with 71 additions and 33 deletions
|
|
@ -14,14 +14,19 @@ import com.google.android.material.textfield.TextInputEditText
|
|||
import com.google.android.material.textfield.TextInputLayout
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.db.CustomHeader
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.util.AfterChangedTextWatcher
|
||||
import io.heckel.ntfy.util.dangerButton
|
||||
import io.heckel.ntfy.util.validUrl
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class CustomHeaderFragment : DialogFragment() {
|
||||
private var header: CustomHeader? = null
|
||||
private lateinit var baseUrlsInUse: ArrayList<String>
|
||||
private lateinit var listener: CustomHeaderDialogListener
|
||||
private lateinit var repository: Repository
|
||||
|
||||
private lateinit var baseUrlView: TextInputEditText
|
||||
private lateinit var headerNameView: TextInputEditText
|
||||
|
|
@ -38,6 +43,7 @@ class CustomHeaderFragment : DialogFragment() {
|
|||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
listener = activity as CustomHeaderDialogListener
|
||||
repository = Repository.getInstance(context)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
|
|
@ -50,9 +56,6 @@ class CustomHeaderFragment : DialogFragment() {
|
|||
header = CustomHeader(baseUrl, headerName, headerValue)
|
||||
}
|
||||
|
||||
// Required for validation
|
||||
baseUrlsInUse = arguments?.getStringArrayList(BUNDLE_BASE_URLS_IN_USE) ?: arrayListOf()
|
||||
|
||||
// Build root view
|
||||
val view = requireActivity().layoutInflater.inflate(R.layout.fragment_custom_header_dialog, null)
|
||||
|
||||
|
|
@ -161,29 +164,44 @@ class CustomHeaderFragment : DialogFragment() {
|
|||
// Clear previous errors
|
||||
headerNameLayout.error = null
|
||||
|
||||
// Validate header name
|
||||
var isValid = true
|
||||
if (headerName.isNotEmpty()) {
|
||||
if (!validateHeaderName(headerName)) {
|
||||
headerNameLayout.error = getString(R.string.custom_headers_invalid_name)
|
||||
isValid = false
|
||||
} else if (isReservedHeader(headerName)) {
|
||||
headerNameLayout.error = getString(R.string.custom_headers_reserved_name)
|
||||
isValid = false
|
||||
// Validate header name and check if a user already exists for this server
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
var isValid = true
|
||||
if (headerName.isNotEmpty()) {
|
||||
if (!validateHeaderName(headerName)) {
|
||||
headerNameLayout.error = getString(R.string.custom_headers_invalid_name)
|
||||
isValid = false
|
||||
} else if (isReservedHeader(headerName)) {
|
||||
headerNameLayout.error = getString(R.string.custom_headers_reserved_name)
|
||||
isValid = false
|
||||
} else if (headerName.equals("Authorization", ignoreCase = true)) {
|
||||
// Check if a user exists for this server (async)
|
||||
val targetBaseUrl = if (header != null) header!!.baseUrl else baseUrl
|
||||
val userExists = if (this@CustomHeaderFragment::repository.isInitialized && validUrl(targetBaseUrl)) {
|
||||
withContext(Dispatchers.IO) {
|
||||
repository.getUser(targetBaseUrl) != null
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
if (userExists) {
|
||||
headerNameLayout.error = getString(R.string.custom_headers_user_exists)
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (header == null) {
|
||||
// New header: baseUrl, name, and value required
|
||||
positiveButton.isEnabled = validUrl(baseUrl)
|
||||
&& headerName.isNotEmpty()
|
||||
&& headerValue.isNotEmpty()
|
||||
&& isValid
|
||||
} else {
|
||||
// Editing header: name and value required
|
||||
positiveButton.isEnabled = headerName.isNotEmpty()
|
||||
&& headerValue.isNotEmpty()
|
||||
&& isValid
|
||||
}
|
||||
}
|
||||
|
||||
if (header == null) {
|
||||
// New header: baseUrl, name, and value required
|
||||
positiveButton.isEnabled = validUrl(baseUrl)
|
||||
&& headerName.isNotEmpty()
|
||||
&& headerValue.isNotEmpty()
|
||||
&& isValid
|
||||
} else {
|
||||
// Editing header: name and value required
|
||||
positiveButton.isEnabled = headerName.isNotEmpty()
|
||||
&& headerValue.isNotEmpty()
|
||||
&& isValid
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -215,12 +233,10 @@ class CustomHeaderFragment : DialogFragment() {
|
|||
private const val BUNDLE_BASE_URL = "baseUrl"
|
||||
private const val BUNDLE_HEADER_NAME = "headerName"
|
||||
private const val BUNDLE_HEADER_VALUE = "headerValue"
|
||||
private const val BUNDLE_BASE_URLS_IN_USE = "baseUrlsInUse"
|
||||
|
||||
fun newInstance(header: CustomHeader?, baseUrlsInUse: List<String>): CustomHeaderFragment {
|
||||
fun newInstance(header: CustomHeader?): CustomHeaderFragment {
|
||||
val fragment = CustomHeaderFragment()
|
||||
val args = Bundle()
|
||||
args.putStringArrayList(BUNDLE_BASE_URLS_IN_USE, ArrayList(baseUrlsInUse))
|
||||
if (header != null) {
|
||||
args.putString(BUNDLE_BASE_URL, header.baseUrl)
|
||||
args.putString(BUNDLE_HEADER_NAME, header.name)
|
||||
|
|
|
|||
|
|
@ -980,8 +980,6 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
|
|||
}
|
||||
|
||||
private fun addCustomHeaderPreferences(headersByBaseUrl: List<CustomHeaderWithMetadata>) {
|
||||
val baseUrlsInUse = headersByBaseUrl.map { it.baseUrl }
|
||||
|
||||
headersByBaseUrl.forEach { serverHeaders ->
|
||||
val baseUrl = serverHeaders.baseUrl
|
||||
val headers = serverHeaders.headers
|
||||
|
|
@ -997,7 +995,7 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
|
|||
preference.onPreferenceClickListener = OnPreferenceClickListener { _ ->
|
||||
activity?.let {
|
||||
CustomHeaderFragment
|
||||
.newInstance(header, baseUrlsInUse)
|
||||
.newInstance(header)
|
||||
.show(it.supportFragmentManager, CustomHeaderFragment.TAG)
|
||||
}
|
||||
true
|
||||
|
|
@ -1017,7 +1015,7 @@ class SettingsActivity : AppCompatActivity(), PreferenceFragmentCompat.OnPrefere
|
|||
headerAddPref.onPreferenceClickListener = OnPreferenceClickListener { _ ->
|
||||
activity?.let {
|
||||
CustomHeaderFragment
|
||||
.newInstance(header = null, baseUrlsInUse = baseUrlsInUse)
|
||||
.newInstance(header = null)
|
||||
.show(it.supportFragmentManager, CustomHeaderFragment.TAG)
|
||||
}
|
||||
true
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import io.heckel.ntfy.R
|
||||
import io.heckel.ntfy.db.Repository
|
||||
import io.heckel.ntfy.db.User
|
||||
import io.heckel.ntfy.util.AfterChangedTextWatcher
|
||||
import io.heckel.ntfy.util.dangerButton
|
||||
|
|
@ -22,6 +23,7 @@ class UserFragment : DialogFragment() {
|
|||
private var user: User? = null
|
||||
private lateinit var baseUrlsInUse: ArrayList<String>
|
||||
private lateinit var listener: UserDialogListener
|
||||
private lateinit var repository: Repository
|
||||
|
||||
private lateinit var baseUrlViewLayout: TextInputLayout
|
||||
private lateinit var baseUrlView: TextInputEditText
|
||||
|
|
@ -38,6 +40,7 @@ class UserFragment : DialogFragment() {
|
|||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
listener = activity as UserDialogListener
|
||||
repository = Repository.getInstance(context)
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
|
|
@ -155,9 +158,26 @@ class UserFragment : DialogFragment() {
|
|||
val baseUrl = baseUrlView.text?.toString() ?: ""
|
||||
val username = usernameView.text?.toString() ?: ""
|
||||
val password = passwordView.text?.toString() ?: ""
|
||||
|
||||
// Clear previous errors
|
||||
baseUrlViewLayout.error = null
|
||||
|
||||
if (user == null) {
|
||||
// Check if Authorization header already exists in custom headers
|
||||
val hasAuthorizationHeader = if (this::repository.isInitialized && validUrl(baseUrl)) {
|
||||
repository.getCustomHeadersForServer(baseUrl)
|
||||
.any { it.name.equals("Authorization", ignoreCase = true) }
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
if (hasAuthorizationHeader) {
|
||||
baseUrlViewLayout.error = getString(R.string.user_dialog_base_url_error_authorization_header_exists)
|
||||
}
|
||||
|
||||
positiveButton.isEnabled = validUrl(baseUrl)
|
||||
&& !baseUrlsInUse.contains(baseUrl)
|
||||
&& !hasAuthorizationHeader
|
||||
&& username.isNotEmpty() && password.isNotEmpty()
|
||||
} else {
|
||||
positiveButton.isEnabled = username.isNotEmpty() // Unchanged if left blank
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@
|
|||
<string name="user_dialog_description_add">Du kannst hier einen Benutzer hinzufügen. Alle Themen auf dem angegebenen Server verwenden dann diesen Benutzer.</string>
|
||||
<string name="user_dialog_description_edit">Du kannst Benutzernamen/Kennwort für den gewählten Benutzer ändern oder löschen.</string>
|
||||
<string name="user_dialog_base_url_hint">Service-URL</string>
|
||||
<string name="user_dialog_base_url_error_authorization_header_exists">Authorization-Header ist bereits in benutzerdefinierten Headern für diesen Server gesetzt</string>
|
||||
<string name="user_dialog_username_hint">Benutzername</string>
|
||||
<string name="user_dialog_password_hint_add">Kennwort</string>
|
||||
<string name="user_dialog_password_hint_edit">Kennwort (leer lassen für keine Änderung)</string>
|
||||
|
|
@ -364,6 +365,7 @@
|
|||
<string name="custom_headers_error_title">Ungültiger Header</string>
|
||||
<string name="custom_headers_invalid_name">Ungültige Zeichen im Header-Namen</string>
|
||||
<string name="custom_headers_reserved_name">Dieser Header ist reserviert und wird von ntfy gesetzt</string>
|
||||
<string name="custom_headers_user_exists">Authorization-Header kann nicht hinzugefügt werden: Ein Benutzer ist bereits für diesen Server konfiguriert</string>
|
||||
<string name="custom_headers_name_hint">Name (z.B. CF-Access-Client-Id)</string>
|
||||
<string name="custom_headers_value_hint">Wert</string>
|
||||
<string name="custom_header_dialog_description_add">Einen benutzerdefinierten HTTP-Header hinzufügen, der mit jeder Anfrage an den angegebenen Server gesendet wird.</string>
|
||||
|
|
|
|||
|
|
@ -458,6 +458,7 @@
|
|||
<string name="user_dialog_description_add">You can add a user here. All topics for the given server will use this user.</string>
|
||||
<string name="user_dialog_description_edit">You may edit username/password for the selected user, or delete it.</string>
|
||||
<string name="user_dialog_base_url_hint">Service URL</string>
|
||||
<string name="user_dialog_base_url_error_authorization_header_exists">Authorization header already set in custom headers for this server</string>
|
||||
<string name="user_dialog_username_hint">Username</string>
|
||||
<string name="user_dialog_password_hint_add">Password</string>
|
||||
<string name="user_dialog_password_hint_edit">Password (unchanged if left blank)</string>
|
||||
|
|
@ -479,6 +480,7 @@
|
|||
<string name="custom_headers_error_title">Invalid Header</string>
|
||||
<string name="custom_headers_invalid_name">Header name contains invalid characters</string>
|
||||
<string name="custom_headers_reserved_name">This header is reserved and set by ntfy</string>
|
||||
<string name="custom_headers_user_exists">Cannot add Authorization header: A user is already configured for this server</string>
|
||||
<string name="custom_headers_name_hint">Name (e.g. CF-Access-Client-Id)</string>
|
||||
<string name="custom_headers_value_hint">Value</string>
|
||||
<string name="custom_header_dialog_description_add">Add a custom HTTP header that will be sent with every request to the specified server.</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue