A library for ATProtocol identities.

refactor: updated list_records to support optional dpop auth

Changed files
+302 -150
crates
atproto-client
atproto-identity
+10 -4
crates/atproto-client/src/com_atproto_repo.rs
··· 213 213 /// A paginated list of records from the collection 214 214 pub async fn list_records<T: DeserializeOwned>( 215 215 http_client: &reqwest::Client, 216 - dpop_auth: &DPoPAuth, 216 + dpop_auth: Option<&DPoPAuth>, 217 217 base_url: &str, 218 218 repo: String, 219 219 collection: String, ··· 240 240 241 241 let url = url_builder.build(); 242 242 243 - get_dpop_json(http_client, dpop_auth, &url) 244 - .await 245 - .and_then(|value| serde_json::from_value(value).map_err(|err| err.into())) 243 + if let Some(dpop_auth) = dpop_auth { 244 + get_dpop_json(http_client, dpop_auth, &url) 245 + .await 246 + .and_then(|value| serde_json::from_value(value).map_err(|err| err.into())) 247 + } else { 248 + get_json(http_client, &url) 249 + .await 250 + .and_then(|value| serde_json::from_value(value).map_err(|err| err.into())) 251 + } 246 252 } 247 253 248 254 /// Request to create a new record in an AT Protocol repository.
+292 -146
crates/atproto-identity/src/validation.rs
··· 52 52 const RESERVED_TLDS: [&str; 4] = [".localhost", ".internal", ".arpa", ".local"]; 53 53 54 54 /// Validates if a string is a valid hostname according to RFC 1035. 55 - /// 55 + /// 56 56 /// A valid hostname must: 57 57 /// - Be between 1 and 253 characters in length 58 58 /// - Not use reserved top-level domains (.localhost, .internal, .arpa, .local) 59 59 /// - Not be an IPv4 or IPv6 address 60 60 /// - Contain only valid hostname characters (letters, digits, hyphens, dots) 61 61 /// - Have valid DNS labels (no leading/trailing hyphens, max 63 chars per label) 62 - /// 62 + /// 63 63 /// # Arguments 64 - /// 64 + /// 65 65 /// * `hostname` - The hostname string to validate 66 - /// 66 + /// 67 67 /// # Returns 68 - /// 68 + /// 69 69 /// `true` if the hostname is valid according to RFC 1035, `false` otherwise 70 - /// 70 + /// 71 71 /// # Examples 72 - /// 72 + /// 73 73 /// ``` 74 74 /// use atproto_identity::validation::is_valid_hostname; 75 - /// 75 + /// 76 76 /// // Valid hostnames 77 77 /// assert!(is_valid_hostname("example.com")); 78 78 /// assert!(is_valid_hostname("sub.example.com")); 79 79 /// assert!(is_valid_hostname("test-host.example.com")); 80 80 /// assert!(is_valid_hostname("localhost")); 81 - /// 81 + /// 82 82 /// // Invalid hostnames 83 83 /// assert!(!is_valid_hostname("192.168.1.1")); // IPv4 address 84 84 /// assert!(!is_valid_hostname("example.localhost")); // Reserved TLD ··· 135 135 } 136 136 137 137 /// Checks if a string is a valid IPv4 address. 138 - /// 138 + /// 139 139 /// Validates that the string consists of exactly four decimal numbers 140 140 /// separated by dots, where each number is between 0 and 255. 141 - /// 141 + /// 142 142 /// # Arguments 143 - /// 143 + /// 144 144 /// * `s` - The string to validate as an IPv4 address 145 - /// 145 + /// 146 146 /// # Returns 147 - /// 147 + /// 148 148 /// `true` if the string is a valid IPv4 address, `false` otherwise 149 - /// 149 + /// 150 150 /// # Examples 151 - /// 151 + /// 152 152 /// ``` 153 153 /// use atproto_identity::validation::is_ipv4; 154 - /// 154 + /// 155 155 /// // Valid IPv4 addresses 156 156 /// assert!(is_ipv4("192.168.1.1")); 157 157 /// assert!(is_ipv4("127.0.0.1")); 158 158 /// assert!(is_ipv4("255.255.255.255")); 159 159 /// assert!(is_ipv4("0.0.0.0")); 160 - /// 160 + /// 161 161 /// // Invalid IPv4 addresses 162 162 /// assert!(!is_ipv4("256.1.1.1")); // Number too large 163 163 /// assert!(!is_ipv4("192.168.1")); // Missing octet ··· 174 174 } 175 175 176 176 /// Checks if a string is a valid IPv6 address. 177 - /// 177 + /// 178 178 /// Performs basic IPv6 validation including: 179 179 /// - Must contain colons (distinguishing from IPv4) 180 180 /// - Supports brackets for URLs (e.g., `[2001:db8::1]`) 181 181 /// - Validates compressed notation with `::` (at most one occurrence) 182 182 /// - Each segment must be valid hexadecimal (1-4 characters) 183 183 /// - At most 8 segments total 184 - /// 184 + /// 185 185 /// # Arguments 186 - /// 186 + /// 187 187 /// * `s` - The string to validate as an IPv6 address 188 - /// 188 + /// 189 189 /// # Returns 190 - /// 190 + /// 191 191 /// `true` if the string is a valid IPv6 address, `false` otherwise 192 - /// 192 + /// 193 193 /// # Examples 194 - /// 194 + /// 195 195 /// ``` 196 196 /// use atproto_identity::validation::is_ipv6; 197 - /// 197 + /// 198 198 /// // Valid IPv6 addresses 199 199 /// assert!(is_ipv6("2001:db8::1")); 200 200 /// assert!(is_ipv6("::1")); 201 201 /// assert!(is_ipv6("fe80::1")); 202 202 /// assert!(is_ipv6("[2001:db8::1]")); // With brackets 203 203 /// assert!(is_ipv6("2001:0db8:0000:0000:0000:ff00:0042:8329")); 204 - /// 204 + /// 205 205 /// // Invalid IPv6 addresses 206 206 /// assert!(!is_ipv6("192.168.1.1")); // IPv4, not IPv6 207 207 /// assert!(!is_ipv6("example.com")); // No colons ··· 241 241 } 242 242 243 243 /// Validates and normalizes an AT Protocol handle. 244 - /// 244 + /// 245 245 /// A valid AT Protocol handle must: 246 246 /// - Be a valid hostname (after stripping prefixes) 247 247 /// - Contain at least one period (to distinguish from simple hostnames) 248 248 /// - Follow all hostname validation rules (RFC 1035) 249 - /// 249 + /// 250 250 /// The function automatically strips common prefixes (`at://` and `@`) before validation. 251 - /// 251 + /// 252 252 /// # Arguments 253 - /// 253 + /// 254 254 /// * `handle` - The handle string to validate and normalize 255 - /// 255 + /// 256 256 /// # Returns 257 - /// 257 + /// 258 258 /// `Some(String)` containing the normalized handle if valid, `None` if invalid 259 - /// 259 + /// 260 260 /// # Examples 261 - /// 261 + /// 262 262 /// ``` 263 263 /// use atproto_identity::validation::is_valid_handle; 264 - /// 264 + /// 265 265 /// // Valid handles 266 266 /// assert_eq!(is_valid_handle("alice.bsky.social"), Some("alice.bsky.social".to_string())); 267 267 /// assert_eq!(is_valid_handle("@bob.example.com"), Some("bob.example.com".to_string())); 268 268 /// assert_eq!(is_valid_handle("at://charlie.test.com"), Some("charlie.test.com".to_string())); 269 - /// 269 + /// 270 270 /// // Invalid handles 271 271 /// assert_eq!(is_valid_handle("localhost"), None); // No period 272 272 /// assert_eq!(is_valid_handle("192.168.1.1"), None); // IPv4 address ··· 285 285 } 286 286 287 287 /// Strips common AT Protocol handle prefixes from a handle string. 288 - /// 288 + /// 289 289 /// Removes the `at://` or `@` prefix if present, returning the clean handle. 290 290 /// This is useful for normalizing handle input from various sources. 291 - /// 291 + /// 292 292 /// # Arguments 293 - /// 293 + /// 294 294 /// * `handle` - The handle string that may contain prefixes 295 - /// 295 + /// 296 296 /// # Returns 297 - /// 297 + /// 298 298 /// The handle string with prefixes removed 299 - /// 299 + /// 300 300 /// # Examples 301 - /// 301 + /// 302 302 /// ``` 303 303 /// use atproto_identity::validation::strip_handle_prefixes; 304 - /// 304 + /// 305 305 /// assert_eq!(strip_handle_prefixes("@alice.bsky.social"), "alice.bsky.social"); 306 306 /// assert_eq!(strip_handle_prefixes("at://bob.example.com"), "bob.example.com"); 307 307 /// assert_eq!(strip_handle_prefixes("charlie.test.com"), "charlie.test.com"); ··· 317 317 } 318 318 319 319 /// Validates if a string is a properly formatted PLC DID. 320 - /// 320 + /// 321 321 /// A valid PLC DID must: 322 322 /// - Start with the prefix `did:plc:` 323 323 /// - Be followed by exactly 24 characters of base32 encoding (lowercase letters a-z and digits 2-7) 324 - /// 324 + /// 325 325 /// # Arguments 326 - /// 326 + /// 327 327 /// * `did` - The DID string to validate 328 - /// 328 + /// 329 329 /// # Returns 330 - /// 330 + /// 331 331 /// `true` if the DID is a valid PLC DID, `false` otherwise 332 - /// 332 + /// 333 333 /// # Examples 334 - /// 334 + /// 335 335 /// ``` 336 336 /// use atproto_identity::validation::is_valid_did_method_plc; 337 - /// 337 + /// 338 338 /// // Valid PLC DIDs 339 339 /// assert!(is_valid_did_method_plc("did:plc:z3f2222fa222f5c33c2f27ez")); 340 340 /// assert!(is_valid_did_method_plc("did:plc:abcdefghijklmnopqrstuvwx")); 341 - /// 341 + /// 342 342 /// // Invalid PLC DIDs 343 343 /// assert!(!is_valid_did_method_plc("did:web:example.com")); 344 344 /// assert!(!is_valid_did_method_plc("did:plc:invalid0length")); ··· 358 358 } 359 359 360 360 /// Validates if a string is a properly formatted Web DID. 361 - /// 361 + /// 362 362 /// A valid Web DID must start with the prefix `did:web:` followed by content that 363 363 /// depends on the strictness mode: 364 - /// 364 + /// 365 365 /// # Strict Mode (`strict = true`) 366 366 /// - Only a valid hostname is allowed after `did:web:` 367 367 /// - No additional path segments permitted 368 - /// 368 + /// 369 369 /// # Non-Strict Mode (`strict = false`) 370 370 /// - First segment must be a valid hostname 371 371 /// - Additional colon-separated segments are allowed 372 372 /// - Each additional segment must be non-empty and alphanumeric 373 - /// 373 + /// 374 374 /// # Arguments 375 - /// 375 + /// 376 376 /// * `did` - The DID string to validate 377 377 /// * `strict` - Whether to use strict hostname-only validation 378 - /// 378 + /// 379 379 /// # Returns 380 - /// 380 + /// 381 381 /// `true` if the DID is a valid Web DID according to the specified mode, `false` otherwise 382 - /// 382 + /// 383 383 /// # Examples 384 - /// 384 + /// 385 385 /// ``` 386 386 /// use atproto_identity::validation::is_valid_did_method_web; 387 - /// 387 + /// 388 388 /// // Valid in both modes 389 389 /// assert!(is_valid_did_method_web("did:web:example.com", true)); 390 390 /// assert!(is_valid_did_method_web("did:web:example.com", false)); 391 - /// 391 + /// 392 392 /// // Valid only in non-strict mode 393 393 /// assert!(!is_valid_did_method_web("did:web:example.com:path", true)); 394 394 /// assert!(is_valid_did_method_web("did:web:example.com:path", false)); 395 395 /// assert!(is_valid_did_method_web("did:web:example.com:path:subpath", false)); 396 - /// 396 + /// 397 397 /// // Invalid in both modes 398 398 /// assert!(!is_valid_did_method_web("did:web:192.168.1.1", true)); 399 399 /// assert!(!is_valid_did_method_web("did:web:example.com:", false)); ··· 429 429 } 430 430 431 431 /// Validates if a string is a properly formatted WebVH DID. 432 - /// 432 + /// 433 433 /// A WebVH DID extends the Web DID format by adding a SCIM (Self-Controlled Identity Marker) 434 434 /// segment immediately after the `did:webvh:` prefix. 435 - /// 435 + /// 436 436 /// # Format 437 - /// 437 + /// 438 438 /// ```text 439 439 /// did:webvh:<scim>:<content> 440 440 /// ``` 441 - /// 441 + /// 442 442 /// Where: 443 443 /// - `<scim>` must contain only base58-btc alphabet characters (`123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`) 444 444 /// - `<content>` follows the same validation rules as `did:web` content 445 - /// 445 + /// 446 446 /// # Strict vs Non-Strict Mode 447 - /// 447 + /// 448 448 /// **Strict Mode (`strict = true`)**: 449 449 /// - `<content>` must be a valid hostname only 450 450 /// - No additional path segments permitted 451 - /// 451 + /// 452 452 /// **Non-Strict Mode (`strict = false`)**: 453 453 /// - First segment of `<content>` must be a valid hostname 454 454 /// - Additional colon-separated segments are allowed 455 455 /// - Each additional segment must be non-empty and alphanumeric 456 - /// 456 + /// 457 457 /// # Arguments 458 - /// 458 + /// 459 459 /// * `did` - The DID string to validate 460 460 /// * `strict` - Whether to use strict hostname-only validation for the content portion 461 - /// 461 + /// 462 462 /// # Returns 463 - /// 463 + /// 464 464 /// `true` if the DID is a valid WebVH DID according to the specified mode, `false` otherwise 465 - /// 465 + /// 466 466 /// # Examples 467 - /// 467 + /// 468 468 /// ``` 469 469 /// use atproto_identity::validation::is_valid_did_method_webvh; 470 - /// 470 + /// 471 471 /// // Valid WebVH DIDs in both modes 472 472 /// assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com", true)); 473 473 /// assert!(is_valid_did_method_webvh("did:webvh:XYZ789:sub.example.com", false)); 474 - /// 474 + /// 475 475 /// // Valid only in non-strict mode (has path segments) 476 476 /// assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path", true)); 477 477 /// assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com:path", false)); 478 478 /// assert!(is_valid_did_method_webvh("did:webvh:def456:example.com:path:subpath", false)); 479 - /// 479 + /// 480 480 /// // Invalid - SCIM contains excluded base58 characters (0, O, I, l) 481 481 /// assert!(!is_valid_did_method_webvh("did:webvh:0abc:example.com", true)); 482 482 /// assert!(!is_valid_did_method_webvh("did:webvh:Oabc:example.com", false)); 483 483 /// assert!(!is_valid_did_method_webvh("did:webvh:Iabc:example.com", true)); 484 484 /// assert!(!is_valid_did_method_webvh("did:webvh:labc:example.com", false)); 485 - /// 485 + /// 486 486 /// // Invalid - wrong format or missing components 487 487 /// assert!(!is_valid_did_method_webvh("did:web:abc123:example.com", true)); // Wrong prefix 488 488 /// assert!(!is_valid_did_method_webvh("did:webvh:abc123", true)); // Missing content ··· 496 496 497 497 // Split by the first colon to separate scim from content 498 498 let parts: Vec<&str> = did_value.splitn(2, ':').collect(); 499 - 499 + 500 500 // Must have exactly 2 parts: scim and content 501 501 if parts.len() != 2 { 502 502 return false; 503 503 } 504 - 504 + 505 505 let scim = parts[0]; 506 506 let content = parts[1]; 507 - 507 + 508 508 // Validate scim - must be non-empty and contain only base58-btc alphabet characters 509 509 if scim.is_empty() || !is_valid_base58_btc(scim) { 510 510 return false; 511 511 } 512 - 512 + 513 513 // Validate content using the same rules as did:web 514 514 if strict { 515 515 // In strict mode, only a valid hostname is allowed ··· 536 536 } 537 537 538 538 /// Checks if a string contains only base58-btc alphabet characters. 539 - /// 539 + /// 540 540 /// The base58-btc alphabet is used in Bitcoin and other cryptocurrency systems. 541 541 /// It includes all alphanumeric characters except those that are easily confused: 542 542 /// - Excludes: `0` (zero), `O` (capital O), `I` (capital I), `l` (lowercase L) 543 543 /// - Includes: `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz` 544 - /// 544 + /// 545 545 /// # Arguments 546 - /// 546 + /// 547 547 /// * `s` - The string to validate for base58-btc character compliance 548 - /// 548 + /// 549 549 /// # Returns 550 - /// 550 + /// 551 551 /// `true` if the string is non-empty and contains only valid base58-btc characters, `false` otherwise 552 - /// 552 + /// 553 553 /// # Examples 554 - /// 554 + /// 555 555 /// ``` 556 556 /// use atproto_identity::validation::is_valid_base58_btc; 557 - /// 557 + /// 558 558 /// // Valid base58-btc strings 559 559 /// assert!(is_valid_base58_btc("123456789")); 560 560 /// assert!(is_valid_base58_btc("ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz")); 561 561 /// assert!(is_valid_base58_btc("abc123XYZ")); 562 - /// 562 + /// 563 563 /// // Invalid - contains excluded characters 564 564 /// assert!(!is_valid_base58_btc("abc0def")); // Contains 0 565 565 /// assert!(!is_valid_base58_btc("abcOdef")); // Contains O 566 566 /// assert!(!is_valid_base58_btc("abcIdef")); // Contains I 567 567 /// assert!(!is_valid_base58_btc("abcldef")); // Contains l 568 - /// 568 + /// 569 569 /// // Invalid - empty or non-alphanumeric 570 570 /// assert!(!is_valid_base58_btc("")); 571 571 /// assert!(!is_valid_base58_btc("abc-def")); ··· 795 795 #[test] 796 796 fn test_is_valid_did_method_webvh() { 797 797 // Test strict mode - valid cases 798 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com", true)); 799 - assert!(is_valid_did_method_webvh("did:webvh:XYZ789:sub.example.com", true)); 800 - assert!(is_valid_did_method_webvh("did:webvh:ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789:example.com", true)); 798 + assert!(is_valid_did_method_webvh( 799 + "did:webvh:abc123:example.com", 800 + true 801 + )); 802 + assert!(is_valid_did_method_webvh( 803 + "did:webvh:XYZ789:sub.example.com", 804 + true 805 + )); 806 + assert!(is_valid_did_method_webvh( 807 + "did:webvh:ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789:example.com", 808 + true 809 + )); 801 810 assert!(is_valid_did_method_webvh("did:webvh:1:example.com", true)); // single char scim 802 - assert!(is_valid_did_method_webvh("did:webvh:zzzzzz:localhost", true)); 811 + assert!(is_valid_did_method_webvh( 812 + "did:webvh:zzzzzz:localhost", 813 + true 814 + )); 803 815 804 816 // Test strict mode - invalid cases with path segments 805 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path", true)); 806 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path:subpath", true)); 817 + assert!(!is_valid_did_method_webvh( 818 + "did:webvh:abc123:example.com:path", 819 + true 820 + )); 821 + assert!(!is_valid_did_method_webvh( 822 + "did:webvh:abc123:example.com:path:subpath", 823 + true 824 + )); 807 825 808 826 // Test non-strict mode - valid cases 809 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com", false)); 810 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com:path", false)); 811 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com:path:subpath", false)); 812 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com:123", false)); 813 - assert!(is_valid_did_method_webvh("did:webvh:abc123:example.com:ABC123", false)); 827 + assert!(is_valid_did_method_webvh( 828 + "did:webvh:abc123:example.com", 829 + false 830 + )); 831 + assert!(is_valid_did_method_webvh( 832 + "did:webvh:abc123:example.com:path", 833 + false 834 + )); 835 + assert!(is_valid_did_method_webvh( 836 + "did:webvh:abc123:example.com:path:subpath", 837 + false 838 + )); 839 + assert!(is_valid_did_method_webvh( 840 + "did:webvh:abc123:example.com:123", 841 + false 842 + )); 843 + assert!(is_valid_did_method_webvh( 844 + "did:webvh:abc123:example.com:ABC123", 845 + false 846 + )); 814 847 815 848 // Invalid - wrong prefix 816 - assert!(!is_valid_did_method_webvh("did:web:abc123:example.com", true)); 817 - assert!(!is_valid_did_method_webvh("did:web:abc123:example.com", false)); 818 - assert!(!is_valid_did_method_webvh("did:plc:abc123:example.com", true)); 849 + assert!(!is_valid_did_method_webvh( 850 + "did:web:abc123:example.com", 851 + true 852 + )); 853 + assert!(!is_valid_did_method_webvh( 854 + "did:web:abc123:example.com", 855 + false 856 + )); 857 + assert!(!is_valid_did_method_webvh( 858 + "did:plc:abc123:example.com", 859 + true 860 + )); 819 861 assert!(!is_valid_did_method_webvh("webvh:abc123:example.com", true)); 820 862 assert!(!is_valid_did_method_webvh("abc123:example.com", true)); 821 863 ··· 827 869 assert!(!is_valid_did_method_webvh("did:webvh:example.com", true)); // no scim separator 828 870 829 871 // Invalid - scim contains invalid base58 characters 830 - assert!(!is_valid_did_method_webvh("did:webvh:0abc:example.com", true)); // contains 0 831 - assert!(!is_valid_did_method_webvh("did:webvh:Oabc:example.com", true)); // contains O 832 - assert!(!is_valid_did_method_webvh("did:webvh:Iabc:example.com", true)); // contains I 833 - assert!(!is_valid_did_method_webvh("did:webvh:labc:example.com", true)); // contains l 834 - assert!(!is_valid_did_method_webvh("did:webvh:abc-123:example.com", true)); // contains - 835 - assert!(!is_valid_did_method_webvh("did:webvh:abc_123:example.com", true)); // contains _ 836 - assert!(!is_valid_did_method_webvh("did:webvh:abc.123:example.com", true)); // contains . 837 - assert!(!is_valid_did_method_webvh("did:webvh:abc@123:example.com", true)); // contains @ 838 - assert!(!is_valid_did_method_webvh("did:webvh:abc 123:example.com", true)); // contains space 839 - assert!(!is_valid_did_method_webvh("did:webvh:abc!123:example.com", true)); // contains ! 840 - assert!(!is_valid_did_method_webvh("did:webvh:abc#123:example.com", true)); // contains # 841 - assert!(!is_valid_did_method_webvh("did:webvh:abc$123:example.com", true)); // contains $ 872 + assert!(!is_valid_did_method_webvh( 873 + "did:webvh:0abc:example.com", 874 + true 875 + )); // contains 0 876 + assert!(!is_valid_did_method_webvh( 877 + "did:webvh:Oabc:example.com", 878 + true 879 + )); // contains O 880 + assert!(!is_valid_did_method_webvh( 881 + "did:webvh:Iabc:example.com", 882 + true 883 + )); // contains I 884 + assert!(!is_valid_did_method_webvh( 885 + "did:webvh:labc:example.com", 886 + true 887 + )); // contains l 888 + assert!(!is_valid_did_method_webvh( 889 + "did:webvh:abc-123:example.com", 890 + true 891 + )); // contains - 892 + assert!(!is_valid_did_method_webvh( 893 + "did:webvh:abc_123:example.com", 894 + true 895 + )); // contains _ 896 + assert!(!is_valid_did_method_webvh( 897 + "did:webvh:abc.123:example.com", 898 + true 899 + )); // contains . 900 + assert!(!is_valid_did_method_webvh( 901 + "did:webvh:abc@123:example.com", 902 + true 903 + )); // contains @ 904 + assert!(!is_valid_did_method_webvh( 905 + "did:webvh:abc 123:example.com", 906 + true 907 + )); // contains space 908 + assert!(!is_valid_did_method_webvh( 909 + "did:webvh:abc!123:example.com", 910 + true 911 + )); // contains ! 912 + assert!(!is_valid_did_method_webvh( 913 + "did:webvh:abc#123:example.com", 914 + true 915 + )); // contains # 916 + assert!(!is_valid_did_method_webvh( 917 + "did:webvh:abc$123:example.com", 918 + true 919 + )); // contains $ 842 920 843 921 // Invalid - bad hostname in content 844 922 assert!(!is_valid_did_method_webvh("did:webvh:abc123:", false)); // empty hostname 845 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:..example.com", true)); 846 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:.example.com", true)); 847 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com.", true)); 848 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:-example.com", true)); 849 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.localhost", true)); // reserved TLD 850 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:192.168.1.1", true)); // IPv4 851 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:2001:db8::1", true)); // IPv6 923 + assert!(!is_valid_did_method_webvh( 924 + "did:webvh:abc123:..example.com", 925 + true 926 + )); 927 + assert!(!is_valid_did_method_webvh( 928 + "did:webvh:abc123:.example.com", 929 + true 930 + )); 931 + assert!(!is_valid_did_method_webvh( 932 + "did:webvh:abc123:example.com.", 933 + true 934 + )); 935 + assert!(!is_valid_did_method_webvh( 936 + "did:webvh:abc123:-example.com", 937 + true 938 + )); 939 + assert!(!is_valid_did_method_webvh( 940 + "did:webvh:abc123:example.localhost", 941 + true 942 + )); // reserved TLD 943 + assert!(!is_valid_did_method_webvh( 944 + "did:webvh:abc123:192.168.1.1", 945 + true 946 + )); // IPv4 947 + assert!(!is_valid_did_method_webvh( 948 + "did:webvh:abc123:2001:db8::1", 949 + true 950 + )); // IPv6 852 951 853 952 // Invalid in non-strict mode - empty path segments 854 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:", false)); 855 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com::", false)); 856 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path:", false)); 857 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com::path", false)); 953 + assert!(!is_valid_did_method_webvh( 954 + "did:webvh:abc123:example.com:", 955 + false 956 + )); 957 + assert!(!is_valid_did_method_webvh( 958 + "did:webvh:abc123:example.com::", 959 + false 960 + )); 961 + assert!(!is_valid_did_method_webvh( 962 + "did:webvh:abc123:example.com:path:", 963 + false 964 + )); 965 + assert!(!is_valid_did_method_webvh( 966 + "did:webvh:abc123:example.com::path", 967 + false 968 + )); 858 969 859 970 // Invalid in non-strict mode - non-alphanumeric in path segments 860 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path/subpath", false)); 861 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path-name", false)); 862 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path_name", false)); 863 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path.name", false)); 864 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path@name", false)); 865 - assert!(!is_valid_did_method_webvh("did:webvh:abc123:example.com:path name", false)); 971 + assert!(!is_valid_did_method_webvh( 972 + "did:webvh:abc123:example.com:path/subpath", 973 + false 974 + )); 975 + assert!(!is_valid_did_method_webvh( 976 + "did:webvh:abc123:example.com:path-name", 977 + false 978 + )); 979 + assert!(!is_valid_did_method_webvh( 980 + "did:webvh:abc123:example.com:path_name", 981 + false 982 + )); 983 + assert!(!is_valid_did_method_webvh( 984 + "did:webvh:abc123:example.com:path.name", 985 + false 986 + )); 987 + assert!(!is_valid_did_method_webvh( 988 + "did:webvh:abc123:example.com:path@name", 989 + false 990 + )); 991 + assert!(!is_valid_did_method_webvh( 992 + "did:webvh:abc123:example.com:path name", 993 + false 994 + )); 866 995 867 996 // Edge cases with base58 characters 868 - assert!(is_valid_did_method_webvh("did:webvh:111111:example.com", true)); // all 1s 869 - assert!(is_valid_did_method_webvh("did:webvh:999999:example.com", true)); // all 9s 870 - assert!(is_valid_did_method_webvh("did:webvh:AAAAAA:example.com", true)); // all As 871 - assert!(is_valid_did_method_webvh("did:webvh:zzzzzz:example.com", true)); // all zs 872 - assert!(is_valid_did_method_webvh("did:webvh:HJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz:example.com", true)); // no excluded letters 997 + assert!(is_valid_did_method_webvh( 998 + "did:webvh:111111:example.com", 999 + true 1000 + )); // all 1s 1001 + assert!(is_valid_did_method_webvh( 1002 + "did:webvh:999999:example.com", 1003 + true 1004 + )); // all 9s 1005 + assert!(is_valid_did_method_webvh( 1006 + "did:webvh:AAAAAA:example.com", 1007 + true 1008 + )); // all As 1009 + assert!(is_valid_did_method_webvh( 1010 + "did:webvh:zzzzzz:example.com", 1011 + true 1012 + )); // all zs 1013 + assert!(is_valid_did_method_webvh( 1014 + "did:webvh:HJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz:example.com", 1015 + true 1016 + )); // no excluded letters 873 1017 } 874 1018 875 1019 #[test] 876 1020 fn test_is_valid_base58_btc() { 877 1021 // Valid base58 strings 878 1022 assert!(is_valid_base58_btc("123456789")); 879 - assert!(is_valid_base58_btc("ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz")); 1023 + assert!(is_valid_base58_btc( 1024 + "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz" 1025 + )); 880 1026 assert!(is_valid_base58_btc("1")); 881 1027 assert!(is_valid_base58_btc("z")); 882 1028 assert!(is_valid_base58_btc("ABC123xyz"));