mirror of https://git.sr.ht/~oppen/ariane
tls prefs wip
This commit is contained in:
parent
29f1ccc00e
commit
63081b2cf9
|
@ -46,7 +46,7 @@ dependencies {
|
|||
implementation 'androidx.activity:activity-ktx:1.1.0'
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0"
|
||||
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
|
||||
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
|
||||
|
||||
|
@ -60,8 +60,11 @@ dependencies {
|
|||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-ktx:$room_version"
|
||||
|
||||
testImplementation 'junit:junit:4.13'
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||
androidTestImplementation 'androidx.test:runner:1.3.0'
|
||||
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||
androidTestImplementation 'com.google.truth:truth:1.0'
|
||||
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package oppen.ariane.io.gemini
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.security.SecureRandom
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class DeviceTLSTests {
|
||||
|
||||
lateinit var socket: SSLSocket
|
||||
|
||||
@Before
|
||||
fun setupSocket(){
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
sslContext.init(null, null, SecureRandom())
|
||||
val factory: SSLSocketFactory = sslContext.socketFactory
|
||||
socket = factory.createSocket() as SSLSocket
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTLSv1(){
|
||||
socket.supportedProtocols.contains("TLSv1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTLSv1_1(){
|
||||
socket.supportedProtocols.contains("TLSv1.1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTLSv1_2(){
|
||||
socket.supportedProtocols.contains("TLSv1.2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun supportsTLSv1_3(){
|
||||
socket.supportedProtocols.contains("TLSv1.3")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package oppen.ariane.io.gemini
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import oppen.ariane.Ariane
|
||||
import oppen.toURI
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import oppen.ariane.io.GemState
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class GeminiDatasourceTests {
|
||||
|
||||
private lateinit var gemini: Datasource
|
||||
private val capsules = listOf(
|
||||
"gemini://gemini.circumlunar.space",
|
||||
"gemini://rawtext.club",
|
||||
"gemini://drewdevault.com",
|
||||
"gemini://talon.computer",
|
||||
"gemini://tilde.team",
|
||||
"gemini://tilde.pink",
|
||||
"gemini://gemini.conman.org",
|
||||
"gemini://idiomdrottning.org"
|
||||
)
|
||||
|
||||
private val capsuleIndex = 3
|
||||
|
||||
private fun setTLSProtocol(protocol: String){
|
||||
gemini = Datasource.factory(InstrumentationRegistry.getInstrumentation().targetContext, protocol)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun arianeHomePageTest(){
|
||||
setTLSProtocol("TLSv1")
|
||||
var hasRequested = false
|
||||
var hasResponded = false
|
||||
|
||||
gemini.request(Ariane.DEFAULT_HOME_CAPSULE.toURI()){ state ->
|
||||
|
||||
when(state){
|
||||
is GemState.Requesting -> {
|
||||
assertThat(state.uri.toString()).isEqualTo(Ariane.DEFAULT_HOME_CAPSULE)
|
||||
hasRequested = true
|
||||
}
|
||||
is GemState.ResponseGemtext -> {
|
||||
assertThat(state.uri.toString()).isEqualTo(Ariane.DEFAULT_HOME_CAPSULE)
|
||||
hasResponded = true
|
||||
}
|
||||
else -> {
|
||||
//This will cause a failed test if request fails
|
||||
assertThat(hasRequested).isTrue()
|
||||
assertThat(hasResponded).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aCapsuleTest(){
|
||||
setTLSProtocol("TLSv1.3")
|
||||
var hasRequested = false
|
||||
var hasResponded = false
|
||||
|
||||
|
||||
|
||||
gemini.request(capsules[capsuleIndex].toURI()){ state ->
|
||||
|
||||
when(state){
|
||||
is GemState.Requesting -> {
|
||||
assertThat(state.uri.toString()).isEqualTo(capsules[capsuleIndex])
|
||||
hasRequested = true
|
||||
}
|
||||
is GemState.ResponseGemtext -> {
|
||||
assertThat(state.uri.toString()).isEqualTo(capsules[capsuleIndex])
|
||||
hasResponded = true
|
||||
}
|
||||
else -> {
|
||||
//This will cause a failed test if request fails
|
||||
assertThat(hasRequested).isTrue()
|
||||
assertThat(hasResponded).isTrue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@
|
|||
<data android:scheme="gemini" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="oppen.ariane.ui.settings.SettingsActivity" android:label="@string/settings" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -5,6 +5,7 @@ import android.os.CountDownTimer
|
|||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.core.content.ContextCompat.getSystemService
|
||||
import java.net.URI
|
||||
|
||||
|
||||
fun View.visible(visible: Boolean) = when {
|
||||
|
@ -22,6 +23,10 @@ fun View.hideKeyboard(){
|
|||
imm?.hideSoftInputFromWindow(windowToken, 0)
|
||||
}
|
||||
|
||||
fun String.toURI(): URI {
|
||||
return URI.create(this)
|
||||
}
|
||||
|
||||
fun delay(ms: Long, action: () -> Unit){
|
||||
object : CountDownTimer(ms, ms/2) {
|
||||
override fun onTick(millisUntilFinished: Long) {}
|
||||
|
|
|
@ -8,8 +8,8 @@ interface Datasource {
|
|||
fun request(uri: URI, onUpdate: (state: GemState) -> Unit)
|
||||
|
||||
companion object{
|
||||
fun factory(context: Context): Datasource {
|
||||
return GeminiDatasource(context)
|
||||
fun factory(context: Context, protocol: String): Datasource {
|
||||
return GeminiDatasource(context, protocol)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,15 @@ import javax.net.ssl.*
|
|||
|
||||
const val GEMINI_SCHEME = "gemini"
|
||||
|
||||
class GeminiDatasource(val context: Context): Datasource {
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param protocol see: https://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SSLContext
|
||||
*
|
||||
*/
|
||||
class GeminiDatasource(
|
||||
private val context: Context,
|
||||
private val protocol: String): Datasource {
|
||||
|
||||
private var last: URI? = null
|
||||
override fun request(uri: URI, onUpdate: (state: GemState) -> Unit) {
|
||||
|
@ -116,28 +124,15 @@ class GeminiDatasource(val context: Context): Datasource {
|
|||
last = uri
|
||||
val port = if(uri.port == -1) 1965 else uri.port
|
||||
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
val sslContext = SSLContext.getInstance(protocol)
|
||||
sslContext.init(null, trustAllCerts, SecureRandom())
|
||||
|
||||
val factory: SSLSocketFactory = sslContext.socketFactory
|
||||
|
||||
val allCipher = factory.supportedCipherSuites
|
||||
|
||||
allCipher.forEach { suite ->
|
||||
println("Supported cipher suite: $suite")
|
||||
}
|
||||
|
||||
val socket: SSLSocket?
|
||||
try {
|
||||
socket = factory.createSocket(uri.host, port) as SSLSocket
|
||||
|
||||
socket.supportedProtocols.forEach { protocol ->
|
||||
println("Supported protocol $protocol")
|
||||
}
|
||||
|
||||
socket.enabledCipherSuites = allCipher
|
||||
|
||||
//socket.enabledProtocols = socket.supportedProtocols
|
||||
socket.enabledCipherSuites = factory.supportedCipherSuites
|
||||
socket.enabledProtocols = socket.supportedProtocols
|
||||
socket.startHandshake()
|
||||
}catch(ce: ConnectException){
|
||||
|
|
|
@ -34,6 +34,7 @@ import oppen.ariane.ui.modals_menus.history.HistoryDialog
|
|||
import oppen.ariane.ui.modals_menus.input.InputDialog
|
||||
import oppen.ariane.ui.modals_menus.overflow.OverflowPopup
|
||||
import oppen.ariane.ui.modals_menus.set_home.SetHomeDialog
|
||||
import oppen.ariane.ui.settings.SettingsActivity
|
||||
import oppen.hideKeyboard
|
||||
import oppen.visibleRetainingSpace
|
||||
import java.io.File
|
||||
|
@ -87,7 +88,6 @@ class GemActivity : AppCompatActivity() {
|
|||
|
||||
bookmarkDatasource = BookmarksDatasource.getDefault(applicationContext)
|
||||
|
||||
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_gem)
|
||||
binding.viewmodel = model
|
||||
binding.lifecycleOwner = this
|
||||
|
@ -97,9 +97,13 @@ class GemActivity : AppCompatActivity() {
|
|||
|
||||
history = HistoryInterface.default(this)
|
||||
|
||||
val prefs = getSharedPreferences("oppen.tva.ui.dialogs.set_home", Context.MODE_PRIVATE)
|
||||
val home = prefs.getString("home", Ariane.DEFAULT_HOME_CAPSULE)
|
||||
|
||||
model.initialise(
|
||||
Datasource.factory(this),
|
||||
BookmarksDatasource.getDefault(applicationContext)
|
||||
home = home ?: Ariane.DEFAULT_HOME_CAPSULE,
|
||||
gemini = Datasource.factory(this, "TLSv1.2"),
|
||||
bookmarks = BookmarksDatasource.getDefault(applicationContext)
|
||||
){ state ->
|
||||
|
||||
binding.pullToRefresh.isRefreshing = false
|
||||
|
@ -206,6 +210,9 @@ class GemActivity : AppCompatActivity() {
|
|||
showAlert("Home capsule updated")
|
||||
}
|
||||
}
|
||||
R.id.overflow_menu_settings -> {
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ class GemViewModel: ViewModel() {
|
|||
|
||||
private val history = mutableListOf<URI>()
|
||||
|
||||
fun initialise(gemini: Datasource, bookmarks: BookmarksDatasource, onState: (state: GemState) -> Unit){
|
||||
fun initialise(home: String, gemini: Datasource, bookmarks: BookmarksDatasource, onState: (state: GemState) -> Unit){
|
||||
this.gemini = gemini
|
||||
this.bookmarks = bookmarks
|
||||
this.onState = onState
|
||||
|
||||
request(URI.create(Ariane.DEFAULT_HOME_CAPSULE))//todo - regression: should check prefs...
|
||||
request(home)
|
||||
}
|
||||
|
||||
fun request(address: String) {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
package oppen.ariane.ui.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import oppen.ariane.R
|
||||
|
||||
class SettingsActivity: AppCompatActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(R.layout.activity_settings)
|
||||
supportFragmentManager.beginTransaction().replace(R.id.settings_container, SettingsFragment()).commit()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package oppen.ariane.ui.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
|
||||
class SettingsFragment: PreferenceFragmentCompat(), Preference.OnPreferenceChangeListener {
|
||||
|
||||
lateinit var protocols: Array<String>
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
val context = preferenceManager.context
|
||||
val screen = preferenceManager.createPreferenceScreen(context)
|
||||
|
||||
val tlsCategory = PreferenceCategory(context)
|
||||
tlsCategory.key = "tls_category"
|
||||
tlsCategory.title = "TLS Config"
|
||||
screen.addPreference(tlsCategory)
|
||||
|
||||
val sslContext = SSLContext.getInstance("TLS")
|
||||
sslContext.init(null, null, SecureRandom())
|
||||
val factory: SSLSocketFactory = sslContext.socketFactory
|
||||
val socket = factory.createSocket() as SSLSocket
|
||||
protocols = socket.supportedProtocols
|
||||
protocols.forEach { protocol ->
|
||||
val tlsPreference = SwitchPreferenceCompat(context)
|
||||
tlsPreference.key = "tls_${protocol.toLowerCase(Locale.getDefault())}"
|
||||
tlsPreference.title = protocol
|
||||
tlsPreference.onPreferenceChangeListener = this
|
||||
tlsCategory.addPreference(tlsPreference)
|
||||
}
|
||||
|
||||
preferenceScreen = screen
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference?, newValue: Any?): Boolean {
|
||||
|
||||
preference?.key?.let{ key ->
|
||||
if(key.startsWith("tls_")){
|
||||
protocols.forEach {protocol ->
|
||||
val tlsSwitchKey = "tls_${protocol.toLowerCase(Locale.getDefault())}"
|
||||
if(tlsSwitchKey != key){
|
||||
val otherTLSSwitch = preferenceScreen.findPreference<SwitchPreferenceCompat>(tlsSwitchKey)
|
||||
otherTLSSwitch?.isChecked = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/settings_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</RelativeLayout>
|
|
@ -33,6 +33,10 @@
|
|||
android:id="@+id/overflow_menu_set_home"
|
||||
android:title="@string/set_home"
|
||||
android:icon="@drawable/vector_set_home"/>
|
||||
<item
|
||||
android:id="@+id/overflow_menu_settings"
|
||||
android:title="@string/settings"
|
||||
android:icon="@drawable/vector_set_home"/>
|
||||
<item
|
||||
android:id="@+id/overflow_menu_about"
|
||||
android:title="@string/about"
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
<string name="gemini_address">Gemini address</string>
|
||||
<string name="share">Share</string>
|
||||
<string name="set_home">Set Home</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="home_icon_attribution">Home icon by Icongeek26 on FlatIcon.com</string>
|
||||
<string name="about_body">Ariane: Gemini protocol client from Öppenlab</string>
|
||||
<string name="gnu_link">GPL v3</string>
|
||||
|
|
Loading…
Reference in New Issue