Microservice to bring 2FA to self hosted PDSes

WIP

+2 -1
.gitignore
··· 1 1 /target 2 - .idea 2 + .idea 3 + pds.env
+1355 -225
Cargo.lock
··· 24 24 checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" 25 25 dependencies = [ 26 26 "cfg-if", 27 - "getrandom 0.3.3", 28 27 "once_cell", 29 28 "version_check", 30 29 "zerocopy", ··· 46 45 checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 47 46 48 47 [[package]] 48 + name = "android-tzdata" 49 + version = "0.1.1" 50 + source = "registry+https://github.com/rust-lang/crates.io-index" 51 + checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 52 + 53 + [[package]] 54 + name = "android_system_properties" 55 + version = "0.1.5" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 58 + dependencies = [ 59 + "libc", 60 + ] 61 + 62 + [[package]] 63 + name = "anyhow" 64 + version = "1.0.99" 65 + source = "registry+https://github.com/rust-lang/crates.io-index" 66 + checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 67 + 68 + [[package]] 69 + name = "async-compression" 70 + version = "0.4.27" 71 + source = "registry+https://github.com/rust-lang/crates.io-index" 72 + checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" 73 + dependencies = [ 74 + "futures-core", 75 + "memchr", 76 + "pin-project-lite", 77 + "tokio", 78 + "zstd", 79 + "zstd-safe", 80 + ] 81 + 82 + [[package]] 49 83 name = "async-trait" 50 84 version = "0.1.89" 51 85 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 53 87 dependencies = [ 54 88 "proc-macro2", 55 89 "quote", 56 - "syn 2.0.105", 90 + "syn", 57 91 ] 58 92 59 93 [[package]] ··· 66 100 ] 67 101 68 102 [[package]] 103 + name = "atomic-waker" 104 + version = "1.1.2" 105 + source = "registry+https://github.com/rust-lang/crates.io-index" 106 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 107 + 108 + [[package]] 69 109 name = "autocfg" 70 110 version = "1.5.0" 71 111 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 73 113 74 114 [[package]] 75 115 name = "axum" 76 - version = "0.7.9" 116 + version = "0.8.4" 77 117 source = "registry+https://github.com/rust-lang/crates.io-index" 78 - checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" 118 + checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" 79 119 dependencies = [ 80 - "async-trait", 81 120 "axum-core", 82 121 "axum-macros", 83 122 "bytes", 123 + "form_urlencoded", 84 124 "futures-util", 85 125 "http", 86 126 "http-body", ··· 108 148 109 149 [[package]] 110 150 name = "axum-core" 111 - version = "0.4.5" 151 + version = "0.5.2" 112 152 source = "registry+https://github.com/rust-lang/crates.io-index" 113 - checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" 153 + checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" 114 154 dependencies = [ 115 - "async-trait", 116 155 "bytes", 117 - "futures-util", 156 + "futures-core", 118 157 "http", 119 158 "http-body", 120 159 "http-body-util", ··· 129 168 130 169 [[package]] 131 170 name = "axum-macros" 132 - version = "0.4.2" 171 + version = "0.5.0" 133 172 source = "registry+https://github.com/rust-lang/crates.io-index" 134 - checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" 173 + checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" 135 174 dependencies = [ 136 175 "proc-macro2", 137 176 "quote", 138 - "syn 2.0.105", 177 + "syn", 178 + ] 179 + 180 + [[package]] 181 + name = "axum-template" 182 + version = "3.0.0" 183 + source = "registry+https://github.com/rust-lang/crates.io-index" 184 + checksum = "3df50f7d669bfc3a8c348f08f536fe37e7acfbeded3cfdffd2ad3d76725fc40c" 185 + dependencies = [ 186 + "axum", 187 + "handlebars", 188 + "serde", 189 + "thiserror 2.0.14", 139 190 ] 140 191 141 192 [[package]] ··· 155 206 156 207 [[package]] 157 208 name = "base64" 158 - version = "0.21.7" 209 + version = "0.22.1" 159 210 source = "registry+https://github.com/rust-lang/crates.io-index" 160 - checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" 211 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 161 212 162 213 [[package]] 163 214 name = "base64ct" ··· 184 235 ] 185 236 186 237 [[package]] 238 + name = "bstr" 239 + version = "1.12.0" 240 + source = "registry+https://github.com/rust-lang/crates.io-index" 241 + checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" 242 + dependencies = [ 243 + "memchr", 244 + "serde", 245 + ] 246 + 247 + [[package]] 248 + name = "bumpalo" 249 + version = "3.19.0" 250 + source = "registry+https://github.com/rust-lang/crates.io-index" 251 + checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" 252 + 253 + [[package]] 187 254 name = "byteorder" 188 255 version = "1.5.0" 189 256 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 201 268 source = "registry+https://github.com/rust-lang/crates.io-index" 202 269 checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" 203 270 dependencies = [ 271 + "jobserver", 272 + "libc", 204 273 "shlex", 205 274 ] 206 275 ··· 211 280 checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 212 281 213 282 [[package]] 283 + name = "chrono" 284 + version = "0.4.41" 285 + source = "registry+https://github.com/rust-lang/crates.io-index" 286 + checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" 287 + dependencies = [ 288 + "android-tzdata", 289 + "iana-time-zone", 290 + "num-traits", 291 + "windows-link", 292 + ] 293 + 294 + [[package]] 295 + name = "chumsky" 296 + version = "0.9.3" 297 + source = "registry+https://github.com/rust-lang/crates.io-index" 298 + checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9" 299 + dependencies = [ 300 + "hashbrown 0.14.5", 301 + "stacker", 302 + ] 303 + 304 + [[package]] 305 + name = "ciborium" 306 + version = "0.2.2" 307 + source = "registry+https://github.com/rust-lang/crates.io-index" 308 + checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 309 + dependencies = [ 310 + "ciborium-io", 311 + "ciborium-ll", 312 + "serde", 313 + ] 314 + 315 + [[package]] 316 + name = "ciborium-io" 317 + version = "0.2.2" 318 + source = "registry+https://github.com/rust-lang/crates.io-index" 319 + checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 320 + 321 + [[package]] 322 + name = "ciborium-ll" 323 + version = "0.2.2" 324 + source = "registry+https://github.com/rust-lang/crates.io-index" 325 + checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 326 + dependencies = [ 327 + "ciborium-io", 328 + "half", 329 + ] 330 + 331 + [[package]] 332 + name = "cipher" 333 + version = "0.4.4" 334 + source = "registry+https://github.com/rust-lang/crates.io-index" 335 + checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 336 + dependencies = [ 337 + "crypto-common", 338 + "inout", 339 + ] 340 + 341 + [[package]] 342 + name = "concurrent-queue" 343 + version = "2.5.0" 344 + source = "registry+https://github.com/rust-lang/crates.io-index" 345 + checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 346 + dependencies = [ 347 + "crossbeam-utils", 348 + ] 349 + 350 + [[package]] 214 351 name = "const-oid" 215 352 version = "0.9.6" 216 353 source = "registry+https://github.com/rust-lang/crates.io-index" 217 354 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" 218 355 219 356 [[package]] 357 + name = "core-foundation" 358 + version = "0.9.4" 359 + source = "registry+https://github.com/rust-lang/crates.io-index" 360 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 361 + dependencies = [ 362 + "core-foundation-sys", 363 + "libc", 364 + ] 365 + 366 + [[package]] 367 + name = "core-foundation-sys" 368 + version = "0.8.7" 369 + source = "registry+https://github.com/rust-lang/crates.io-index" 370 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 371 + 372 + [[package]] 220 373 name = "cpufeatures" 221 374 version = "0.2.17" 222 375 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 256 409 checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 257 410 258 411 [[package]] 412 + name = "crunchy" 413 + version = "0.2.4" 414 + source = "registry+https://github.com/rust-lang/crates.io-index" 415 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 416 + 417 + [[package]] 259 418 name = "crypto-common" 260 419 version = "0.1.6" 261 420 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 266 425 ] 267 426 268 427 [[package]] 428 + name = "darling" 429 + version = "0.20.11" 430 + source = "registry+https://github.com/rust-lang/crates.io-index" 431 + checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" 432 + dependencies = [ 433 + "darling_core", 434 + "darling_macro", 435 + ] 436 + 437 + [[package]] 438 + name = "darling_core" 439 + version = "0.20.11" 440 + source = "registry+https://github.com/rust-lang/crates.io-index" 441 + checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" 442 + dependencies = [ 443 + "fnv", 444 + "ident_case", 445 + "proc-macro2", 446 + "quote", 447 + "strsim", 448 + "syn", 449 + ] 450 + 451 + [[package]] 452 + name = "darling_macro" 453 + version = "0.20.11" 454 + source = "registry+https://github.com/rust-lang/crates.io-index" 455 + checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" 456 + dependencies = [ 457 + "darling_core", 458 + "quote", 459 + "syn", 460 + ] 461 + 462 + [[package]] 463 + name = "dashmap" 464 + version = "6.1.0" 465 + source = "registry+https://github.com/rust-lang/crates.io-index" 466 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 467 + dependencies = [ 468 + "cfg-if", 469 + "crossbeam-utils", 470 + "hashbrown 0.14.5", 471 + "lock_api", 472 + "once_cell", 473 + "parking_lot_core", 474 + ] 475 + 476 + [[package]] 269 477 name = "der" 270 478 version = "0.7.10" 271 479 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 277 485 ] 278 486 279 487 [[package]] 488 + name = "derive_builder" 489 + version = "0.20.2" 490 + source = "registry+https://github.com/rust-lang/crates.io-index" 491 + checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" 492 + dependencies = [ 493 + "derive_builder_macro", 494 + ] 495 + 496 + [[package]] 497 + name = "derive_builder_core" 498 + version = "0.20.2" 499 + source = "registry+https://github.com/rust-lang/crates.io-index" 500 + checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" 501 + dependencies = [ 502 + "darling", 503 + "proc-macro2", 504 + "quote", 505 + "syn", 506 + ] 507 + 508 + [[package]] 509 + name = "derive_builder_macro" 510 + version = "0.20.2" 511 + source = "registry+https://github.com/rust-lang/crates.io-index" 512 + checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" 513 + dependencies = [ 514 + "derive_builder_core", 515 + "syn", 516 + ] 517 + 518 + [[package]] 280 519 name = "digest" 281 520 version = "0.10.7" 282 521 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 296 535 dependencies = [ 297 536 "proc-macro2", 298 537 "quote", 299 - "syn 2.0.105", 538 + "syn", 300 539 ] 301 540 302 541 [[package]] ··· 315 554 ] 316 555 317 556 [[package]] 557 + name = "email-encoding" 558 + version = "0.4.1" 559 + source = "registry+https://github.com/rust-lang/crates.io-index" 560 + checksum = "9298e6504d9b9e780ed3f7dfd43a61be8cd0e09eb07f7706a945b0072b6670b6" 561 + dependencies = [ 562 + "base64", 563 + "memchr", 564 + ] 565 + 566 + [[package]] 567 + name = "email_address" 568 + version = "0.2.9" 569 + source = "registry+https://github.com/rust-lang/crates.io-index" 570 + checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" 571 + 572 + [[package]] 318 573 name = "equivalent" 319 574 version = "1.0.2" 320 575 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 327 582 checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 328 583 dependencies = [ 329 584 "libc", 330 - "windows-sys 0.60.2", 585 + "windows-sys 0.59.0", 331 586 ] 332 587 333 588 [[package]] ··· 343 598 344 599 [[package]] 345 600 name = "event-listener" 346 - version = "2.5.3" 601 + version = "5.4.1" 347 602 source = "registry+https://github.com/rust-lang/crates.io-index" 348 - checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 603 + checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" 604 + dependencies = [ 605 + "concurrent-queue", 606 + "parking", 607 + "pin-project-lite", 608 + ] 349 609 350 610 [[package]] 351 611 name = "fastrand" ··· 371 631 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 372 632 373 633 [[package]] 634 + name = "foldhash" 635 + version = "0.1.5" 636 + source = "registry+https://github.com/rust-lang/crates.io-index" 637 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 638 + 639 + [[package]] 640 + name = "foreign-types" 641 + version = "0.3.2" 642 + source = "registry+https://github.com/rust-lang/crates.io-index" 643 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 644 + dependencies = [ 645 + "foreign-types-shared", 646 + ] 647 + 648 + [[package]] 649 + name = "foreign-types-shared" 650 + version = "0.1.1" 651 + source = "registry+https://github.com/rust-lang/crates.io-index" 652 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 653 + 654 + [[package]] 374 655 name = "form_urlencoded" 375 656 version = "1.2.1" 376 657 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 380 661 ] 381 662 382 663 [[package]] 664 + name = "forwarded-header-value" 665 + version = "0.1.1" 666 + source = "registry+https://github.com/rust-lang/crates.io-index" 667 + checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9" 668 + dependencies = [ 669 + "nonempty", 670 + "thiserror 1.0.69", 671 + ] 672 + 673 + [[package]] 383 674 name = "futures-channel" 384 675 version = "0.3.31" 385 676 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 436 727 checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 437 728 438 729 [[package]] 730 + name = "futures-timer" 731 + version = "3.0.3" 732 + source = "registry+https://github.com/rust-lang/crates.io-index" 733 + checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" 734 + 735 + [[package]] 439 736 name = "futures-util" 440 737 version = "0.3.31" 441 738 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 479 776 checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 480 777 dependencies = [ 481 778 "cfg-if", 779 + "js-sys", 482 780 "libc", 483 781 "r-efi", 484 782 "wasi 0.14.2+wasi-0.2.4", 783 + "wasm-bindgen", 485 784 ] 486 785 487 786 [[package]] ··· 491 790 checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 492 791 493 792 [[package]] 793 + name = "globset" 794 + version = "0.4.16" 795 + source = "registry+https://github.com/rust-lang/crates.io-index" 796 + checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" 797 + dependencies = [ 798 + "aho-corasick", 799 + "bstr", 800 + "log", 801 + "regex-automata 0.4.9", 802 + "regex-syntax 0.8.5", 803 + ] 804 + 805 + [[package]] 806 + name = "governor" 807 + version = "0.10.1" 808 + source = "registry+https://github.com/rust-lang/crates.io-index" 809 + checksum = "444405bbb1a762387aa22dd569429533b54a1d8759d35d3b64cb39b0293eaa19" 810 + dependencies = [ 811 + "cfg-if", 812 + "dashmap", 813 + "futures-sink", 814 + "futures-timer", 815 + "futures-util", 816 + "getrandom 0.3.3", 817 + "hashbrown 0.15.5", 818 + "nonzero_ext", 819 + "parking_lot", 820 + "portable-atomic", 821 + "quanta", 822 + "rand 0.9.2", 823 + "smallvec", 824 + "spinning_top", 825 + "web-time", 826 + ] 827 + 828 + [[package]] 829 + name = "h2" 830 + version = "0.4.12" 831 + source = "registry+https://github.com/rust-lang/crates.io-index" 832 + checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" 833 + dependencies = [ 834 + "atomic-waker", 835 + "bytes", 836 + "fnv", 837 + "futures-core", 838 + "futures-sink", 839 + "http", 840 + "indexmap", 841 + "slab", 842 + "tokio", 843 + "tokio-util", 844 + "tracing", 845 + ] 846 + 847 + [[package]] 848 + name = "half" 849 + version = "2.6.0" 850 + source = "registry+https://github.com/rust-lang/crates.io-index" 851 + checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" 852 + dependencies = [ 853 + "cfg-if", 854 + "crunchy", 855 + ] 856 + 857 + [[package]] 858 + name = "handlebars" 859 + version = "6.3.2" 860 + source = "registry+https://github.com/rust-lang/crates.io-index" 861 + checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" 862 + dependencies = [ 863 + "derive_builder", 864 + "log", 865 + "num-order", 866 + "pest", 867 + "pest_derive", 868 + "rust-embed", 869 + "serde", 870 + "serde_json", 871 + "thiserror 2.0.14", 872 + ] 873 + 874 + [[package]] 494 875 name = "hashbrown" 495 876 version = "0.14.5" 496 877 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 505 886 version = "0.15.5" 506 887 source = "registry+https://github.com/rust-lang/crates.io-index" 507 888 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 889 + dependencies = [ 890 + "allocator-api2", 891 + "equivalent", 892 + "foldhash", 893 + ] 508 894 509 895 [[package]] 510 896 name = "hashlink" 511 - version = "0.8.4" 897 + version = "0.10.0" 512 898 source = "registry+https://github.com/rust-lang/crates.io-index" 513 - checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" 899 + checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" 514 900 dependencies = [ 515 - "hashbrown 0.14.5", 901 + "hashbrown 0.15.5", 516 902 ] 517 903 518 904 [[package]] 519 905 name = "heck" 520 - version = "0.4.1" 906 + version = "0.5.0" 521 907 source = "registry+https://github.com/rust-lang/crates.io-index" 522 - checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 523 - dependencies = [ 524 - "unicode-segmentation", 525 - ] 908 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 526 909 527 910 [[package]] 528 911 name = "hex" ··· 558 941 ] 559 942 560 943 [[package]] 944 + name = "hostname" 945 + version = "0.4.1" 946 + source = "registry+https://github.com/rust-lang/crates.io-index" 947 + checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" 948 + dependencies = [ 949 + "cfg-if", 950 + "libc", 951 + "windows-link", 952 + ] 953 + 954 + [[package]] 561 955 name = "http" 562 956 version = "1.3.1" 563 957 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 612 1006 "bytes", 613 1007 "futures-channel", 614 1008 "futures-util", 1009 + "h2", 615 1010 "http", 616 1011 "http-body", 617 1012 "httparse", ··· 620 1015 "pin-project-lite", 621 1016 "smallvec", 622 1017 "tokio", 1018 + "want", 1019 + ] 1020 + 1021 + [[package]] 1022 + name = "hyper-timeout" 1023 + version = "0.5.2" 1024 + source = "registry+https://github.com/rust-lang/crates.io-index" 1025 + checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" 1026 + dependencies = [ 1027 + "hyper", 1028 + "hyper-util", 1029 + "pin-project-lite", 1030 + "tokio", 1031 + "tower-service", 623 1032 ] 624 1033 625 1034 [[package]] ··· 629 1038 checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" 630 1039 dependencies = [ 631 1040 "bytes", 1041 + "futures-channel", 632 1042 "futures-core", 1043 + "futures-util", 633 1044 "http", 634 1045 "http-body", 635 1046 "hyper", 1047 + "libc", 636 1048 "pin-project-lite", 1049 + "socket2", 637 1050 "tokio", 638 1051 "tower-service", 1052 + "tracing", 1053 + ] 1054 + 1055 + [[package]] 1056 + name = "iana-time-zone" 1057 + version = "0.1.63" 1058 + source = "registry+https://github.com/rust-lang/crates.io-index" 1059 + checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" 1060 + dependencies = [ 1061 + "android_system_properties", 1062 + "core-foundation-sys", 1063 + "iana-time-zone-haiku", 1064 + "js-sys", 1065 + "log", 1066 + "wasm-bindgen", 1067 + "windows-core", 1068 + ] 1069 + 1070 + [[package]] 1071 + name = "iana-time-zone-haiku" 1072 + version = "0.1.2" 1073 + source = "registry+https://github.com/rust-lang/crates.io-index" 1074 + checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1075 + dependencies = [ 1076 + "cc", 639 1077 ] 640 1078 641 1079 [[package]] ··· 725 1163 ] 726 1164 727 1165 [[package]] 1166 + name = "ident_case" 1167 + version = "1.0.1" 1168 + source = "registry+https://github.com/rust-lang/crates.io-index" 1169 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 1170 + 1171 + [[package]] 728 1172 name = "idna" 729 1173 version = "1.0.3" 730 1174 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 756 1200 ] 757 1201 758 1202 [[package]] 1203 + name = "inout" 1204 + version = "0.1.4" 1205 + source = "registry+https://github.com/rust-lang/crates.io-index" 1206 + checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" 1207 + dependencies = [ 1208 + "generic-array", 1209 + ] 1210 + 1211 + [[package]] 759 1212 name = "io-uring" 760 1213 version = "0.7.9" 761 1214 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 773 1226 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 774 1227 775 1228 [[package]] 1229 + name = "jobserver" 1230 + version = "0.1.33" 1231 + source = "registry+https://github.com/rust-lang/crates.io-index" 1232 + checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" 1233 + dependencies = [ 1234 + "getrandom 0.3.3", 1235 + "libc", 1236 + ] 1237 + 1238 + [[package]] 1239 + name = "js-sys" 1240 + version = "0.3.77" 1241 + source = "registry+https://github.com/rust-lang/crates.io-index" 1242 + checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 1243 + dependencies = [ 1244 + "once_cell", 1245 + "wasm-bindgen", 1246 + ] 1247 + 1248 + [[package]] 1249 + name = "jwt-compact" 1250 + version = "0.8.0" 1251 + source = "registry+https://github.com/rust-lang/crates.io-index" 1252 + checksum = "25cb2458ca54de48ef237ac0d68d4e80e512ae81d6aeb9775f4c835da0d193d3" 1253 + dependencies = [ 1254 + "anyhow", 1255 + "base64ct", 1256 + "chrono", 1257 + "ciborium", 1258 + "hmac", 1259 + "lazy_static", 1260 + "rand_core 0.6.4", 1261 + "secp256k1", 1262 + "serde", 1263 + "serde_json", 1264 + "sha2", 1265 + "smallvec", 1266 + "subtle", 1267 + "zeroize", 1268 + ] 1269 + 1270 + [[package]] 776 1271 name = "lazy_static" 777 1272 version = "1.5.0" 778 1273 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 782 1277 ] 783 1278 784 1279 [[package]] 1280 + name = "lettre" 1281 + version = "0.11.18" 1282 + source = "registry+https://github.com/rust-lang/crates.io-index" 1283 + checksum = "5cb54db6ff7a89efac87dba5baeac57bb9ccd726b49a9b6f21fb92b3966aaf56" 1284 + dependencies = [ 1285 + "async-trait", 1286 + "base64", 1287 + "chumsky", 1288 + "email-encoding", 1289 + "email_address", 1290 + "fastrand", 1291 + "futures-io", 1292 + "futures-util", 1293 + "hostname", 1294 + "httpdate", 1295 + "idna", 1296 + "mime", 1297 + "native-tls", 1298 + "nom", 1299 + "percent-encoding", 1300 + "quoted_printable", 1301 + "socket2", 1302 + "tokio", 1303 + "tokio-native-tls", 1304 + "url", 1305 + ] 1306 + 1307 + [[package]] 785 1308 name = "libc" 786 1309 version = "0.2.175" 787 1310 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 806 1329 807 1330 [[package]] 808 1331 name = "libsqlite3-sys" 809 - version = "0.27.0" 1332 + version = "0.30.1" 810 1333 source = "registry+https://github.com/rust-lang/crates.io-index" 811 - checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" 1334 + checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" 812 1335 dependencies = [ 813 1336 "cc", 814 1337 "pkg-config", ··· 854 1377 855 1378 [[package]] 856 1379 name = "matchit" 857 - version = "0.7.3" 1380 + version = "0.8.4" 858 1381 source = "registry+https://github.com/rust-lang/crates.io-index" 859 - checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 1382 + checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 860 1383 861 1384 [[package]] 862 1385 name = "md-5" ··· 881 1404 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 882 1405 883 1406 [[package]] 884 - name = "minimal-lexical" 885 - version = "0.2.1" 886 - source = "registry+https://github.com/rust-lang/crates.io-index" 887 - checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 888 - 889 - [[package]] 890 1407 name = "miniz_oxide" 891 1408 version = "0.8.9" 892 1409 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 907 1424 ] 908 1425 909 1426 [[package]] 1427 + name = "native-tls" 1428 + version = "0.2.14" 1429 + source = "registry+https://github.com/rust-lang/crates.io-index" 1430 + checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" 1431 + dependencies = [ 1432 + "libc", 1433 + "log", 1434 + "openssl", 1435 + "openssl-probe", 1436 + "openssl-sys", 1437 + "schannel", 1438 + "security-framework", 1439 + "security-framework-sys", 1440 + "tempfile", 1441 + ] 1442 + 1443 + [[package]] 910 1444 name = "nom" 911 - version = "7.1.3" 1445 + version = "8.0.0" 912 1446 source = "registry+https://github.com/rust-lang/crates.io-index" 913 - checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 1447 + checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" 914 1448 dependencies = [ 915 1449 "memchr", 916 - "minimal-lexical", 917 1450 ] 918 1451 919 1452 [[package]] 1453 + name = "nonempty" 1454 + version = "0.7.0" 1455 + source = "registry+https://github.com/rust-lang/crates.io-index" 1456 + checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7" 1457 + 1458 + [[package]] 1459 + name = "nonzero_ext" 1460 + version = "0.3.0" 1461 + source = "registry+https://github.com/rust-lang/crates.io-index" 1462 + checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" 1463 + 1464 + [[package]] 920 1465 name = "nu-ansi-term" 921 1466 version = "0.46.0" 922 1467 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 938 1483 "num-integer", 939 1484 "num-iter", 940 1485 "num-traits", 941 - "rand", 1486 + "rand 0.8.5", 942 1487 "smallvec", 943 1488 "zeroize", 944 1489 ] ··· 964 1509 ] 965 1510 966 1511 [[package]] 1512 + name = "num-modular" 1513 + version = "0.6.1" 1514 + source = "registry+https://github.com/rust-lang/crates.io-index" 1515 + checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" 1516 + 1517 + [[package]] 1518 + name = "num-order" 1519 + version = "1.2.0" 1520 + source = "registry+https://github.com/rust-lang/crates.io-index" 1521 + checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" 1522 + dependencies = [ 1523 + "num-modular", 1524 + ] 1525 + 1526 + [[package]] 967 1527 name = "num-traits" 968 1528 version = "0.2.19" 969 1529 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 989 1549 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 990 1550 991 1551 [[package]] 1552 + name = "openssl" 1553 + version = "0.10.73" 1554 + source = "registry+https://github.com/rust-lang/crates.io-index" 1555 + checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" 1556 + dependencies = [ 1557 + "bitflags", 1558 + "cfg-if", 1559 + "foreign-types", 1560 + "libc", 1561 + "once_cell", 1562 + "openssl-macros", 1563 + "openssl-sys", 1564 + ] 1565 + 1566 + [[package]] 1567 + name = "openssl-macros" 1568 + version = "0.1.1" 1569 + source = "registry+https://github.com/rust-lang/crates.io-index" 1570 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1571 + dependencies = [ 1572 + "proc-macro2", 1573 + "quote", 1574 + "syn", 1575 + ] 1576 + 1577 + [[package]] 1578 + name = "openssl-probe" 1579 + version = "0.1.6" 1580 + source = "registry+https://github.com/rust-lang/crates.io-index" 1581 + checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" 1582 + 1583 + [[package]] 1584 + name = "openssl-sys" 1585 + version = "0.9.109" 1586 + source = "registry+https://github.com/rust-lang/crates.io-index" 1587 + checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" 1588 + dependencies = [ 1589 + "cc", 1590 + "libc", 1591 + "pkg-config", 1592 + "vcpkg", 1593 + ] 1594 + 1595 + [[package]] 992 1596 name = "overload" 993 1597 version = "0.1.1" 994 1598 source = "registry+https://github.com/rust-lang/crates.io-index" 995 1599 checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 1600 + 1601 + [[package]] 1602 + name = "parking" 1603 + version = "2.2.1" 1604 + source = "registry+https://github.com/rust-lang/crates.io-index" 1605 + checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" 996 1606 997 1607 [[package]] 998 1608 name = "parking_lot" ··· 1018 1628 ] 1019 1629 1020 1630 [[package]] 1021 - name = "paste" 1022 - version = "1.0.15" 1631 + name = "password-hash" 1632 + version = "0.5.0" 1633 + source = "registry+https://github.com/rust-lang/crates.io-index" 1634 + checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 1635 + dependencies = [ 1636 + "base64ct", 1637 + "rand_core 0.6.4", 1638 + "subtle", 1639 + ] 1640 + 1641 + [[package]] 1642 + name = "pbkdf2" 1643 + version = "0.12.2" 1023 1644 source = "registry+https://github.com/rust-lang/crates.io-index" 1024 - checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1645 + checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 1646 + dependencies = [ 1647 + "digest", 1648 + "hmac", 1649 + ] 1025 1650 1026 1651 [[package]] 1027 - name = "pds_bells_and_whistles" 1652 + name = "pds_gatekeeper" 1028 1653 version = "0.1.0" 1029 1654 dependencies = [ 1030 1655 "axum", 1656 + "axum-template", 1031 1657 "dotenvy", 1658 + "handlebars", 1659 + "hex", 1660 + "hyper-util", 1661 + "jwt-compact", 1662 + "lettre", 1663 + "rust-embed", 1664 + "scrypt", 1032 1665 "serde", 1033 1666 "serde_json", 1034 1667 "sqlx", 1035 1668 "tokio", 1669 + "tower-http", 1670 + "tower_governor", 1036 1671 "tracing", 1037 1672 "tracing-subscriber", 1038 1673 ] ··· 1053 1688 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1054 1689 1055 1690 [[package]] 1691 + name = "pest" 1692 + version = "2.8.1" 1693 + source = "registry+https://github.com/rust-lang/crates.io-index" 1694 + checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" 1695 + dependencies = [ 1696 + "memchr", 1697 + "thiserror 2.0.14", 1698 + "ucd-trie", 1699 + ] 1700 + 1701 + [[package]] 1702 + name = "pest_derive" 1703 + version = "2.8.1" 1704 + source = "registry+https://github.com/rust-lang/crates.io-index" 1705 + checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" 1706 + dependencies = [ 1707 + "pest", 1708 + "pest_generator", 1709 + ] 1710 + 1711 + [[package]] 1712 + name = "pest_generator" 1713 + version = "2.8.1" 1714 + source = "registry+https://github.com/rust-lang/crates.io-index" 1715 + checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" 1716 + dependencies = [ 1717 + "pest", 1718 + "pest_meta", 1719 + "proc-macro2", 1720 + "quote", 1721 + "syn", 1722 + ] 1723 + 1724 + [[package]] 1725 + name = "pest_meta" 1726 + version = "2.8.1" 1727 + source = "registry+https://github.com/rust-lang/crates.io-index" 1728 + checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" 1729 + dependencies = [ 1730 + "pest", 1731 + "sha2", 1732 + ] 1733 + 1734 + [[package]] 1735 + name = "pin-project" 1736 + version = "1.1.10" 1737 + source = "registry+https://github.com/rust-lang/crates.io-index" 1738 + checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" 1739 + dependencies = [ 1740 + "pin-project-internal", 1741 + ] 1742 + 1743 + [[package]] 1744 + name = "pin-project-internal" 1745 + version = "1.1.10" 1746 + source = "registry+https://github.com/rust-lang/crates.io-index" 1747 + checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" 1748 + dependencies = [ 1749 + "proc-macro2", 1750 + "quote", 1751 + "syn", 1752 + ] 1753 + 1754 + [[package]] 1056 1755 name = "pin-project-lite" 1057 1756 version = "0.2.16" 1058 1757 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1092 1791 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1093 1792 1094 1793 [[package]] 1794 + name = "portable-atomic" 1795 + version = "1.11.1" 1796 + source = "registry+https://github.com/rust-lang/crates.io-index" 1797 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 1798 + 1799 + [[package]] 1095 1800 name = "potential_utf" 1096 1801 version = "0.1.2" 1097 1802 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1119 1824 ] 1120 1825 1121 1826 [[package]] 1827 + name = "psm" 1828 + version = "0.1.26" 1829 + source = "registry+https://github.com/rust-lang/crates.io-index" 1830 + checksum = "6e944464ec8536cd1beb0bbfd96987eb5e3b72f2ecdafdc5c769a37f1fa2ae1f" 1831 + dependencies = [ 1832 + "cc", 1833 + ] 1834 + 1835 + [[package]] 1836 + name = "quanta" 1837 + version = "0.12.6" 1838 + source = "registry+https://github.com/rust-lang/crates.io-index" 1839 + checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" 1840 + dependencies = [ 1841 + "crossbeam-utils", 1842 + "libc", 1843 + "once_cell", 1844 + "raw-cpuid", 1845 + "wasi 0.11.1+wasi-snapshot-preview1", 1846 + "web-sys", 1847 + "winapi", 1848 + ] 1849 + 1850 + [[package]] 1122 1851 name = "quote" 1123 1852 version = "1.0.40" 1124 1853 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1128 1857 ] 1129 1858 1130 1859 [[package]] 1860 + name = "quoted_printable" 1861 + version = "0.5.1" 1862 + source = "registry+https://github.com/rust-lang/crates.io-index" 1863 + checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73" 1864 + 1865 + [[package]] 1131 1866 name = "r-efi" 1132 1867 version = "5.3.0" 1133 1868 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1140 1875 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1141 1876 dependencies = [ 1142 1877 "libc", 1143 - "rand_chacha", 1144 - "rand_core", 1878 + "rand_chacha 0.3.1", 1879 + "rand_core 0.6.4", 1880 + ] 1881 + 1882 + [[package]] 1883 + name = "rand" 1884 + version = "0.9.2" 1885 + source = "registry+https://github.com/rust-lang/crates.io-index" 1886 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 1887 + dependencies = [ 1888 + "rand_chacha 0.9.0", 1889 + "rand_core 0.9.3", 1145 1890 ] 1146 1891 1147 1892 [[package]] ··· 1151 1896 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1152 1897 dependencies = [ 1153 1898 "ppv-lite86", 1154 - "rand_core", 1899 + "rand_core 0.6.4", 1900 + ] 1901 + 1902 + [[package]] 1903 + name = "rand_chacha" 1904 + version = "0.9.0" 1905 + source = "registry+https://github.com/rust-lang/crates.io-index" 1906 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1907 + dependencies = [ 1908 + "ppv-lite86", 1909 + "rand_core 0.9.3", 1155 1910 ] 1156 1911 1157 1912 [[package]] ··· 1161 1916 checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1162 1917 dependencies = [ 1163 1918 "getrandom 0.2.16", 1919 + ] 1920 + 1921 + [[package]] 1922 + name = "rand_core" 1923 + version = "0.9.3" 1924 + source = "registry+https://github.com/rust-lang/crates.io-index" 1925 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1926 + dependencies = [ 1927 + "getrandom 0.3.3", 1928 + ] 1929 + 1930 + [[package]] 1931 + name = "raw-cpuid" 1932 + version = "11.5.0" 1933 + source = "registry+https://github.com/rust-lang/crates.io-index" 1934 + checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" 1935 + dependencies = [ 1936 + "bitflags", 1164 1937 ] 1165 1938 1166 1939 [[package]] ··· 1243 2016 "num-traits", 1244 2017 "pkcs1", 1245 2018 "pkcs8", 1246 - "rand_core", 2019 + "rand_core 0.6.4", 1247 2020 "signature", 1248 2021 "spki", 1249 2022 "subtle", ··· 1251 2024 ] 1252 2025 1253 2026 [[package]] 2027 + name = "rust-embed" 2028 + version = "8.7.2" 2029 + source = "registry+https://github.com/rust-lang/crates.io-index" 2030 + checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" 2031 + dependencies = [ 2032 + "rust-embed-impl", 2033 + "rust-embed-utils", 2034 + "walkdir", 2035 + ] 2036 + 2037 + [[package]] 2038 + name = "rust-embed-impl" 2039 + version = "8.7.2" 2040 + source = "registry+https://github.com/rust-lang/crates.io-index" 2041 + checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" 2042 + dependencies = [ 2043 + "proc-macro2", 2044 + "quote", 2045 + "rust-embed-utils", 2046 + "syn", 2047 + "walkdir", 2048 + ] 2049 + 2050 + [[package]] 2051 + name = "rust-embed-utils" 2052 + version = "8.7.2" 2053 + source = "registry+https://github.com/rust-lang/crates.io-index" 2054 + checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" 2055 + dependencies = [ 2056 + "globset", 2057 + "sha2", 2058 + "walkdir", 2059 + ] 2060 + 2061 + [[package]] 1254 2062 name = "rustc-demangle" 1255 2063 version = "0.1.26" 1256 2064 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1266 2074 "errno", 1267 2075 "libc", 1268 2076 "linux-raw-sys", 1269 - "windows-sys 0.60.2", 2077 + "windows-sys 0.59.0", 1270 2078 ] 1271 2079 1272 2080 [[package]] 1273 2081 name = "rustls" 1274 - version = "0.21.12" 2082 + version = "0.23.31" 1275 2083 source = "registry+https://github.com/rust-lang/crates.io-index" 1276 - checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" 2084 + checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" 1277 2085 dependencies = [ 2086 + "once_cell", 1278 2087 "ring", 2088 + "rustls-pki-types", 1279 2089 "rustls-webpki", 1280 - "sct", 2090 + "subtle", 2091 + "zeroize", 1281 2092 ] 1282 2093 1283 2094 [[package]] 1284 - name = "rustls-pemfile" 1285 - version = "1.0.4" 2095 + name = "rustls-pki-types" 2096 + version = "1.12.0" 1286 2097 source = "registry+https://github.com/rust-lang/crates.io-index" 1287 - checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" 2098 + checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 1288 2099 dependencies = [ 1289 - "base64", 2100 + "zeroize", 1290 2101 ] 1291 2102 1292 2103 [[package]] 1293 2104 name = "rustls-webpki" 1294 - version = "0.101.7" 2105 + version = "0.103.4" 1295 2106 source = "registry+https://github.com/rust-lang/crates.io-index" 1296 - checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" 2107 + checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" 1297 2108 dependencies = [ 1298 2109 "ring", 2110 + "rustls-pki-types", 1299 2111 "untrusted", 1300 2112 ] 1301 2113 ··· 1312 2124 checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 1313 2125 1314 2126 [[package]] 2127 + name = "salsa20" 2128 + version = "0.10.2" 2129 + source = "registry+https://github.com/rust-lang/crates.io-index" 2130 + checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" 2131 + dependencies = [ 2132 + "cipher", 2133 + ] 2134 + 2135 + [[package]] 2136 + name = "same-file" 2137 + version = "1.0.6" 2138 + source = "registry+https://github.com/rust-lang/crates.io-index" 2139 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 2140 + dependencies = [ 2141 + "winapi-util", 2142 + ] 2143 + 2144 + [[package]] 2145 + name = "schannel" 2146 + version = "0.1.27" 2147 + source = "registry+https://github.com/rust-lang/crates.io-index" 2148 + checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" 2149 + dependencies = [ 2150 + "windows-sys 0.59.0", 2151 + ] 2152 + 2153 + [[package]] 1315 2154 name = "scopeguard" 1316 2155 version = "1.2.0" 1317 2156 source = "registry+https://github.com/rust-lang/crates.io-index" 1318 2157 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1319 2158 1320 2159 [[package]] 1321 - name = "sct" 1322 - version = "0.7.1" 2160 + name = "scrypt" 2161 + version = "0.11.0" 2162 + source = "registry+https://github.com/rust-lang/crates.io-index" 2163 + checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" 2164 + dependencies = [ 2165 + "password-hash", 2166 + "pbkdf2", 2167 + "salsa20", 2168 + "sha2", 2169 + ] 2170 + 2171 + [[package]] 2172 + name = "secp256k1" 2173 + version = "0.28.2" 2174 + source = "registry+https://github.com/rust-lang/crates.io-index" 2175 + checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" 2176 + dependencies = [ 2177 + "secp256k1-sys", 2178 + ] 2179 + 2180 + [[package]] 2181 + name = "secp256k1-sys" 2182 + version = "0.9.2" 2183 + source = "registry+https://github.com/rust-lang/crates.io-index" 2184 + checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" 2185 + dependencies = [ 2186 + "cc", 2187 + ] 2188 + 2189 + [[package]] 2190 + name = "security-framework" 2191 + version = "2.11.1" 2192 + source = "registry+https://github.com/rust-lang/crates.io-index" 2193 + checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 2194 + dependencies = [ 2195 + "bitflags", 2196 + "core-foundation", 2197 + "core-foundation-sys", 2198 + "libc", 2199 + "security-framework-sys", 2200 + ] 2201 + 2202 + [[package]] 2203 + name = "security-framework-sys" 2204 + version = "2.14.0" 1323 2205 source = "registry+https://github.com/rust-lang/crates.io-index" 1324 - checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" 2206 + checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" 1325 2207 dependencies = [ 1326 - "ring", 1327 - "untrusted", 2208 + "core-foundation-sys", 2209 + "libc", 1328 2210 ] 1329 2211 1330 2212 [[package]] ··· 1344 2226 dependencies = [ 1345 2227 "proc-macro2", 1346 2228 "quote", 1347 - "syn 2.0.105", 2229 + "syn", 1348 2230 ] 1349 2231 1350 2232 [[package]] ··· 1434 2316 checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 1435 2317 dependencies = [ 1436 2318 "digest", 1437 - "rand_core", 2319 + "rand_core 0.6.4", 1438 2320 ] 1439 2321 1440 2322 [[package]] ··· 1448 2330 version = "1.15.1" 1449 2331 source = "registry+https://github.com/rust-lang/crates.io-index" 1450 2332 checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 2333 + dependencies = [ 2334 + "serde", 2335 + ] 1451 2336 1452 2337 [[package]] 1453 2338 name = "socket2" ··· 1469 2354 ] 1470 2355 1471 2356 [[package]] 2357 + name = "spinning_top" 2358 + version = "0.3.0" 2359 + source = "registry+https://github.com/rust-lang/crates.io-index" 2360 + checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" 2361 + dependencies = [ 2362 + "lock_api", 2363 + ] 2364 + 2365 + [[package]] 1472 2366 name = "spki" 1473 2367 version = "0.7.3" 1474 2368 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1479 2373 ] 1480 2374 1481 2375 [[package]] 1482 - name = "sqlformat" 1483 - version = "0.2.6" 1484 - source = "registry+https://github.com/rust-lang/crates.io-index" 1485 - checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" 1486 - dependencies = [ 1487 - "nom", 1488 - "unicode_categories", 1489 - ] 1490 - 1491 - [[package]] 1492 2376 name = "sqlx" 1493 - version = "0.7.4" 2377 + version = "0.8.6" 1494 2378 source = "registry+https://github.com/rust-lang/crates.io-index" 1495 - checksum = "c9a2ccff1a000a5a59cd33da541d9f2fdcd9e6e8229cc200565942bff36d0aaa" 2379 + checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" 1496 2380 dependencies = [ 1497 2381 "sqlx-core", 1498 2382 "sqlx-macros", ··· 1503 2387 1504 2388 [[package]] 1505 2389 name = "sqlx-core" 1506 - version = "0.7.4" 2390 + version = "0.8.6" 1507 2391 source = "registry+https://github.com/rust-lang/crates.io-index" 1508 - checksum = "24ba59a9342a3d9bab6c56c118be528b27c9b60e490080e9711a04dccac83ef6" 2392 + checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" 1509 2393 dependencies = [ 1510 - "ahash", 1511 - "atoi", 1512 - "byteorder", 2394 + "base64", 1513 2395 "bytes", 1514 2396 "crc", 1515 2397 "crossbeam-queue", 1516 2398 "either", 1517 2399 "event-listener", 1518 - "futures-channel", 1519 2400 "futures-core", 1520 2401 "futures-intrusive", 1521 2402 "futures-io", 1522 2403 "futures-util", 2404 + "hashbrown 0.15.5", 1523 2405 "hashlink", 1524 - "hex", 1525 2406 "indexmap", 1526 2407 "log", 1527 2408 "memchr", 1528 2409 "once_cell", 1529 - "paste", 1530 2410 "percent-encoding", 1531 2411 "rustls", 1532 - "rustls-pemfile", 1533 2412 "serde", 1534 2413 "serde_json", 1535 2414 "sha2", 1536 2415 "smallvec", 1537 - "sqlformat", 1538 - "thiserror", 2416 + "thiserror 2.0.14", 1539 2417 "tokio", 1540 2418 "tokio-stream", 1541 2419 "tracing", 1542 2420 "url", 1543 - "webpki-roots", 2421 + "webpki-roots 0.26.11", 1544 2422 ] 1545 2423 1546 2424 [[package]] 1547 2425 name = "sqlx-macros" 1548 - version = "0.7.4" 2426 + version = "0.8.6" 1549 2427 source = "registry+https://github.com/rust-lang/crates.io-index" 1550 - checksum = "4ea40e2345eb2faa9e1e5e326db8c34711317d2b5e08d0d5741619048a803127" 2428 + checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" 1551 2429 dependencies = [ 1552 2430 "proc-macro2", 1553 2431 "quote", 1554 2432 "sqlx-core", 1555 2433 "sqlx-macros-core", 1556 - "syn 1.0.109", 2434 + "syn", 1557 2435 ] 1558 2436 1559 2437 [[package]] 1560 2438 name = "sqlx-macros-core" 1561 - version = "0.7.4" 2439 + version = "0.8.6" 1562 2440 source = "registry+https://github.com/rust-lang/crates.io-index" 1563 - checksum = "5833ef53aaa16d860e92123292f1f6a3d53c34ba8b1969f152ef1a7bb803f3c8" 2441 + checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" 1564 2442 dependencies = [ 1565 2443 "dotenvy", 1566 2444 "either", ··· 1574 2452 "sha2", 1575 2453 "sqlx-core", 1576 2454 "sqlx-mysql", 2455 + "sqlx-postgres", 1577 2456 "sqlx-sqlite", 1578 - "syn 1.0.109", 1579 - "tempfile", 2457 + "syn", 1580 2458 "tokio", 1581 2459 "url", 1582 2460 ] 1583 2461 1584 2462 [[package]] 1585 2463 name = "sqlx-mysql" 1586 - version = "0.7.4" 2464 + version = "0.8.6" 1587 2465 source = "registry+https://github.com/rust-lang/crates.io-index" 1588 - checksum = "1ed31390216d20e538e447a7a9b959e06ed9fc51c37b514b46eb758016ecd418" 2466 + checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" 1589 2467 dependencies = [ 1590 2468 "atoi", 1591 2469 "base64", ··· 1610 2488 "memchr", 1611 2489 "once_cell", 1612 2490 "percent-encoding", 1613 - "rand", 2491 + "rand 0.8.5", 1614 2492 "rsa", 1615 2493 "serde", 1616 2494 "sha1", ··· 1618 2496 "smallvec", 1619 2497 "sqlx-core", 1620 2498 "stringprep", 1621 - "thiserror", 2499 + "thiserror 2.0.14", 1622 2500 "tracing", 1623 2501 "whoami", 1624 2502 ] 1625 2503 1626 2504 [[package]] 1627 2505 name = "sqlx-postgres" 1628 - version = "0.7.4" 2506 + version = "0.8.6" 1629 2507 source = "registry+https://github.com/rust-lang/crates.io-index" 1630 - checksum = "7c824eb80b894f926f89a0b9da0c7f435d27cdd35b8c655b114e58223918577e" 2508 + checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" 1631 2509 dependencies = [ 1632 2510 "atoi", 1633 2511 "base64", ··· 1638 2516 "etcetera", 1639 2517 "futures-channel", 1640 2518 "futures-core", 1641 - "futures-io", 1642 2519 "futures-util", 1643 2520 "hex", 1644 2521 "hkdf", ··· 1649 2526 "md-5", 1650 2527 "memchr", 1651 2528 "once_cell", 1652 - "rand", 2529 + "rand 0.8.5", 1653 2530 "serde", 1654 2531 "serde_json", 1655 2532 "sha2", 1656 2533 "smallvec", 1657 2534 "sqlx-core", 1658 2535 "stringprep", 1659 - "thiserror", 2536 + "thiserror 2.0.14", 1660 2537 "tracing", 1661 2538 "whoami", 1662 2539 ] 1663 2540 1664 2541 [[package]] 1665 2542 name = "sqlx-sqlite" 1666 - version = "0.7.4" 2543 + version = "0.8.6" 1667 2544 source = "registry+https://github.com/rust-lang/crates.io-index" 1668 - checksum = "b244ef0a8414da0bed4bb1910426e890b19e5e9bccc27ada6b797d05c55ae0aa" 2545 + checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" 1669 2546 dependencies = [ 1670 2547 "atoi", 1671 2548 "flume", ··· 1678 2555 "log", 1679 2556 "percent-encoding", 1680 2557 "serde", 2558 + "serde_urlencoded", 1681 2559 "sqlx-core", 2560 + "thiserror 2.0.14", 1682 2561 "tracing", 1683 2562 "url", 1684 - "urlencoding", 1685 2563 ] 1686 2564 1687 2565 [[package]] ··· 1691 2569 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 1692 2570 1693 2571 [[package]] 2572 + name = "stacker" 2573 + version = "0.1.21" 2574 + source = "registry+https://github.com/rust-lang/crates.io-index" 2575 + checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" 2576 + dependencies = [ 2577 + "cc", 2578 + "cfg-if", 2579 + "libc", 2580 + "psm", 2581 + "windows-sys 0.59.0", 2582 + ] 2583 + 2584 + [[package]] 1694 2585 name = "stringprep" 1695 2586 version = "0.1.5" 1696 2587 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1702 2593 ] 1703 2594 1704 2595 [[package]] 2596 + name = "strsim" 2597 + version = "0.11.1" 2598 + source = "registry+https://github.com/rust-lang/crates.io-index" 2599 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 2600 + 2601 + [[package]] 1705 2602 name = "subtle" 1706 2603 version = "2.6.1" 1707 2604 source = "registry+https://github.com/rust-lang/crates.io-index" 1708 2605 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1709 - 1710 - [[package]] 1711 - name = "syn" 1712 - version = "1.0.109" 1713 - source = "registry+https://github.com/rust-lang/crates.io-index" 1714 - checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1715 - dependencies = [ 1716 - "proc-macro2", 1717 - "quote", 1718 - "unicode-ident", 1719 - ] 1720 2606 1721 2607 [[package]] 1722 2608 name = "syn" ··· 1743 2629 dependencies = [ 1744 2630 "proc-macro2", 1745 2631 "quote", 1746 - "syn 2.0.105", 2632 + "syn", 1747 2633 ] 1748 2634 1749 2635 [[package]] 1750 2636 name = "tempfile" 1751 - version = "3.20.0" 2637 + version = "3.21.0" 1752 2638 source = "registry+https://github.com/rust-lang/crates.io-index" 1753 - checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" 2639 + checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" 1754 2640 dependencies = [ 1755 2641 "fastrand", 1756 2642 "getrandom 0.3.3", ··· 1765 2651 source = "registry+https://github.com/rust-lang/crates.io-index" 1766 2652 checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 1767 2653 dependencies = [ 1768 - "thiserror-impl", 2654 + "thiserror-impl 1.0.69", 2655 + ] 2656 + 2657 + [[package]] 2658 + name = "thiserror" 2659 + version = "2.0.14" 2660 + source = "registry+https://github.com/rust-lang/crates.io-index" 2661 + checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" 2662 + dependencies = [ 2663 + "thiserror-impl 2.0.14", 1769 2664 ] 1770 2665 1771 2666 [[package]] ··· 1776 2671 dependencies = [ 1777 2672 "proc-macro2", 1778 2673 "quote", 1779 - "syn 2.0.105", 2674 + "syn", 2675 + ] 2676 + 2677 + [[package]] 2678 + name = "thiserror-impl" 2679 + version = "2.0.14" 2680 + source = "registry+https://github.com/rust-lang/crates.io-index" 2681 + checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" 2682 + dependencies = [ 2683 + "proc-macro2", 2684 + "quote", 2685 + "syn", 1780 2686 ] 1781 2687 1782 2688 [[package]] ··· 1840 2746 dependencies = [ 1841 2747 "proc-macro2", 1842 2748 "quote", 1843 - "syn 2.0.105", 2749 + "syn", 2750 + ] 2751 + 2752 + [[package]] 2753 + name = "tokio-native-tls" 2754 + version = "0.3.1" 2755 + source = "registry+https://github.com/rust-lang/crates.io-index" 2756 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 2757 + dependencies = [ 2758 + "native-tls", 2759 + "tokio", 1844 2760 ] 1845 2761 1846 2762 [[package]] ··· 1855 2771 ] 1856 2772 1857 2773 [[package]] 2774 + name = "tokio-util" 2775 + version = "0.7.15" 2776 + source = "registry+https://github.com/rust-lang/crates.io-index" 2777 + checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" 2778 + dependencies = [ 2779 + "bytes", 2780 + "futures-core", 2781 + "futures-sink", 2782 + "pin-project-lite", 2783 + "tokio", 2784 + ] 2785 + 2786 + [[package]] 2787 + name = "tonic" 2788 + version = "0.14.1" 2789 + source = "registry+https://github.com/rust-lang/crates.io-index" 2790 + checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" 2791 + dependencies = [ 2792 + "async-trait", 2793 + "axum", 2794 + "base64", 2795 + "bytes", 2796 + "h2", 2797 + "http", 2798 + "http-body", 2799 + "http-body-util", 2800 + "hyper", 2801 + "hyper-timeout", 2802 + "hyper-util", 2803 + "percent-encoding", 2804 + "pin-project", 2805 + "socket2", 2806 + "sync_wrapper", 2807 + "tokio", 2808 + "tokio-stream", 2809 + "tower", 2810 + "tower-layer", 2811 + "tower-service", 2812 + "tracing", 2813 + ] 2814 + 2815 + [[package]] 1858 2816 name = "tower" 1859 2817 version = "0.5.2" 1860 2818 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1862 2820 dependencies = [ 1863 2821 "futures-core", 1864 2822 "futures-util", 2823 + "indexmap", 1865 2824 "pin-project-lite", 2825 + "slab", 1866 2826 "sync_wrapper", 1867 2827 "tokio", 2828 + "tokio-util", 1868 2829 "tower-layer", 1869 2830 "tower-service", 1870 2831 "tracing", 1871 2832 ] 1872 2833 1873 2834 [[package]] 2835 + name = "tower-http" 2836 + version = "0.6.6" 2837 + source = "registry+https://github.com/rust-lang/crates.io-index" 2838 + checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" 2839 + dependencies = [ 2840 + "async-compression", 2841 + "bitflags", 2842 + "bytes", 2843 + "futures-core", 2844 + "http", 2845 + "http-body", 2846 + "pin-project-lite", 2847 + "tokio", 2848 + "tokio-util", 2849 + "tower-layer", 2850 + "tower-service", 2851 + ] 2852 + 2853 + [[package]] 1874 2854 name = "tower-layer" 1875 2855 version = "0.3.3" 1876 2856 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1883 2863 checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1884 2864 1885 2865 [[package]] 2866 + name = "tower_governor" 2867 + version = "0.8.0" 2868 + source = "registry+https://github.com/rust-lang/crates.io-index" 2869 + checksum = "44de9b94d849d3c46e06a883d72d408c2de6403367b39df2b1c9d9e7b6736fe6" 2870 + dependencies = [ 2871 + "axum", 2872 + "forwarded-header-value", 2873 + "governor", 2874 + "http", 2875 + "pin-project", 2876 + "thiserror 2.0.14", 2877 + "tonic", 2878 + "tower", 2879 + "tracing", 2880 + ] 2881 + 2882 + [[package]] 1886 2883 name = "tracing" 1887 2884 version = "0.1.41" 1888 2885 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1902 2899 dependencies = [ 1903 2900 "proc-macro2", 1904 2901 "quote", 1905 - "syn 2.0.105", 2902 + "syn", 1906 2903 ] 1907 2904 1908 2905 [[package]] ··· 1945 2942 ] 1946 2943 1947 2944 [[package]] 2945 + name = "try-lock" 2946 + version = "0.2.5" 2947 + source = "registry+https://github.com/rust-lang/crates.io-index" 2948 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 2949 + 2950 + [[package]] 1948 2951 name = "typenum" 1949 2952 version = "1.18.0" 1950 2953 source = "registry+https://github.com/rust-lang/crates.io-index" 1951 2954 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 1952 2955 1953 2956 [[package]] 2957 + name = "ucd-trie" 2958 + version = "0.1.7" 2959 + source = "registry+https://github.com/rust-lang/crates.io-index" 2960 + checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" 2961 + 2962 + [[package]] 1954 2963 name = "unicode-bidi" 1955 2964 version = "0.3.18" 1956 2965 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1978 2987 checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 1979 2988 1980 2989 [[package]] 1981 - name = "unicode-segmentation" 1982 - version = "1.12.0" 1983 - source = "registry+https://github.com/rust-lang/crates.io-index" 1984 - checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 1985 - 1986 - [[package]] 1987 - name = "unicode_categories" 1988 - version = "0.1.1" 1989 - source = "registry+https://github.com/rust-lang/crates.io-index" 1990 - checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" 1991 - 1992 - [[package]] 1993 2990 name = "untrusted" 1994 2991 version = "0.9.0" 1995 2992 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2005 3002 "idna", 2006 3003 "percent-encoding", 2007 3004 ] 2008 - 2009 - [[package]] 2010 - name = "urlencoding" 2011 - version = "2.1.3" 2012 - source = "registry+https://github.com/rust-lang/crates.io-index" 2013 - checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" 2014 3005 2015 3006 [[package]] 2016 3007 name = "utf8_iter" ··· 2037 3028 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2038 3029 2039 3030 [[package]] 3031 + name = "walkdir" 3032 + version = "2.5.0" 3033 + source = "registry+https://github.com/rust-lang/crates.io-index" 3034 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 3035 + dependencies = [ 3036 + "same-file", 3037 + "winapi-util", 3038 + ] 3039 + 3040 + [[package]] 3041 + name = "want" 3042 + version = "0.3.1" 3043 + source = "registry+https://github.com/rust-lang/crates.io-index" 3044 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 3045 + dependencies = [ 3046 + "try-lock", 3047 + ] 3048 + 3049 + [[package]] 2040 3050 name = "wasi" 2041 3051 version = "0.11.1+wasi-snapshot-preview1" 2042 3052 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2058 3068 checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 2059 3069 2060 3070 [[package]] 3071 + name = "wasm-bindgen" 3072 + version = "0.2.100" 3073 + source = "registry+https://github.com/rust-lang/crates.io-index" 3074 + checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 3075 + dependencies = [ 3076 + "cfg-if", 3077 + "once_cell", 3078 + "rustversion", 3079 + "wasm-bindgen-macro", 3080 + ] 3081 + 3082 + [[package]] 3083 + name = "wasm-bindgen-backend" 3084 + version = "0.2.100" 3085 + source = "registry+https://github.com/rust-lang/crates.io-index" 3086 + checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 3087 + dependencies = [ 3088 + "bumpalo", 3089 + "log", 3090 + "proc-macro2", 3091 + "quote", 3092 + "syn", 3093 + "wasm-bindgen-shared", 3094 + ] 3095 + 3096 + [[package]] 3097 + name = "wasm-bindgen-macro" 3098 + version = "0.2.100" 3099 + source = "registry+https://github.com/rust-lang/crates.io-index" 3100 + checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 3101 + dependencies = [ 3102 + "quote", 3103 + "wasm-bindgen-macro-support", 3104 + ] 3105 + 3106 + [[package]] 3107 + name = "wasm-bindgen-macro-support" 3108 + version = "0.2.100" 3109 + source = "registry+https://github.com/rust-lang/crates.io-index" 3110 + checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 3111 + dependencies = [ 3112 + "proc-macro2", 3113 + "quote", 3114 + "syn", 3115 + "wasm-bindgen-backend", 3116 + "wasm-bindgen-shared", 3117 + ] 3118 + 3119 + [[package]] 3120 + name = "wasm-bindgen-shared" 3121 + version = "0.2.100" 3122 + source = "registry+https://github.com/rust-lang/crates.io-index" 3123 + checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 3124 + dependencies = [ 3125 + "unicode-ident", 3126 + ] 3127 + 3128 + [[package]] 3129 + name = "web-sys" 3130 + version = "0.3.77" 3131 + source = "registry+https://github.com/rust-lang/crates.io-index" 3132 + checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 3133 + dependencies = [ 3134 + "js-sys", 3135 + "wasm-bindgen", 3136 + ] 3137 + 3138 + [[package]] 3139 + name = "web-time" 3140 + version = "1.1.0" 3141 + source = "registry+https://github.com/rust-lang/crates.io-index" 3142 + checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" 3143 + dependencies = [ 3144 + "js-sys", 3145 + "wasm-bindgen", 3146 + ] 3147 + 3148 + [[package]] 2061 3149 name = "webpki-roots" 2062 - version = "0.25.4" 3150 + version = "0.26.11" 3151 + source = "registry+https://github.com/rust-lang/crates.io-index" 3152 + checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 3153 + dependencies = [ 3154 + "webpki-roots 1.0.2", 3155 + ] 3156 + 3157 + [[package]] 3158 + name = "webpki-roots" 3159 + version = "1.0.2" 2063 3160 source = "registry+https://github.com/rust-lang/crates.io-index" 2064 - checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" 3161 + checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" 3162 + dependencies = [ 3163 + "rustls-pki-types", 3164 + ] 2065 3165 2066 3166 [[package]] 2067 3167 name = "whoami" ··· 2090 3190 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2091 3191 2092 3192 [[package]] 3193 + name = "winapi-util" 3194 + version = "0.1.9" 3195 + source = "registry+https://github.com/rust-lang/crates.io-index" 3196 + checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 3197 + dependencies = [ 3198 + "windows-sys 0.59.0", 3199 + ] 3200 + 3201 + [[package]] 2093 3202 name = "winapi-x86_64-pc-windows-gnu" 2094 3203 version = "0.4.0" 2095 3204 source = "registry+https://github.com/rust-lang/crates.io-index" 2096 3205 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2097 3206 2098 3207 [[package]] 3208 + name = "windows-core" 3209 + version = "0.61.2" 3210 + source = "registry+https://github.com/rust-lang/crates.io-index" 3211 + checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" 3212 + dependencies = [ 3213 + "windows-implement", 3214 + "windows-interface", 3215 + "windows-link", 3216 + "windows-result", 3217 + "windows-strings", 3218 + ] 3219 + 3220 + [[package]] 3221 + name = "windows-implement" 3222 + version = "0.60.0" 3223 + source = "registry+https://github.com/rust-lang/crates.io-index" 3224 + checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" 3225 + dependencies = [ 3226 + "proc-macro2", 3227 + "quote", 3228 + "syn", 3229 + ] 3230 + 3231 + [[package]] 3232 + name = "windows-interface" 3233 + version = "0.59.1" 3234 + source = "registry+https://github.com/rust-lang/crates.io-index" 3235 + checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" 3236 + dependencies = [ 3237 + "proc-macro2", 3238 + "quote", 3239 + "syn", 3240 + ] 3241 + 3242 + [[package]] 2099 3243 name = "windows-link" 2100 3244 version = "0.1.3" 2101 3245 source = "registry+https://github.com/rust-lang/crates.io-index" 2102 3246 checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" 3247 + 3248 + [[package]] 3249 + name = "windows-result" 3250 + version = "0.3.4" 3251 + source = "registry+https://github.com/rust-lang/crates.io-index" 3252 + checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" 3253 + dependencies = [ 3254 + "windows-link", 3255 + ] 3256 + 3257 + [[package]] 3258 + name = "windows-strings" 3259 + version = "0.4.2" 3260 + source = "registry+https://github.com/rust-lang/crates.io-index" 3261 + checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" 3262 + dependencies = [ 3263 + "windows-link", 3264 + ] 2103 3265 2104 3266 [[package]] 2105 3267 name = "windows-sys" ··· 2129 3291 ] 2130 3292 2131 3293 [[package]] 2132 - name = "windows-sys" 2133 - version = "0.60.2" 2134 - source = "registry+https://github.com/rust-lang/crates.io-index" 2135 - checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 2136 - dependencies = [ 2137 - "windows-targets 0.53.3", 2138 - ] 2139 - 2140 - [[package]] 2141 3294 name = "windows-targets" 2142 3295 version = "0.48.5" 2143 3296 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2161 3314 "windows_aarch64_gnullvm 0.52.6", 2162 3315 "windows_aarch64_msvc 0.52.6", 2163 3316 "windows_i686_gnu 0.52.6", 2164 - "windows_i686_gnullvm 0.52.6", 3317 + "windows_i686_gnullvm", 2165 3318 "windows_i686_msvc 0.52.6", 2166 3319 "windows_x86_64_gnu 0.52.6", 2167 3320 "windows_x86_64_gnullvm 0.52.6", 2168 3321 "windows_x86_64_msvc 0.52.6", 2169 - ] 2170 - 2171 - [[package]] 2172 - name = "windows-targets" 2173 - version = "0.53.3" 2174 - source = "registry+https://github.com/rust-lang/crates.io-index" 2175 - checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" 2176 - dependencies = [ 2177 - "windows-link", 2178 - "windows_aarch64_gnullvm 0.53.0", 2179 - "windows_aarch64_msvc 0.53.0", 2180 - "windows_i686_gnu 0.53.0", 2181 - "windows_i686_gnullvm 0.53.0", 2182 - "windows_i686_msvc 0.53.0", 2183 - "windows_x86_64_gnu 0.53.0", 2184 - "windows_x86_64_gnullvm 0.53.0", 2185 - "windows_x86_64_msvc 0.53.0", 2186 3322 ] 2187 3323 2188 3324 [[package]] ··· 2198 3334 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2199 3335 2200 3336 [[package]] 2201 - name = "windows_aarch64_gnullvm" 2202 - version = "0.53.0" 2203 - source = "registry+https://github.com/rust-lang/crates.io-index" 2204 - checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 2205 - 2206 - [[package]] 2207 3337 name = "windows_aarch64_msvc" 2208 3338 version = "0.48.5" 2209 3339 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2216 3346 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2217 3347 2218 3348 [[package]] 2219 - name = "windows_aarch64_msvc" 2220 - version = "0.53.0" 2221 - source = "registry+https://github.com/rust-lang/crates.io-index" 2222 - checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 2223 - 2224 - [[package]] 2225 3349 name = "windows_i686_gnu" 2226 3350 version = "0.48.5" 2227 3351 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2234 3358 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2235 3359 2236 3360 [[package]] 2237 - name = "windows_i686_gnu" 2238 - version = "0.53.0" 2239 - source = "registry+https://github.com/rust-lang/crates.io-index" 2240 - checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 2241 - 2242 - [[package]] 2243 3361 name = "windows_i686_gnullvm" 2244 3362 version = "0.52.6" 2245 3363 source = "registry+https://github.com/rust-lang/crates.io-index" 2246 3364 checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2247 3365 2248 3366 [[package]] 2249 - name = "windows_i686_gnullvm" 2250 - version = "0.53.0" 2251 - source = "registry+https://github.com/rust-lang/crates.io-index" 2252 - checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 2253 - 2254 - [[package]] 2255 3367 name = "windows_i686_msvc" 2256 3368 version = "0.48.5" 2257 3369 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2264 3376 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2265 3377 2266 3378 [[package]] 2267 - name = "windows_i686_msvc" 2268 - version = "0.53.0" 2269 - source = "registry+https://github.com/rust-lang/crates.io-index" 2270 - checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 2271 - 2272 - [[package]] 2273 3379 name = "windows_x86_64_gnu" 2274 3380 version = "0.48.5" 2275 3381 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2282 3388 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2283 3389 2284 3390 [[package]] 2285 - name = "windows_x86_64_gnu" 2286 - version = "0.53.0" 2287 - source = "registry+https://github.com/rust-lang/crates.io-index" 2288 - checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 2289 - 2290 - [[package]] 2291 3391 name = "windows_x86_64_gnullvm" 2292 3392 version = "0.48.5" 2293 3393 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2300 3400 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2301 3401 2302 3402 [[package]] 2303 - name = "windows_x86_64_gnullvm" 2304 - version = "0.53.0" 2305 - source = "registry+https://github.com/rust-lang/crates.io-index" 2306 - checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 2307 - 2308 - [[package]] 2309 3403 name = "windows_x86_64_msvc" 2310 3404 version = "0.48.5" 2311 3405 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2316 3410 version = "0.52.6" 2317 3411 source = "registry+https://github.com/rust-lang/crates.io-index" 2318 3412 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2319 - 2320 - [[package]] 2321 - name = "windows_x86_64_msvc" 2322 - version = "0.53.0" 2323 - source = "registry+https://github.com/rust-lang/crates.io-index" 2324 - checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2325 3413 2326 3414 [[package]] 2327 3415 name = "wit-bindgen-rt" ··· 2358 3446 dependencies = [ 2359 3447 "proc-macro2", 2360 3448 "quote", 2361 - "syn 2.0.105", 3449 + "syn", 2362 3450 "synstructure", 2363 3451 ] 2364 3452 ··· 2379 3467 dependencies = [ 2380 3468 "proc-macro2", 2381 3469 "quote", 2382 - "syn 2.0.105", 3470 + "syn", 2383 3471 ] 2384 3472 2385 3473 [[package]] ··· 2399 3487 dependencies = [ 2400 3488 "proc-macro2", 2401 3489 "quote", 2402 - "syn 2.0.105", 3490 + "syn", 2403 3491 "synstructure", 2404 3492 ] 2405 3493 ··· 2408 3496 version = "1.8.1" 2409 3497 source = "registry+https://github.com/rust-lang/crates.io-index" 2410 3498 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 3499 + dependencies = [ 3500 + "zeroize_derive", 3501 + ] 3502 + 3503 + [[package]] 3504 + name = "zeroize_derive" 3505 + version = "1.4.2" 3506 + source = "registry+https://github.com/rust-lang/crates.io-index" 3507 + checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 3508 + dependencies = [ 3509 + "proc-macro2", 3510 + "quote", 3511 + "syn", 3512 + ] 2411 3513 2412 3514 [[package]] 2413 3515 name = "zerotrie" ··· 2439 3541 dependencies = [ 2440 3542 "proc-macro2", 2441 3543 "quote", 2442 - "syn 2.0.105", 3544 + "syn", 3545 + ] 3546 + 3547 + [[package]] 3548 + name = "zstd" 3549 + version = "0.13.3" 3550 + source = "registry+https://github.com/rust-lang/crates.io-index" 3551 + checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 3552 + dependencies = [ 3553 + "zstd-safe", 3554 + ] 3555 + 3556 + [[package]] 3557 + name = "zstd-safe" 3558 + version = "7.2.4" 3559 + source = "registry+https://github.com/rust-lang/crates.io-index" 3560 + checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 3561 + dependencies = [ 3562 + "zstd-sys", 3563 + ] 3564 + 3565 + [[package]] 3566 + name = "zstd-sys" 3567 + version = "2.0.15+zstd.1.5.7" 3568 + source = "registry+https://github.com/rust-lang/crates.io-index" 3569 + checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" 3570 + dependencies = [ 3571 + "cc", 3572 + "pkg-config", 2443 3573 ]
+14 -4
Cargo.toml
··· 1 1 [package] 2 - name = "pds_bells_and_whistles" 2 + name = "pds_gatekeeper" 3 3 version = "0.1.0" 4 4 edition = "2024" 5 5 6 6 [dependencies] 7 - axum = { version = "0.7", features = ["macros", "json"] } 8 - tokio = { version = "1.39", features = ["rt-multi-thread", "macros", "signal"] } 9 - sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "sqlite"] } 7 + axum = { version = "0.8.4", features = ["macros", "json"] } 8 + tokio = { version = "1.47.1", features = ["rt-multi-thread", "macros", "signal"] } 9 + sqlx = { version = "0.8.6", features = ["runtime-tokio-rustls", "sqlite", "migrate"] } 10 10 dotenvy = "0.15.7" 11 11 serde = { version = "1.0", features = ["derive"] } 12 12 serde_json = "1.0" 13 13 tracing = "0.1" 14 14 tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] } 15 + hyper-util = { version = "0.1.16", features = ["client", "client-legacy"] } 16 + tower-http = { version = "0.6", features = ["cors", "compression-zstd"] } 17 + tower_governor = "0.8.0" 18 + hex = "0.4" 19 + jwt-compact = { version = "0.8.0", features = ["es256k"] } 20 + scrypt = "0.11" 21 + lettre = { version = "0.11.18", features = ["tokio1", "pool", "tokio1-native-tls"] } 22 + handlebars = { version = "6.3.2", features = ["rust-embed"] } 23 + rust-embed = "8.7.2" 24 + axum-template = { version = "3.0.0", features = ["handlebars"] }
+49
README.md
··· 1 + # PDS gatekeeper 2 + 3 + A microservice that sits on the same server as the PDS to add some of the security that the entryway does. 4 + 5 + ![Picture in black and white of a grassy hill with a gate at the top](./images/gate.jpg) 6 + 7 + PDS gatekeeper works by overriding some of the PDS endpoints inside your Caddyfile to provide gatekeeping to certain 8 + endpoints. Mainly, the ability to have 2FA on a self hosted PDS like it does on a Bluesky mushroom(PDS). Most of the 9 + logic of these endpoints still happens on the PDS via a proxied request, just some are gatekept. 10 + 11 + # Features 12 + 13 + ## 2FA 14 + 15 + - [x] Ability to turn on/off 2FA 16 + - [x] getSession overwrite to set the `emailAuthFactor` flag if the user has 2FA turned on 17 + - [x] send an email using the `PDS_EMAIL_SMTP_URL` with a handlebar email template like Bluesky's 2FA sign in email. 18 + - [ ] generate a 2FA code 19 + - [ ] createSession gatekeeping (It does stop logins, just eh, doesn't actually send a real code or check it yet) 20 + - [ ] oauth endpoint gatekeeping 21 + 22 + ## Captcha on Create Account 23 + 24 + Future feature? 25 + 26 + # Setup 27 + 28 + Nothing here yet! If you are brave enough to try before full release, let me know and I'll help you set it up. 29 + But I want to run it locally on my own PDS first to test run it a bit. 30 + 31 + Example Caddyfile (mostly so I don't lose it for now. Will have a better one in the future) 32 + 33 + ```caddyfile 34 + http://localhost { 35 + 36 + @gatekeeper { 37 + path /xrpc/com.atproto.server.getSession 38 + path /xrpc/com.atproto.server.updateEmail 39 + path /xrpc/com.atproto.server.createSession 40 + } 41 + 42 + handle @gatekeeper { 43 + reverse_proxy http://localhost:8080 44 + } 45 + 46 + reverse_proxy /* http://localhost:3000 47 + } 48 + 49 + ```
+5
build.rs
··· 1 + // generated by `sqlx migrate build-script` 2 + fn main() { 3 + // trigger recompilation when a new migration is added 4 + println!("cargo:rerun-if-changed=migrations"); 5 + }
+159
email_templates/two_factor_code.hbs
··· 1 + <html dir="ltr" lang="en"> 2 + 3 + <head> 4 + <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/> 5 + <meta name="x-apple-disable-message-reformatting"/> 6 + <title>Sign in to Bluesky</title> 7 + <meta 8 + name="description" 9 + content="We received a sign in request for your account." 10 + /> 11 + </head> 12 + <div 13 + style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0" 14 + >We received a sign-in request for the account @{{handle}}. Use the code below to sign in 15 + <div 16 + > 17 +  ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ ‌​‍‎‏ 18 + </div> 19 + </div> 20 + 21 + <body 22 + style="padding:12px;padding-bottom:40px;background-color:hsl(211, 20%, 95.3%)" 23 + > 24 + <table 25 + align="center" 26 + width="100%" 27 + border="0" 28 + cellPadding="0" 29 + cellSpacing="0" 30 + role="presentation" 31 + style="max-width:37.5em" 32 + > 33 + <tbody> 34 + <tr style="width:100%"> 35 + <td> 36 + <table 37 + align="center" 38 + width="100%" 39 + border="0" 40 + cellPadding="0" 41 + cellSpacing="0" 42 + role="presentation" 43 + style="padding-top:24px;padding-bottom:24px" 44 + > 45 + <tbody> 46 + <tr> 47 + <td><img 48 + alt="Bluesky" 49 + src="https://bsky.social/about/images/email/email_logo_default.png" 50 + style="display:block;outline:none;border:none;text-decoration:none;width:110px;margin:0 auto" 51 + /></td> 52 + </tr> 53 + </tbody> 54 + </table> 55 + <table 56 + align="center" 57 + width="100%" 58 + border="0" 59 + cellPadding="0" 60 + cellSpacing="0" 61 + role="presentation" 62 + style="padding:24px;padding-bottom:16px;border-radius:12px;background-color:#FFFFFF" 63 + > 64 + <tbody> 65 + <tr> 66 + <td> 67 + <h1 68 + style="font-size:26px;letter-spacing:0.25px;color:#000000;font-family:-apple-system, BlinkMacSystemFont, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;, &#x27;Helvetica Neue&#x27;, sans-serif;margin:0px 0px;line-height:1.0" 69 + >Sign in to Bluesky</h1> 70 + <p 71 + style="font-size:16px;line-height:1.4;margin:0px 0px;letter-spacing:0.25px;color:hsl(211, 24%, 34.2%);font-family:-apple-system, BlinkMacSystemFont, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;, &#x27;Helvetica Neue&#x27;, sans-serif;padding-top:12px;padding-bottom:12px" 72 + >We received a sign-in request for the account @{{handle}}. Use the code below to sign in.</p> 73 + <code 74 + style="display:block;padding:16px;border-radius:8px;border-width:1px;border-style:solid;background-color:hsl(211, 20%, 95.3%);border-color:hsl(211, 20%, 85.89999999999999%);font-size:14px;letter-spacing:0.25px;font-family:monospace;text-transform:uppercase" 75 + >{{token}}</code> 76 + <p style="font-size:14px;line-height:1.4;margin:0px 0px;letter-spacing:0.25px;color:hsl(211,20%,53%);font-family:-apple-system,BlinkMacSystemFont,'Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif;padding-top:12px"> 77 + If this wasn't you, we recommend taking steps to 78 + protect your account by 79 + <a href="https://bsky.app/settings" 80 + style="color:hsl(211,20%,53%);text-decoration:none;text-decoration-line:underline;font-family:-apple-system,BlinkMacSystemFont,'Roboto','Oxygen','Ubuntu','Cantarell','Fira Sans','Droid Sans','Helvetica Neue',sans-serif;margin:0px 0px;line-height:1.0;font-size:14px;letter-spacing:0.25px" 81 + target="_blank" 82 + data-saferedirecturl="https://bsky.app/settings">changing 83 + your password.</a></p> 84 + <table 85 + align="center" 86 + width="100%" 87 + border="0" 88 + cellPadding="0" 89 + cellSpacing="0" 90 + role="presentation" 91 + style="padding-top:24px" 92 + > 93 + <tbody> 94 + <tr> 95 + <td> 96 + <hr 97 + style="width:100%;border:none;border-top:1px solid #eaeaea;margin:0" 98 + /> 99 + <table 100 + align="center" 101 + width="100%" 102 + border="0" 103 + cellPadding="0" 104 + cellSpacing="0" 105 + role="presentation" 106 + style="padding-top:16px;vertical-align:middle" 107 + > 108 + <tbody style="width:100%"> 109 + <tr style="width:100%"> 110 + <td data-id="__react-email-column"> 111 + <p 112 + style="font-size:14px;line-height:1.4;margin:0px 0px;color:hsl(211, 20%, 53%);font-family:-apple-system, BlinkMacSystemFont, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;, &#x27;Helvetica Neue&#x27;, sans-serif;letter-spacing:0.25px" 113 + ><a 114 + href="https://bsky.app" 115 + style="color:hsl(211, 20%, 53%);text-decoration:none;text-decoration-line:underline;font-family:-apple-system, BlinkMacSystemFont, &#x27;Roboto&#x27;, &#x27;Oxygen&#x27;, &#x27;Ubuntu&#x27;, &#x27;Cantarell&#x27;, &#x27;Fira Sans&#x27;, &#x27;Droid Sans&#x27;, &#x27;Helvetica Neue&#x27;, sans-serif;margin:0px 0px;line-height:1.0;font-size:14px;letter-spacing:0.25px" 116 + target="_blank" 117 + >Bluesky</a>, the social internet</p> 118 + </td> 119 + <td 120 + data-id="__react-email-column" 121 + style="width:24px" 122 + ><img 123 + alt="🦋" 124 + src="https://bsky.social/about/images/email/email_mark_dark.png" 125 + style="display:block;outline:none;border:none;text-decoration:none;width:24px" 126 + /></td> 127 + </tr> 128 + </tbody> 129 + </table> 130 + </td> 131 + </tr> 132 + </tbody> 133 + </table> 134 + </td> 135 + </tr> 136 + </tbody> 137 + </table> 138 + <table 139 + align="center" 140 + width="100%" 141 + border="0" 142 + cellPadding="0" 143 + cellSpacing="0" 144 + role="presentation" 145 + style="height:500px" 146 + > 147 + <tbody> 148 + <tr> 149 + <td></td> 150 + </tr> 151 + </tbody> 152 + </table> 153 + </td> 154 + </tr> 155 + </tbody> 156 + </table> 157 + </body> 158 + 159 + </html>
images/gate.jpg

