use std::sync::Arc;
use anodizer_core::config::GitHubUrlsConfig;
use anyhow::{Context as _, Result};
use http::header::HeaderValue;
use octocrab::service::middleware::auth_header::AuthHeaderLayer;
use octocrab::service::middleware::base_uri::BaseUriLayer;
use octocrab::service::middleware::extra_headers::ExtraHeadersLayer;
use crate::release_log;
pub(crate) fn build_octocrab_client(
token: &str,
github_urls: &Option<GitHubUrlsConfig>,
) -> Result<octocrab::Octocrab> {
let skip_tls = github_urls
.as_ref()
.and_then(|u| u.skip_tls_verify)
.unwrap_or(false);
if skip_tls {
build_octocrab_client_insecure(token, github_urls)
} else {
let mut builder = octocrab::Octocrab::builder()
.personal_token(token.to_owned())
.set_connect_timeout(Some(std::time::Duration::from_secs(30)))
.set_read_timeout(Some(std::time::Duration::from_secs(120)))
.set_write_timeout(Some(std::time::Duration::from_secs(120)));
if let Some(urls) = github_urls {
if let Some(api) = &urls.api {
builder = builder
.base_uri(api.as_str())
.context("release: invalid github_urls.api URL")?;
}
if let Some(upload) = &urls.upload {
builder = builder
.upload_uri(upload.as_str())
.context("release: invalid github_urls.upload URL")?;
}
}
builder.build().context("release: build octocrab client")
}
}
fn build_octocrab_client_insecure(
token: &str,
github_urls: &Option<GitHubUrlsConfig>,
) -> Result<octocrab::Octocrab> {
release_log().warn("TLS certificate verification disabled for GitHub API — this is insecure");
let crypto_provider = rustls::crypto::ring::default_provider();
let tls_config = rustls::ClientConfig::builder_with_provider(Arc::new(crypto_provider))
.with_safe_default_protocol_versions()
.context("release: configure TLS protocol versions")?
.dangerous()
.with_custom_certificate_verifier(Arc::new(DangerousNoCertVerifier::new()))
.with_no_client_auth();
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls_config)
.https_or_http()
.enable_http1()
.build();
let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.build(connector);
let base_uri: http::Uri = if let Some(api) = github_urls.as_ref().and_then(|u| u.api.as_ref()) {
api.parse()
.context("release: invalid github_urls.api URL")?
} else {
"https://api.github.com"
.parse()
.unwrap_or_else(|e| panic!("hardcoded URI is valid: {e}"))
};
let upload_uri: http::Uri =
if let Some(upload) = github_urls.as_ref().and_then(|u| u.upload.as_ref()) {
upload
.parse()
.context("release: invalid github_urls.upload URL")?
} else {
"https://uploads.github.com"
.parse()
.unwrap_or_else(|e| panic!("hardcoded URI is valid: {e}"))
};
let auth_header: HeaderValue = format!("Bearer {}", token)
.parse()
.context("release: format auth header")?;
octocrab::OctocrabBuilder::new_empty()
.with_service(client)
.with_layer(&ExtraHeadersLayer::new(Arc::new(vec![(
http::header::USER_AGENT,
HeaderValue::from_static("octocrab"),
)])))
.with_layer(&BaseUriLayer::new(base_uri.clone()))
.with_layer(&AuthHeaderLayer::new(
Some(auth_header),
base_uri,
upload_uri,
))
.with_auth(octocrab::AuthState::None)
.build()
.map_err(|e| match e {}) }
#[derive(Debug)]
struct DangerousNoCertVerifier {
schemes: Vec<rustls::SignatureScheme>,
}
impl DangerousNoCertVerifier {
fn new() -> Self {
Self {
schemes: rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes(),
}
}
}
impl rustls::client::danger::ServerCertVerifier for DangerousNoCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> std::result::Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &rustls::pki_types::CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> std::result::Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.schemes.clone()
}
}