Add support for custom certificates (#3203)
* Add support for custom certificates * Update lints * Update licenses * Changes after feedback * More changes
This commit is contained in:
parent
1957566c39
commit
9e3a34f17f
@ -174,6 +174,7 @@ Escape0707 <tothesong@gmail.com>
|
|||||||
Loudwig <https://github.com/Loudwig>
|
Loudwig <https://github.com/Loudwig>
|
||||||
Wu Yi-Wei <https://github.com/Ianwu0812>
|
Wu Yi-Wei <https://github.com/Ianwu0812>
|
||||||
|
|
||||||
|
|
||||||
********************
|
********************
|
||||||
|
|
||||||
The text of the 3 clause BSD license follows:
|
The text of the 3 clause BSD license follows:
|
||||||
|
23
Cargo.lock
generated
23
Cargo.lock
generated
@ -144,6 +144,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rusqlite",
|
"rusqlite",
|
||||||
|
"rustls-pemfile 2.1.2",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-aux",
|
"serde-aux",
|
||||||
@ -511,6 +512,12 @@ version = "0.21.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.22.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
@ -4605,7 +4612,7 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rustls 0.21.11",
|
"rustls 0.21.11",
|
||||||
"rustls-native-certs",
|
"rustls-native-certs",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile 1.0.4",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
@ -4786,7 +4793,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"openssl-probe",
|
"openssl-probe",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile 1.0.4",
|
||||||
"schannel",
|
"schannel",
|
||||||
"security-framework",
|
"security-framework",
|
||||||
]
|
]
|
||||||
@ -4800,6 +4807,16 @@ dependencies = [
|
|||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@ -6265,7 +6282,7 @@ dependencies = [
|
|||||||
"mime_guess",
|
"mime_guess",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile 1.0.4",
|
||||||
"scoped-tls",
|
"scoped-tls",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -111,6 +111,7 @@ rand = "0.8.5"
|
|||||||
regex = "1.10.3"
|
regex = "1.10.3"
|
||||||
reqwest = { version = "0.11.24", default-features = false, features = ["json", "socks", "stream", "multipart"] }
|
reqwest = { version = "0.11.24", default-features = false, features = ["json", "socks", "stream", "multipart"] }
|
||||||
rusqlite = { version = "0.30.0", features = ["trace", "functions", "collation", "bundled"] }
|
rusqlite = { version = "0.30.0", features = ["trace", "functions", "collation", "bundled"] }
|
||||||
|
rustls-pemfile = "2.1.2"
|
||||||
scopeguard = "1.2.0"
|
scopeguard = "1.2.0"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
serde-aux = "4.5.0"
|
serde-aux = "4.5.0"
|
||||||
|
@ -287,6 +287,15 @@
|
|||||||
"license_file": null,
|
"license_file": null,
|
||||||
"description": "encodes and decodes base64 as bytes or utf8"
|
"description": "encodes and decodes base64 as bytes or utf8"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "base64",
|
||||||
|
"version": "0.22.1",
|
||||||
|
"authors": "Marshall Pierce <marshall@mpierce.org>",
|
||||||
|
"repository": "https://github.com/marshallpierce/rust-base64",
|
||||||
|
"license": "Apache-2.0 OR MIT",
|
||||||
|
"license_file": null,
|
||||||
|
"description": "encodes and decodes base64 as bytes or utf8"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "base64ct",
|
"name": "base64ct",
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
@ -3257,6 +3266,15 @@
|
|||||||
"license_file": null,
|
"license_file": null,
|
||||||
"description": "Basic .pem file parser for keys and certificates"
|
"description": "Basic .pem file parser for keys and certificates"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "rustls-pemfile",
|
||||||
|
"version": "2.1.2",
|
||||||
|
"authors": null,
|
||||||
|
"repository": "https://github.com/rustls/pemfile",
|
||||||
|
"license": "Apache-2.0 OR ISC OR MIT",
|
||||||
|
"license_file": null,
|
||||||
|
"description": "Basic .pem file parser for keys and certificates"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "rustls-pki-types",
|
"name": "rustls-pki-types",
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
|
@ -45,6 +45,7 @@ message BackendError {
|
|||||||
// Originated from and usually specific to the OS.
|
// Originated from and usually specific to the OS.
|
||||||
OS_ERROR = 20;
|
OS_ERROR = 20;
|
||||||
SCHEDULER_UPGRADE_REQUIRED = 21;
|
SCHEDULER_UPGRADE_REQUIRED = 21;
|
||||||
|
INVALID_CERTIFICATE_FORMAT = 22;
|
||||||
}
|
}
|
||||||
|
|
||||||
// error description, usually localized, suitable for displaying to the user
|
// error description, usually localized, suitable for displaying to the user
|
||||||
|
@ -23,6 +23,7 @@ service BackendSyncService {
|
|||||||
rpc SyncCollection(SyncCollectionRequest) returns (SyncCollectionResponse);
|
rpc SyncCollection(SyncCollectionRequest) returns (SyncCollectionResponse);
|
||||||
rpc FullUploadOrDownload(FullUploadOrDownloadRequest) returns (generic.Empty);
|
rpc FullUploadOrDownload(FullUploadOrDownloadRequest) returns (generic.Empty);
|
||||||
rpc AbortSync(generic.Empty) returns (generic.Empty);
|
rpc AbortSync(generic.Empty) returns (generic.Empty);
|
||||||
|
rpc SetCustomCertificate(generic.String) returns (generic.Bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
message SyncAuth {
|
message SyncAuth {
|
||||||
|
@ -83,6 +83,7 @@ rand.workspace = true
|
|||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
rusqlite.workspace = true
|
rusqlite.workspace = true
|
||||||
|
rustls-pemfile.workspace = true
|
||||||
scopeguard.workspace = true
|
scopeguard.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde-aux.workspace = true
|
serde-aux.workspace = true
|
||||||
|
@ -49,6 +49,7 @@ impl AnkiError {
|
|||||||
AnkiError::WindowsError { .. } => Kind::OsError,
|
AnkiError::WindowsError { .. } => Kind::OsError,
|
||||||
AnkiError::SchedulerUpgradeRequired => Kind::SchedulerUpgradeRequired,
|
AnkiError::SchedulerUpgradeRequired => Kind::SchedulerUpgradeRequired,
|
||||||
AnkiError::FsrsInsufficientReviews { .. } => Kind::InvalidInput,
|
AnkiError::FsrsInsufficientReviews { .. } => Kind::InvalidInput,
|
||||||
|
AnkiError::InvalidCertificateFormat => Kind::InvalidCertificateFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
anki_proto::backend::BackendError {
|
anki_proto::backend::BackendError {
|
||||||
|
@ -56,7 +56,7 @@ pub struct BackendInner {
|
|||||||
state: Mutex<BackendState>,
|
state: Mutex<BackendState>,
|
||||||
backup_task: Mutex<Option<JoinHandle<Result<()>>>>,
|
backup_task: Mutex<Option<JoinHandle<Result<()>>>>,
|
||||||
media_sync_task: Mutex<Option<JoinHandle<Result<()>>>>,
|
media_sync_task: Mutex<Option<JoinHandle<Result<()>>>>,
|
||||||
web_client: OnceCell<Client>,
|
web_client: Mutex<Option<Client>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -91,7 +91,7 @@ impl Backend {
|
|||||||
state: Mutex::new(BackendState::default()),
|
state: Mutex::new(BackendState::default()),
|
||||||
backup_task: Mutex::new(None),
|
backup_task: Mutex::new(None),
|
||||||
media_sync_task: Mutex::new(None),
|
media_sync_task: Mutex::new(None),
|
||||||
web_client: OnceCell::new(),
|
web_client: Mutex::new(None),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,10 +137,46 @@ impl Backend {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn web_client(&self) -> &Client {
|
#[cfg(feature = "rustls")]
|
||||||
|
fn set_custom_certificate_inner(&self, cert_str: String) -> Result<()> {
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::io::Read;
|
||||||
|
|
||||||
|
use reqwest::Certificate;
|
||||||
|
|
||||||
|
let mut web_client = self.web_client.lock().unwrap();
|
||||||
|
|
||||||
|
if cert_str.is_empty() {
|
||||||
|
let _ = web_client.insert(Client::builder().http1_only().build().unwrap());
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if rustls_pemfile::read_all(Cursor::new(cert_str.as_bytes()).by_ref()).count() != 1 {
|
||||||
|
return Err(AnkiError::InvalidCertificateFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(certificate) = Certificate::from_pem(cert_str.as_bytes()) {
|
||||||
|
if let Ok(new_client) = Client::builder()
|
||||||
|
.use_rustls_tls()
|
||||||
|
.add_root_certificate(certificate)
|
||||||
|
.http1_only()
|
||||||
|
.build()
|
||||||
|
{
|
||||||
|
let _ = web_client.insert(new_client);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(AnkiError::InvalidCertificateFormat)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn web_client(&self) -> Client {
|
||||||
// currently limited to http1, as nginx doesn't support http2 proxies
|
// currently limited to http1, as nginx doesn't support http2 proxies
|
||||||
self.web_client
|
let mut web_client = self.web_client.lock().unwrap();
|
||||||
.get_or_init(|| Client::builder().http1_only().build().unwrap())
|
|
||||||
|
return web_client
|
||||||
|
.get_or_insert_with(|| Client::builder().http1_only().build().unwrap())
|
||||||
|
.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn db_command(&self, input: &[u8]) -> Result<Vec<u8>> {
|
fn db_command(&self, input: &[u8]) -> Result<Vec<u8>> {
|
||||||
|
@ -154,6 +154,16 @@ impl crate::services::BackendSyncService for Backend {
|
|||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_custom_certificate(
|
||||||
|
&self,
|
||||||
|
_input: anki_proto::generic::String,
|
||||||
|
) -> Result<anki_proto::generic::Bool> {
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
return Ok(self.set_custom_certificate_inner(_input.val).is_ok().into());
|
||||||
|
#[cfg(not(feature = "rustls"))]
|
||||||
|
return Ok(false.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Backend {
|
impl Backend {
|
||||||
@ -284,7 +294,7 @@ impl Backend {
|
|||||||
input.username,
|
input.username,
|
||||||
input.password,
|
input.password,
|
||||||
input.endpoint.clone(),
|
input.endpoint.clone(),
|
||||||
self.web_client().clone(),
|
self.web_client(),
|
||||||
);
|
);
|
||||||
let abortable_sync = Abortable::new(sync_fut, abort_reg);
|
let abortable_sync = Abortable::new(sync_fut, abort_reg);
|
||||||
let ret = match rt.block_on(abortable_sync) {
|
let ret = match rt.block_on(abortable_sync) {
|
||||||
@ -323,7 +333,7 @@ impl Backend {
|
|||||||
let rt = self.runtime_handle();
|
let rt = self.runtime_handle();
|
||||||
let time_at_check_begin = TimestampSecs::now();
|
let time_at_check_begin = TimestampSecs::now();
|
||||||
let local = self.with_col(|col| col.sync_meta())?;
|
let local = self.with_col(|col| col.sync_meta())?;
|
||||||
let mut client = HttpSyncClient::new(auth, self.web_client().clone());
|
let mut client = HttpSyncClient::new(auth, self.web_client());
|
||||||
let state = rt.block_on(online_sync_status_check(local, &mut client))?;
|
let state = rt.block_on(online_sync_status_check(local, &mut client))?;
|
||||||
{
|
{
|
||||||
let mut guard = self.state.lock().unwrap();
|
let mut guard = self.state.lock().unwrap();
|
||||||
@ -348,7 +358,7 @@ impl Backend {
|
|||||||
let (_guard, abort_reg) = self.sync_abort_handle()?;
|
let (_guard, abort_reg) = self.sync_abort_handle()?;
|
||||||
|
|
||||||
let rt = self.runtime_handle();
|
let rt = self.runtime_handle();
|
||||||
let client = self.web_client().clone();
|
let client = self.web_client();
|
||||||
let auth2 = auth.clone();
|
let auth2 = auth.clone();
|
||||||
|
|
||||||
let ret = self.with_col(|col| {
|
let ret = self.with_col(|col| {
|
||||||
|
@ -122,6 +122,7 @@ pub enum AnkiError {
|
|||||||
},
|
},
|
||||||
FsrsUnableToDetermineDesiredRetention,
|
FsrsUnableToDetermineDesiredRetention,
|
||||||
SchedulerUpgradeRequired,
|
SchedulerUpgradeRequired,
|
||||||
|
InvalidCertificateFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
// error helpers
|
// error helpers
|
||||||
@ -169,7 +170,8 @@ impl AnkiError {
|
|||||||
| AnkiError::Existing
|
| AnkiError::Existing
|
||||||
| AnkiError::InvalidServiceIndex
|
| AnkiError::InvalidServiceIndex
|
||||||
| AnkiError::InvalidMethodIndex
|
| AnkiError::InvalidMethodIndex
|
||||||
| AnkiError::UndoEmpty => format!("{:?}", self),
|
| AnkiError::UndoEmpty
|
||||||
|
| AnkiError::InvalidCertificateFormat => format!("{:?}", self),
|
||||||
AnkiError::FileIoError { source } => source.message(),
|
AnkiError::FileIoError { source } => source.message(),
|
||||||
AnkiError::InvalidInput { source } => source.message(),
|
AnkiError::InvalidInput { source } => source.message(),
|
||||||
AnkiError::NotFound { source } => source.message(tr),
|
AnkiError::NotFound { source } => source.message(tr),
|
||||||
|
Loading…
Reference in New Issue
Block a user