cts-common 0.4.1

Common types and traits used across the CipherStash ecosystem
Documentation
use crate::{Region, RegionError};
use regex::Regex;
use std::sync::LazyLock;
use url::Url;

/// Regex to extract the region from a host FQDN or a URL.
static REGION_HOST_REGEX: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"^(?:https://){0,1}([^\.]+)\.([^\.]+)\.viturhosted\.net/?$").expect("Invalid regex")
});

/// Domain name for the service discovery.
static DOMAIN_NAME: &str = "viturhosted.net";

/// Simple service discover trait that defines the name of the service and the endpoint for a given region.
pub trait ServiceDiscovery {
    fn name(&self) -> &'static str;
    fn fqdn(region: Region) -> String;
    fn endpoint(region: Region) -> Result<Url, RegionError>;
}

/// ZeroKms service.
pub struct ZeroKmsServiceDiscovery;

impl ServiceDiscovery for ZeroKmsServiceDiscovery {
    fn name(&self) -> &'static str {
        "zerokms"
    }

    /// Returns the FQDN for the service in the given region.
    /// The FQDN is in the format `<region>.<provider>.viturhosted.net`.
    fn fqdn(region: Region) -> String {
        format!("{}.viturhosted.net", region.identifier())
    }

    /// Returns the URL for the service in the given region.
    /// The URL is in the format `https://<region>.<provider>.viturhosted.net/`.
    fn endpoint(region: Region) -> Result<Url, RegionError> {
        Url::parse(&format!("https://{}.{DOMAIN_NAME}/", region.identifier())).map_err(|e| {
            RegionError::InvalidRegion(format!(
                "Invalid service URL for ZeroKMS in region {}: {}",
                region.identifier(),
                e
            ))
        })
    }
}

impl ZeroKmsServiceDiscovery {
    /// Attempt to extract the region from a host FQDN or URL.
    /// The FQDN must be in the format `<region>.<provider>.viturhosted.net`.
    /// The URL must be in the format `https://<region>.<provider>.viturhosted.net/`.
    pub fn region_from_host_fqdn(host_fqdn: &str) -> Result<Region, RegionError> {
        REGION_HOST_REGEX
            .captures(host_fqdn)
            .ok_or_else(|| RegionError::InvalidHostFqdn(host_fqdn.to_string()))
            .and_then(|caps| {
                let region = caps.get(1).unwrap().as_str().to_string();
                let provider = caps.get(2).unwrap().as_str().to_string();
                Region::new(&format!("{region}.{provider}"))
            })
    }
}

/// CTS service.
pub struct CtsServiceDiscovery;

impl ServiceDiscovery for CtsServiceDiscovery {
    fn name(&self) -> &'static str {
        "cts"
    }

    fn fqdn(region: Region) -> String {
        format!("{}.auth.{DOMAIN_NAME}", region.identifier())
    }

    fn endpoint(region: Region) -> Result<Url, RegionError> {
        if matches!(region, Region::Aws(crate::AwsRegion::ApSoutheast2)) {
            return Url::parse(&format!(
                "https://{}.auth.{DOMAIN_NAME}/",
                region.identifier()
            ))
            .map_err(|e| {
                RegionError::InvalidRegion(format!(
                    "Invalid service URL for ZeroKMS in region {}: {}",
                    region.identifier(),
                    e
                ))
            });
        }
        Err(RegionError::InvalidRegion(format!(
            "Region {} is not supported by the CTS service",
            region.identifier()
        )))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_region_from_host_fqdn() -> anyhow::Result<()> {
        let host_fqdn = "us-west-1.aws.viturhosted.net";
        let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
        assert_eq!(region.identifier(), "us-west-1.aws");

        Ok(())
    }

    #[test]
    fn test_region_from_host_endpoint() -> anyhow::Result<()> {
        let host_fqdn = "https://us-west-1.aws.viturhosted.net";
        let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
        assert_eq!(region.identifier(), "us-west-1.aws");
        Ok(())
    }

    #[test]
    fn test_region_from_host_endpoint_with_trailing_slash() -> anyhow::Result<()> {
        let host_fqdn = "https://us-west-1.aws.viturhosted.net/";
        let region = ZeroKmsServiceDiscovery::region_from_host_fqdn(host_fqdn)?;
        assert_eq!(region.identifier(), "us-west-1.aws");
        Ok(())
    }
}