Migrate legacy ca cert in newer version

This commit is contained in:
Niko Diamadis
2024-04-23 19:56:33 +02:00
parent 60946e4e1e
commit a3dd80cd20
8 changed files with 58 additions and 44 deletions

View File

@@ -6,6 +6,7 @@ import com.github.gotify.client.model.User
internal class Settings(context: Context) { internal class Settings(context: Context) {
private val sharedPreferences: SharedPreferences private val sharedPreferences: SharedPreferences
val filesDir: String
var url: String var url: String
get() = sharedPreferences.getString("url", "")!! get() = sharedPreferences.getString("url", "")!!
set(value) = sharedPreferences.edit().putString("url", value).apply() set(value) = sharedPreferences.edit().putString("url", value).apply()
@@ -26,6 +27,9 @@ internal class Settings(context: Context) {
var serverVersion: String var serverVersion: String
get() = sharedPreferences.getString("version", "UNKNOWN")!! get() = sharedPreferences.getString("version", "UNKNOWN")!!
set(value) = sharedPreferences.edit().putString("version", value).apply() set(value) = sharedPreferences.edit().putString("version", value).apply()
var legacyCert: String?
get() = sharedPreferences.getString("cert", null)
set(value) = sharedPreferences.edit().putString("cert", value).apply()
var caCertPath: String? var caCertPath: String?
get() = sharedPreferences.getString("caCertPath", null) get() = sharedPreferences.getString("caCertPath", null)
set(value) = sharedPreferences.edit().putString("caCertPath", value).apply() set(value) = sharedPreferences.edit().putString("caCertPath", value).apply()
@@ -44,6 +48,7 @@ internal class Settings(context: Context) {
init { init {
sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE) sharedPreferences = context.getSharedPreferences("gotify", Context.MODE_PRIVATE)
filesDir = context.filesDir.absolutePath
} }
fun tokenExists(): Boolean = !token.isNullOrEmpty() fun tokenExists(): Boolean = !token.isNullOrEmpty()
@@ -52,6 +57,7 @@ internal class Settings(context: Context) {
url = "" url = ""
token = null token = null
validateSSL = true validateSSL = true
legacyCert = null
caCertPath = null caCertPath = null
caCertCN = null caCertCN = null
clientCertPath = null clientCertPath = null

View File

@@ -7,57 +7,78 @@ import com.github.gotify.client.api.UserApi
import com.github.gotify.client.api.VersionApi import com.github.gotify.client.api.VersionApi
import com.github.gotify.client.auth.ApiKeyAuth import com.github.gotify.client.auth.ApiKeyAuth
import com.github.gotify.client.auth.HttpBasicAuth import com.github.gotify.client.auth.HttpBasicAuth
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import org.tinylog.kotlin.Logger
internal object ClientFactory { internal object ClientFactory {
private fun unauthorized(baseUrl: String, sslSettings: SSLSettings): ApiClient { private fun unauthorized(
return defaultClient(arrayOf(), "$baseUrl/", sslSettings) settings: Settings,
sslSettings: SSLSettings,
baseUrl: String
): ApiClient {
return defaultClient(arrayOf(), settings, sslSettings, baseUrl)
} }
fun basicAuth( fun basicAuth(
baseUrl: String, settings: Settings,
sslSettings: SSLSettings, sslSettings: SSLSettings,
username: String, username: String,
password: String password: String
): ApiClient { ): ApiClient {
val client = defaultClient( val client = defaultClient(arrayOf("basicAuth"), settings, sslSettings)
arrayOf("basicAuth"),
"$baseUrl/",
sslSettings
)
val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth val auth = client.apiAuthorizations["basicAuth"] as HttpBasicAuth
auth.username = username auth.username = username
auth.password = password auth.password = password
return client return client
} }
fun clientToken(baseUrl: String, sslSettings: SSLSettings, token: String?): ApiClient { fun clientToken(settings: Settings, token: String? = settings.token): ApiClient {
val client = defaultClient( val client = defaultClient(arrayOf("clientTokenHeader"), settings)
arrayOf("clientTokenHeader"),
"$baseUrl/",
sslSettings
)
val tokenAuth = client.apiAuthorizations["clientTokenHeader"] as ApiKeyAuth val tokenAuth = client.apiAuthorizations["clientTokenHeader"] as ApiKeyAuth
tokenAuth.apiKey = token tokenAuth.apiKey = token
return client return client
} }
fun versionApi(baseUrl: String, sslSettings: SSLSettings): VersionApi { fun versionApi(
return unauthorized(baseUrl, sslSettings).createService(VersionApi::class.java) settings: Settings,
sslSettings: SSLSettings = settings.sslSettings(),
baseUrl: String = settings.url
): VersionApi {
return unauthorized(settings, sslSettings, baseUrl).createService(VersionApi::class.java)
} }
fun userApiWithToken(settings: Settings): UserApi { fun userApiWithToken(settings: Settings): UserApi {
return clientToken(settings.url, settings.sslSettings(), settings.token) return clientToken(settings).createService(UserApi::class.java)
.createService(UserApi::class.java)
} }
private fun defaultClient( private fun defaultClient(
authentications: Array<String>, authentications: Array<String>,
baseUrl: String, settings: Settings,
sslSettings: SSLSettings sslSettings: SSLSettings = settings.sslSettings(),
baseUrl: String = settings.url
): ApiClient { ): ApiClient {
val client = ApiClient(authentications) val client = ApiClient(authentications)
if (settings.legacyCert != null) {
Logger.info("Migrating legacy CA cert to new location")
var legacyCert: String? = null
try {
legacyCert = settings.legacyCert
settings.legacyCert = null
val caCertFile = File(settings.filesDir, CertUtils.CA_CERT_NAME)
FileOutputStream(caCertFile).use {
it.write(legacyCert?.encodeToByteArray())
}
settings.caCertPath = caCertFile.absolutePath
Logger.info("Migration of legacy CA cert succeeded")
} catch (e: IOException) {
Logger.error(e, "Migration of legacy CA cert failed")
if (legacyCert != null) settings.legacyCert = legacyCert
}
}
CertUtils.applySslSettings(client.okBuilder, sslSettings) CertUtils.applySslSettings(client.okBuilder, sslSettings)
client.adapterBuilder.baseUrl(baseUrl) client.adapterBuilder.baseUrl("$baseUrl/")
return client return client
} }
} }

View File

@@ -167,7 +167,7 @@ internal class InitializationActivity : AppCompatActivity() {
callback: SuccessCallback<VersionInfo>, callback: SuccessCallback<VersionInfo>,
errorCallback: Callback.ErrorCallback errorCallback: Callback.ErrorCallback
) { ) {
ClientFactory.versionApi(settings.url, settings.sslSettings()) ClientFactory.versionApi(settings)
.version .version
.enqueue(Callback.callInUI(this, callback, errorCallback)) .enqueue(Callback.callInUI(this, callback, errorCallback))
} }

View File

@@ -144,7 +144,7 @@ internal class LoginActivity : AppCompatActivity() {
binding.checkurl.visibility = View.GONE binding.checkurl.visibility = View.GONE
try { try {
ClientFactory.versionApi(url, tempSslSettings()) ClientFactory.versionApi(settings, tempSslSettings(), url)
.version .version
.enqueue(Callback.callInUI(this, onValidUrl(url), onInvalidUrl(url))) .enqueue(Callback.callInUI(this, onValidUrl(url), onInvalidUrl(url)))
} catch (e: Exception) { } catch (e: Exception) {
@@ -252,7 +252,7 @@ internal class LoginActivity : AppCompatActivity() {
binding.login.visibility = View.GONE binding.login.visibility = View.GONE
binding.loginProgress.visibility = View.VISIBLE binding.loginProgress.visibility = View.VISIBLE
val client = ClientFactory.basicAuth(settings.url, tempSslSettings(), username, password) val client = ClientFactory.basicAuth(settings, tempSslSettings(), username, password)
client.createService(UserApi::class.java) client.createService(UserApi::class.java)
.currentUser() .currentUser()
.enqueue( .enqueue(

View File

@@ -540,7 +540,7 @@ internal class MessagesActivity :
private fun deleteApp(appId: Long) { private fun deleteApp(appId: Long) {
val settings = viewModel.settings val settings = viewModel.settings
val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) val client = ClientFactory.clientToken(settings)
client.createService(ApplicationApi::class.java) client.createService(ApplicationApi::class.java)
.deleteApp(appId) .deleteApp(appId)
.enqueue( .enqueue(
@@ -597,8 +597,7 @@ internal class MessagesActivity :
private fun deleteClientAndNavigateToLogin() { private fun deleteClientAndNavigateToLogin() {
val settings = viewModel.settings val settings = viewModel.settings
val api = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) val api = ClientFactory.clientToken(settings).createService(ClientApi::class.java)
.createService(ClientApi::class.java)
stopService(Intent(this@MessagesActivity, WebSocketService::class.java)) stopService(Intent(this@MessagesActivity, WebSocketService::class.java))
try { try {
val clients = Api.execute(api.clients) val clients = Api.execute(api.clients)

View File

@@ -14,7 +14,7 @@ import com.squareup.picasso.Target
internal class MessagesModel(parentView: Activity) : ViewModel() { internal class MessagesModel(parentView: Activity) : ViewModel() {
val settings = Settings(parentView) val settings = Settings(parentView)
val picassoHandler = PicassoHandler(parentView, settings) val picassoHandler = PicassoHandler(parentView, settings)
val client = ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) val client = ClientFactory.clientToken(settings)
val appsHolder = ApplicationHolder(parentView, client) val appsHolder = ApplicationHolder(parentView, client)
val messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder) val messages = MessageFacade(client.createService(MessageApi::class.java), appsHolder)

View File

@@ -68,11 +68,7 @@ internal class WebSocketService : Service() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
settings = Settings(this) settings = Settings(this)
val client = ClientFactory.clientToken( val client = ClientFactory.clientToken(settings)
settings.url,
settings.sslSettings(),
settings.token
)
missingMessageUtil = MissedMessageUtil(client.createService(MessageApi::class.java)) missingMessageUtil = MissedMessageUtil(client.createService(MessageApi::class.java))
Logger.info("Create ${javaClass.simpleName}") Logger.info("Create ${javaClass.simpleName}")
picassoHandler = PicassoHandler(this, settings) picassoHandler = PicassoHandler(this, settings)
@@ -129,7 +125,7 @@ internal class WebSocketService : Service() {
} }
private fun fetchApps() { private fun fetchApps() {
ClientFactory.clientToken(settings.url, settings.sslSettings(), settings.token) ClientFactory.clientToken(settings)
.createService(ApplicationApi::class.java) .createService(ApplicationApi::class.java)
.apps .apps
.enqueue( .enqueue(

View File

@@ -61,11 +61,7 @@ internal class ShareActivity : AppCompatActivity() {
return return
} }
val client = ClientFactory.clientToken( val client = ClientFactory.clientToken(settings)
settings.url,
settings.sslSettings(),
settings.token
)
appsHolder = ApplicationHolder(this, client) appsHolder = ApplicationHolder(this, client)
appsHolder.onUpdate { appsHolder.onUpdate {
val apps = appsHolder.get() val apps = appsHolder.get()
@@ -136,11 +132,7 @@ internal class ShareActivity : AppCompatActivity() {
} }
private fun executeMessageCall(appIndex: Int, message: Message): Boolean { private fun executeMessageCall(appIndex: Int, message: Message): Boolean {
val pushClient = ClientFactory.clientToken( val pushClient = ClientFactory.clientToken(settings, appsHolder.get()[appIndex].token)
settings.url,
settings.sslSettings(),
appsHolder.get()[appIndex].token
)
return try { return try {
val messageApi = pushClient.createService(MessageApi::class.java) val messageApi = pushClient.createService(MessageApi::class.java)
Api.execute(messageApi.createMessage(message)) Api.execute(messageApi.createMessage(message))