Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/src/main/kotlin/com/github/gotify/CfAccessSettings.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.gotify

internal data class CfAccessSettings(
val enabled: Boolean,
val clientId: String,
val clientSecret: String
)
29 changes: 21 additions & 8 deletions app/src/main/kotlin/com/github/gotify/CoilInstance.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import coil.request.ImageRequest
import coil.request.Options
import coil.request.SuccessResult
import com.github.gotify.api.CertUtils
import com.github.gotify.api.CloudflareAccessInterceptor
import com.github.gotify.client.model.Application
import java.io.IOException
import okhttp3.Credentials
Expand All @@ -31,7 +32,7 @@ import okhttp3.Response
import org.tinylog.kotlin.Logger

object CoilInstance {
private var holder: Pair<SSLSettings, ImageLoader>? = null
private var holder: Triple<SSLSettings, CfAccessSettings, ImageLoader>? = null

@Throws(IOException::class)
fun getImageFromUrl(
Expand Down Expand Up @@ -78,22 +79,34 @@ object CoilInstance {

@Synchronized
fun get(context: Context): ImageLoader {
val newSettings = Settings(context).sslSettings()
val settings = Settings(context)
val newSslSettings = settings.sslSettings()
val newCfSettings = settings.cfAccessSettings()
val copy = holder
if (copy != null && copy.first == newSettings) {
return copy.second
if (copy != null && copy.first == newSslSettings && copy.second == newCfSettings) {
return copy.third
}
return makeImageLoader(context, newSettings).also { holder = it }.second
return makeImageLoader(context, newSslSettings, newCfSettings)
.also { holder = it }.third
}

private fun makeImageLoader(
context: Context,
sslSettings: SSLSettings
): Pair<SSLSettings, ImageLoader> {
sslSettings: SSLSettings,
cfAccessSettings: CfAccessSettings
): Triple<SSLSettings, CfAccessSettings, ImageLoader> {
val builder = OkHttpClient
.Builder()
.addInterceptor(BasicAuthInterceptor())
CertUtils.applySslSettings(builder, sslSettings)
if (cfAccessSettings.enabled) {
builder.addInterceptor(
CloudflareAccessInterceptor(
cfAccessSettings.clientId,
cfAccessSettings.clientSecret
)
)
}
val loader = ImageLoader.Builder(context)
.okHttpClient(builder.build())
.diskCache {
Expand All @@ -106,7 +119,7 @@ object CoilInstance {
add(DataDecoderFactory())
}
.build()
return sslSettings to loader
return Triple(sslSettings, cfAccessSettings, loader)
}
}

Expand Down
20 changes: 20 additions & 0 deletions app/src/main/kotlin/com/github/gotify/Settings.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ internal class Settings(context: Context) {
var clientCertPassword: String?
get() = sharedPreferences.getString("clientCertPass", null)
set(value) = sharedPreferences.edit { putString("clientCertPass", value) }
var cfAccessEnabled: Boolean
get() = sharedPreferences.getBoolean("cfAccessEnabled", false)
set(value) = sharedPreferences.edit { putBoolean("cfAccessEnabled", value) }
var cfAccessClientId: String
get() = sharedPreferences.getString("cfAccessClientId", "")!!
set(value) = sharedPreferences.edit { putString("cfAccessClientId", value) }
var cfAccessClientSecret: String
get() = sharedPreferences.getString("cfAccessClientSecret", "")!!
set(value) = sharedPreferences.edit { putString("cfAccessClientSecret", value) }

init {
sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE)
Expand All @@ -61,6 +70,9 @@ internal class Settings(context: Context) {
caCertPath = null
clientCertPath = null
clientCertPassword = null
cfAccessEnabled = false
cfAccessClientId = ""
cfAccessClientSecret = ""
}

fun setUser(name: String?, admin: Boolean) {
Expand All @@ -76,6 +88,14 @@ internal class Settings(context: Context) {
)
}

fun cfAccessSettings(): CfAccessSettings {
return CfAccessSettings(
cfAccessEnabled,
cfAccessClientId,
cfAccessClientSecret
)
}

@Suppress("UnusedReceiverParameter")
private fun Any?.toUnit() = Unit
}
31 changes: 24 additions & 7 deletions app/src/main/kotlin/com/github/gotify/api/ClientFactory.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.github.gotify.api

import com.github.gotify.CfAccessSettings
import com.github.gotify.SSLSettings
import com.github.gotify.Settings
import com.github.gotify.client.ApiClient
Expand All @@ -12,18 +13,23 @@ internal object ClientFactory {
private fun unauthorized(
settings: Settings,
sslSettings: SSLSettings,
baseUrl: String
baseUrl: String,
cfAccessSettings: CfAccessSettings = settings.cfAccessSettings()
): ApiClient {
return defaultClient(arrayOf(), settings, sslSettings, baseUrl)
return defaultClient(arrayOf(), settings, sslSettings, baseUrl, cfAccessSettings)
}

fun basicAuth(
settings: Settings,
sslSettings: SSLSettings,
username: String,
password: String
password: String,
cfAccessSettings: CfAccessSettings = settings.cfAccessSettings()
): ApiClient {
val client = defaultClient(arrayOf("basicAuth"), settings, sslSettings)
val client = defaultClient(
arrayOf("basicAuth"), settings, sslSettings,
cfAccessSettings = cfAccessSettings
)
val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth
auth.username = username
auth.password = password
Expand All @@ -40,9 +46,11 @@ internal object ClientFactory {
fun versionApi(
settings: Settings,
sslSettings: SSLSettings = settings.sslSettings(),
baseUrl: String = settings.url
baseUrl: String = settings.url,
cfAccessSettings: CfAccessSettings = settings.cfAccessSettings()
): VersionApi {
return unauthorized(settings, sslSettings, baseUrl).createService(VersionApi::class.java)
return unauthorized(settings, sslSettings, baseUrl, cfAccessSettings)
.createService(VersionApi::class.java)
}

fun userApiWithToken(settings: Settings): UserApi {
Expand All @@ -53,10 +61,19 @@ internal object ClientFactory {
authentications: Array<String>,
settings: Settings,
sslSettings: SSLSettings = settings.sslSettings(),
baseUrl: String = settings.url
baseUrl: String = settings.url,
cfAccessSettings: CfAccessSettings = settings.cfAccessSettings()
): ApiClient {
val client = ApiClient(authentications)
CertUtils.applySslSettings(client.okBuilder, sslSettings)
if (cfAccessSettings.enabled) {
client.okBuilder.addInterceptor(
CloudflareAccessInterceptor(
cfAccessSettings.clientId,
cfAccessSettings.clientSecret
)
)
}
client.adapterBuilder.baseUrl("$baseUrl/")
return client
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.github.gotify.api

import okhttp3.Interceptor
import okhttp3.Response

internal class CloudflareAccessInterceptor(
private val clientId: String,
private val clientSecret: String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
.newBuilder()
.addHeader("CF-Access-Client-Id", clientId)
.addHeader("CF-Access-Client-Secret", clientSecret)
.build()
return chain.proceed(request)
}
}
35 changes: 31 additions & 4 deletions app/src/main/kotlin/com/github/gotify/login/AdvancedDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ internal class AdvancedDialog(
private lateinit var onClickRemoveCaCertificate: Runnable
private lateinit var onClickSelectClientCertificate: Runnable
private lateinit var onClickRemoveClientCertificate: Runnable
private lateinit var onClose: (password: String) -> Unit
private lateinit var onClose: (password: String, cfAccessEnabled: Boolean, cfAccessClientId: String, cfAccessClientSecret: String) -> Unit

fun onDisableSSLChanged(
onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?
Expand Down Expand Up @@ -47,7 +47,9 @@ internal class AdvancedDialog(
return this
}

fun onClose(onClose: (password: String) -> Unit): AdvancedDialog {
fun onClose(
onClose: (password: String, cfAccessEnabled: Boolean, cfAccessClientId: String, cfAccessClientSecret: String) -> Unit
): AdvancedDialog {
this.onClose = onClose
return this
}
Expand All @@ -57,7 +59,10 @@ internal class AdvancedDialog(
caCertPath: String? = null,
caCertCN: String?,
clientCertPath: String? = null,
clientCertPassword: String?
clientCertPassword: String?,
cfAccessEnabled: Boolean = false,
cfAccessClientId: String = "",
cfAccessClientSecret: String = ""
): AdvancedDialog {
binding = AdvancedSettingsDialogBinding.inflate(layoutInflater)
binding.disableSSL.isChecked = disableSSL
Expand All @@ -82,17 +87,39 @@ internal class AdvancedDialog(
} else {
showRemoveClientCertificate()
}
binding.enableCfAccess.isChecked = cfAccessEnabled
setCfAccessFieldsVisibility(cfAccessEnabled)
if (cfAccessClientId.isNotEmpty()) {
binding.cfAccessClientIdEdittext.setText(cfAccessClientId)
}
if (cfAccessClientSecret.isNotEmpty()) {
binding.cfAccessClientSecretEdittext.setText(cfAccessClientSecret)
}
binding.enableCfAccess.setOnCheckedChangeListener { _, isChecked ->
setCfAccessFieldsVisibility(isChecked)
}
MaterialAlertDialogBuilder(context)
.setView(binding.root)
.setTitle(R.string.advanced_settings)
.setPositiveButton(context.getString(R.string.done), null)
.setOnDismissListener {
onClose(binding.clientCertPasswordEdittext.text.toString())
onClose(
binding.clientCertPasswordEdittext.text.toString(),
binding.enableCfAccess.isChecked,
binding.cfAccessClientIdEdittext.text.toString(),
binding.cfAccessClientSecretEdittext.text.toString()
)
}
.show()
return this
}

private fun setCfAccessFieldsVisibility(visible: Boolean) {
val visibility = if (visible) android.view.View.VISIBLE else android.view.View.GONE
binding.cfAccessClientIdLayout.visibility = visibility
binding.cfAccessClientSecretLayout.visibility = visibility
}

private fun showSelectCaCertificate() {
binding.toggleCaCert.setText(R.string.select_ca_certificate)
binding.toggleCaCert.setOnClickListener { onClickSelectCaCertificate.run() }
Expand Down
31 changes: 27 additions & 4 deletions app/src/main/kotlin/com/github/gotify/login/LoginActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import com.github.gotify.R
import com.github.gotify.CfAccessSettings
import com.github.gotify.SSLSettings
import com.github.gotify.Settings
import com.github.gotify.Utils
Expand Down Expand Up @@ -51,6 +52,9 @@ internal class LoginActivity : AppCompatActivity() {
private var caCertPath: String? = null
private var clientCertPath: String? = null
private var clientCertPassword: String? = null
private var cfAccessEnabled = false
private var cfAccessClientId = ""
private var cfAccessClientSecret = ""
private lateinit var advancedDialog: AdvancedDialog

private val caDialogResultLauncher =
Expand Down Expand Up @@ -145,7 +149,7 @@ internal class LoginActivity : AppCompatActivity() {
binding.checkurl.visibility = View.GONE

try {
ClientFactory.versionApi(settings, tempSslSettings(), url)
ClientFactory.versionApi(settings, tempSslSettings(), url, tempCfAccessSettings())
.version
.enqueue(Callback.callInUI(this, onValidUrl(url), onInvalidUrl(url)))
} catch (e: Exception) {
Expand Down Expand Up @@ -192,15 +196,21 @@ internal class LoginActivity : AppCompatActivity() {
invalidateUrl()
clientCertPath = null
}
.onClose { newPassword ->
.onClose { newPassword, newCfEnabled, newCfClientId, newCfClientSecret ->
clientCertPassword = newPassword
cfAccessEnabled = newCfEnabled
cfAccessClientId = newCfClientId
cfAccessClientSecret = newCfClientSecret
}
.show(
disableSslValidation,
caCertPath,
caCertCN,
clientCertPath,
clientCertPassword
clientCertPassword,
cfAccessEnabled,
cfAccessClientId,
cfAccessClientSecret
)
}

Expand Down Expand Up @@ -254,7 +264,9 @@ internal class LoginActivity : AppCompatActivity() {
binding.login.visibility = View.GONE
binding.loginProgress.visibility = View.VISIBLE

val client = ClientFactory.basicAuth(settings, tempSslSettings(), username, password)
val client = ClientFactory.basicAuth(
settings, tempSslSettings(), username, password, tempCfAccessSettings()
)
client.createService(UserApi::class.java)
.currentUser()
.enqueue(
Expand Down Expand Up @@ -311,6 +323,9 @@ internal class LoginActivity : AppCompatActivity() {
settings.caCertPath = caCertPath
settings.clientCertPath = clientCertPath
settings.clientCertPassword = clientCertPassword
settings.cfAccessEnabled = cfAccessEnabled
settings.cfAccessClientId = cfAccessClientId
settings.cfAccessClientSecret = cfAccessClientSecret

Utils.showSnackBar(this, getString(R.string.created_client))
startActivity(Intent(this, InitializationActivity::class.java))
Expand Down Expand Up @@ -341,6 +356,14 @@ internal class LoginActivity : AppCompatActivity() {
)
}

private fun tempCfAccessSettings(): CfAccessSettings {
return CfAccessSettings(
cfAccessEnabled,
cfAccessClientId,
cfAccessClientSecret
)
}

private fun copyStreamToFile(inputStream: InputStream, file: File) {
FileOutputStream(file).use {
inputStream.copyTo(it)
Expand Down
Loading