Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mauth-client"
version = "0.8.1"
version = "0.9.0"
authors = ["Mason Gup <mgup@mdsol.com>"]
edition = "2024"
rust-version = "1.88"
Expand All @@ -21,7 +21,7 @@ async-trait = ">= 0.1.83"
url = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yml = ">= 0.0.10"
yaml_serde = "0.10"
uuid = { version = "1", features = ["v4"] }
dirs = "7"
chrono = "0.4"
Expand Down
97 changes: 92 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ impl MAuthInfo {
let mut home = dirs::home_dir().unwrap();
home.push(CONFIG_FILE);
let config_data = std::fs::read_to_string(&home)?;
Self::config_section_from_yaml(&config_data)
}

let config_data_value: serde_yml::Value = serde_yml::from_slice(&config_data.into_bytes())?;
/// Parse the `common` section out of the YAML contents of a config file.
pub(crate) fn config_section_from_yaml(
config_data: &str,
) -> Result<ConfigFileSection, ConfigReadError> {
let config_data_value: yaml_serde::Value = yaml_serde::from_str(config_data)?;
let common_section = config_data_value
.get("common")
.ok_or(ConfigReadError::InvalidFile(None))?;
let common_section_typed: ConfigFileSection =
serde_yml::from_value(common_section.clone())?;
yaml_serde::from_value(common_section.clone())?;
Ok(common_section_typed)
}

Expand Down Expand Up @@ -124,7 +130,7 @@ pub enum ConfigReadError {
#[error("File Read Error: {0}")]
FileReadError(#[from] io::Error),
#[error("Not a valid maudit config file: {0:?}")]
InvalidFile(Option<serde_yml::Error>),
InvalidFile(Option<yaml_serde::Error>),
#[error("MAudit URI not valid: {0}")]
InvalidUri(#[from] url::ParseError),
#[error("App UUID not valid: {0}")]
Expand All @@ -146,8 +152,8 @@ impl From<mauth_core::error::Error> for ConfigReadError {
}
}

impl From<serde_yml::Error> for ConfigReadError {
fn from(err: serde_yml::Error) -> ConfigReadError {
impl From<yaml_serde::Error> for ConfigReadError {
fn from(err: yaml_serde::Error) -> ConfigReadError {
ConfigReadError::InvalidFile(Some(err))
}
}
Expand All @@ -157,6 +163,87 @@ mod test {
use super::*;
use tokio::fs;

#[test]
fn parses_documented_common_section() {
let yaml = "\
common: &common
mauth_baseurl: https://mauth.example.com
mauth_api_version: v1
app_uuid: c7db7fde-2448-11ef-b358-125eb8485a60
private_key_file: /path/to/key.pem
";
let section = MAuthInfo::config_section_from_yaml(yaml).unwrap();
assert_eq!(section.mauth_baseurl, "https://mauth.example.com");
assert_eq!(section.mauth_api_version, "v1");
assert_eq!(section.app_uuid, "c7db7fde-2448-11ef-b358-125eb8485a60");
assert_eq!(
section.private_key_file.as_deref(),
Some("/path/to/key.pem")
);
assert!(section.private_key_data.is_none());
assert!(section.v2_only_sign_requests.is_none());
assert!(section.v2_only_authenticate.is_none());
assert!(section.pubkey_cache_capacity.is_none());
}

#[test]
fn parses_optional_fields_and_ignores_other_sections() {
let yaml = "\
common: &common
mauth_baseurl: https://mauth.example.com
mauth_api_version: v1
app_uuid: c7db7fde-2448-11ef-b358-125eb8485a60
private_key_data: |
-----BEGIN RSA PRIVATE KEY-----
not-really-a-key
-----END RSA PRIVATE KEY-----
v2_only_sign_requests: true
v2_only_authenticate: false
pubkey_cache_capacity: 42
development:
<<: *common
mauth_baseurl: https://mauth-dev.example.com
";
let section = MAuthInfo::config_section_from_yaml(yaml).unwrap();
assert_eq!(section.mauth_baseurl, "https://mauth.example.com");
assert!(section.private_key_file.is_none());
let key = section.private_key_data.unwrap();
assert!(key.starts_with("-----BEGIN RSA PRIVATE KEY-----\n"));
assert!(key.contains("not-really-a-key"));
assert_eq!(section.v2_only_sign_requests, Some(true));
assert_eq!(section.v2_only_authenticate, Some(false));
assert_eq!(section.pubkey_cache_capacity, Some(42));
}

#[test]
fn missing_common_section_returns_right_error() {
let yaml = "\
development:
mauth_baseurl: https://mauth.example.com
mauth_api_version: v1
app_uuid: c7db7fde-2448-11ef-b358-125eb8485a60
";
let result = MAuthInfo::config_section_from_yaml(yaml);
assert!(matches!(result, Err(ConfigReadError::InvalidFile(None))));
}

#[test]
fn missing_required_field_returns_right_error() {
let yaml = "\
common:
mauth_baseurl: https://mauth.example.com
mauth_api_version: v1
";
let result = MAuthInfo::config_section_from_yaml(yaml);
assert!(matches!(result, Err(ConfigReadError::InvalidFile(Some(_)))));
}

#[test]
fn malformed_yaml_returns_right_error() {
let result = MAuthInfo::config_section_from_yaml("common: [unterminated");
assert!(matches!(result, Err(ConfigReadError::InvalidFile(Some(_)))));
}

#[tokio::test]
async fn invalid_uri_returns_right_error() {
let bad_config = ConfigFileSection {
Expand Down