#![cfg(all(feature = "server", feature = "client"))]
use std::sync::Arc;
use est_ca::auth::{AuthBackend, Principal};
use est_ca::ca::serial::InMemorySerialStore;
use est_ca::ca::{CertProfile, Issuer};
use est_ca::est::server::EstServer;
use est_ca::est::{CONTENT_TYPE_PKCS10, WELL_KNOWN_PREFIX};
struct StrictAuth;
impl AuthBackend for StrictAuth {
fn verify_bootstrap(&self, user: &str, pass: &str) -> Result<Principal, String> {
if user == "good-user" && pass == "right" {
Ok(Principal::new(user))
} else {
Err("bad credential".into())
}
}
}
async fn spawn() -> String {
let issuer = Issuer::generate_self_signed(
"error-paths test CA",
CertProfile::client_auth_for_days(7),
Arc::new(InMemorySerialStore::new()),
)
.unwrap();
let app = EstServer::new(issuer, Arc::new(StrictAuth)).router();
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
tokio::spawn(async move {
axum::Server::from_tcp(listener).unwrap().serve(app.into_make_service()).await.unwrap();
});
format!("http://{addr}")
}
fn url(base: &str, endpoint: &str) -> String {
format!("{base}{WELL_KNOWN_PREFIX}{endpoint}")
}
#[tokio::test]
async fn simpleenroll_without_auth_returns_401() {
let base = spawn().await;
let http = reqwest::Client::new();
let r = http
.post(url(&base, "/simpleenroll"))
.header(reqwest::header::CONTENT_TYPE, CONTENT_TYPE_PKCS10)
.body(b"any-body".to_vec())
.send()
.await
.unwrap();
assert_eq!(r.status(), 401);
assert!(
r.headers().get(reqwest::header::WWW_AUTHENTICATE).is_some(),
"401 must advertise Basic realm"
);
}
#[tokio::test]
async fn simpleenroll_with_wrong_password_returns_403() {
let base = spawn().await;
let http = reqwest::Client::new();
let r = http
.post(url(&base, "/simpleenroll"))
.basic_auth("good-user", Some("WRONG"))
.header(reqwest::header::CONTENT_TYPE, CONTENT_TYPE_PKCS10)
.body(b"any-body".to_vec())
.send()
.await
.unwrap();
assert_eq!(r.status(), 403);
}
#[tokio::test]
async fn simpleenroll_with_wrong_content_type_returns_415() {
let base = spawn().await;
let http = reqwest::Client::new();
let r = http
.post(url(&base, "/simpleenroll"))
.basic_auth("good-user", Some("right"))
.header(reqwest::header::CONTENT_TYPE, "application/json")
.body(b"any-body".to_vec())
.send()
.await
.unwrap();
assert_eq!(r.status(), 415);
}
#[tokio::test]
async fn simpleenroll_with_malformed_csr_returns_400() {
let base = spawn().await;
let http = reqwest::Client::new();
let r = http
.post(url(&base, "/simpleenroll"))
.basic_auth("good-user", Some("right"))
.header(reqwest::header::CONTENT_TYPE, CONTENT_TYPE_PKCS10)
.body(vec![0u8; 32]) .send()
.await
.unwrap();
assert_eq!(r.status(), 400);
}
#[tokio::test]
async fn simplereenroll_without_principal_extension_returns_401() {
let base = spawn().await;
let http = reqwest::Client::new();
let r = http
.post(url(&base, "/simplereenroll"))
.header(reqwest::header::CONTENT_TYPE, CONTENT_TYPE_PKCS10)
.body(b"any-body".to_vec())
.send()
.await
.unwrap();
assert_eq!(
r.status(),
401,
"renewal requires a Principal extension set by the TLS layer"
);
}