bucketwarden-server 0.1.0

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

pub(crate) fn parse_bucket_metadata_configuration(
    bucket: &str,
    body: &[u8],
) -> Result<BucketMetadataConfiguration, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    if !xml.contains("<MetadataConfiguration") {
        return Err(RuntimeError::InvalidBucketMetadataConfiguration(
            "missing MetadataConfiguration root".to_string(),
        ));
    }
    let journal_xml = text_between(
        &xml,
        "<JournalTableConfiguration>",
        "</JournalTableConfiguration>",
    )
    .ok_or_else(|| {
        RuntimeError::InvalidBucketMetadataConfiguration(
            "MetadataConfiguration must include JournalTableConfiguration".to_string(),
        )
    })?;
    let record_expiration = parse_record_expiration(journal_xml)?;
    let journal_encryption = parse_metadata_table_encryption(journal_xml)?;
    let inventory_table_configuration = text_between(
        &xml,
        "<InventoryTableConfiguration>",
        "</InventoryTableConfiguration>",
    )
    .map(parse_inventory_table_configuration)
    .transpose()?;
    Ok(BucketMetadataConfiguration {
        bucket: bucket.to_string(),
        journal_table_configuration: JournalTableConfiguration {
            encryption_configuration: journal_encryption,
            record_expiration,
        },
        inventory_table_configuration,
    })
}

pub(crate) fn parse_journal_table_configuration(
    body: &[u8],
) -> Result<JournalTableConfiguration, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    if !xml.contains("<JournalTableConfiguration") {
        return Err(RuntimeError::InvalidBucketMetadataConfiguration(
            "missing JournalTableConfiguration root".to_string(),
        ));
    }
    let record_expiration = parse_record_expiration(&xml)?;
    let encryption_configuration = parse_metadata_table_encryption(&xml)?;
    Ok(JournalTableConfiguration {
        encryption_configuration,
        record_expiration,
    })
}

pub(crate) fn parse_inventory_table_configuration_body(
    body: &[u8],
) -> Result<InventoryTableConfiguration, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    if !xml.contains("<InventoryTableConfiguration") {
        return Err(RuntimeError::InvalidBucketMetadataConfiguration(
            "missing InventoryTableConfiguration root".to_string(),
        ));
    }
    parse_inventory_table_configuration(&xml)
}

pub(crate) fn parse_bucket_metadata_table_configuration(
    bucket: &str,
    body: &[u8],
) -> Result<BucketMetadataTableConfiguration, RuntimeError> {
    let xml = String::from_utf8_lossy(body);
    if !xml.contains("<MetadataTableConfiguration") {
        return Err(RuntimeError::InvalidBucketMetadataTableConfiguration(
            "missing MetadataTableConfiguration root".to_string(),
        ));
    }
    let destination_xml = text_between(&xml, "<S3TablesDestination>", "</S3TablesDestination>")
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataTableConfiguration(
                "MetadataTableConfiguration must include S3TablesDestination".to_string(),
            )
        })?;
    let table_bucket_arn = text_between(destination_xml, "<TableBucketArn>", "</TableBucketArn>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataTableConfiguration(
                "S3TablesDestination must include TableBucketArn".to_string(),
            )
        })?;
    let table_name = text_between(destination_xml, "<TableName>", "</TableName>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataTableConfiguration(
                "S3TablesDestination must include TableName".to_string(),
            )
        })?;
    Ok(BucketMetadataTableConfiguration {
        bucket: bucket.to_string(),
        s3_tables_destination: S3TablesDestination {
            table_bucket_arn: table_bucket_arn.to_string(),
            table_name: table_name.to_string(),
        },
    })
}

fn parse_inventory_table_configuration(
    xml: &str,
) -> Result<InventoryTableConfiguration, RuntimeError> {
    let configuration_state = text_between(xml, "<ConfigurationState>", "</ConfigurationState>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataConfiguration(
                "InventoryTableConfiguration must include ConfigurationState".to_string(),
            )
        })?;
    Ok(InventoryTableConfiguration {
        configuration_state: configuration_state.to_string(),
        encryption_configuration: parse_metadata_table_encryption(xml)?,
    })
}

fn parse_record_expiration(xml: &str) -> Result<RecordExpiration, RuntimeError> {
    let record_expiration = text_between(xml, "<RecordExpiration>", "</RecordExpiration>")
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataConfiguration(
                "JournalTableConfiguration must include RecordExpiration".to_string(),
            )
        })?;
    let expiration = text_between(record_expiration, "<Expiration>", "</Expiration>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataConfiguration(
                "RecordExpiration must include Expiration".to_string(),
            )
        })?;
    let days = text_between(record_expiration, "<Days>", "</Days>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(|value| {
            value.parse::<u32>().map_err(|_| {
                RuntimeError::InvalidBucketMetadataConfiguration(format!(
                    "invalid RecordExpiration Days: {value}"
                ))
            })
        })
        .transpose()?;
    Ok(RecordExpiration {
        expiration: expiration.to_string(),
        days,
    })
}

fn parse_metadata_table_encryption(
    xml: &str,
) -> Result<Option<MetadataTableEncryptionConfiguration>, RuntimeError> {
    let Some(encryption_xml) = text_between(
        xml,
        "<EncryptionConfiguration>",
        "</EncryptionConfiguration>",
    ) else {
        return Ok(None);
    };
    let sse_algorithm = text_between(encryption_xml, "<SseAlgorithm>", "</SseAlgorithm>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .ok_or_else(|| {
            RuntimeError::InvalidBucketMetadataConfiguration(
                "EncryptionConfiguration must include SseAlgorithm".to_string(),
            )
        })?;
    let kms_key_arn = text_between(encryption_xml, "<KmsKeyArn>", "</KmsKeyArn>")
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(str::to_string);
    Ok(Some(MetadataTableEncryptionConfiguration {
        sse_algorithm: sse_algorithm.to_string(),
        kms_key_arn,
    }))
}