bucketwarden-server 0.1.0

BucketWarden storage server runtime.
Documentation
use super::*;

pub(crate) fn parse_bucket_object_lock_configuration(
    bucket: &str,
    body: &[u8],
) -> Result<BucketObjectLockConfiguration, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    let enabled = text_between(&xml, "<ObjectLockEnabled>", "</ObjectLockEnabled>")
        .map(str::trim)
        .map(|value| value == "Enabled")
        .unwrap_or(true);
    if !enabled {
        return Err(RuntimeError::InvalidObjectLockConfiguration(
            "ObjectLockEnabled must be Enabled".to_string(),
        ));
    }
    let default_retention =
        if let Some(rule_xml) = text_between(&xml, "<DefaultRetention>", "</DefaultRetention>") {
            let mode = text_between(rule_xml, "<Mode>", "</Mode>")
                .map(str::trim)
                .ok_or_else(|| {
                    RuntimeError::InvalidObjectLockConfiguration(
                        "DefaultRetention Mode is required".to_string(),
                    )
                })?;
            retention_mode_from_text(mode)?;
            let days = text_between(rule_xml, "<Days>", "</Days>")
                .map(str::trim)
                .map(parse_positive_u64)
                .transpose()?;
            let years = text_between(rule_xml, "<Years>", "</Years>")
                .map(str::trim)
                .map(parse_positive_u64)
                .transpose()?;
            let default_retention = ObjectLockDefaultRetention {
                mode: mode.to_ascii_uppercase(),
                days,
                years,
            };
            validate_default_retention(&Some(default_retention.clone()))?;
            Some(default_retention)
        } else {
            None
        };
    Ok(BucketObjectLockConfiguration {
        bucket: bucket.to_string(),
        enabled,
        default_retention,
    })
}

pub(crate) fn parse_bucket_encryption_configuration(
    body: &[u8],
) -> Result<ServerSideEncryption, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    let rule_xml = text_between(
        &xml,
        "<ApplyServerSideEncryptionByDefault>",
        "</ApplyServerSideEncryptionByDefault>",
    )
    .ok_or_else(|| {
        RuntimeError::InvalidEncryption(
            "ApplyServerSideEncryptionByDefault is required".to_string(),
        )
    })?;
    let algorithm = text_between(rule_xml, "<SSEAlgorithm>", "</SSEAlgorithm>")
        .map(str::trim)
        .ok_or_else(|| RuntimeError::InvalidEncryption("SSEAlgorithm is required".to_string()))?;
    let kms_key_id = text_between(rule_xml, "<KMSMasterKeyID>", "</KMSMasterKeyID>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(str::to_string);
    Ok(ServerSideEncryption {
        algorithm: algorithm.to_string(),
        kms_key_id,
    })
}

pub(crate) fn parse_object_legal_hold(body: &[u8]) -> Result<bool, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    let status = text_between(&xml, "<Status>", "</Status>")
        .map(str::trim)
        .ok_or_else(|| RuntimeError::InvalidLegalHold("Status is required".to_string()))?;
    parse_legal_hold_status(status)
}

pub(crate) fn parse_object_retention(body: &[u8]) -> Result<(String, u64), RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    let mode = text_between(&xml, "<Mode>", "</Mode>")
        .map(str::trim)
        .ok_or_else(|| RuntimeError::InvalidRetention("Mode is required".to_string()))?;
    retention_mode_from_text(mode)?;
    let retain_until = text_between(&xml, "<RetainUntilDate>", "</RetainUntilDate>")
        .map(str::trim)
        .ok_or_else(|| RuntimeError::InvalidRetention("RetainUntilDate is required".to_string()))?;
    Ok((
        mode.to_ascii_uppercase(),
        parse_retention_timestamp(retain_until)?,
    ))
}

pub(crate) fn validate_default_retention(
    default_retention: &Option<ObjectLockDefaultRetention>,
) -> Result<(), RuntimeError> {
    let Some(default_retention) = default_retention else {
        return Ok(());
    };
    retention_mode_from_text(&default_retention.mode)?;
    match (default_retention.days, default_retention.years) {
        (Some(days), None) if days > 0 => Ok(()),
        (None, Some(years)) if years > 0 => Ok(()),
        _ => Err(RuntimeError::InvalidObjectLockConfiguration(
            "DefaultRetention requires exactly one positive Days or Years value".to_string(),
        )),
    }
}