Skip to content

Commit

Permalink
Add support for custom certificate
Browse files Browse the repository at this point in the history
  • Loading branch information
voczi authored and mikehardy committed Jun 27, 2024
1 parent b064f49 commit edd15ee
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 1 deletion.
25 changes: 25 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/CollectionManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ object CollectionManager {

private val testMutex = ReentrantLock()

private var currentSyncCertificate: String = ""

/**
* Execute the provided block on a serial background queue, to ensure
* concurrent access does not happen.
Expand Down Expand Up @@ -400,4 +402,27 @@ object CollectionManager {
// as it does not seem to be compatible with the test scheduler
queue = dispatcher
}

/**
* Update the custom TLS certificate used in the backend for its requests to the sync server.
*
* If the cert parameter hasn't changed from the cached sync certificate, then just return true.
* Otherwise, set the custom certificate in the backend and get the success value.
*
* If cert was a valid certificate, then cache it in currentSyncCertificate and return true.
* Otherwise, return false to indicate that a custom sync certificate was not applied.
*
* Passing in an empty string unsets any custom sync certificate in the backend.
*/
fun updateCustomCertificate(cert: String): Boolean {
if (cert == currentSyncCertificate) {
return true
}

return getBackend().setCustomCertificate(cert).apply {
if (this) {
currentSyncCertificate = cert
}
}
}
}
6 changes: 6 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/Sync.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ object SyncPreferences {
const val CURRENT_SYNC_URI = "currentSyncUri"
const val CUSTOM_SYNC_URI = "syncBaseUrl"
const val CUSTOM_SYNC_ENABLED = CUSTOM_SYNC_URI + VersatileTextWithASwitchPreference.SWITCH_SUFFIX
const val CUSTOM_SYNC_CERTIFICATE = "customSyncCertificate"

// Used in the legacy schema path
const val HOSTNUM = "hostNum"
Expand All @@ -79,6 +80,11 @@ interface SyncCompletionListener {

fun DeckPicker.syncAuth(): SyncAuth? {
val preferences = this.sharedPrefs()

// Grab custom sync certificate from preferences (default is the empty string) and set it in CollectionManager
val currentSyncCertificate = preferences.getString(SyncPreferences.CUSTOM_SYNC_CERTIFICATE, "") ?: ""
CollectionManager.updateCustomCertificate(currentSyncCertificate)

val hkey = preferences.getString(SyncPreferences.HKEY, null)
val resolvedEndpoint = getEndpoint(this)
return hkey?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ package com.ichi2.anki.preferences

import android.content.SharedPreferences
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.R
import com.ichi2.anki.SyncPreferences
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.preferences.VersatileTextPreference
import com.ichi2.utils.show
import okhttp3.HttpUrl.Companion.toHttpUrl

class CustomSyncServerSettingsFragment : SettingsFragment() {
Expand All @@ -36,6 +40,23 @@ class CustomSyncServerSettingsFragment : SettingsFragment() {
if (value.isNotEmpty()) value.toHttpUrl()
}
}

requirePreference<VersatileTextPreference>(R.string.custom_sync_certificate_key).setOnPreferenceChangeListener { _, newValue: Any? ->
val newCert = newValue as String

// Empty string input causes the certificate to be unset in the backend, i.e., no error
if (!CollectionManager.updateCustomCertificate(newCert)) {
AlertDialog.Builder(requireContext()).show {
setTitle(R.string.dialog_invalid_custom_certificate_title)
setMessage(R.string.dialog_invalid_custom_certificate)
setPositiveButton(R.string.dialog_ok) { _, _ -> }
}
return@setOnPreferenceChangeListener false
}

showSnackbar(R.string.dialog_updated_custom_certificate)
true
}
}

// See discussion at https://github.com/ankidroid/Anki-Android/pull/12367#discussion_r967681337
Expand Down
3 changes: 3 additions & 0 deletions AnkiDroid/src/main/res/values/03-dialogs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
<string name="dialog_positive_replace">Replace</string>

<string name="dialog_collection_path_not_dir">The path specified wasn’t a valid directory</string>
<string name="dialog_invalid_custom_certificate">The provided text does not resolve to a valid PEM-encoded X509 certificate</string>
<string name="dialog_invalid_custom_certificate_title">Error parsing certificate</string>
<string name="dialog_updated_custom_certificate">Certificate updated</string>

<!-- Media checking -->
<string name="check_media_title">Check media?</string>
Expand Down
1 change: 1 addition & 0 deletions AnkiDroid/src/main/res/values/10-preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@
<a href="%s">Learn more</a>
]]></string>
<string name="custom_sync_server_base_url_title" maxLength="41">Sync url</string>
<string name="custom_sync_certificate_title" maxLength="41">Custom root certificate (PEM)</string>
<!-- Key Bindings -->
<string name="keyboard">Keyboard</string>
<string name="bluetooth">Bluetooth</string>
Expand Down
1 change: 1 addition & 0 deletions AnkiDroid/src/main/res/values/preferences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
<!-- Custom sync server -->
<string name="pref_custom_sync_server_screen_key">customSyncServerScreen</string>
<string name="custom_sync_server_collection_url_key">syncBaseUrl</string>
<string name="custom_sync_certificate_key">customSyncCertificate</string>
<!-- Advanced -->
<string name="pref_advanced_screen_key">pref_screen_advanced</string>
<string name="pref_cat_workarounds_key">category_workarounds</string>
Expand Down
4 changes: 4 additions & 0 deletions AnkiDroid/src/main/res/xml/preferences_custom_sync_server.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@
android:title="@string/custom_sync_server_base_url_title"
android:inputType="textUri"
app:useSimpleSummaryProvider="true" />
<com.ichi2.preferences.VersatileTextPreference
android:key="@string/custom_sync_certificate_key"
android:title="@string/custom_sync_certificate_title"
android:inputType="textMultiLine" />
</PreferenceScreen>
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ class PreferencesAnalyticsTest : RobolectricTest() {
// potential personal data
"syncAccount",
"syncBaseUrl",
"language"
"language",
"customSyncCertificate"
)

@Test
Expand Down

0 comments on commit edd15ee

Please sign in to comment.