use miette::{miette, Context as _, IntoDiagnostic};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use std::path::Path;
use std::str::FromStr;
use crate::Result;
use ockam::identity::Identifier;
use ockam_abac::PolicyExpression::BooleanExpression;
use ockam_abac::{BooleanExpr, PolicyExpression};
use ockam_api::nodes::service::default_address::DefaultAddress;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct ServicesConfig {
#[serde(
alias = "startup_services",
alias = "startup-services",
skip_serializing_if = "Option::is_none"
)]
pub(crate) services: Option<ServiceConfigs>,
}
impl ServicesConfig {
pub(crate) fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
let s = std::fs::read_to_string(path.as_ref())
.into_diagnostic()
.context(format!(
"failed to read services config from {:?}",
path.as_ref()
))?;
Self::from_string(&s)
}
pub(crate) fn from_string(contents: &str) -> Result<Self> {
if let Ok(c) = serde_yaml::from_str(contents) {
return Ok(c);
}
if let Ok(c) = serde_json::from_str(contents) {
return Ok(c);
}
Err(miette!(format!("invalid config {:?}", contents)))
}
pub(crate) fn to_string(&self) -> Result<String> {
serde_json::to_string(&self).into_diagnostic()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct ServiceConfigs {
#[serde(
alias = "secure-channel-listener",
skip_serializing_if = "Option::is_none"
)]
pub(crate) secure_channel_listener: Option<SecureChannelListenerConfig>,
#[serde(alias = "control-api", skip_serializing_if = "Option::is_none")]
pub(crate) control_api: Option<ControlApiConfig>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(default)]
pub struct SecureChannelListenerConfig {
#[serde(default = "default_secure_listener_address")]
pub(crate) address: String,
#[serde(
alias = "authorized-identifiers",
skip_serializing_if = "Option::is_none"
)]
pub(crate) authorized_identifiers: Option<Vec<Identifier>>,
pub(crate) disabled: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) identity: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
#[serde(rename_all = "kebab-case")]
pub enum ControlApiNodeResolution {
#[default]
Relay,
DirectConnection,
}
fn default_control_api_bind_address() -> SocketAddr {
SocketAddr::from(([0, 0, 0, 0], 4080))
}
fn default_frontend_policy() -> PolicyExpression {
BooleanExpression(BooleanExpr::from_str("node_control_api_frontend").unwrap())
}
fn default_backend_policy() -> PolicyExpression {
BooleanExpression(BooleanExpr::from_str("node_control_api_backend").unwrap())
}
fn default_connection_node_port() -> u16 {
4100
}
fn default_node_resolution_pattern() -> String {
"{name}".to_string()
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ControlApiConfig {
#[serde(default)]
pub(crate) frontend: bool,
#[serde(default)]
pub(crate) backend: bool,
#[serde(alias = "frontend-policy", default = "default_frontend_policy")]
pub(crate) frontend_policy: PolicyExpression,
#[serde(alias = "backend-policy", default = "default_backend_policy")]
pub(crate) backend_policy: PolicyExpression,
#[serde(alias = "node-resolution", default)]
pub(crate) node_resolution: ControlApiNodeResolution,
#[serde(
alias = "http-bind-address",
default = "default_control_api_bind_address"
)]
pub(crate) http_bind_address: SocketAddr,
#[serde(
alias = "connection-node-port",
default = "default_connection_node_port"
)]
pub(crate) connection_node_port: u16,
#[serde(
alias = "node-resolution-pattern",
default = "default_node_resolution_pattern"
)]
pub(crate) node_resolution_pattern: String,
#[serde(
alias = "node-resolution-relay-node",
skip_serializing_if = "Option::is_none"
)]
pub(crate) node_resolution_relay_node: Option<String>,
#[serde(
alias = "authentication-token",
skip_serializing_if = "Option::is_none"
)]
pub(crate) authentication_token: Option<String>,
}
fn default_secure_listener_address() -> String {
DefaultAddress::SECURE_CHANNEL_LISTENER.to_string()
}