Basic certificate URI validation

This commit is contained in:
Aidan Follestad 2019-01-11 19:07:51 -08:00
parent 26d6d9abf8
commit cd1651672f
7 changed files with 84 additions and 31 deletions

View file

@ -28,7 +28,7 @@ import com.afollestad.nocknock.data.model.ValidationMode
import com.afollestad.nocknock.ui.DarkModeSwitchActivity
import com.afollestad.nocknock.ui.viewsite.KEY_SITE
import com.afollestad.nocknock.utilities.ext.onTextChanged
import com.afollestad.nocknock.utilities.ext.toUri
import com.afollestad.nocknock.utilities.ext.setTextAndMaintainSelection
import com.afollestad.nocknock.utilities.livedata.distinct
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
import com.afollestad.nocknock.viewcomponents.ext.onScroll
@ -141,6 +141,13 @@ class AddSiteActivity : DarkModeSwitchActivity() {
minutesData = viewModel.retryPolicyMinutes
)
// SSL certificate
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it }
viewModel.certificateUri.distinct()
.observe(this, Observer { sslCertificateInput.setTextAndMaintainSelection(it) })
viewModel.onCertificateError()
.toViewError(this, sslCertificateInput)
// Headers
headersLayout.attach(viewModel.headers)
}
@ -179,9 +186,6 @@ class AddSiteActivity : DarkModeSwitchActivity() {
}
// SSL certificate
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it.toUri() }
viewModel.certificateUri.distinct()
.observe(this, Observer { sslCertificateInput.setText(it.toString()) })
sslCertificateBrowse.setOnClickListener {
val intent = Intent(ACTION_OPEN_DOCUMENT).apply {
addCategory(CATEGORY_OPENABLE)
@ -193,12 +197,11 @@ class AddSiteActivity : DarkModeSwitchActivity() {
override fun onResume() {
super.onResume()
appToolbar.elevation =
if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
appToolbar.dimenFloat(R.dimen.default_elevation)
} else {
0f
}
appToolbar.elevation = if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
appToolbar.dimenFloat(R.dimen.default_elevation)
} else {
0f
}
}
override fun onActivityResult(

View file

@ -15,7 +15,6 @@
*/
package com.afollestad.nocknock.ui.addsite
import android.net.Uri
import androidx.annotation.CheckResult
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
@ -40,6 +39,7 @@ import com.afollestad.nocknock.data.putSite
import com.afollestad.nocknock.engine.validation.ValidationExecutor
import com.afollestad.nocknock.ui.ScopedViewModel
import com.afollestad.nocknock.utilities.ext.MINUTE
import com.afollestad.nocknock.utilities.ext.toUri
import com.afollestad.nocknock.utilities.livedata.map
import com.afollestad.nocknock.viewcomponents.ext.isNullOrLessThan
import kotlinx.coroutines.CoroutineDispatcher
@ -69,7 +69,7 @@ class AddSiteViewModel(
val retryPolicyTimes = MutableLiveData<Int>()
val retryPolicyMinutes = MutableLiveData<Int>()
val headers = MutableLiveData<List<Header>>()
val certificateUri = MutableLiveData<Uri>()
val certificateUri = MutableLiveData<String>()
@OnLifecycleEvent(ON_START)
fun setDefaults() {
@ -91,6 +91,7 @@ class AddSiteViewModel(
private val validationSearchTermError = MutableLiveData<Int?>()
private val validationScriptError = MutableLiveData<Int?>()
private val checkIntervalValueError = MutableLiveData<Int?>()
private val certificateError = MutableLiveData<Int?>()
// Expose private properties or calculated properties
@CheckResult fun onIsLoading(): LiveData<Boolean> = isLoading
@ -130,6 +131,8 @@ class AddSiteViewModel(
@CheckResult fun onCheckIntervalError(): LiveData<Int?> = checkIntervalValueError
@CheckResult fun onCertificateError(): LiveData<Int?> = certificateError
// Actions
fun commit(done: () -> Unit) {
scope.launch {
@ -228,6 +231,25 @@ class AddSiteViewModel(
validationScriptError.value = null
}
// Validate SSL certificate
val certString = certificateUri.value
if (certString != null) {
val rawCertUri = certString.toUri()
val certUri = if (rawCertUri.scheme == null) {
rawCertUri.buildUpon()
.scheme("file")
.build()
} else {
rawCertUri
}
if (certUri.scheme != "content" && certUri.scheme != "file") {
errorCount++
certificateError.value = R.string.please_enter_validCertUri
} else {
certificateError.value = null
}
}
if (errorCount > 0) {
return null
}

View file

@ -28,7 +28,7 @@ import com.afollestad.nocknock.data.model.Site
import com.afollestad.nocknock.data.model.ValidationMode
import com.afollestad.nocknock.ui.DarkModeSwitchActivity
import com.afollestad.nocknock.utilities.ext.onTextChanged
import com.afollestad.nocknock.utilities.ext.toUri
import com.afollestad.nocknock.utilities.ext.setTextAndMaintainSelection
import com.afollestad.nocknock.utilities.livedata.distinct
import com.afollestad.nocknock.utilities.providers.IntentProvider
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
@ -158,6 +158,13 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
minutesData = viewModel.retryPolicyMinutes
)
// SSL certificate
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it }
viewModel.certificateUri.distinct()
.observe(this, Observer { sslCertificateInput.setTextAndMaintainSelection(it) })
viewModel.onCertificateError()
.toViewError(this, sslCertificateInput)
// Headers
headersLayout.attach(viewModel.headers)
@ -214,7 +221,7 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
.isVisible = it
})
// Done button
// Done item text
viewModel.onDoneButtonText()
.observe(this, Observer {
toolbar.menu.findItem(R.id.commit)
@ -222,9 +229,6 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
})
// SSL certificate
sslCertificateInput.onTextChanged { viewModel.certificateUri.value = it.toUri() }
viewModel.certificateUri.distinct()
.observe(this, Observer { sslCertificateInput.setText(it.toString()) })
sslCertificateBrowse.setOnClickListener {
val intent = Intent(ACTION_OPEN_DOCUMENT).apply {
addCategory(CATEGORY_OPENABLE)
@ -236,12 +240,11 @@ class ViewSiteActivity : DarkModeSwitchActivity() {
override fun onResume() {
super.onResume()
appToolbar.elevation =
if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
appToolbar.dimenFloat(R.dimen.default_elevation)
} else {
0f
}
appToolbar.elevation = if (scrollView.scrollY > appToolbar.measuredHeight / 2) {
appToolbar.dimenFloat(R.dimen.default_elevation)
} else {
0f
}
}
override fun onActivityResult(

View file

@ -15,7 +15,6 @@
*/
package com.afollestad.nocknock.ui.viewsite
import android.net.Uri
import androidx.annotation.CheckResult
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting.PRIVATE
@ -24,9 +23,9 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.afollestad.nocknock.R
import com.afollestad.nocknock.data.AppDatabase
import com.afollestad.nocknock.data.model.RetryPolicy
import com.afollestad.nocknock.data.deleteSite
import com.afollestad.nocknock.data.model.Header
import com.afollestad.nocknock.data.model.RetryPolicy
import com.afollestad.nocknock.data.model.Site
import com.afollestad.nocknock.data.model.Status
import com.afollestad.nocknock.data.model.Status.WAITING
@ -41,6 +40,7 @@ import com.afollestad.nocknock.engine.validation.ValidationExecutor
import com.afollestad.nocknock.notifications.NockNotificationManager
import com.afollestad.nocknock.ui.ScopedViewModel
import com.afollestad.nocknock.utilities.ext.formatDate
import com.afollestad.nocknock.utilities.ext.toUri
import com.afollestad.nocknock.utilities.livedata.map
import com.afollestad.nocknock.utilities.livedata.zip
import com.afollestad.nocknock.utilities.providers.StringProvider
@ -77,7 +77,7 @@ class ViewSiteViewModel(
val retryPolicyTimes = MutableLiveData<Int>()
val retryPolicyMinutes = MutableLiveData<Int>()
val headers = MutableLiveData<List<Header>>()
val certificateUri = MutableLiveData<Uri>()
val certificateUri = MutableLiveData<String>()
internal val disabled = MutableLiveData<Boolean>()
internal val lastResult = MutableLiveData<ValidationResult?>()
@ -89,6 +89,7 @@ class ViewSiteViewModel(
private val validationSearchTermError = MutableLiveData<Int?>()
private val validationScriptError = MutableLiveData<Int?>()
private val checkIntervalValueError = MutableLiveData<Int?>()
private val certificateError = MutableLiveData<Int?>()
// Expose private properties or calculated properties
@CheckResult fun onIsLoading(): LiveData<Boolean> = isLoading
@ -131,6 +132,8 @@ class ViewSiteViewModel(
@CheckResult fun onDisableChecksVisibility(): LiveData<Boolean> =
disabled.map { !it }
@CheckResult fun onCertificateError(): LiveData<Int?> = certificateError
@CheckResult fun onDoneButtonText(): LiveData<Int> =
disabled.map {
if (it) R.string.renable_and_save_changes
@ -307,6 +310,25 @@ class ViewSiteViewModel(
validationScriptError.value = null
}
// Validate SSL certificate
val certString = certificateUri.value
if (certString != null) {
val rawCertUri = certString.toUri()
val certUri = if (rawCertUri.scheme == null) {
rawCertUri.buildUpon()
.scheme("file")
.build()
} else {
rawCertUri
}
if (certUri.scheme != "content" && certUri.scheme != "file") {
errorCount++
certificateError.value = R.string.please_enter_validCertUri
} else {
certificateError.value = null
}
}
if (errorCount > 0) {
return null
}

View file

@ -24,7 +24,6 @@ import com.afollestad.nocknock.utilities.ext.DAY
import com.afollestad.nocknock.utilities.ext.HOUR
import com.afollestad.nocknock.utilities.ext.MINUTE
import com.afollestad.nocknock.utilities.ext.WEEK
import com.afollestad.nocknock.utilities.ext.toUri
import kotlin.math.ceil
fun ViewSiteViewModel.setModel(site: Site) {
@ -56,9 +55,7 @@ fun ViewSiteViewModel.setModel(site: Site) {
setCheckInterval(settings.validationIntervalMs)
setRetryPolicy(site.retryPolicy)
headers.value = site.headers
if (settings.certificate != null) {
certificateUri.value = settings.certificate!!.toUri()
}
certificateUri.value = settings.certificate
this.disabled.value = settings.disabled
this.lastResult.value = site.lastResult

View file

@ -33,6 +33,7 @@
<string name="please_enter_search_term">Please input a search term.</string>
<string name="please_enter_javaScript">Please input a validation script.</string>
<string name="please_enter_networkTimeout">Please enter a network timeout greater than 0.</string>
<string name="please_enter_validCertUri">Certificate should be a valid file or content URI.</string>
<string name="options">Options</string>
<string name="remove_site">Remove Site</string>

View file

@ -21,7 +21,12 @@ import android.widget.EditText
import androidx.annotation.IntRange
import kotlin.math.min
fun EditText.setTextAndMaintainSelection(text: CharSequence) {
fun EditText.setTextAndMaintainSelection(text: CharSequence?) {
if (text == null) {
setText("")
return
}
val formerStart = min(selectionStart, text.length)
val formerEnd = min(selectionEnd, text.length)
setText(text)