use std::{env, path::PathBuf, str::FromStr, time::Duration};
use crate::{
commons::util::file,
constants::KRILL_HTTPS_ROOT_CERTS_ENV,
constants::{test_mode_enabled, OPENID_CONNECT_HTTP_CLIENT_TIMEOUT_SECS},
};
use crate::commons::error::Error;
use crate::commons::util::httpclient;
fn load_root_cert(path: &str) -> Result<reqwestblocking::Certificate, httpclient::Error> {
let path = PathBuf::from_str(path).map_err(httpclient::Error::https_root_cert_error)?;
let file = file::read(&path).map_err(httpclient::Error::https_root_cert_error)?;
reqwestblocking::Certificate::from_pem(file.as_ref()).map_err(httpclient::Error::https_root_cert_error)
}
fn openid_connect_provider_timeout() -> Duration {
if test_mode_enabled() {
Duration::from_secs(5)
} else {
Duration::from_secs(OPENID_CONNECT_HTTP_CLIENT_TIMEOUT_SECS)
}
}
fn configure_http_client_for_krill(
mut builder: reqwestblocking::ClientBuilder,
uri: &str,
) -> Result<reqwestblocking::ClientBuilder, httpclient::Error> {
builder = builder.timeout(openid_connect_provider_timeout());
if let Ok(cert_list) = env::var(KRILL_HTTPS_ROOT_CERTS_ENV) {
for path in cert_list.split(':') {
let cert = load_root_cert(path)?;
builder = builder.add_root_certificate(cert);
}
}
if uri.starts_with("https://localhost") || uri.starts_with("https://127.0.0.1") {
builder = builder.danger_accept_invalid_certs(true);
}
Ok(builder)
}
fn http_client(request: openidconnect::HttpRequest) -> Result<openidconnect::HttpResponse, Error> {
let mut client_builder = reqwestblocking::Client::builder()
.redirect(reqwestblocking::RedirectPolicy::none());
client_builder = configure_http_client_for_krill(client_builder, request.url.as_str())
.map_err(|err| Error::custom(format!("Failed to configure HTTP client: {}", err)))?;
let client = client_builder.build().map_err(Error::custom)?;
let mut request_builder = client
.request(
reqwestblocking::Method::from_bytes(request.method.as_str().as_ref())
.expect("failed to convert Method from http 0.2 to 0.1"),
request.url.as_str(),
)
.body(request.body);
for (name, value) in &request.headers {
request_builder = request_builder.header(name.as_str(), value.as_bytes());
}
let request = request_builder.build().map_err(Error::custom)?;
let mut response = client.execute(request).map_err(Error::custom)?;
let mut body = Vec::new();
{
use std::io::Read;
response.read_to_end(&mut body).map_err(Error::custom)?;
}
let headers = response
.headers()
.iter()
.map(|(name, value)| {
(
openidconnect::http::header::HeaderName::from_bytes(name.as_str().as_ref())
.expect("failed to convert HeaderName from http 0.2 to 0.1"),
openidconnect::http::header::HeaderValue::from_bytes(value.as_bytes())
.expect("failed to convert HeaderValue from http 0.2 to 0.1"),
)
})
.collect::<openidconnect::http::HeaderMap>();
Ok(openidconnect::HttpResponse {
status_code: openidconnect::http::StatusCode::from_u16(response.status().as_u16())
.expect("failed to convert StatusCode from http 0.2 to 0.1"),
headers,
body,
})
}
pub fn logging_http_client(req: openidconnect::HttpRequest) -> Result<openidconnect::HttpResponse, Error> {
if log_enabled!(log::Level::Trace) {
let body = match std::str::from_utf8(&req.body) {
Ok(text) => text.to_string(),
Err(_) => format!("{:?}", &req.body),
};
debug!(
"OpenID Connect request: url: {:?}, method: {:?}, headers: {:?}, body: {}",
req.url, req.method, req.headers, body
);
}
let res = http_client(req);
if log_enabled!(log::Level::Trace) {
match &res {
Ok(res) => {
let body = match std::str::from_utf8(&res.body) {
Ok(text) => text.to_string(),
Err(_) => format!("{:?}", &res.body),
};
debug!(
"OpenID Connect response: status_code: {:?}, headers: {:?}, body: {}",
res.status_code, res.headers, body
);
}
Err(err) => {
debug!("OpenID Connect response: {:?}", err)
}
}
}
res
}