cipherstash-client 0.12.5

The official CipherStash SDK
Documentation
use std::path::PathBuf;

use url::Url;

use crate::{config::paths::resolve_config_dir, credentials::user_credentials::UserCredentials};

use super::{
    defaults::{
        DEFAULT_CONSOLE_BASE_URL, DEFAULT_CONSOLE_IDP_AUDIENCE, DEFAULT_CONSOLE_IDP_BASE_URL,
        DEFAULT_CONSOLE_IDP_CLIENT_ID, DEFAULT_CONSOLE_IDP_PROVIDER, DEFAULT_MANAGEMENT_BASE_URL,
        DEFAULT_STASHDATA_DOMAIN,
    },
    errors::ConfigError,
    idp_provider::IdpProvider,
    vars::{
        CS_CONSOLE_HOST, CS_IDP_AUDIENCE, CS_IDP_CLIENT_ID, CS_IDP_HOST, CS_IDP_PROVIDER,
        CS_MANAGEMENT_HOST,
    },
};

#[derive(Debug, Clone)]
pub struct ConsoleConfigBuilder {
    config_dir: Option<String>,
    base_url: String,
    management_base_url: String,

    // auth
    idp_audience: String,
    idp_base_url: String,
    idp_client_id: String,
    idp_provider: String,
}

impl Default for ConsoleConfigBuilder {
    fn default() -> Self {
        Self {
            config_dir: None,
            management_base_url: DEFAULT_MANAGEMENT_BASE_URL.to_string(),
            base_url: DEFAULT_CONSOLE_BASE_URL.to_string(),
            idp_audience: DEFAULT_CONSOLE_IDP_AUDIENCE.to_string(),
            idp_base_url: DEFAULT_CONSOLE_IDP_BASE_URL.to_string(),
            idp_client_id: DEFAULT_CONSOLE_IDP_CLIENT_ID.to_string(),
            idp_provider: DEFAULT_CONSOLE_IDP_PROVIDER.to_string(),
        }
    }
}

impl ConsoleConfigBuilder {
    pub fn build(&self) -> Result<ConsoleConfig, ConfigError> {
        let config_dir = match &self.config_dir {
            Some(s) => std::path::PathBuf::from(s),
            None => resolve_config_dir(None).map_err(|_| ConfigError::ValueNotSet("config_dir"))?,
        };
        std::fs::create_dir_all(&config_dir).map_err(|e| {
            ConfigError::Io(
                e.to_string(),
                config_dir.to_str().unwrap_or("Unknown").to_string(),
            )
        })?;

        Ok(ConsoleConfig {
            config_dir,
            base_url: Box::new(self.base_url.parse()?),
            management_base_url: Box::new(self.management_base_url.parse()?),
            idp_base_url: Box::new(self.idp_base_url.parse()?),
            idp_client_id: self.idp_client_id.to_string(),
            idp_audience: self.idp_audience.to_string(),
            idp_provider: self.idp_provider.parse()?,
        })
    }

    pub fn config_dir(mut self, value: &str) -> Self {
        self.config_dir = Some(value.to_string());
        self
    }

    pub fn set_config_dir(mut self, value: Option<&str>) -> Self {
        self.config_dir = value.map(|s| s.to_string());
        self
    }

    pub fn base_url(mut self, value: &str) -> Self {
        self.base_url = value.to_string();
        self
    }

    pub fn idp_audience(mut self, value: &str) -> Self {
        self.idp_audience = value.to_string();
        self
    }

    pub fn idp_base_url(mut self, value: &str) -> Self {
        self.idp_base_url = value.to_string();
        self
    }

    pub fn idp_client_id(mut self, value: &str) -> Self {
        self.idp_client_id = value.to_string();
        self
    }

    pub fn idp_provider(mut self, value: &str) -> Self {
        self.idp_provider = value.to_string();
        self
    }

    /// Hydrate the configuration using environment variables
    pub fn with_env(mut self) -> Self {
        if let Ok(value) = std::env::var(CS_CONSOLE_HOST) {
            self.base_url = value;
        }

        if let Ok(value) = std::env::var(CS_MANAGEMENT_HOST) {
            self.management_base_url = value;
        }

        if let Ok(value) = std::env::var(CS_IDP_AUDIENCE) {
            self.idp_audience = value;
        }

        if let Ok(value) = std::env::var(CS_IDP_HOST) {
            self.idp_base_url = value;
        }

        if let Ok(value) = std::env::var(CS_IDP_CLIENT_ID) {
            self.idp_client_id = value;
        }

        if let Ok(value) = std::env::var(CS_IDP_PROVIDER) {
            self.idp_provider = value;
        }

        self
    }
}

#[derive(Clone)]
pub struct ConsoleConfig {
    config_dir: PathBuf,
    base_url: Box<Url>,
    management_base_url: Box<Url>,

    // auth
    idp_audience: String,
    idp_base_url: Box<Url>,
    idp_client_id: String,
    idp_provider: IdpProvider,
}

impl ConsoleConfig {
    /// Create a new [`ConsoleConfigBuilder`] to build a [`ConsoleConfig`] used to configure
    /// internal services
    ///
    /// ```rust
    /// # use cipherstash_client::ConsoleConfig;
    /// let config = ConsoleConfig::builder()
    ///   .with_env()
    ///   .build()
    ///   .expect("failed to build config");
    /// ```
    pub fn builder() -> ConsoleConfigBuilder {
        ConsoleConfigBuilder::default()
    }

    /// Return the confiruation directory specified in the config
    pub fn config_dir(&self) -> PathBuf {
        self.config_dir.to_owned()
    }

    /// Return the path to the cached authentication token for talking to Console
    pub fn token_path(&self) -> PathBuf {
        self.config_dir.join("console-auth.json")
    }

    /// Return the Console API base URL
    pub fn base_url(&self) -> Url {
        *self.base_url.to_owned()
    }

    /// Return the Management API base URL. This will be the Console API URL unless in a customer
    /// hosted environment.
    pub fn management_base_url(&self) -> Url {
        *self.management_base_url.to_owned()
    }

    /// Return a [`crate::credentials::Credentials`] provider that can retrieve an access token to
    /// the Console API.
    pub fn credentials(&self) -> UserCredentials {
        UserCredentials::new(
            &self.token_path(),
            &self.idp_base_url,
            &self.idp_audience,
            &self.idp_client_id,
            self.idp_provider,
        )
    }

    /// Whether the current config is pointing to a CipherStash hosted instance
    pub fn is_cipherstash_management_instance(&self) -> bool {
        self.management_base_url.host_str() == Some(DEFAULT_STASHDATA_DOMAIN)
    }
}