Added dialog when client certificate is required

The biometric unlock feature is on the way, if you couldn't tell
This commit is contained in:
Corewala 2022-05-03 18:29:44 -04:00
parent b2ef138402
commit d60c1a3b20
9 changed files with 38 additions and 38 deletions

View File

@ -8,7 +8,6 @@ sealed class GemState {
data class AppQuery(val uri: URI): GemState() data class AppQuery(val uri: URI): GemState()
data class Requesting(val uri: URI): GemState() data class Requesting(val uri: URI): GemState()
data class NotGeminiRequest(val uri: URI) : GemState() data class NotGeminiRequest(val uri: URI) : GemState()
data class ResponseGemtext(val uri: URI, val header: GeminiResponse.Header, val lines: List<String>) : GemState() data class ResponseGemtext(val uri: URI, val header: GeminiResponse.Header, val lines: List<String>) : GemState()
data class ResponseInput(val uri: URI, val header: GeminiResponse.Header) : GemState() data class ResponseInput(val uri: URI, val header: GeminiResponse.Header) : GemState()
data class ResponseText(val uri: URI, val header: GeminiResponse.Header, val content: String) : GemState() data class ResponseText(val uri: URI, val header: GeminiResponse.Header, val content: String) : GemState()
@ -17,8 +16,7 @@ sealed class GemState {
data class ResponseUnknownMime(val uri: URI, val header: GeminiResponse.Header) : GemState() data class ResponseUnknownMime(val uri: URI, val header: GeminiResponse.Header) : GemState()
data class ResponseError(val header: GeminiResponse.Header): GemState() data class ResponseError(val header: GeminiResponse.Header): GemState()
data class ResponseUnknownHost(val uri: URI): GemState() data class ResponseUnknownHost(val uri: URI): GemState()
data class ClientCertRequired(val uri: URI, val header: GeminiResponse.Header): GemState()
data class ClientCertError(val header: GeminiResponse.Header): GemState() data class ClientCertError(val header: GeminiResponse.Header): GemState()
object Blank: GemState() object Blank: GemState()
} }

View File

@ -6,8 +6,7 @@ import corewala.buran.io.database.history.BuranHistory
import java.net.URI import java.net.URI
interface Datasource { interface Datasource {
fun request(address: String, onUpdate: (state: GemState) -> Unit) fun request(address: String, forceDownload: Boolean, clientCertAllowed: Boolean, onUpdate: (state: GemState) -> Unit)
fun request(address: String, forceDownload: Boolean, onUpdate: (state: GemState) -> Unit)
fun canGoBack(): Boolean fun canGoBack(): Boolean
fun goBack(onUpdate: (state: GemState) -> Unit) fun goBack(onUpdate: (state: GemState) -> Unit)

View File

@ -32,12 +32,9 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
private var socketFactory: SSLSocketFactory? = null private var socketFactory: SSLSocketFactory? = null
override fun request(address: String, forceDownload: Boolean, onUpdate: (state: GemState) -> Unit) { override fun request(address: String, forceDownload: Boolean, clientCertAllowed: Boolean, onUpdate: (state: GemState) -> Unit) {
this.forceDownload = forceDownload this.forceDownload = forceDownload
request(address, onUpdate)
}
override fun request(address: String, onUpdate: (state: GemState) -> Unit) {
this.onUpdate = onUpdate this.onUpdate = onUpdate
val uri = URI.create(address) val uri = URI.create(address)
@ -45,29 +42,29 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
onUpdate(GemState.Requesting(uri)) onUpdate(GemState.Requesting(uri))
GlobalScope.launch { GlobalScope.launch {
geminiRequest(uri, onUpdate) geminiRequest(uri, onUpdate, clientCertAllowed)
} }
} }
private fun initSSLFactory(protocol: String){ private fun initSSLFactory(protocol: String, clientCertAllowed: Boolean){
val sslContext = when (protocol) { val sslContext = when (protocol) {
"TLS_ALL" -> SSLContext.getInstance("TLS") "TLS_ALL" -> SSLContext.getInstance("TLS")
else -> SSLContext.getInstance(protocol) else -> SSLContext.getInstance(protocol)
} }
sslContext.init(buranKeyManager.getFactory()?.keyManagers, DummyTrustManager.get(), null) sslContext.init(buranKeyManager.getFactory(clientCertAllowed)?.keyManagers, DummyTrustManager.get(), null)
socketFactory = sslContext.socketFactory socketFactory = sslContext.socketFactory
} }
private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit){ private fun geminiRequest(uri: URI, onUpdate: (state: GemState) -> Unit, clientCertAllowed: Boolean){
val protocol = "TLS" val protocol = "TLS"
val useClientCert = prefs.getBoolean(Buran.PREF_KEY_CLIENT_CERT_ACTIVE, false) val useClientCert = prefs.getBoolean(Buran.PREF_KEY_CLIENT_CERT_ACTIVE, false)
//Update factory if operating mode has changed //Update factory if operating mode has changed
when { when {
socketFactory == null -> initSSLFactory(protocol!!) socketFactory == null -> initSSLFactory(protocol!!, clientCertAllowed)
useClientCert && !buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!) useClientCert && !buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!, clientCertAllowed)
!useClientCert && buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!) !useClientCert && buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!, clientCertAllowed)
} }
val socket: SSLSocket? val socket: SSLSocket?
@ -124,7 +121,8 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
when { when {
header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header)) header.code == GeminiResponse.INPUT -> onUpdate(GemState.ResponseInput(uri, header))
header.code == GeminiResponse.REDIRECT -> request(URI.create(header.meta).toString(), onUpdate) header.code == GeminiResponse.REDIRECT -> request(URI.create(header.meta).toString(), false, false, onUpdate)
header.code == GeminiResponse.CLIENT_CERTIFICATE_REQUIRED -> onUpdate(GemState.ClientCertRequired(uri, header))
header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header)) header.code != GeminiResponse.SUCCESS -> onUpdate(GemState.ResponseError(header))
header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, uri, header, onUpdate) header.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, uri, header, onUpdate)
header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate) header.meta.startsWith("text/") -> getString(socket, uri, header, onUpdate)
@ -225,7 +223,7 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory):
override fun goBack(onUpdate: (state: GemState) -> Unit) { override fun goBack(onUpdate: (state: GemState) -> Unit) {
runtimeHistory.removeLast() runtimeHistory.removeLast()
request(runtimeHistory.last().toString(), onUpdate) request(runtimeHistory.last().toString(), false, false, onUpdate)
} }
//This just forces the factory to rebuild before the next request //This just forces the factory to rebuild before the next request

