From d60c1a3b20e08010a0edaead68b9840f4e6f7a76 Mon Sep 17 00:00:00 2001 From: Corewala Date: Tue, 3 May 2022 18:29:44 -0400 Subject: [PATCH] Added dialog when client certificate is required The biometric unlock feature is on the way, if you couldn't tell --- .../main/java/corewala/buran/io/GemState.kt | 4 +--- .../corewala/buran/io/gemini/Datasource.kt | 3 +-- .../buran/io/gemini/GeminiDatasource.kt | 24 +++++++++---------- .../buran/io/keymanager/BuranKeyManager.kt | 4 ++-- .../java/corewala/buran/ui/GemActivity.kt | 18 +++++++++++--- .../java/corewala/buran/ui/GemViewModel.kt | 10 ++++---- .../buran/ui/settings/SettingsFragment.kt | 9 ------- app/src/main/res/values-fr/strings.xml | 3 ++- app/src/main/res/values/strings.xml | 1 + 9 files changed, 38 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/corewala/buran/io/GemState.kt b/app/src/main/java/corewala/buran/io/GemState.kt index 3c97f9f..f69b6d5 100644 --- a/app/src/main/java/corewala/buran/io/GemState.kt +++ b/app/src/main/java/corewala/buran/io/GemState.kt @@ -8,7 +8,6 @@ sealed class GemState { data class AppQuery(val uri: URI): GemState() data class Requesting(val uri: URI): GemState() data class NotGeminiRequest(val uri: URI) : GemState() - data class ResponseGemtext(val uri: URI, val header: GeminiResponse.Header, val lines: List) : 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() @@ -17,8 +16,7 @@ sealed class GemState { data class ResponseUnknownMime(val uri: URI, val header: GeminiResponse.Header) : GemState() data class ResponseError(val header: GeminiResponse.Header): 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() - object Blank: GemState() } \ No newline at end of file diff --git a/app/src/main/java/corewala/buran/io/gemini/Datasource.kt b/app/src/main/java/corewala/buran/io/gemini/Datasource.kt index f07a26d..4c2c85f 100644 --- a/app/src/main/java/corewala/buran/io/gemini/Datasource.kt +++ b/app/src/main/java/corewala/buran/io/gemini/Datasource.kt @@ -6,8 +6,7 @@ import corewala.buran.io.database.history.BuranHistory import java.net.URI interface Datasource { - fun request(address: String, onUpdate: (state: GemState) -> Unit) - fun request(address: String, forceDownload: Boolean, onUpdate: (state: GemState) -> Unit) + fun request(address: String, forceDownload: Boolean, clientCertAllowed: Boolean, onUpdate: (state: GemState) -> Unit) fun canGoBack(): Boolean fun goBack(onUpdate: (state: GemState) -> Unit) diff --git a/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt b/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt index a342fcc..c4584c8 100644 --- a/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt +++ b/app/src/main/java/corewala/buran/io/gemini/GeminiDatasource.kt @@ -32,12 +32,9 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory): 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 - request(address, onUpdate) - } - override fun request(address: String, onUpdate: (state: GemState) -> Unit) { this.onUpdate = onUpdate val uri = URI.create(address) @@ -45,29 +42,29 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory): onUpdate(GemState.Requesting(uri)) 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) { "TLS_ALL" -> SSLContext.getInstance("TLS") else -> SSLContext.getInstance(protocol) } - sslContext.init(buranKeyManager.getFactory()?.keyManagers, DummyTrustManager.get(), null) + sslContext.init(buranKeyManager.getFactory(clientCertAllowed)?.keyManagers, DummyTrustManager.get(), null) 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 useClientCert = prefs.getBoolean(Buran.PREF_KEY_CLIENT_CERT_ACTIVE, false) //Update factory if operating mode has changed when { - socketFactory == null -> initSSLFactory(protocol!!) - useClientCert && !buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!) - !useClientCert && buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!) + socketFactory == null -> initSSLFactory(protocol!!, clientCertAllowed) + useClientCert && !buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!, clientCertAllowed) + !useClientCert && buranKeyManager.lastCallUsedKey -> initSSLFactory(protocol!!, clientCertAllowed) } val socket: SSLSocket? @@ -124,7 +121,8 @@ class GeminiDatasource(private val context: Context, val history: BuranHistory): when { 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.meta.startsWith("text/gemini") -> getGemtext(bufferedReader, 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) { 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 diff --git a/app/src/main/java/corewala/buran/io/keymanager/BuranKeyManager.kt b/app/src/main/java/corewala/buran/io/keymanager/BuranKeyManager.kt index 653d14f..28eb076 100644 --- a/app/src/main/java/corewala/buran/io/keymanager/BuranKeyManager.kt +++ b/app/src/main/java/corewala/buran/io/keymanager/BuranKeyManager.kt @@ -19,10 +19,10 @@ class BuranKeyManager(val context: Context, val onKeyError: (error: String) -> U var lastCallUsedKey = false //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) return when { - isClientCertActive -> { + isClientCertActive and clientCertAllowed -> { lastCallUsedKey = true val keyStore: KeyStore = KeyStore.getInstance("pkcs12") diff --git a/app/src/main/java/corewala/buran/ui/GemActivity.kt b/app/src/main/java/corewala/buran/ui/GemActivity.kt index 4cc4342..0bef0d6 100644 --- a/app/src/main/java/corewala/buran/ui/GemActivity.kt +++ b/app/src/main/java/corewala/buran/ui/GemActivity.kt @@ -71,7 +71,7 @@ class GemActivity : AppCompatActivity() { private val omniTerm = OmniTerm(object : OmniTerm.Listener { override fun request(address: String) { - model.request(address) + model.request(address, false) } override fun openBrowser(address: String) = openWebLink(address) @@ -393,7 +393,7 @@ class GemActivity : AppCompatActivity() { request("${state.uri}?${Uri.encode(editText.text.toString())}") editText.hideKeyboard() } - setNegativeButton(R.string.cancel){ dialog, which -> + setNegativeButton(getString(R.string.cancel)){ dialog, which -> editText.hideKeyboard() } 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.NotGeminiRequest -> externalProtocol(state) is GemState.ResponseError -> { @@ -672,7 +684,7 @@ class GemActivity : AppCompatActivity() { if(getInternetStatus()){ if(initialised){ loadingView(true) - return model.request(address) + return model.request(address, false) }else{ val intent = baseContext.packageManager.getLaunchIntentForPackage(baseContext.packageName) intent!!.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) diff --git a/app/src/main/java/corewala/buran/ui/GemViewModel.kt b/app/src/main/java/corewala/buran/ui/GemViewModel.kt index 2ff95cf..0bc1c15 100644 --- a/app/src/main/java/corewala/buran/ui/GemViewModel.kt +++ b/app/src/main/java/corewala/buran/ui/GemViewModel.kt @@ -19,24 +19,24 @@ class GemViewModel: ViewModel() { this.db = db this.onState = onState - request(home) + request(home, false) } - fun request(address: String) { - gemini.request(address){ state -> + fun request(address: String, clientCertAllowed: Boolean) { + gemini.request(address, false, clientCertAllowed){ state -> onState(state) } } fun requestBinaryDownload(uri: URI) { - gemini.request(uri.toString(), true){ state -> + gemini.request(uri.toString(), true, false){ state -> onState(state) } } //todo - same action as above... refactor fun requestInlineImage(uri: URI, onImageReady: (cacheUri: Uri?) -> Unit){ - gemini.request(uri.toString()){ state -> + gemini.request(uri.toString(), false, false){ state -> when (state) { is GemState.ResponseImage -> onImageReady(state.cacheUri) else -> onState(state) diff --git a/app/src/main/java/corewala/buran/ui/settings/SettingsFragment.kt b/app/src/main/java/corewala/buran/ui/settings/SettingsFragment.kt index 5faca2e..1a22673 100644 --- a/app/src/main/java/corewala/buran/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/corewala/buran/ui/settings/SettingsFragment.kt @@ -294,15 +294,6 @@ class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChang } 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 { diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index d32fe14..1a32e1d 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -65,7 +65,8 @@ Cliquez pour sélectionner un certificat client Mot de passe du Certificat Client Pas de mot de passe - Utiliser un Certificat Client + Utiliser Certificat Client + Certificat Client Requis Choisir comme capsule d\'accueil Rechercher des nouvelles versions Nouvelle version disponible diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 95ff2dc..f4ab008 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -66,6 +66,7 @@ Client Certificate Password No Password Use Client Certificate + Client Certificate Required Set home capsule Check for updates New version available