diff --git a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs index 15637df441..51d54756e2 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_configuration.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_configuration.rs @@ -109,6 +109,7 @@ impl DomainLinkageConfiguration { #[cfg(feature = "domain-linkage-fetch")] mod __fetch_configuration { + use crate::domain_linkage::utils::url_only_includes_origin; use crate::domain_linkage::DomainLinkageConfiguration; use crate::error::Result; use crate::Error::DomainLinkageError; @@ -128,8 +129,10 @@ mod __fetch_configuration { if domain.scheme() != "https" { return Err(DomainLinkageError("domain` does not use `https` protocol".into())); } - if domain.path() != "/" || domain.query().is_some() || domain.fragment().is_some() { - return Err(DomainLinkageError("domain mut not include any path, query of fragment".into())); + if !url_only_includes_origin(&domain) { + return Err(DomainLinkageError( + "domain must not include any path, query or fragment".into(), + )); } domain.set_path(".well-known/did-configuration.json"); diff --git a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs index 2ae7db01af..518d79956a 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_credential_builder.rs @@ -14,6 +14,8 @@ use identity_core::common::Url; use identity_did::CoreDID; use identity_did::DID; +use super::utils::url_only_includes_origin; + /// Convenient builder to create a spec compliant Domain Linkage Credential. /// /// See: @@ -74,10 +76,10 @@ impl DomainLinkageCredentialBuilder { "origin must be a domain with http(s) scheme".into(), )); } - if origin.path() != "/" || origin.query().is_some() || origin.fragment().is_some() { + if !url_only_includes_origin(&origin) { return Err(Error::DomainLinkageError( - "origin must not contain any path, query or fragment".into() - )) + "origin must not contain any path, query or fragment".into(), + )); } let mut properties: Object = Object::new(); @@ -146,15 +148,23 @@ mod tests { } #[test] fn test_builder_origin_is_a_url() { + let urls = [ + "https://example.com/foo?bar=420#baz", + "https://example.com/?bar=420", + "https://example.com/#baz", + "https://example.com/?bar=420#baz", + ]; let issuer: CoreDID = "did:example:issuer".parse().unwrap(); - let err: Error = DomainLinkageCredentialBuilder::new() - .issuance_date(Timestamp::now_utc()) - .expiration_date(Timestamp::now_utc()) - .issuer(issuer) - .origin(Url::parse("https://example.com/foo?bar=420#baz").unwrap()) - .build() - .unwrap_err(); - assert!(matches!(err, Error::DomainLinkageError(_))); + for url in urls { + let err: Error = DomainLinkageCredentialBuilder::new() + .issuance_date(Timestamp::now_utc()) + .expiration_date(Timestamp::now_utc()) + .issuer(issuer.clone()) + .origin(Url::parse(url).unwrap()) + .build() + .unwrap_err(); + assert!(matches!(err, Error::DomainLinkageError(_))); + } } #[test] diff --git a/identity_credential/src/domain_linkage/domain_linkage_validator.rs b/identity_credential/src/domain_linkage/domain_linkage_validator.rs index bacde4ef75..169701b9fe 100644 --- a/identity_credential/src/domain_linkage/domain_linkage_validator.rs +++ b/identity_credential/src/domain_linkage/domain_linkage_validator.rs @@ -17,6 +17,7 @@ use identity_verification::jws::JwsVerifier; use crate::validator::DecodedJwtCredential; +use super::utils::url_only_includes_origin; use super::DomainLinkageValidationResult; /// A validator for a Domain Linkage Configuration and Credentials. @@ -190,7 +191,7 @@ impl JwtDomainLinkageValidator { source: Some(Box::new(other_error)), }), }?; - if origin_url.path() != "/" || origin_url.query().is_some() || origin_url.fragment().is_some() { + if !url_only_includes_origin(&origin_url) { return Err(DomainLinkageValidationError { cause: DomainLinkageValidationErrorCause::InvalidSubjectOrigin, source: None, diff --git a/identity_credential/src/domain_linkage/mod.rs b/identity_credential/src/domain_linkage/mod.rs index 7595641990..8f31ddfa7f 100644 --- a/identity_credential/src/domain_linkage/mod.rs +++ b/identity_credential/src/domain_linkage/mod.rs @@ -7,6 +7,7 @@ mod domain_linkage_configuration; mod domain_linkage_credential_builder; mod domain_linkage_validator; mod error; +mod utils; pub use self::domain_linkage_configuration::*; pub use self::domain_linkage_credential_builder::*; diff --git a/identity_credential/src/domain_linkage/utils.rs b/identity_credential/src/domain_linkage/utils.rs new file mode 100644 index 0000000000..d8fd8944e7 --- /dev/null +++ b/identity_credential/src/domain_linkage/utils.rs @@ -0,0 +1,19 @@ +use identity_core::common::Url; + +pub(crate) fn url_only_includes_origin(url: &Url) -> bool { + url.path() == "/" && url.query().is_none() && url.fragment().is_none() +} + +#[cfg(test)] +mod tests { + use super::url_only_includes_origin; + use identity_core::common::Url; + + #[test] + fn empty_path_and_root_are_valid_origins() { + let urls = ["https://example.com", "https://example.com/"]; + for url in urls { + assert!(url_only_includes_origin(&Url::parse(url).unwrap())); + } + } +} diff --git a/identity_storage/src/key_storage/memstore.rs b/identity_storage/src/key_storage/memstore.rs index 7dd92fc3c2..f101af4759 100644 --- a/identity_storage/src/key_storage/memstore.rs +++ b/identity_storage/src/key_storage/memstore.rs @@ -186,7 +186,7 @@ enum MemStoreKeyType { } impl JwkMemStore { - const ED25519_KEY_TYPE_STR: &str = "Ed25519"; + const ED25519_KEY_TYPE_STR: &'static str = "Ed25519"; /// The Ed25519 key type. pub const ED25519_KEY_TYPE: KeyType = KeyType::from_static_str(Self::ED25519_KEY_TYPE_STR); }