Skip to content

Commit

Permalink
Use additional http headers to bypass Cloudflare SSO. (Or other SSO)
Browse files Browse the repository at this point in the history
  • Loading branch information
lorinc-ambrozy-work committed May 2, 2023
1 parent 8b32f9b commit c4f36a9
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 6 deletions.
Expand Up @@ -2,6 +2,7 @@ package io.homeassistant.companion.android.settings.server

import android.util.Log
import androidx.preference.PreferenceDataStore
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import io.homeassistant.companion.android.common.data.servers.ServerManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -12,7 +13,8 @@ import kotlinx.coroutines.runBlocking
import javax.inject.Inject

class ServerSettingsPresenterImpl @Inject constructor(
private val serverManager: ServerManager
private val serverManager: ServerManager,
private val prefsRepository: PrefsRepository
) : ServerSettingsPresenter, PreferenceDataStore() {

companion object {
Expand Down Expand Up @@ -56,6 +58,10 @@ class ServerSettingsPresenterImpl @Inject constructor(
"registration_name" -> serverManager.getServer(serverId)?.deviceName
"connection_internal" -> (serverManager.getServer(serverId)?.connection?.getUrl(isInternal = true, force = true) ?: "").toString()
"session_timeout" -> serverManager.integrationRepository(serverId).getSessionTimeOut().toString()
"header_name_1" -> prefsRepository.getHeaderName1()
"header_name_2" -> prefsRepository.getHeaderName2()
"header_value_1" -> prefsRepository.getHeaderValue1()
"header_value_2" -> prefsRepository.getHeaderValue2()
else -> throw IllegalArgumentException("No string found by this key: $key")
}
}
Expand Down Expand Up @@ -100,6 +106,10 @@ class ServerSettingsPresenterImpl @Inject constructor(
Log.e(TAG, "Issue saving session timeout value", e)
}
}
"header_name_1" -> prefsRepository.saveHeaderName1(value?.ifBlank { null })
"header_name_2" -> prefsRepository.saveHeaderName2(value?.ifBlank { null })
"header_value_1" -> prefsRepository.saveHeaderValue1(value?.ifBlank { null })
"header_value_2" -> prefsRepository.saveHeaderValue2(value?.ifBlank { null })
else -> throw IllegalArgumentException("No string found by this key: $key")
}
}
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/res/xml/preferences_server.xml
Expand Up @@ -64,6 +64,35 @@
app:isPreferenceVisible="false"
app:useSimpleSummaryProvider="true"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/http_header_settings">
<Preference
android:selectable="false"
android:enabled="true"
android:key="http_header_settings_summary"
android:title=""
android:summary="@string/http_header_settings_summary" />
<EditTextPreference
android:key="header_name_1"
android:icon="@drawable/ic_edit"
android:title="@string/header_name_1"
app:useSimpleSummaryProvider="true"/>
<EditTextPreference
android:key="header_value_1"
android:icon="@drawable/ic_edit"
android:title="@string/header_value_1"
app:useSimpleSummaryProvider="true"/>
<EditTextPreference
android:key="header_name_2"
android:icon="@drawable/ic_edit"
android:title="@string/header_name_2"
app:useSimpleSummaryProvider="true"/>
<EditTextPreference
android:key="header_value_2"
android:icon="@drawable/ic_edit"
android:title="@string/header_value_2"
app:useSimpleSummaryProvider="true"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/other_settings">
<Preference
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Expand Up @@ -11,7 +11,7 @@ buildscript {
gradlePluginPortal()
}
dependencies {
classpath("com.android.tools.build:gradle:8.0.0")
classpath("com.android.tools.build:gradle:8.0.1")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.10")
classpath("com.google.gms:google-services:4.3.15")
classpath("com.google.firebase:firebase-appdistribution-gradle:4.0.0")
Expand Down
Expand Up @@ -7,23 +7,27 @@ import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.module.kotlin.registerKotlinModule
import io.homeassistant.companion.android.common.BuildConfig
import io.homeassistant.companion.android.common.data.prefs.PrefsRepository
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.jackson.JacksonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Inject

class HomeAssistantApis @Inject constructor(
private val tlsHelper: TLSHelper
private val tlsHelper: TLSHelper,
private val prefsRepository: PrefsRepository
) {
companion object {
private const val LOCAL_HOST = "http://localhost/"
const val USER_AGENT = "User-Agent"
val USER_AGENT_STRING = "Home Assistant/${BuildConfig.VERSION_NAME} (Android ${Build.VERSION.RELEASE}; ${Build.MODEL})"

private val CALL_TIMEOUT = 30L
private val READ_TIMEOUT = 30L
private const val CALL_TIMEOUT = 30L
private const val READ_TIMEOUT = 30L
}
private fun configureOkHttpClient(builder: OkHttpClient.Builder): OkHttpClient.Builder {
if (BuildConfig.DEBUG) {
Expand All @@ -38,6 +42,7 @@ class HomeAssistantApis @Inject constructor(
it.request()
.newBuilder()
.header(USER_AGENT, USER_AGENT_STRING)
.addAdditionalHeaders()
.build()
)
}
Expand Down Expand Up @@ -76,4 +81,28 @@ class HomeAssistantApis @Inject constructor(
.build()

val okHttpClient = configureOkHttpClient(OkHttpClient.Builder()).build()

private fun Request.Builder.addAdditionalHeaders(): Request.Builder {
var headerName1: String?
var headerName2: String?
var headerValue1: String?
var headerValue2: String?

runBlocking {
headerName1 = prefsRepository.getHeaderName1()
headerName2 = prefsRepository.getHeaderName2()
headerValue1 = prefsRepository.getHeaderValue1()
headerValue2 = prefsRepository.getHeaderValue2()
}

if (!headerName1.isNullOrEmpty() && !headerValue1.isNullOrEmpty()) {
this.addHeader(headerName1!!, headerValue1!!)
}

if (!headerName2.isNullOrEmpty() && !headerValue2.isNullOrEmpty()) {
this.addHeader(headerName2!!, headerValue2!!)
}

return this
}
}
Expand Up @@ -39,6 +39,22 @@ interface PrefsRepository {

suspend fun saveScreenOrientation(orientation: String?)

suspend fun getHeaderName1(): String?

suspend fun saveHeaderName1(headerName1: String?)

suspend fun getHeaderName2(): String?

suspend fun saveHeaderName2(headerName2: String?)

suspend fun getHeaderValue1(): String?

suspend fun saveHeaderValue1(headerValue1: String?)

suspend fun getHeaderValue2(): String?

suspend fun saveHeaderValue2(headerValue2: String?)

suspend fun isPinchToZoomEnabled(): Boolean

suspend fun setPinchToZoomEnabled(enabled: Boolean)
Expand Down
Expand Up @@ -20,6 +20,10 @@ class PrefsRepositoryImpl @Inject constructor(
private const val PREF_LANG = "lang"
private const val PREF_LOCALES = "locales"
private const val PREF_SCREEN_ORIENTATION = "screen_orientation"
private const val PREF_HEADER_NAME_1 = "header_name_1"
private const val PREF_HEADER_NAME_2 = "header_name_2"
private const val PREF_HEADER_VALUE_1 = "header_value_1"
private const val PREF_HEADER_VALUE_2 = "header_value_2"
private const val PREF_CONTROLS_AUTH_REQUIRED = "controls_auth_required"
private const val PREF_CONTROLS_AUTH_ENTITIES = "controls_auth_entities"
private const val PREF_FULLSCREEN_ENABLED = "fullscreen_enabled"
Expand Down Expand Up @@ -106,6 +110,38 @@ class PrefsRepositoryImpl @Inject constructor(
localStorage.putString(PREF_SCREEN_ORIENTATION, orientation)
}

override suspend fun getHeaderName1(): String? {
return localStorage.getString(PREF_HEADER_NAME_1)
}

override suspend fun getHeaderName2(): String? {
return localStorage.getString(PREF_HEADER_NAME_2)
}

override suspend fun getHeaderValue1(): String? {
return localStorage.getString(PREF_HEADER_VALUE_1)
}

override suspend fun getHeaderValue2(): String? {
return localStorage.getString(PREF_HEADER_VALUE_2)
}

override suspend fun saveHeaderName1(headerName1: String?) {
localStorage.putString(PREF_HEADER_NAME_1, headerName1)
}

override suspend fun saveHeaderName2(headerName2: String?) {
localStorage.putString(PREF_HEADER_NAME_2, headerName2)
}

override suspend fun saveHeaderValue1(headerValue1: String?) {
localStorage.putString(PREF_HEADER_VALUE_1, headerValue1)
}

override suspend fun saveHeaderValue2(headerValue2: String?) {
localStorage.putString(PREF_HEADER_VALUE_2, headerValue2)
}

override suspend fun getControlsAuthRequired(): ControlsAuthRequiredSetting {
val current = localStorage.getString(PREF_CONTROLS_AUTH_REQUIRED)
return ControlsAuthRequiredSetting.values().firstOrNull {
Expand Down
6 changes: 6 additions & 0 deletions common/src/main/res/values/strings.xml
Expand Up @@ -256,13 +256,19 @@
<string name="screen_orientation_option_value_landscape">landscape</string>
<string name="get_help">Get Help</string>
<string name="grant_permission">Grant Permission</string>
<string name="header_name_1">Name of the 1. header</string>
<string name="header_name_2">Name of the 2. header</string>
<string name="header_value_1">Value of the 1. header</string>
<string name="header_value_2">Value of the 2. header</string>
<string name="help">Help</string>
<string name="hide">Hide</string>
<string name="high_accuracy_mode_channel_name">High accuracy location</string>
<string name="high_accuracy_mode_notification_title">High accuracy (GPS) mode enabled</string>
<string name="history">History</string>
<string name="hold_to_reorder">Tap and hold to reorder</string>
<string name="home_assistant_not_discover">Unable to find your\nHome Assistant instance</string>
<string name="http_header_settings">Additional Http Headers</string>
<string name="http_header_settings_summary">Enter Http header items with names and values. These header items will be added to all API calls to the Home Assistant server. These header items can be used (for example) to bypass Cloudflare SSO.</string>
<string name="icon">Icon</string>
<string name="input_booleans">Input Booleans</string>
<string name="input_buttons">Input Buttons</string>
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Expand Up @@ -28,4 +28,5 @@ org.gradle.vfs.watch=true
android.defaults.buildfeatures.resvalues=false
android.defaults.buildfeatures.shaders=false
android.defaults.buildfeatures.buildconfig=true
android.nonFinalResIds=false
android.nonFinalResIds=false
org.gradle.unsafe.configuration-cache=true

0 comments on commit c4f36a9

Please sign in to comment.