Skip to content

Commit 263cb51

Browse files
authored
add --ca-cert, --insecure, and --timeout flags (#31)
1 parent ad9ceae commit 263cb51

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

src/api.rs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use crate::privatebin::{Comment, DecryptedComment, Paste, PostCommentResponse, P
55
use crate::util::check_filesize;
66
use crate::DecryptedPaste;
77
use rand_chacha::rand_core::{RngCore, SeedableRng};
8+
use reqwest::tls::Certificate;
89
use reqwest::{Method, Url};
910
use scraper::{Html, Selector};
1011
use std::str::FromStr;
12+
use std::time::Duration;
1113

1214
#[cfg_attr(feature = "uniffi", derive(uniffi::Object))]
1315
pub struct API {
@@ -29,6 +31,34 @@ impl API {
2931
}
3032

3133
impl API {
34+
fn build_client(&self) -> PbResult<reqwest::blocking::Client> {
35+
let mut builder = reqwest::blocking::Client::builder();
36+
37+
let timeout_secs = self.opts.timeout.unwrap_or(30);
38+
builder = builder
39+
.connect_timeout(Duration::from_secs(timeout_secs))
40+
.timeout(Duration::from_secs(timeout_secs * 4));
41+
42+
if self.opts.insecure {
43+
builder = builder.danger_accept_invalid_certs(true);
44+
}
45+
46+
if let Some(ref ca_path) = self.opts.ca_cert {
47+
let pem = std::fs::read(ca_path).map_err(|e| {
48+
PbError::InvalidCertificate(format!(
49+
"failed to read CA cert {}: {}",
50+
ca_path.display(),
51+
e
52+
))
53+
})?;
54+
for cert in pem_certs_from_bundle(&pem)? {
55+
builder = builder.add_root_certificate(cert);
56+
}
57+
}
58+
59+
Ok(builder.build()?)
60+
}
61+
3262
fn get_oidc_access_token(&self) -> PbResult<String> {
3363
let oidc_token_endpoint = self.opts.oidc_token_url.as_ref().unwrap();
3464
let oidc_client_id = self.opts.oidc_client_id.as_ref().unwrap();
@@ -41,7 +71,7 @@ impl API {
4171
post_fields.insert("username", oidc_username);
4272
post_fields.insert("password", oidc_password);
4373

44-
let client = reqwest::blocking::Client::builder().build()?;
74+
let client = self.build_client()?;
4575
let mut request = client.post(oidc_token_endpoint);
4676
request = request.form(&post_fields);
4777

@@ -78,7 +108,7 @@ impl API {
78108
url: Url,
79109
json_request: bool,
80110
) -> PbResult<reqwest::blocking::RequestBuilder> {
81-
let client = reqwest::blocking::Client::builder().build()?;
111+
let client = self.build_client()?;
82112

83113
let mut request = client.request(Method::from_str(method).unwrap(), url);
84114
if json_request {
@@ -95,6 +125,35 @@ impl API {
95125
}
96126
}
97127

128+
fn pem_certs_from_bundle(pem: &[u8]) -> PbResult<Vec<Certificate>> {
129+
let pem_str = std::str::from_utf8(pem)
130+
.map_err(|e| PbError::InvalidCertificate(format!("CA cert is not valid UTF-8: {}", e)))?;
131+
let mut certs = Vec::new();
132+
let mut current = String::new();
133+
let mut in_cert = false;
134+
for line in pem_str.lines() {
135+
if line.contains("BEGIN CERTIFICATE") {
136+
in_cert = true;
137+
current.clear();
138+
current.push_str(line);
139+
current.push('\n');
140+
} else if line.contains("END CERTIFICATE") {
141+
current.push_str(line);
142+
current.push('\n');
143+
certs.push(
144+
Certificate::from_pem(current.as_bytes()).map_err(|e| {
145+
PbError::InvalidCertificate(format!("invalid certificate in bundle: {}", e))
146+
})?,
147+
);
148+
in_cert = false;
149+
} else if in_cert {
150+
current.push_str(line);
151+
current.push('\n');
152+
}
153+
}
154+
Ok(certs)
155+
}
156+
98157
#[cfg_attr(feature = "uniffi", uniffi::export)]
99158
impl API {
100159
pub fn get_paste(&self, paste_id: &str) -> PbResult<Paste> {

src/error.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub enum PasteError {
3737
InvalidTokenType(String),
3838
OidcBadRequest(serde_json::Value),
3939
LoggerInit(log::SetLoggerError),
40+
InvalidCertificate(String),
4041
}
4142

4243
impl std::error::Error for PasteError {}
@@ -76,6 +77,7 @@ impl fmt::Display for PasteError {
7677
PasteError::LoggerInit(err) => {
7778
write!(f, "Failed to init logger: {}", err)
7879
}
80+
PasteError::InvalidCertificate(msg) => write!(f, "{}", msg),
7981
}
8082
}
8183
}

src/opts.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ pub struct Opts {
108108
#[clap(help("password to send to the token endpoint"))]
109109
pub oidc_password: Option<String>,
110110

111+
#[cfg_attr(feature = "uniffi", uniffi(default = None))]
112+
#[clap(long, value_name = "FILE")]
113+
#[clap(help("path to a PEM CA certificate bundle for TLS verification"))]
114+
pub ca_cert: Option<std::path::PathBuf>,
115+
116+
#[cfg_attr(feature = "uniffi", uniffi(default = false))]
117+
#[clap(long)]
118+
#[clap(help("accept invalid TLS certificates (insecure)"))]
119+
pub insecure: bool,
120+
121+
#[cfg_attr(feature = "uniffi", uniffi(default = None))]
122+
#[clap(long, value_name = "SECONDS")]
123+
#[clap(help("connection timeout in seconds (default: 30)"))]
124+
pub timeout: Option<u64>,
125+
111126
#[cfg_attr(feature = "uniffi", uniffi(default = false))]
112127
#[clap(long)]
113128
#[clap(help("print debug output to stderr"))]

0 commit comments

Comments
 (0)