mirror of
https://github.com/valmojr/armatak.git
synced 2026-08-18 16:02:58 +00:00
Extract testable TAK enrollment protocol helpers
This commit is contained in:
@@ -0,0 +1,200 @@
|
|||||||
|
pub(crate) const DEFAULT_MTLS_SERVER_PORT: &str = "8089";
|
||||||
|
pub(crate) const DEFAULT_ENROLL_PATH: &str = "/Marti/api/tls/signClient/v2";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub(crate) struct EnrollmentConfig {
|
||||||
|
pub server_port: String,
|
||||||
|
pub enroll_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn enrollment_config_url(host: &str, enroll_port: &str) -> String {
|
||||||
|
format!(
|
||||||
|
"https://{}:{}/Marti/api/tls/config",
|
||||||
|
host.trim(),
|
||||||
|
enroll_port.trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn enrollment_sign_url(
|
||||||
|
host: &str,
|
||||||
|
enroll_port: &str,
|
||||||
|
enroll_path: &str,
|
||||||
|
client_uid: &str,
|
||||||
|
) -> String {
|
||||||
|
let path = normalize_enroll_path(enroll_path);
|
||||||
|
format!(
|
||||||
|
"https://{}:{}{}?clientUid={}",
|
||||||
|
host.trim(),
|
||||||
|
enroll_port.trim(),
|
||||||
|
path,
|
||||||
|
client_uid.trim()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn parse_enrollment_config(xml: &str) -> EnrollmentConfig {
|
||||||
|
EnrollmentConfig {
|
||||||
|
server_port: extract_tag_value(xml, "serverPort")
|
||||||
|
.unwrap_or_else(|| DEFAULT_MTLS_SERVER_PORT.to_string()),
|
||||||
|
enroll_path: extract_tag_value(xml, "enrollPath")
|
||||||
|
.map(|path| normalize_enroll_path(&path))
|
||||||
|
.unwrap_or_else(|| DEFAULT_ENROLL_PATH.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn normalize_certificate_pem(certificate: &str) -> String {
|
||||||
|
let trimmed = certificate.trim();
|
||||||
|
if trimmed.contains("-----BEGIN CERTIFICATE-----") {
|
||||||
|
if trimmed.ends_with('\n') {
|
||||||
|
trimmed.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}\n", trimmed)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wrap_pem_body(
|
||||||
|
trimmed,
|
||||||
|
"-----BEGIN CERTIFICATE-----",
|
||||||
|
"-----END CERTIFICATE-----",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_enroll_path(path: &str) -> String {
|
||||||
|
let trimmed = path.trim();
|
||||||
|
if trimmed.starts_with('/') {
|
||||||
|
trimmed.to_string()
|
||||||
|
} else {
|
||||||
|
format!("/{}", trimmed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_tag_value(xml: &str, tag_name: &str) -> Option<String> {
|
||||||
|
let open_tag = format!("<{}>", tag_name);
|
||||||
|
let close_tag = format!("</{}>", tag_name);
|
||||||
|
let start = xml.find(&open_tag)? + open_tag.len();
|
||||||
|
let end = xml[start..].find(&close_tag)? + start;
|
||||||
|
Some(xml[start..end].trim().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_pem_body(base64_body: &str, begin: &str, end: &str) -> String {
|
||||||
|
let mut wrapped = String::new();
|
||||||
|
let normalized = base64_body.trim().replace(['\r', '\n'], "");
|
||||||
|
|
||||||
|
wrapped.push_str(begin);
|
||||||
|
wrapped.push('\n');
|
||||||
|
for chunk in normalized.as_bytes().chunks(64) {
|
||||||
|
wrapped.push_str(std::str::from_utf8(chunk).unwrap_or_default());
|
||||||
|
wrapped.push('\n');
|
||||||
|
}
|
||||||
|
wrapped.push_str(end);
|
||||||
|
wrapped.push('\n');
|
||||||
|
wrapped
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn uses_official_tak_defaults() {
|
||||||
|
assert_eq!(DEFAULT_MTLS_SERVER_PORT, "8089");
|
||||||
|
assert_eq!(DEFAULT_ENROLL_PATH, "/Marti/api/tls/signClient/v2");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builds_enrollment_config_url_from_trimmed_values() {
|
||||||
|
assert_eq!(
|
||||||
|
enrollment_config_url(" tak.example.test ", " 8446 "),
|
||||||
|
"https://tak.example.test:8446/Marti/api/tls/config"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builds_enrollment_sign_url_and_normalizes_path() {
|
||||||
|
assert_eq!(
|
||||||
|
enrollment_sign_url(
|
||||||
|
" tak.example.test ",
|
||||||
|
" 8446 ",
|
||||||
|
" Marti/api/tls/signClient/v2 ",
|
||||||
|
" client-1 "
|
||||||
|
),
|
||||||
|
"https://tak.example.test:8446/Marti/api/tls/signClient/v2?clientUid=client-1"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
enrollment_sign_url(
|
||||||
|
"tak.example.test",
|
||||||
|
"8446",
|
||||||
|
"/custom/enroll",
|
||||||
|
"client-2"
|
||||||
|
),
|
||||||
|
"https://tak.example.test:8446/custom/enroll?clientUid=client-2"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parses_explicit_tak_enrollment_config() {
|
||||||
|
let xml = "<certificateConfig><serverPort>9443</serverPort><enrollPath>Marti/api/tls/signClient/v2</enrollPath></certificateConfig>";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_enrollment_config(xml),
|
||||||
|
EnrollmentConfig {
|
||||||
|
server_port: "9443".to_string(),
|
||||||
|
enroll_path: "/Marti/api/tls/signClient/v2".to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn falls_back_when_optional_config_values_are_missing() {
|
||||||
|
let xml = "<certificateConfig><nameEntries /></certificateConfig>";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_enrollment_config(xml),
|
||||||
|
EnrollmentConfig {
|
||||||
|
server_port: DEFAULT_MTLS_SERVER_PORT.to_string(),
|
||||||
|
enroll_path: DEFAULT_ENROLL_PATH.to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn trims_extracted_config_values() {
|
||||||
|
let xml = "<certificateConfig><serverPort> 8089 </serverPort><enrollPath> /Marti/api/tls/signClient/v2 </enrollPath></certificateConfig>";
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
parse_enrollment_config(xml),
|
||||||
|
EnrollmentConfig {
|
||||||
|
server_port: "8089".to_string(),
|
||||||
|
enroll_path: DEFAULT_ENROLL_PATH.to_string(),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn normalizes_raw_certificate_body_to_pem() {
|
||||||
|
let raw = format!("{}\r\n{}", "A".repeat(64), "B".repeat(8));
|
||||||
|
let pem = normalize_certificate_pem(&raw);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
pem,
|
||||||
|
format!(
|
||||||
|
"-----BEGIN CERTIFICATE-----\n{}\n{}\n-----END CERTIFICATE-----\n",
|
||||||
|
"A".repeat(64),
|
||||||
|
"B".repeat(8)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preserves_existing_pem_and_adds_trailing_newline_when_needed() {
|
||||||
|
let with_newline =
|
||||||
|
"-----BEGIN CERTIFICATE-----\nQUJD\n-----END CERTIFICATE-----\n";
|
||||||
|
let without_newline = "-----BEGIN CERTIFICATE-----\nQUJD\n-----END CERTIFICATE-----";
|
||||||
|
|
||||||
|
assert_eq!(normalize_certificate_pem(with_newline), with_newline);
|
||||||
|
assert_eq!(
|
||||||
|
normalize_certificate_pem(without_newline),
|
||||||
|
format!("{}\n", without_newline)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user