Skip to content

Commit

Permalink
Merge pull request #729 from webern/url-bug-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
webern authored Nov 30, 2023
2 parents e6b25b1 + cbc3a39 commit baa19b6
Showing 1 changed file with 168 additions and 56 deletions.
224 changes: 168 additions & 56 deletions tuftool/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@
//! "aws-ssm:///a/key" (notice the 3 slashes after the colon)
use crate::error::{self, Result};
use snafu::{OptionExt, ResultExt};
use std::path::Path;
use snafu::ResultExt;
use std::path::PathBuf;
use tough::key_source::{KeySource, LocalKeySource};
use tough::SafeUrlPath;
use tough_kms::{KmsKeySource, KmsSigningAlgorithm};
use tough_ssm::SsmKeySource;
use url::Url;
Expand All @@ -51,60 +50,173 @@ use url::Url;
/// the `KeySource` trait in the `tough` library. A user can then add
/// to this parser to support them in `tuftool`.
pub(crate) fn parse_key_source(input: &str) -> Result<Box<dyn KeySource>> {
let input_as_path = Path::new(input);
let url = if input_as_path.exists() {
Url::from_file_path(input)
.ok() // dump unhelpful `()` error
.context(error::FileUrlSnafu {
path: input_as_path.to_owned(),
})?
} else {
Url::parse(input).context(error::UrlParseSnafu { url: input })?
};
match url.scheme() {
"file" => Ok(Box::new(LocalKeySource {
path: url.safe_url_filepath(),
})),
#[cfg(any(feature = "aws-sdk-rust-native-tls", feature = "aws-sdk-rust-rustls"))]
"aws-ssm" => Ok(Box::new(SsmKeySource {
profile: url.host_str().and_then(|s| {
if s.is_empty() {
None
} else {
Some(s.to_owned())
}
}),
parameter_name: url.path().to_owned(),
// If a key ID isn't provided, the system uses the default key
// associated with your AWS account.
key_id: url.query_pairs().find_map(|(k, v)| {
if k == "kms-key-id" {
Some(v.into_owned())
} else {
None
}
}),
})),
"aws-kms" => Ok(Box::new(KmsKeySource {
profile: url.host_str().and_then(|s| {
if s.is_empty() {
None
} else {
Some(s.to_owned())
let path_or_url = parse_path_or_url(input)?;
match path_or_url {
PathOrUrl::Path(path) => Ok(Box::new(LocalKeySource { path })),
PathOrUrl::Url(url) => {
match url.scheme() {
#[cfg(any(feature = "aws-sdk-rust-native-tls", feature = "aws-sdk-rust-rustls"))]
"aws-ssm" => Ok(Box::new(SsmKeySource {
profile: url.host_str().and_then(|s| {
if s.is_empty() {
None
} else {
Some(s.to_owned())
}
}),
parameter_name: url.path().to_owned(),
// If a key ID isn't provided, the system uses the default key
// associated with your AWS account.
key_id: url.query_pairs().find_map(|(k, v)| {
if k == "kms-key-id" {
Some(v.into_owned())
} else {
None
}
}),
})),
"aws-kms" => Ok(Box::new(KmsKeySource {
profile: url.host_str().and_then(|s| {
if s.is_empty() {
None
} else {
Some(s.to_owned())
}
}),
// remove first '/' from the path to get the key_id
key_id: if url.path().is_empty() {
String::new()
} else {
url.path()[1..].to_string()
},
client: None,
signing_algorithm: KmsSigningAlgorithm::RsassaPssSha256,
})),
_ => error::UnrecognizedSchemeSnafu {
scheme: url.scheme(),
}
}),
// remove first '/' from the path to get the key_id
key_id: if url.path().is_empty() {
String::new()
} else {
url.path()[1..].to_string()
},
client: None,
signing_algorithm: KmsSigningAlgorithm::RsassaPssSha256,
})),
_ => error::UnrecognizedSchemeSnafu {
scheme: url.scheme(),
.fail(),
}
}
.fail(),
}
}

/// The `Url` crate does not handle relative file paths. We will only use `Url`` for known schemes.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
enum PathOrUrl {
Path(PathBuf),
Url(Url),
}

fn parse_path_or_url(s: &str) -> Result<PathOrUrl> {
if s.starts_with("file://") {
// It's a file path but we don't want to use Url. Strip the scheme and parse as a path.
Ok(PathOrUrl::Path(PathBuf::from(
s.chars().skip(7).collect::<String>(),
)))
} else if s.starts_with("aws-ssm://") | s.starts_with("aws-kms://") {
// One of our know-supported schemes, parse as a Url.
Ok(PathOrUrl::Url(
Url::parse(s).context(error::UrlParseSnafu { url: s })?,
))
} else {
// It's not one of our known schemes and it's not a file:// scheme, treat is as a path.
Ok(PathOrUrl::Path(PathBuf::from(s)))
}
}

#[test]
fn test_parse_path_or_url_path_1() {
let input = "/foo/bar";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_2() {
let input = "/foo/bar/";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_3() {
let input = "./x";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_4() {
let input = "../x";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_5() {
let input = "C:";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_6() {
let input = r#"C:\foo.txt"#;
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_8() {
let input = r#".\"#;
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_9() {
let input = "aws-kms:/";
let expected = PathOrUrl::Path(PathBuf::from(input));
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_10() {
let input = "aws-kms://";
let expected = PathOrUrl::Url(Url::parse(input).unwrap());
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_11() {
let input = "aws-kms://x";
let expected = PathOrUrl::Url(Url::parse(input).unwrap());
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_12() {
let input = "aws-ssm://";
let expected = PathOrUrl::Url(Url::parse(input).unwrap());
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

#[test]
fn test_parse_path_or_url_path_13() {
let input = "aws-ssm://x";
let expected = PathOrUrl::Url(Url::parse(input).unwrap());
let actual = parse_path_or_url(input).unwrap();
assert_eq!(expected, actual);
}

0 comments on commit baa19b6

Please sign in to comment.