Skip to content

Commit

Permalink
Merge pull request #92 from damontecres/feat/self-signed-certs
Browse files Browse the repository at this point in the history
Add option to trust self signed certs
  • Loading branch information
damontecres authored Feb 11, 2024
2 parents 5d07341 + 4334424 commit 66ecf5b
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 17 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Hardcoded crentials
StashCredentials.kt
stash_strings.xml
test-server/

# Gradle files
.gradle/
Expand Down
6 changes: 5 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ plugins {
id("org.jetbrains.kotlin.android")
id("kotlin-parcelize")
id("com.apollographql.apollo3") version "3.8.2"
id("kotlin-kapt")
}

fun getVersionCode(): Int {
Expand Down Expand Up @@ -124,13 +125,16 @@ tasks.create("generateStrings", Exec::class.java) {
tasks.preBuild.dependsOn("generateStrings")

val mediaVersion = "1.2.1"
val glideVersion = "4.16.0"

dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.leanback:leanback:1.1.0-rc02")
implementation("androidx.leanback:leanback-preference:1.1.0-rc01")
implementation("androidx.leanback:leanback-paging:1.1.0-alpha11")
implementation("com.github.bumptech.glide:glide:4.11.0")
implementation("com.github.bumptech.glide:glide:$glideVersion")
implementation("com.github.bumptech.glide:okhttp3-integration:$glideVersion")
kapt("com.github.bumptech.glide:compiler:$glideVersion")

implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
android:exported="true"
android:icon="@mipmap/stash_logo"
android:logo="@mipmap/stash_logo"
android:noHistory="true"
android:screenOrientation="landscape"
android:theme="@style/NoTitleTheme"
android:noHistory="true">
android:theme="@style/NoTitleTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreference
import com.github.damontecres.stashapp.util.MutationEngine
import com.github.damontecres.stashapp.util.configureHttpsTrust
import com.github.damontecres.stashapp.util.testStashConnection
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -295,6 +296,12 @@ class SettingsFragment : LeanbackSettingsFragmentCompat() {
startActivity(Intent(requireContext(), LicenseActivity::class.java))
true
}

findPreference<Preference>("trustAllCerts")?.setOnPreferenceChangeListener { _, newValue ->
val app = requireActivity().application as StashApplication
configureHttpsTrust(app, newValue as Boolean)
true
}
}

override fun onStop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,17 @@ import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.preference.PreferenceManager
import com.github.damontecres.stashapp.util.configureHttpsTrust
import javax.net.ssl.HttpsURLConnection

class StashApplication : Application() {
private var wasEnterBackground = false
private var mainDestroyed = false
var hasAskedForPin = false

val defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory()
val defaultHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier()

override fun onCreate() {
super.onCreate()

Expand All @@ -42,6 +47,8 @@ class StashApplication : Application() {
putLong(VERSION_CODE_CURRENT_KEY, pkgInfo.versionCode.toLong())
}
}

configureHttpsTrust(this)
}

fun showPinActivity() {
Expand Down
93 changes: 79 additions & 14 deletions app/src/main/java/com/github/damontecres/stashapp/util/Constants.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.damontecres.stashapp.util

import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.Build
Expand All @@ -16,20 +17,29 @@ import com.apollographql.apollo3.api.http.HttpRequest
import com.apollographql.apollo3.api.http.HttpResponse
import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.network.http.DefaultHttpEngine
import com.apollographql.apollo3.network.http.HttpInterceptor
import com.apollographql.apollo3.network.http.HttpInterceptorChain
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.github.damontecres.stashapp.StashApplication
import com.github.damontecres.stashapp.api.ServerInfoQuery
import com.github.damontecres.stashapp.api.fragment.PerformerData
import com.github.damontecres.stashapp.api.fragment.SavedFilterData
import com.github.damontecres.stashapp.api.fragment.SlimSceneData
import com.github.damontecres.stashapp.api.type.FindFilterType
import com.github.damontecres.stashapp.data.DataType
import okhttp3.OkHttpClient
import java.io.File
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.time.LocalDate
import java.time.Period
import java.time.format.DateTimeFormatter
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.time.DurationUnit
Expand All @@ -53,6 +63,60 @@ object Constants {
}
}

val TRUST_ALL_CERTS: TrustManager =
@SuppressLint("CustomX509TrustManager")
object : X509TrustManager {
@SuppressLint("TrustAllX509TrustManager")
override fun checkClientTrusted(
chain: Array<X509Certificate>,
authType: String,
) {
}

@SuppressLint("TrustAllX509TrustManager")
override fun checkServerTrusted(
chain: Array<X509Certificate>,
authType: String,
) {
}

override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
}

fun createUnsafeOkHttpClient(): OkHttpClient {
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf(TRUST_ALL_CERTS), SecureRandom())
return OkHttpClient.Builder()
.sslSocketFactory(
sslContext.socketFactory,
TRUST_ALL_CERTS as X509TrustManager,
)
.hostnameVerifier { _, _ ->
true
}
.build()
}

