spiffe-rs 0.1.0

Rust port of spiffe-go with SPIFFE IDs, bundles, SVIDs, Workload API client, federation helpers, and rustls-based SPIFFE TLS utilities.
Documentation
use crate::workloadapi::{wrap_error, Result};
use std::net::IpAddr;
use std::env;
use url::Url;

#[allow(non_upper_case_globals)]
pub const SocketEnv: &str = "SPIFFE_ENDPOINT_SOCKET";

pub fn get_default_address() -> Option<String> {
    env::var(SocketEnv).ok()
}

pub fn validate_address(addr: &str) -> Result<()> {
    target_from_address(addr).map(|_| ())
}

pub fn target_from_address(addr: &str) -> Result<String> {
    if addr.chars().any(|c| c.is_control()) {
        return Err(wrap_error(
            "workload endpoint socket is not a valid URI: invalid control character in URL",
        ));
    }
    let url = match Url::parse(addr) {
        Ok(url) => url,
        Err(url::ParseError::RelativeUrlWithoutBase) => {
            return Err(wrap_error(
                "workload endpoint socket URI must have a \"tcp\" or \"unix\" scheme",
            ));
        }
        Err(err) => {
            return Err(wrap_error(format!(
                "workload endpoint socket is not a valid URI: {}",
                err
            )));
        }
    };
    parse_target_from_url(&url)
}

fn parse_target_from_url(url: &Url) -> Result<String> {
    if url.scheme() == "tcp" {
        if url.cannot_be_a_base() {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must not be opaque",
            ));
        }
        if !url.username().is_empty() || url.password().is_some() {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must not include user info",
            ));
        }
        if url.host_str().is_none() {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must include a host",
            ));
        }
        if !url.path().is_empty() && url.path() != "/" {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must not include a path",
            ));
        }
        if url.query().is_some() {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must not include query values",
            ));
        }
        if url.fragment().is_some() {
            return Err(wrap_error(
                "workload endpoint tcp socket URI must not include a fragment",
            ));
        }
        let host = url
            .host_str()
            .ok_or_else(|| wrap_error("workload endpoint tcp socket URI must include a host"))?;
        let ip: IpAddr = host
            .parse()
            .map_err(|_| wrap_error("workload endpoint tcp socket URI host component must be an IP:port"))?;
        let port = url
            .port()
            .ok_or_else(|| wrap_error("workload endpoint tcp socket URI host component must include a port"))?;
        return Ok(format!("{}:{}", ip, port));
    }

    parse_target_from_url_os(url)
}

fn parse_target_from_url_os(url: &Url) -> Result<String> {
    match url.scheme() {
        "unix" => {
            if url.cannot_be_a_base() {
                return Err(wrap_error(
                    "workload endpoint unix socket URI must not be opaque",
                ));
            }
            if !url.username().is_empty() || url.password().is_some() {
                return Err(wrap_error(
                    "workload endpoint unix socket URI must not include user info",
                ));
            }
            if url.host_str().unwrap_or("").is_empty() && url.path().is_empty() {
                return Err(wrap_error(
                    "workload endpoint unix socket URI must include a path",
                ));
            }
            if url.query().is_some() {
                return Err(wrap_error(
                    "workload endpoint unix socket URI must not include query values",
                ));
            }
            if url.fragment().is_some() {
                return Err(wrap_error(
                    "workload endpoint unix socket URI must not include a fragment",
                ));
            }
            Ok(url.to_string())
        }
        _ => Err(wrap_error(
            "workload endpoint socket URI must have a \"tcp\" or \"unix\" scheme",
        )),
    }
}