This is a binary file and will not be displayed.

+6
migrations/20250814232704_two_factor_accounts.sql
··· 1 + -- Add migration script here 2 + CREATE TABLE IF NOT EXISTS two_factor_accounts 3 + ( 4 + did VARCHAR PRIMARY KEY, 5 + required INT2 NOT NULL 6 + );
+3
migrations_bells_and_whistles/.keep
··· 1 + # This directory holds SQLx migrations for the bells_and_whistles.sqlite database. 2 + # It is intentionally empty for now; running `sqlx::migrate!` will still ensure the 3 + # migrations table exists and succeed with zero migrations.
+177 -51
src/main.rs
··· 1 + use crate::xrpc::com_atproto_server::{create_session, get_session, update_email}; 2 + use axum::middleware as ax_middleware; 3 + mod middleware; 4 + use axum::body::Body; 5 + use axum::handler::Handler; 6 + use axum::http::{Method, header}; 7 + use axum::routing::post; 8 + use axum::{Router, routing::get}; 9 + use axum_template::engine::Engine; 10 + use handlebars::Handlebars; 11 + use hyper_util::client::legacy::connect::HttpConnector; 12 + use hyper_util::rt::TokioExecutor; 13 + use lettre::{AsyncSmtpTransport, Tokio1Executor}; 14 + use rust_embed::RustEmbed; 15 + use sqlx::sqlite::{SqliteConnectOptions, SqliteJournalMode}; 16 + use sqlx::{SqlitePool, sqlite::SqlitePoolOptions}; 17 + use std::path::Path; 18 + use std::time::Duration; 1 19 use std::{env, net::SocketAddr}; 2 - use axum::{extract::State, routing::get, Json, Router}; 3 - // use dotenvy::dotenv; 4 - use serde::Serialize; 5 - use sqlx::{sqlite::SqlitePoolOptions, SqlitePool}; 6 - use tracing::{error, info, log}; 7 - use tracing_subscriber::{fmt, prelude::*, EnvFilter}; 20 + use tower_governor::GovernorLayer; 21 + use tower_governor::governor::GovernorConfigBuilder; 22 + use tower_http::compression::CompressionLayer; 23 + use tower_http::cors::{Any, CorsLayer}; 24 + use tracing::{error, log}; 25 + use tracing_subscriber::{EnvFilter, fmt, prelude::*}; 8 26 9 27 mod xrpc; 10 28 29 + type HyperUtilClient = hyper_util::client::legacy::Client<HttpConnector, Body>; 30 + 31 + #[derive(RustEmbed)] 32 + #[folder = "email_templates"] 33 + #[include = "*.hbs"] 34 + struct EmailTemplates; 35 + 11 36 #[derive(Clone)] 12 37 struct AppState { 13 - pool: SqlitePool, 38 + account_pool: SqlitePool, 39 + pds_gatekeeper_pool: SqlitePool, 40 + reverse_proxy_client: HyperUtilClient, 41 + pds_base_url: String, 42 + mailer: AsyncSmtpTransport<Tokio1Executor>, 43 + mailer_from: String, 44 + template_engine: Engine<Handlebars<'static>>, 14 45 } 15 46 16 - #[derive(Serialize)] 17 - struct HealthResponse { 18 - status: &'static str, 19 - } 47 + async fn root_handler() -> impl axum::response::IntoResponse { 48 + let body = r" 20 49 21 - #[derive(Serialize)] 22 - struct DbPingResponse { 23 - db: &'static str, 24 - value: i64, 50 + ...oO _.--X~~OO~~X--._ ...oOO 51 + _.-~ / \ II / \ ~-._ 52 + [].-~ \ / \||/ \ / ~-.[] ...o 53 + ...o _ ||/ \ / || \ / \|| _ 54 + (_) |X X || X X| (_) 55 + _-~-_ ||\ / \ || / \ /|| _-~-_ 56 + ||||| || \ / \ /||\ / \ / || ||||| 57 + | |_|| \ / \ / || \ / \ / ||_| | 58 + | |~|| X X || X X ||~| | 59 + ==============| | || / \ / \ || / \ / \ || | |============== 60 + ______________| | || / \ / \||/ \ / \ || | |______________ 61 + . . | | ||/ \ / || \ / \|| | | . . 62 + / | | |X X || X X| | | / / 63 + / . | | ||\ / \ || / \ /|| | | . / . 64 + . / | | || \ / \ /||\ / \ / || | | . . 65 + . . | | || \ / \ / || \ / \ / || | | . 66 + / | | || X X || X X || | | . / . / 67 + / . | | || / \ / \ || / \ / \ || | | / 68 + / | | || / \ / \||/ \ / \ || | | . / 69 + . . . | | ||/ \ / /||\ \ / \|| | | /. . 70 + | |_|X X / II \ X X|_| | . . / 71 + ==============| |~II~~~~~~~~~~~~~~OO~~~~~~~~~~~~~~II~| |============== 72 + "; 73 + 74 + let intro = "\n\nThis is a PDS gatekeeper\n\nCode: https://tangled.sh/@baileytownsend.dev/pds-gatekeeper\n"; 75 + 76 + let banner = format!(" {}\n{}", body, intro); 77 + 78 + ( 79 + [(header::CONTENT_TYPE, "text/plain; charset=utf-8")], 80 + banner, 81 + ) 25 82 } 26 83 27 84 #[tokio::main] 28 85 async fn main() -> Result<(), Box<dyn std::error::Error>> { 29 86 setup_tracing(); 30 87 //TODO prod 31 - // dotenvy::from_path(Path::new("/pds.env"))?; 32 - // let pds_root = env::var("PDS_DATA_DIRECTORY")?; 33 - let pds_root = "/home/baileytownsend/Documents/code/docker_compose/pds/pds_data"; 88 + dotenvy::from_path(Path::new("./pds.env"))?; 89 + let pds_root = env::var("PDS_DATA_DIRECTORY")?; 90 + // let pds_root = "/home/baileytownsend/Documents/code/docker_compose/pds/pds_data"; 34 91 let account_db_url = format!("{}/account.sqlite", pds_root); 35 92 log::info!("accounts_db_url: {}", account_db_url); 36 - let max_connections: u32 = env::var("DATABASE_MAX_CONNECTIONS") 37 - .ok() 38 - .and_then(|s| s.parse().ok()) 39 - .unwrap_or(5); 40 93 41 - //TODO may need to add journal_mode=WAL ? 42 - let pool = SqlitePoolOptions::new() 43 - .max_connections(max_connections) 44 - .connect(&account_db_url) 94 + let account_options = SqliteConnectOptions::new() 95 + .journal_mode(SqliteJournalMode::Wal) 96 + .filename(account_db_url); 97 + 98 + let account_pool = SqlitePoolOptions::new() 99 + .max_connections(5) 100 + .connect_with(account_options) 45 101 .await?; 46 102 47 - let state = AppState { pool }; 103 + let bells_db_url = format!("{}/pds_gatekeeper.sqlite", pds_root); 104 + let options = SqliteConnectOptions::new() 105 + .journal_mode(SqliteJournalMode::Wal) 106 + .filename(bells_db_url) 107 + .create_if_missing(true); 108 + let pds_gatekeeper_pool = SqlitePoolOptions::new() 109 + .max_connections(5) 110 + .connect_with(options) 111 + .await?; 112 + 113 + // Run migrations for the bells_and_whistles database 114 + // Note: the migrations are embedded at compile time from the given directory 115 + // sqlx 116 + sqlx::migrate!("./migrations") 117 + .run(&pds_gatekeeper_pool) 118 + .await?; 119 + 120 + let client: HyperUtilClient = 121 + hyper_util::client::legacy::Client::<(), ()>::builder(TokioExecutor::new()) 122 + .build(HttpConnector::new()); 123 + 124 + //Emailer set up 125 + let smtp_url = 126 + env::var("PDS_EMAIL_SMTP_URL").expect("PDS_EMAIL_SMTP_URL is not set in your pds.env file"); 127 + let sent_from = env::var("PDS_EMAIL_FROM_ADDRESS") 128 + .expect("PDS_EMAIL_FROM_ADDRESS is not set in your pds.env file"); 129 + let mailer: AsyncSmtpTransport<Tokio1Executor> = 130 + AsyncSmtpTransport::<Tokio1Executor>::from_url(smtp_url.as_str())?.build(); 131 + //Email templates setup 132 + let mut hbs = Handlebars::new(); 133 + let _ = hbs.register_embed_templates::<EmailTemplates>(); 134 + 135 + let state = AppState { 136 + account_pool, 137 + pds_gatekeeper_pool, 138 + reverse_proxy_client: client, 139 + //TODO should be env prob 140 + pds_base_url: "http://localhost:3000".to_string(), 141 + mailer, 142 + mailer_from: sent_from, 143 + template_engine: Engine::from(hbs), 144 + }; 145 + 146 + // Rate limiting 147 + //Allows 5 within 60 seconds, and after 60 should drop one off? So hit 5, then goes to 4 after 60 seconds. 148 + let governor_conf = GovernorConfigBuilder::default() 149 + .per_second(60) 150 + .burst_size(5) 151 + .finish() 152 + .unwrap(); 153 + let governor_limiter = governor_conf.limiter().clone(); 154 + let interval = Duration::from_secs(60); 155 + // a separate background task to clean up 156 + std::thread::spawn(move || { 157 + loop { 158 + std::thread::sleep(interval); 159 + tracing::info!("rate limiting storage size: {}", governor_limiter.len()); 160 + governor_limiter.retain_recent(); 161 + } 162 + }); 163 + 164 + let cors = CorsLayer::new() 165 + .allow_origin(Any) 166 + .allow_methods([Method::GET, Method::OPTIONS, Method::POST]) 167 + .allow_headers(Any); 48 168 49 169 let app = Router::new() 50 - .route("/health", get(health)) 51 - .route("/db/ping", get(db_ping)) 170 + .route("/", get(root_handler)) 171 + .route( 172 + "/xrpc/com.atproto.server.getSession", 173 + get(get_session).layer(ax_middleware::from_fn(middleware::extract_did)), 174 + ) 175 + .route( 176 + "/xrpc/com.atproto.server.updateEmail", 177 + post(update_email).layer(ax_middleware::from_fn(middleware::extract_did)), 178 + ) 179 + .route( 180 + "/xrpc/com.atproto.server.createSession", 181 + post(create_session.layer(GovernorLayer::new(governor_conf))), 182 + ) 183 + .layer(CompressionLayer::new()) 184 + .layer(cors) 52 185 .with_state(state); 53 186 54 187 let host = env::var("HOST").unwrap_or_else(|_| "127.0.0.1".to_string()); 55 - let port: u16 = env::var("PORT").ok().and_then(|s| s.parse().ok()).unwrap_or(8080); 56 - let addr: SocketAddr = format!("{host}:{port}").parse().expect("valid socket address"); 57 - 58 - info!(%addr, %account_db_url, "starting server"); 188 + let port: u16 = env::var("PORT") 189 + .ok() 190 + .and_then(|s| s.parse().ok()) 191 + .unwrap_or(8080); 192 + let addr: SocketAddr = format!("{host}:{port}") 193 + .parse() 194 + .expect("valid socket address"); 59 195 60 196 let listener = tokio::net::TcpListener::bind(addr).await?; 61 197 62 - let server = axum::serve(listener, app).with_graceful_shutdown(shutdown_signal()); 198 + let server = axum::serve( 199 + listener, 200 + app.into_make_service_with_connect_info::<SocketAddr>(), 201 + ) 202 + .with_graceful_shutdown(shutdown_signal()); 63 203 64 204 if let Err(err) = server.await { 65 205 error!(error = %err, "server error"); ··· 68 208 Ok(()) 69 209 } 70 210 71 - async fn health() -> Json<HealthResponse> { 72 - Json(HealthResponse { status: "ok" }) 73 - } 74 - 75 - async fn db_ping(State(state): State<AppState>) -> Result<Json<DbPingResponse>, axum::http::StatusCode> { 76 - // Run a DB-agnostic ping that doesn't depend on user tables. 77 - // In SQLite, SELECT 1 returns a single row with value 1. 78 - let v: i64 = sqlx::query_scalar("SELECT 1") 79 - .fetch_one(&state.pool) 80 - .await 81 - .map_err(|_| axum::http::StatusCode::SERVICE_UNAVAILABLE)?; 82 - 83 - Ok(Json(DbPingResponse { db: "ok", value: v })) 84 - } 85 - 86 211 fn setup_tracing() { 87 212 let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); 88 213 tracing_subscriber::registry() ··· 101 226 102 227 #[cfg(unix)] 103 228 let terminate = async { 104 - use tokio::signal::unix::{signal, SignalKind}; 229 + use tokio::signal::unix::{SignalKind, signal}; 105 230 106 - let mut sigterm = signal(SignalKind::terminate()).expect("failed to install signal handler"); 231 + let mut sigterm = 232 + signal(SignalKind::terminate()).expect("failed to install signal handler"); 107 233 sigterm.recv().await; 108 234 }; 109 235
+97
src/middleware.rs
··· 1 + use crate::xrpc::helpers::json_error_response; 2 + use axum::extract::Request; 3 + use axum::http::{HeaderMap, StatusCode}; 4 + use axum::middleware::Next; 5 + use axum::response::IntoResponse; 6 + use jwt_compact::alg::{Hs256, Hs256Key}; 7 + use jwt_compact::{AlgorithmExt, Claims, Token, UntrustedToken, ValidationError}; 8 + use serde::{Deserialize, Serialize}; 9 + use std::env; 10 + 11 + #[derive(Clone, Debug)] 12 + pub struct Did(pub Option<String>); 13 + 14 + #[derive(Serialize, Deserialize)] 15 + pub struct TokenClaims { 16 + pub sub: String, 17 + } 18 + 19 + pub async fn extract_did(mut req: Request, next: Next) -> impl IntoResponse { 20 + let token = extract_bearer(req.headers()); 21 + 22 + match token { 23 + Ok(token) => { 24 + match token { 25 + None => { 26 + return json_error_response( 27 + StatusCode::BAD_REQUEST, 28 + "TokenRequired", 29 + "", 30 + ).unwrap(); 31 + } 32 + Some(token) => { 33 + let token = UntrustedToken::new(&token); 34 + //Doing weird unwraps cause I can't do Result for middleware? 35 + if token.is_err() { 36 + return json_error_response( 37 + StatusCode::BAD_REQUEST, 38 + "TokenRequired", 39 + "", 40 + ).unwrap(); 41 + } 42 + let parsed_token = token.unwrap(); 43 + let claims: Result<Claims<TokenClaims>, ValidationError> = 44 + parsed_token.deserialize_claims_unchecked(); 45 + if claims.is_err() { 46 + return json_error_response( 47 + StatusCode::BAD_REQUEST, 48 + "TokenRequired", 49 + "", 50 + ).unwrap(); 51 + } 52 + 53 + let key = Hs256Key::new(env::var("PDS_JWT_SECRET").unwrap()); 54 + let token: Result<Token<TokenClaims>, ValidationError> = 55 + Hs256.validator(&key).validate(&parsed_token); 56 + if token.is_err() { 57 + return json_error_response( 58 + StatusCode::BAD_REQUEST, 59 + "InvalidToken", 60 + "", 61 + ).unwrap(); 62 + } 63 + let token = token.unwrap(); 64 + //Not going to worry about expiration since it still goes to the PDS 65 + 66 + req.extensions_mut() 67 + .insert(Did(Some(token.claims().custom.sub.clone()))); 68 + next.run(req).await 69 + } 70 + } 71 + } 72 + Err(_) => { 73 + return json_error_response( 74 + StatusCode::BAD_REQUEST, 75 + "InvalidToken", 76 + "", 77 + ).unwrap(); 78 + } 79 + } 80 + } 81 + 82 + fn extract_bearer(headers: &HeaderMap) -> Result<Option<String>, String> { 83 + match headers.get(axum::http::header::AUTHORIZATION) { 84 + None => Ok(None), 85 + Some(hv) => match hv.to_str() { 86 + Err(_) => Err("Authorization header is not valid".into()), 87 + Ok(s) => { 88 + // Accept forms like: "Bearer <token>" (case-sensitive for the scheme here) 89 + let mut parts = s.splitn(2, ' '); 90 + match (parts.next(), parts.next()) { 91 + (Some("Bearer"), Some(tok)) if !tok.is_empty() => Ok(Some(tok.to_string())), 92 + _ => Err("Authorization header must be in format 'Bearer <token>'".into()), 93 + } 94 + } 95 + }, 96 + } 97 + }
+396 -15
src/xrpc/com_atproto_server.rs
··· 1 + use crate::AppState; 2 + use crate::middleware::Did; 3 + use crate::xrpc::helpers::{ProxiedResult, json_error_response, proxy_get_json}; 4 + use axum::body::Body; 1 5 use axum::extract::State; 2 - use axum::{extract, Json}; 3 - use serde::Deserialize; 4 - use crate::{AppState, DbPingResponse}; 6 + use axum::http::{HeaderMap, StatusCode}; 7 + use axum::response::{IntoResponse, Response}; 8 + use axum::{Extension, Json, debug_handler, extract, extract::Request}; 9 + use axum_template::TemplateEngine; 10 + use lettre::message::{MultiPart, SinglePart, header}; 11 + use lettre::{AsyncTransport, Message}; 12 + use serde::{Deserialize, Serialize}; 13 + use serde_json; 14 + use serde_json::Value; 15 + use serde_json::value::Map; 16 + use tracing::log; 5 17 18 + #[derive(Serialize, Deserialize, Debug, Clone)] 19 + #[serde(rename_all = "camelCase")] 20 + enum AccountStatus { 21 + Takendown, 22 + Suspended, 23 + Deactivated, 24 + } 6 25 7 - #[derive(Deserialize)] 8 - struct CreateSessionRequest { 26 + #[derive(Serialize, Deserialize, Debug, Clone)] 27 + #[serde(rename_all = "camelCase")] 28 + struct GetSessionResponse { 29 + handle: String, 30 + did: String, 31 + #[serde(skip_serializing_if = "Option::is_none")] 32 + email: Option<String>, 33 + #[serde(skip_serializing_if = "Option::is_none")] 34 + email_confirmed: Option<bool>, 35 + #[serde(skip_serializing_if = "Option::is_none")] 36 + email_auth_factor: Option<bool>, 37 + #[serde(skip_serializing_if = "Option::is_none")] 38 + did_doc: Option<String>, 39 + #[serde(skip_serializing_if = "Option::is_none")] 40 + active: Option<bool>, 41 + #[serde(skip_serializing_if = "Option::is_none")] 42 + status: Option<AccountStatus>, 43 + } 44 + 45 + #[derive(Serialize, Deserialize, Debug, Clone)] 46 + #[serde(rename_all = "camelCase")] 47 + pub struct UpdateEmailResponse { 48 + email: String, 49 + #[serde(skip_serializing_if = "Option::is_none")] 50 + email_auth_factor: Option<bool>, 51 + #[serde(skip_serializing_if = "Option::is_none")] 52 + token: Option<String>, 53 + } 54 + 55 + #[allow(dead_code)] 56 + #[derive(Deserialize, Serialize)] 57 + #[serde(rename_all = "camelCase")] 58 + pub struct CreateSessionRequest { 9 59 identifier: String, 10 60 password: String, 11 - #[serde(rename = "authFactorToken")] 12 61 auth_factor_token: String, 13 - #[serde(rename = "allowTakendown")] 14 62 allow_takendown: bool, 15 63 } 16 64 17 - async fn create_session(State(state): State<AppState>, extract::Json(payload): extract::Json<CreateSessionRequest>) -> Result<Json<DbPingResponse>, axum::http::StatusCode> { 18 - // Run a DB-agnostic ping that doesn't depend on user tables. 19 - // In SQLite, SELECT 1 returns a single row with value 1. 20 - let v: i64 = sqlx::query_scalar("SELECT 1") 21 - .fetch_one(&state.pool) 65 + pub enum AuthResult { 66 + WrongIdentityOrPassword, 67 + TwoFactorRequired, 68 + TwoFactorFailed, 69 + /// User does not have 2FA enabled, or passes it 70 + ProxyThrough, 71 + } 72 + 73 + pub enum IdentifierType { 74 + Email, 75 + DID, 76 + Handle, 77 + } 78 + 79 + impl IdentifierType { 80 + fn what_is_it(identifier: String) -> Self { 81 + if identifier.contains("@") { 82 + IdentifierType::Email 83 + } else if identifier.contains("did:") { 84 + IdentifierType::DID 85 + } else { 86 + IdentifierType::Handle 87 + } 88 + } 89 + } 90 + 91 + async fn verify_password(password: &str, password_scrypt: &str) -> Result<bool, StatusCode> { 92 + // Expected format: "salt:hash" where hash is hex of scrypt(password, salt, 64 bytes) 93 + let mut parts = password_scrypt.splitn(2, ':'); 94 + let salt = match parts.next() { 95 + Some(s) if !s.is_empty() => s, 96 + _ => return Ok(false), 97 + }; 98 + let stored_hash_hex = match parts.next() { 99 + Some(h) if !h.is_empty() => h, 100 + _ => return Ok(false), 101 + }; 102 + 103 + //Sets up scrypt to mimic node's scrypt 104 + let params = match scrypt::Params::new(14, 8, 1, 64) { 105 + Ok(p) => p, 106 + Err(_) => return Ok(false), 107 + }; 108 + let mut derived = [0u8; 64]; 109 + if scrypt::scrypt(password.as_bytes(), salt.as_bytes(), &params, &mut derived).is_err() { 110 + return Ok(false); 111 + } 112 + 113 + let stored_bytes = match hex::decode(stored_hash_hex) { 114 + Ok(b) => b, 115 + Err(e) => { 116 + log::error!("Error decoding stored hash: {}", e); 117 + return Ok(false); 118 + } 119 + }; 120 + 121 + Ok(derived.as_slice() == stored_bytes.as_slice()) 122 + } 123 + 124 + async fn preauth_check( 125 + state: &AppState, 126 + identifier: &str, 127 + password: &str, 128 + ) -> Result<AuthResult, StatusCode> { 129 + // Determine identifier type 130 + let id_type = IdentifierType::what_is_it(identifier.to_string()); 131 + 132 + // Query account DB for did and passwordScrypt based on identifier type 133 + let account_row: Option<(String, String, String)> = match id_type { 134 + IdentifierType::Email => sqlx::query_as::<_, (String, String, String)>( 135 + "SELECT did, passwordScrypt, account.email FROM account WHERE email = ? LIMIT 1", 136 + ) 137 + .bind(identifier) 138 + .fetch_optional(&state.account_pool) 139 + .await 140 + .map_err(|_| StatusCode::BAD_REQUEST)?, 141 + IdentifierType::Handle => sqlx::query_as::<_, (String, String, String)>( 142 + "SELECT account.did, account.passwordScrypt, account.email 143 + FROM actor 144 + LEFT JOIN account ON actor.did = account.did 145 + where actor.handle =? LIMIT 1", 146 + ) 147 + .bind(identifier) 148 + .fetch_optional(&state.account_pool) 149 + .await 150 + .map_err(|_| StatusCode::BAD_REQUEST)?, 151 + IdentifierType::DID => sqlx::query_as::<_, (String, String, String)>( 152 + "SELECT did, passwordScrypt, account.email FROM account WHERE did = ? LIMIT 1", 153 + ) 154 + .bind(identifier) 155 + .fetch_optional(&state.account_pool) 156 + .await 157 + .map_err(|_| StatusCode::BAD_REQUEST)?, 158 + }; 159 + 160 + if let Some((did, password_scrypt, email)) = account_row { 161 + // Check two-factor requirement for this DID in the gatekeeper DB 162 + let required_opt = sqlx::query_as::<_, (u8,)>( 163 + "SELECT required FROM two_factor_accounts WHERE did = ? LIMIT 1", 164 + ) 165 + .bind(&did) 166 + .fetch_optional(&state.pds_gatekeeper_pool) 167 + .await 168 + .map_err(|_| StatusCode::BAD_REQUEST)?; 169 + 170 + let two_factor_required = match required_opt { 171 + Some(row) => row.0 != 0, 172 + None => false, 173 + }; 174 + 175 + if two_factor_required { 176 + // Verify password before proceeding to 2FA email step 177 + let verified = verify_password(password, &password_scrypt).await?; 178 + if !verified { 179 + return Ok(AuthResult::WrongIdentityOrPassword); 180 + } 181 + let mut email_data = Map::new(); 182 + //TODO these need real values 183 + let token = "test".to_string(); 184 + let handle = "baileytownsend.dev".to_string(); 185 + email_data.insert("token".to_string(), Value::from(token.clone())); 186 + email_data.insert("handle".to_string(), Value::from(handle.clone())); 187 + //TODO bad unwrap 188 + let email_body = state 189 + .template_engine 190 + .render("two_factor_code.hbs", email_data) 191 + .unwrap(); 192 + 193 + let email = Message::builder() 194 + //TODO prob get the proper type in the state 195 + .from(state.mailer_from.parse().unwrap()) 196 + .to(email.parse().unwrap()) 197 + .subject("Sign in to Bluesky") 198 + .multipart( 199 + MultiPart::alternative() // This is composed of two parts. 200 + .singlepart( 201 + SinglePart::builder() 202 + .header(header::ContentType::TEXT_PLAIN) 203 + .body(format!("We received a sign-in request for the account @{}. Use the code: {} to sign in. If this wasn't you, we recommend taking steps to protect your account by changing your password at https://bsky.app/settings.", handle, token)), // Every message should have a plain text fallback. 204 + ) 205 + .singlepart( 206 + SinglePart::builder() 207 + .header(header::ContentType::TEXT_HTML) 208 + .body(email_body), 209 + ), 210 + ) 211 + //TODO bad 212 + .unwrap(); 213 + return match state.mailer.send(email).await { 214 + Ok(_) => Ok(AuthResult::TwoFactorRequired), 215 + Err(err) => { 216 + log::error!("Error sending the 2FA email: {}", err); 217 + Err(StatusCode::BAD_REQUEST) 218 + } 219 + }; 220 + } 221 + } 222 + 223 + // No local 2FA requirement (or account not found) 224 + Ok(AuthResult::ProxyThrough) 225 + } 226 + 227 + pub async fn create_session( 228 + State(state): State<AppState>, 229 + headers: HeaderMap, 230 + Json(payload): extract::Json<CreateSessionRequest>, 231 + ) -> Result<Response<Body>, StatusCode> { 232 + let identifier = payload.identifier.clone(); 233 + let password = payload.password.clone(); 234 + 235 + // Run the shared pre-auth logic to validate and check 2FA requirement 236 + match preauth_check(&state, &identifier, &password).await? { 237 + AuthResult::WrongIdentityOrPassword => json_error_response( 238 + StatusCode::UNAUTHORIZED, 239 + "AuthenticationRequired", 240 + "Invalid identifier or password", 241 + ), 242 + AuthResult::TwoFactorRequired => { 243 + // Email sending step can be handled here if needed in the future. 244 + json_error_response( 245 + StatusCode::UNAUTHORIZED, 246 + "AuthFactorTokenRequired", 247 + "A sign in code has been sent to your email address", 248 + ) 249 + } 250 + AuthResult::TwoFactorFailed => { 251 + //Not sure what the errors are for this response is yet 252 + json_error_response(StatusCode::UNAUTHORIZED, "PLACEHOLDER", "PLACEHOLDER") 253 + } 254 + AuthResult::ProxyThrough => { 255 + //No 2FA or already passed 256 + let uri = format!( 257 + "{}{}", 258 + state.pds_base_url, "/xrpc/com.atproto.server.createSession" 259 + ); 260 + 261 + let mut req = axum::http::Request::post(uri); 262 + if let Some(req_headers) = req.headers_mut() { 263 + req_headers.extend(headers.clone()); 264 + } 265 + 266 + let payload_bytes = 267 + serde_json::to_vec(&payload).map_err(|_| StatusCode::BAD_REQUEST)?; 268 + let req = req 269 + .body(Body::from(payload_bytes)) 270 + .map_err(|_| StatusCode::BAD_REQUEST)?; 271 + 272 + let proxied = state 273 + .reverse_proxy_client 274 + .request(req) 275 + .await 276 + .map_err(|_| StatusCode::BAD_REQUEST)? 277 + .into_response(); 278 + 279 + Ok(proxied) 280 + } 281 + } 282 + } 283 + 284 + #[debug_handler] 285 + pub async fn update_email( 286 + State(state): State<AppState>, 287 + Extension(did): Extension<Did>, 288 + headers: HeaderMap, 289 + Json(payload): extract::Json<UpdateEmailResponse>, 290 + ) -> Result<Response<Body>, StatusCode> { 291 + //If email auth is not set at all it is a update email address 292 + let email_auth_not_set = payload.email_auth_factor.is_none(); 293 + //If email aurth is set it is to either turn on or off 2fa 294 + let email_auth_update = payload.email_auth_factor.unwrap_or(false); 295 + 296 + // Email update asked for 297 + if email_auth_update { 298 + let email = payload.email.clone(); 299 + let email_confirmed = sqlx::query_as::<_, (String,)>( 300 + "SELECT did FROM account WHERE emailConfirmedAt IS NOT NULL AND email = ?", 301 + ) 302 + .bind(&email) 303 + .fetch_optional(&state.account_pool) 304 + .await 305 + .map_err(|_| StatusCode::BAD_REQUEST)?; 306 + 307 + //Since the email is already confirmed we can enable 2fa 308 + return match email_confirmed { 309 + None => Err(StatusCode::BAD_REQUEST), 310 + Some(did_row) => { 311 + let _ = sqlx::query( 312 + "INSERT INTO two_factor_accounts (did, required) VALUES (?, 1) ON CONFLICT(did) DO UPDATE SET required = 1", 313 + ) 314 + .bind(&did_row.0) 315 + .execute(&state.pds_gatekeeper_pool) 316 + .await 317 + .map_err(|_| StatusCode::BAD_REQUEST)?; 318 + 319 + Ok(StatusCode::OK.into_response()) 320 + } 321 + }; 322 + } 323 + 324 + // User wants auth turned off 325 + if !email_auth_update && !email_auth_not_set { 326 + //User wants auth turned off and has a token 327 + if let Some(token) = &payload.token { 328 + let token_found = sqlx::query_as::<_, (String,)>( 329 + "SELECT token FROM email_token WHERE token = ? AND did = ? AND purpose = 'update_email'", 330 + ) 331 + .bind(token) 332 + .bind(&did.0) 333 + .fetch_optional(&state.account_pool) 334 + .await 335 + .map_err(|_| StatusCode::BAD_REQUEST)?; 336 + 337 + if token_found.is_some() { 338 + let _ = sqlx::query( 339 + "INSERT INTO two_factor_accounts (did, required) VALUES (?, 0) ON CONFLICT(did) DO UPDATE SET required = 0", 340 + ) 341 + .bind(&did.0) 342 + .execute(&state.pds_gatekeeper_pool) 343 + .await 344 + .map_err(|_| StatusCode::BAD_REQUEST)?; 345 + 346 + return Ok(StatusCode::OK.into_response()); 347 + } else { 348 + return Err(StatusCode::BAD_REQUEST); 349 + } 350 + } 351 + } 352 + 353 + // Updating the acutal email address 354 + let uri = format!( 355 + "{}{}", 356 + state.pds_base_url, "/xrpc/com.atproto.server.updateEmail" 357 + ); 358 + let mut req = axum::http::Request::post(uri); 359 + if let Some(req_headers) = req.headers_mut() { 360 + req_headers.extend(headers.clone()); 361 + } 362 + 363 + let payload_bytes = serde_json::to_vec(&payload).map_err(|_| StatusCode::BAD_REQUEST)?; 364 + let req = req 365 + .body(Body::from(payload_bytes)) 366 + .map_err(|_| StatusCode::BAD_REQUEST)?; 367 + 368 + let proxied = state 369 + .reverse_proxy_client 370 + .request(req) 22 371 .await 23 - .map_err(|_| axum::http::StatusCode::SERVICE_UNAVAILABLE)?; 372 + .map_err(|_| StatusCode::BAD_REQUEST)? 373 + .into_response(); 374 + 375 + Ok(proxied) 376 + } 377 + 378 + pub async fn get_session( 379 + State(state): State<AppState>, 380 + req: Request, 381 + ) -> Result<Response<Body>, StatusCode> { 382 + match proxy_get_json::<GetSessionResponse>(&state, req, "/xrpc/com.atproto.server.getSession") 383 + .await? 384 + { 385 + ProxiedResult::Parsed { 386 + value: mut session, .. 387 + } => { 388 + let did = session.did.clone(); 389 + let required_opt = sqlx::query_as::<_, (u8,)>( 390 + "SELECT required FROM two_factor_accounts WHERE did = ? LIMIT 1", 391 + ) 392 + .bind(&did) 393 + .fetch_optional(&state.pds_gatekeeper_pool) 394 + .await 395 + .map_err(|_| StatusCode::BAD_REQUEST)?; 396 + 397 + let email_auth_factor = match required_opt { 398 + Some(row) => row.0 != 0, 399 + None => false, 400 + }; 24 401 25 - Ok(Json(DbPingResponse { db: "ok", value: v })) 26 - } 402 + session.email_auth_factor = Some(email_auth_factor); 403 + Ok(Json(session).into_response()) 404 + } 405 + ProxiedResult::Passthrough(resp) => Ok(resp), 406 + } 407 + }
+150
src/xrpc/helpers.rs
··· 1 + use axum::body::{Body, to_bytes}; 2 + use axum::extract::Request; 3 + use axum::http::{HeaderMap, Method, StatusCode, Uri}; 4 + use axum::http::header::CONTENT_TYPE; 5 + use axum::response::{IntoResponse, Response}; 6 + use serde::de::DeserializeOwned; 7 + use tracing::error; 8 + 9 + use crate::AppState; 10 + 11 + /// The result of a proxied call that attempts to parse JSON. 12 + pub enum ProxiedResult<T> { 13 + /// Successfully parsed JSON body along with original response headers. 14 + Parsed { value: T, _headers: HeaderMap }, 15 + /// Could not or should not parse: return the original (or rebuilt) response as-is. 16 + Passthrough(Response<Body>), 17 + } 18 + 19 + /// Proxy the incoming request to the PDS base URL plus the provided path and attempt to parse 20 + /// the successful response body as JSON into `T`. 21 + /// 22 + /// Behavior: 23 + /// - If the proxied response is non-200, returns Passthrough with the original response. 24 + /// - If the response is 200 but JSON parsing fails, returns Passthrough with the original body and headers. 25 + /// - If parsing succeeds, returns Parsed { value, headers }. 26 + pub async fn proxy_get_json<T>( 27 + state: &AppState, 28 + mut req: Request, 29 + path: &str, 30 + ) -> Result<ProxiedResult<T>, StatusCode> 31 + where 32 + T: DeserializeOwned, 33 + { 34 + let uri = format!("{}{}", state.pds_base_url, path); 35 + *req.uri_mut() = Uri::try_from(uri).map_err(|_| StatusCode::BAD_REQUEST)?; 36 + 37 + let result = state 38 + .reverse_proxy_client 39 + .request(req) 40 + .await 41 + .map_err(|_| StatusCode::BAD_REQUEST)? 42 + .into_response(); 43 + 44 + if result.status() != StatusCode::OK { 45 + return Ok(ProxiedResult::Passthrough(result)); 46 + } 47 + 48 + let response_headers = result.headers().clone(); 49 + let body = result.into_body(); 50 + let body_bytes = to_bytes(body, usize::MAX) 51 + .await 52 + .map_err(|_| StatusCode::BAD_REQUEST)?; 53 + 54 + match serde_json::from_slice::<T>(&body_bytes) { 55 + Ok(value) => Ok(ProxiedResult::Parsed { 56 + value, 57 + _headers: response_headers, 58 + }), 59 + Err(err) => { 60 + error!(%err, "failed to parse proxied JSON response; returning original body"); 61 + let mut builder = Response::builder().status(StatusCode::OK); 62 + if let Some(headers) = builder.headers_mut() { 63 + *headers = response_headers; 64 + } 65 + let resp = builder 66 + .body(Body::from(body_bytes)) 67 + .map_err(|_| StatusCode::BAD_REQUEST)?; 68 + Ok(ProxiedResult::Passthrough(resp)) 69 + } 70 + } 71 + } 72 + 73 + /// Proxy the incoming request as a POST to the PDS base URL plus the provided path and attempt to parse 74 + /// the successful response body as JSON into `T`. 75 + /// 76 + /// Behavior mirrors `proxy_get_json`: 77 + /// - If the proxied response is non-200, returns Passthrough with the original response. 78 + /// - If the response is 200 but JSON parsing fails, returns Passthrough with the original body and headers. 79 + /// - If parsing succeeds, returns Parsed { value, headers }. 80 + pub async fn _proxy_post_json<T>( 81 + state: &AppState, 82 + mut req: Request, 83 + path: &str, 84 + ) -> Result<ProxiedResult<T>, StatusCode> 85 + where 86 + T: DeserializeOwned, 87 + { 88 + let uri = format!("{}{}", state.pds_base_url, path); 89 + *req.uri_mut() = Uri::try_from(uri).map_err(|_| StatusCode::BAD_REQUEST)?; 90 + *req.method_mut() = Method::POST; 91 + 92 + let result = state 93 + .reverse_proxy_client 94 + .request(req) 95 + .await 96 + .map_err(|_| StatusCode::BAD_REQUEST)? 97 + .into_response(); 98 + 99 + if result.status() != StatusCode::OK { 100 + return Ok(ProxiedResult::Passthrough(result)); 101 + } 102 + 103 + let response_headers = result.headers().clone(); 104 + let body = result.into_body(); 105 + let body_bytes = to_bytes(body, usize::MAX) 106 + .await 107 + .map_err(|_| StatusCode::BAD_REQUEST)?; 108 + 109 + match serde_json::from_slice::<T>(&body_bytes) { 110 + Ok(value) => Ok(ProxiedResult::Parsed { 111 + value, 112 + _headers: response_headers, 113 + }), 114 + Err(err) => { 115 + error!(%err, "failed to parse proxied JSON response (POST); returning original body"); 116 + let mut builder = Response::builder().status(StatusCode::OK); 117 + if let Some(headers) = builder.headers_mut() { 118 + *headers = response_headers; 119 + } 120 + let resp = builder 121 + .body(Body::from(body_bytes)) 122 + .map_err(|_| StatusCode::BAD_REQUEST)?; 123 + Ok(ProxiedResult::Passthrough(resp)) 124 + } 125 + } 126 + } 127 + 128 + 129 + /// Build a JSON error response with the required Content-Type header 130 + /// Content-Type: application/json;charset=utf-8 131 + /// Body shape: { "error": string, "message": string } 132 + pub fn json_error_response( 133 + status: StatusCode, 134 + error: impl Into<String>, 135 + message: impl Into<String>, 136 + ) -> Result<Response<Body>, StatusCode> { 137 + let body_str = match serde_json::to_string(&serde_json::json!({ 138 + "error": error.into(), 139 + "message": message.into(), 140 + })) { 141 + Ok(s) => s, 142 + Err(_) => return Err(StatusCode::BAD_REQUEST), 143 + }; 144 + 145 + Response::builder() 146 + .status(status) 147 + .header(CONTENT_TYPE, "application/json;charset=utf-8") 148 + .body(Body::from(body_str)) 149 + .map_err(|_| StatusCode::BAD_REQUEST) 150 + }
+2 -1
src/xrpc/mod.rs
··· 1 - mod com_atproto_server; 1 + pub mod com_atproto_server; 2 + pub mod helpers;