fun configureHttpsTrust(
app: StashApplication,
trustAll: Boolean? = null,
) {
val trust =
trustAll ?: PreferenceManager.getDefaultSharedPreferences(app.baseContext)
.getBoolean("trustAllCerts", false)
if (trust) {
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, arrayOf(TRUST_ALL_CERTS), SecureRandom())
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.socketFactory)
HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true }
} else {
HttpsURLConnection.setDefaultSSLSocketFactory(app.defaultSSLSocketFactory)
HttpsURLConnection.setDefaultHostnameVerifier(app.defaultHostnameVerifier)
}
}

/**
* Create a [GlideUrl], adding the API key to the headers if needed
*/
Expand Down Expand Up @@ -104,12 +168,11 @@ class AuthorizationInterceptor(private val apiKey: String?) : HttpInterceptor {
}

/**
* Create a client for accessing Stash's GraphQL API
* Create a client for accessing Stash's GraphQL API using the default shared preferences for the URL & API key
*/
fun createApolloClient(
stashUrl: String?,
apiKey: String?,
): ApolloClient? {
fun createApolloClient(context: Context): ApolloClient? {
val stashUrl = PreferenceManager.getDefaultSharedPreferences(context).getString("stashUrl", "")
val apiKey = PreferenceManager.getDefaultSharedPreferences(context).getString("stashApiKey", "")
return if (!stashUrl.isNullOrBlank()) {
var cleanedStashUrl = stashUrl.trim()
if (!cleanedStashUrl.startsWith("http://") && !cleanedStashUrl.startsWith("https://")) {
Expand All @@ -126,8 +189,19 @@ fun createApolloClient(
.path(pathSegments.joinToString("/")) // Ensure the URL is the graphql endpoint
.build()
Log.d(Constants.TAG, "StashUrl: $stashUrl => $url")

val trustAll =
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean("trustAllCerts", false)
val httpEngine =
if (trustAll) {
DefaultHttpEngine(createUnsafeOkHttpClient())
} else {
DefaultHttpEngine()
}
ApolloClient.Builder()
.serverUrl(url.toString())
.httpEngine(httpEngine)
.addHttpInterceptor(AuthorizationInterceptor(apiKey))
.build()
} else {
Expand All @@ -139,15 +213,6 @@ fun createApolloClient(
}
}

/**
* Create a client for accessing Stash's GraphQL API using the default shared preferences for the URL & API key
*/
fun createApolloClient(context: Context): ApolloClient? {
val stashUrl = PreferenceManager.getDefaultSharedPreferences(context).getString("stashUrl", "")
val apiKey = PreferenceManager.getDefaultSharedPreferences(context).getString("stashApiKey", "")
return createApolloClient(stashUrl, apiKey)
}

/**
* Test whether the app can connect to Stash
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.github.damontecres.stashapp.util

import android.content.Context
import androidx.preference.PreferenceManager
import com.bumptech.glide.Glide
import com.bumptech.glide.Registry
import com.bumptech.glide.annotation.GlideModule
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.module.AppGlideModule
import java.io.InputStream

@GlideModule
class UnsafeOkHttpGlideModule : AppGlideModule() {
override fun isManifestParsingEnabled(): Boolean {
return false
}

override fun registerComponents(
context: Context,
glide: Glide,
registry: Registry,
) {
val trustAll =
PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean("trustAllCerts", false)
if (trustAll) {
registry.replace<GlideUrl, InputStream>(
GlideUrl::class.java,
InputStream::class.java,
OkHttpUrlLoader.Factory(createUnsafeOkHttpClient()),
)
}
}
}
6 changes: 6 additions & 0 deletions app/src/main/res/xml/root_preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
app:title="Remove a Stash Server"
app:summary="" />

<SwitchPreference
app:key="trustAllCerts"
app:title="Trust self-signed certificates"
app:summary="Restart may be required after changing this"
app:defaultValue="false" />

</PreferenceCategory>

<PreferenceCategory app:title="Search">
Expand Down

0 comments on commit 66ecf5b

Please sign in to comment.