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 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<String>) : 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()
}

View File

@ -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)

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -65,7 +65,8 @@
<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="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="check_for_updates">Rechercher des nouvelles versions</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="no_password">No Password</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="check_for_updates">Check for updates</string>
<string name="new_version_available">New version available</string>