View File

@ -19,10 +19,10 @@ class BuranKeyManager(val context: Context, val onKeyError: (error: String) -> U
var lastCallUsedKey = false var lastCallUsedKey = false
//If the user has a key loaded load it here - or else return null //If the user has a key loaded load it here - or else return null
fun getFactory(): KeyManagerFactory? { fun getFactory(clientCertAllowed: Boolean): KeyManagerFactory? {
val isClientCertActive = prefs.getBoolean(Buran.PREF_KEY_CLIENT_CERT_ACTIVE, false) val isClientCertActive = prefs.getBoolean(Buran.PREF_KEY_CLIENT_CERT_ACTIVE, false)
return when { return when {
isClientCertActive -> { isClientCertActive and clientCertAllowed -> {
lastCallUsedKey = true lastCallUsedKey = true
val keyStore: KeyStore = KeyStore.getInstance("pkcs12") val keyStore: KeyStore = KeyStore.getInstance("pkcs12")

View File

@ -71,7 +71,7 @@ class GemActivity : AppCompatActivity() {
private val omniTerm = OmniTerm(object : OmniTerm.Listener { private val omniTerm = OmniTerm(object : OmniTerm.Listener {
override fun request(address: String) { override fun request(address: String) {
model.request(address) model.request(address, false)
} }
override fun openBrowser(address: String) = openWebLink(address) override fun openBrowser(address: String) = openWebLink(address)
@ -393,7 +393,7 @@ class GemActivity : AppCompatActivity() {
request("${state.uri}?${Uri.encode(editText.text.toString())}") request("${state.uri}?${Uri.encode(editText.text.toString())}")
editText.hideKeyboard() editText.hideKeyboard()
} }
setNegativeButton(R.string.cancel){ dialog, which -> setNegativeButton(getString(R.string.cancel)){ dialog, which ->
editText.hideKeyboard() editText.hideKeyboard()
} }
setView(dialogLayout) setView(dialogLayout)
@ -401,6 +401,18 @@ class GemActivity : AppCompatActivity() {
} }
} }
is GemState.ClientCertRequired -> runOnUiThread {
loadingView(false)
AlertDialog.Builder(this, R.style.AppDialogTheme)
.setTitle(getString(R.string.client_certificate_required))
.setMessage(state.header.meta)
.setPositiveButton(getString(R.string.use_client_certificate)) { _, _ ->
model.request(state.uri.toString(), true)
}
.setNegativeButton(getString(R.string.cancel)) { _, _ -> }
.show()
}
is GemState.Requesting -> loadingView(true) is GemState.Requesting -> loadingView(true)
is GemState.NotGeminiRequest -> externalProtocol(state) is GemState.NotGeminiRequest -> externalProtocol(state)
is GemState.ResponseError -> { is GemState.ResponseError -> {
@ -672,7 +684,7 @@ class GemActivity : AppCompatActivity() {
if(getInternetStatus()){ if(getInternetStatus()){
if(initialised){ if(initialised){
loadingView(true) loadingView(true)
return model.request(address) return model.request(address, false)
}else{ }else{
val intent = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName) val intent = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName)
intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)

View File

@ -19,24 +19,24 @@ class GemViewModel: ViewModel() {
this.db = db this.db = db
this.onState = onState this.onState = onState
request(home) request(home, false)
} }
fun request(address: String) { fun request(address: String, clientCertAllowed: Boolean) {
gemini.request(address){ state -> gemini.request(address, false, clientCertAllowed){ state ->
onState(state) onState(state)
} }
} }
fun requestBinaryDownload(uri: URI) { fun requestBinaryDownload(uri: URI) {
gemini.request(uri.toString(), true){ state -> gemini.request(uri.toString(), true, false){ state ->
onState(state) onState(state)
} }
} }
//todo - same action as above... refactor //todo - same action as above... refactor
fun requestInlineImage(uri: URI, onImageReady: (cacheUri: Uri?) -> Unit){ fun requestInlineImage(uri: URI, onImageReady: (cacheUri: Uri?) -> Unit){
gemini.request(uri.toString()){ state -> gemini.request(uri.toString(), false, false){ state ->
when (state) { when (state) {
is GemState.ResponseImage -> onImageReady(state.cacheUri) is GemState.ResponseImage -> onImageReady(state.cacheUri)
else -> onState(state) else -> onState(state)

View File

@ -294,15 +294,6 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang
} }
certificateCategory.addPreference(clientCertPassword) certificateCategory.addPreference(clientCertPassword)
useClientCertPreference = SwitchPreferenceCompat(context)
useClientCertPreference.key = Buran.PREF_KEY_CLIENT_CERT_ACTIVE
useClientCertPreference.title = getString(R.string.use_client_certificate)
certificateCategory.addPreference(useClientCertPreference)
if (!hasCert) {
useClientCertPreference.isVisible = false
}
} }
private fun getDots(value: String): String { private fun getDots(value: String): String {

View File

@ -65,7 +65,8 @@
<string name="tap_to_select_client_certificate">Cliquez pour sélectionner un certificat client</string> <string name="tap_to_select_client_certificate">Cliquez pour sélectionner un certificat client</string>
<string name="client_certificate_password">Mot de passe du Certificat Client</string> <string name="client_certificate_password">Mot de passe du Certificat Client</string>
<string name="no_password">Pas de mot de passe</string> <string name="no_password">Pas de mot de passe</string>
<string name="use_client_certificate">Utiliser un Certificat Client</string> <string name="use_client_certificate">Utiliser Certificat Client</string>
<string name="client_certificate_required">Certificat Client Requis</string>
<string name="set_home_capsule">Choisir comme capsule d\'accueil</string> <string name="set_home_capsule">Choisir comme capsule d\'accueil</string>
<string name="check_for_updates">Rechercher des nouvelles versions</string> <string name="check_for_updates">Rechercher des nouvelles versions</string>
<string name="new_version_available">Nouvelle version disponible</string> <string name="new_version_available">Nouvelle version disponible</string>

View File

@ -66,6 +66,7 @@
<string name="client_certificate_password">Client Certificate Password</string> <string name="client_certificate_password">Client Certificate Password</string>
<string name="no_password">No Password</string> <string name="no_password">No Password</string>
<string name="use_client_certificate">Use Client Certificate</string> <string name="use_client_certificate">Use Client Certificate</string>
<string name="client_certificate_required">Client Certificate Required</string>
<string name="set_home_capsule">Set home capsule</string> <string name="set_home_capsule">Set home capsule</string>
<string name="check_for_updates">Check for updates</string> <string name="check_for_updates">Check for updates</string>
<string name="new_version_available">New version available</string> <string name="new_version_available">New version available</string>