aboutsummaryrefslogtreecommitdiffstats
path: root/rs
diff options
context:
space:
mode:
Diffstat (limited to 'rs')
-rw-r--r--rs/.gitignore1
-rw-r--r--rs/Cargo.lock2364
-rw-r--r--rs/Cargo.toml8
-rw-r--r--rs/common/Cargo.toml10
-rw-r--r--rs/common/src/lib.rs368
-rw-r--r--rs/git-lfs-authenticate/Cargo.toml8
-rw-r--r--rs/git-lfs-authenticate/src/main.rs236
-rw-r--r--rs/server/Cargo.toml19
-rw-r--r--rs/server/src/main.rs1028
-rw-r--r--rs/shell/Cargo.toml6
-rw-r--r--rs/shell/src/main.rs143
11 files changed, 0 insertions, 4191 deletions
diff --git a/rs/.gitignore b/rs/.gitignore
deleted file mode 100644
index b83d222..0000000
--- a/rs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
1/target/
diff --git a/rs/Cargo.lock b/rs/Cargo.lock
deleted file mode 100644
index f3beb9e..0000000
--- a/rs/Cargo.lock
+++ /dev/null
@@ -1,2364 +0,0 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 3
4
5[[package]]
6name = "addr2line"
7version = "0.21.0"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
10dependencies = [
11 "gimli",
12]
13
14[[package]]
15name = "adler"
16version = "1.0.2"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
19
20[[package]]
21name = "aho-corasick"
22version = "1.1.2"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
25dependencies = [
26 "memchr",
27]
28
29[[package]]
30name = "android-tzdata"
31version = "0.1.1"
32source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
34
35[[package]]
36name = "android_system_properties"
37version = "0.1.5"
38source = "registry+https://github.com/rust-lang/crates.io-index"
39checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
40dependencies = [
41 "libc",
42]
43
44[[package]]
45name = "async-trait"
46version = "0.1.77"
47source = "registry+https://github.com/rust-lang/crates.io-index"
48checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
49dependencies = [
50 "proc-macro2",
51 "quote",
52 "syn",
53]
54
55[[package]]
56name = "autocfg"
57version = "1.1.0"
58source = "registry+https://github.com/rust-lang/crates.io-index"
59checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
60
61[[package]]
62name = "aws-config"
63version = "1.1.2"
64source = "registry+https://github.com/rust-lang/crates.io-index"
65checksum = "7e64b72d4bdbb41a73d27709c65a25b6e4bfc8321bf70fa3a8b19ce7d4eb81b0"
66dependencies = [
67 "aws-credential-types",
68 "aws-http",
69 "aws-runtime",
70 "aws-sdk-sso",
71 "aws-sdk-ssooidc",
72 "aws-sdk-sts",
73 "aws-smithy-async",
74 "aws-smithy-http",
75 "aws-smithy-json",
76 "aws-smithy-runtime",
77 "aws-smithy-runtime-api",
78 "aws-smithy-types",
79 "aws-types",
80 "bytes",
81 "fastrand",
82 "hex",
83 "http 0.2.11",
84 "hyper 0.14.28",
85 "ring",
86 "time",
87 "tokio",
88 "tracing",
89 "zeroize",
90]
91
92[[package]]
93name = "aws-credential-types"
94version = "1.1.2"
95source = "registry+https://github.com/rust-lang/crates.io-index"
96checksum = "4a7cb3510b95492bd9014b60e2e3bee3e48bc516e220316f8e6b60df18b47331"
97dependencies = [
98 "aws-smithy-async",
99 "aws-smithy-runtime-api",
100 "aws-smithy-types",
101 "zeroize",
102]
103
104[[package]]
105name = "aws-http"
106version = "0.60.2"
107source = "registry+https://github.com/rust-lang/crates.io-index"
108checksum = "a95d41abe4e941399fdb4bc2f54713eac3c839d98151875948bb24e66ab658f2"
109dependencies = [
110 "aws-smithy-runtime-api",
111 "aws-smithy-types",
112 "aws-types",
113 "bytes",
114 "http 0.2.11",
115 "http-body 0.4.6",
116 "pin-project-lite",
117 "tracing",
118]
119
120[[package]]
121name = "aws-runtime"
122version = "1.1.2"
123source = "registry+https://github.com/rust-lang/crates.io-index"
124checksum = "233cca219c6705d525ace011d6f9bc51aaf32fce5b4c41661d2d7ff22d9b4d49"
125dependencies = [
126 "aws-credential-types",
127 "aws-http",
128 "aws-sigv4",
129 "aws-smithy-async",
130 "aws-smithy-eventstream",
131 "aws-smithy-http",
132 "aws-smithy-runtime-api",
133 "aws-smithy-types",
134 "aws-types",
135 "fastrand",
136 "http 0.2.11",
137 "percent-encoding",
138 "tracing",
139 "uuid",
140]
141
142[[package]]
143name = "aws-sdk-s3"
144version = "1.12.0"
145source = "registry+https://github.com/rust-lang/crates.io-index"
146checksum = "634fbe5b6591ee2e281cd2ba8641e9bd752dbf5bf338924d6ad4bd5a3304fe31"
147dependencies = [
148 "aws-credential-types",
149 "aws-http",
150 "aws-runtime",
151 "aws-sigv4",
152 "aws-smithy-async",
153 "aws-smithy-checksums",
154 "aws-smithy-eventstream",
155 "aws-smithy-http",
156 "aws-smithy-json",
157 "aws-smithy-runtime",
158 "aws-smithy-runtime-api",
159 "aws-smithy-types",
160 "aws-smithy-xml",
161 "aws-types",
162 "bytes",
163 "http 0.2.11",
164 "http-body 0.4.6",
165 "once_cell",
166 "percent-encoding",
167 "regex-lite",
168 "tracing",
169 "url",
170]
171
172[[package]]
173name = "aws-sdk-sso"
174version = "1.10.0"
175source = "registry+https://github.com/rust-lang/crates.io-index"
176checksum = "ee41005e0f3a19ae749c7953d9e1f1ef8d2183f76f64966e346fa41c1ba0ed44"
177dependencies = [
178 "aws-credential-types",
179 "aws-http",
180 "aws-runtime",
181 "aws-smithy-async",
182 "aws-smithy-http",
183 "aws-smithy-json",
184 "aws-smithy-runtime",
185 "aws-smithy-runtime-api",
186 "aws-smithy-types",
187 "aws-types",
188 "bytes",
189 "http 0.2.11",
190 "once_cell",
191 "regex-lite",
192 "tracing",
193]
194
195[[package]]
196name = "aws-sdk-ssooidc"
197version = "1.10.0"
198source = "registry+https://github.com/rust-lang/crates.io-index"
199checksum = "fa08168f8a27505e7b90f922c32a489feb1f2133878981a15138bebc849ac09c"
200dependencies = [
201 "aws-credential-types",
202 "aws-http",
203 "aws-runtime",
204 "aws-smithy-async",
205 "aws-smithy-http",
206 "aws-smithy-json",
207 "aws-smithy-runtime",
208 "aws-smithy-runtime-api",
209 "aws-smithy-types",
210 "aws-types",
211 "bytes",
212 "http 0.2.11",
213 "once_cell",
214 "regex-lite",
215 "tracing",
216]
217
218[[package]]
219name = "aws-sdk-sts"
220version = "1.10.0"
221source = "registry+https://github.com/rust-lang/crates.io-index"
222checksum = "29102eff04d50ef70f11a48823db33e33c6cc5f027bfb6ff4864efbd5f1f66f3"
223dependencies = [
224 "aws-credential-types",
225 "aws-http",
226 "aws-runtime",
227 "aws-smithy-async",
228 "aws-smithy-http",
229 "aws-smithy-json",
230 "aws-smithy-query",
231 "aws-smithy-runtime",
232 "aws-smithy-runtime-api",
233 "aws-smithy-types",
234 "aws-smithy-xml",
235 "aws-types",
236 "http 0.2.11",
237 "once_cell",
238 "regex-lite",
239 "tracing",
240]
241
242[[package]]
243name = "aws-sigv4"
244version = "1.1.2"
245source = "registry+https://github.com/rust-lang/crates.io-index"
246checksum = "b92384b39aedb258aa734fe0e7b2ffcd13f33e68227251a72cd2635e0acc8f1a"
247dependencies = [
248 "aws-credential-types",
249 "aws-smithy-eventstream",
250 "aws-smithy-http",
251 "aws-smithy-runtime-api",
252 "aws-smithy-types",
253 "bytes",
254 "crypto-bigint 0.5.5",
255 "form_urlencoded",
256 "hex",
257 "hmac",
258 "http 0.2.11",
259 "once_cell",
260 "p256",
261 "percent-encoding",
262 "ring",
263 "sha2",
264 "subtle",
265 "time",
266 "tracing",
267 "zeroize",
268]
269
270[[package]]
271name = "aws-smithy-async"
272version = "1.1.3"
273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "2eac0bb78e9e2765699999a02d7bfb4e6ad8f13e0962ebb9f5202b1d8cd76006"
275dependencies = [
276 "futures-util",
277 "pin-project-lite",
278 "tokio",
279]
280
281[[package]]
282name = "aws-smithy-checksums"
283version = "0.60.3"
284source = "registry+https://github.com/rust-lang/crates.io-index"
285checksum = "535a2d5f1e459bc7709580a77152c8d493982db083236c2b1d1c51dc6217e8a3"
286dependencies = [
287 "aws-smithy-http",
288 "aws-smithy-types",
289 "bytes",
290 "crc32c",
291 "crc32fast",
292 "hex",
293 "http 0.2.11",
294 "http-body 0.4.6",
295 "md-5",
296 "pin-project-lite",
297 "sha1",
298 "sha2",
299 "tracing",
300]
301
302[[package]]
303name = "aws-smithy-eventstream"
304version = "0.60.3"
305source = "registry+https://github.com/rust-lang/crates.io-index"
306checksum = "682371561562d08ab437766903c6bc28f4f95d7ab2ecfb389bda7849dd98aefe"
307dependencies = [
308 "aws-smithy-types",
309 "bytes",
310 "crc32fast",
311]
312
313[[package]]
314name = "aws-smithy-http"
315version = "0.60.3"
316source = "registry+https://github.com/rust-lang/crates.io-index"
317checksum = "365ca49744b2bda2f1e2dc03b856da3fa5a28ca5b0a41e41d7ff5305a8fae190"
318dependencies = [
319 "aws-smithy-eventstream",
320 "aws-smithy-runtime-api",
321 "aws-smithy-types",
322 "bytes",
323 "bytes-utils",
324 "futures-core",
325 "http 0.2.11",
326 "http-body 0.4.6",
327 "once_cell",
328 "percent-encoding",
329 "pin-project-lite",
330 "pin-utils",
331 "tracing",
332]
333
334[[package]]
335name = "aws-smithy-json"
336version = "0.60.3"
337source = "registry+https://github.com/rust-lang/crates.io-index"
338checksum = "733ccdb727ac63370836aa3b3c483d75ad2ef7bc6507db3efe1d01e8d2e50367"
339dependencies = [
340 "aws-smithy-types",
341]
342
343[[package]]
344name = "aws-smithy-query"
345version = "0.60.3"
346source = "registry+https://github.com/rust-lang/crates.io-index"
347checksum = "aff02ae2ee7968bbce2983ffb5ce529d24f4848532300f398347bde8c2196974"
348dependencies = [
349 "aws-smithy-types",
350 "urlencoding",
351]
352
353[[package]]
354name = "aws-smithy-runtime"
355version = "1.1.3"
356source = "registry+https://github.com/rust-lang/crates.io-index"
357checksum = "6ab9cb6fee50680af8ceaa293ae79eba32095ca117161cb323f9ee30dd87d139"
358dependencies = [
359 "aws-smithy-async",
360 "aws-smithy-http",
361 "aws-smithy-runtime-api",
362 "aws-smithy-types",
363 "bytes",
364 "fastrand",
365 "h2 0.3.24",
366 "http 0.2.11",
367 "http-body 0.4.6",
368 "hyper 0.14.28",
369 "hyper-rustls",
370 "once_cell",
371 "pin-project-lite",
372 "pin-utils",
373 "rustls",
374 "tokio",
375 "tracing",
376]
377
378[[package]]
379name = "aws-smithy-runtime-api"
380version = "1.1.3"
381source = "registry+https://github.com/rust-lang/crates.io-index"
382checksum = "02ca2da7619517310bfead6d18abcdde90f1439224d887d608503cfacff46dff"
383dependencies = [
384 "aws-smithy-async",
385 "aws-smithy-types",
386 "bytes",
387 "http 0.2.11",
388 "pin-project-lite",
389 "tokio",
390 "tracing",
391 "zeroize",
392]
393
394[[package]]
395name = "aws-smithy-types"
396version = "1.1.3"
397source = "registry+https://github.com/rust-lang/crates.io-index"
398checksum = "5d4bb944488536cd2fef43212d829bc7e9a8bfc4afa079d21170441e7be8d2d0"
399dependencies = [
400 "base64-simd",
401 "bytes",
402 "bytes-utils",
403 "futures-core",
404 "http 0.2.11",
405 "http-body 0.4.6",
406 "itoa",
407 "num-integer",
408 "pin-project-lite",
409 "pin-utils",
410 "ryu",
411 "serde",
412 "time",
413 "tokio",
414 "tokio-util",
415]
416
417[[package]]
418name = "aws-smithy-xml"
419version = "0.60.3"
420source = "registry+https://github.com/rust-lang/crates.io-index"
421checksum = "ef796feaf894d7fd03869235237aeffe73ed1b29a3927cceeee2eecadf876eba"
422dependencies = [
423 "xmlparser",
424]
425
426[[package]]
427name = "aws-types"
428version = "1.1.2"
429source = "registry+https://github.com/rust-lang/crates.io-index"
430checksum = "8549aa62c5b7db5c57ab915200ee214b4f5d8f19b29a4a8fa0b3ad3bca1380e3"
431dependencies = [
432 "aws-credential-types",
433 "aws-smithy-async",
434 "aws-smithy-runtime-api",
435 "aws-smithy-types",
436 "http 0.2.11",
437 "rustc_version",
438 "tracing",
439]
440
441[[package]]
442name = "axum"
443version = "0.7.4"
444source = "registry+https://github.com/rust-lang/crates.io-index"
445checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
446dependencies = [
447 "async-trait",
448 "axum-core",
449 "bytes",
450 "futures-util",
451 "http 1.0.0",
452 "http-body 1.0.0",
453 "http-body-util",
454 "hyper 1.1.0",
455 "hyper-util",
456 "itoa",
457 "matchit",
458 "memchr",
459 "mime",
460 "percent-encoding",
461 "pin-project-lite",
462 "rustversion",
463 "serde",
464 "serde_json",
465 "serde_path_to_error",
466 "serde_urlencoded",
467 "sync_wrapper",
468 "tokio",
469 "tower",
470 "tower-layer",
471 "tower-service",
472 "tracing",
473]
474
475[[package]]
476name = "axum-core"
477version = "0.4.3"
478source = "registry+https://github.com/rust-lang/crates.io-index"
479checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
480dependencies = [
481 "async-trait",
482 "bytes",
483 "futures-util",
484 "http 1.0.0",
485 "http-body 1.0.0",
486 "http-body-util",
487 "mime",
488 "pin-project-lite",
489 "rustversion",
490 "sync_wrapper",
491 "tower-layer",
492 "tower-service",
493 "tracing",
494]
495
496[[package]]
497name = "backtrace"
498version = "0.3.69"
499source = "registry+https://github.com/rust-lang/crates.io-index"
500checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
501dependencies = [
502 "addr2line",
503 "cc",
504 "cfg-if",
505 "libc",
506 "miniz_oxide",
507 "object",
508 "rustc-demangle",
509]
510
511[[package]]
512name = "base16ct"
513version = "0.1.1"
514source = "registry+https://github.com/rust-lang/crates.io-index"
515checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce"
516
517[[package]]
518name = "base64"
519version = "0.21.7"
520source = "registry+https://github.com/rust-lang/crates.io-index"
521checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
522
523[[package]]
524name = "base64-simd"
525version = "0.8.0"
526source = "registry+https://github.com/rust-lang/crates.io-index"
527checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195"
528dependencies = [
529 "outref",
530 "vsimd",
531]
532
533[[package]]
534name = "base64ct"
535version = "1.6.0"
536source = "registry+https://github.com/rust-lang/crates.io-index"
537checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
538
539[[package]]
540name = "bitflags"
541version = "1.3.2"
542source = "registry+https://github.com/rust-lang/crates.io-index"
543checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
544
545[[package]]
546name = "block-buffer"
547version = "0.10.4"
548source = "registry+https://github.com/rust-lang/crates.io-index"
549checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
550dependencies = [
551 "generic-array",
552]
553
554[[package]]
555name = "bumpalo"
556version = "3.14.0"
557source = "registry+https://github.com/rust-lang/crates.io-index"
558checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
559
560[[package]]
561name = "bytes"
562version = "1.5.0"
563source = "registry+https://github.com/rust-lang/crates.io-index"
564checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
565
566[[package]]
567name = "bytes-utils"
568version = "0.1.4"
569source = "registry+https://github.com/rust-lang/crates.io-index"
570checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35"
571dependencies = [
572 "bytes",
573 "either",
574]
575
576[[package]]
577name = "cc"
578version = "1.0.83"
579source = "registry+https://github.com/rust-lang/crates.io-index"
580checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
581dependencies = [
582 "libc",
583]
584
585[[package]]
586name = "cfg-if"
587version = "1.0.0"
588source = "registry+https://github.com/rust-lang/crates.io-index"
589checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
590
591[[package]]
592name = "chrono"
593version = "0.4.31"
594source = "registry+https://github.com/rust-lang/crates.io-index"
595checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
596dependencies = [
597 "android-tzdata",
598 "iana-time-zone",
599 "js-sys",
600 "num-traits",
601 "serde",
602 "wasm-bindgen",
603 "windows-targets 0.48.5",
604]
605
606[[package]]
607name = "common"
608version = "0.1.0"
609dependencies = [
610 "chrono",
611 "hmac-sha256",
612 "serde",
613 "subtle",
614]
615
616[[package]]
617name = "const-oid"
618version = "0.9.6"
619source = "registry+https://github.com/rust-lang/crates.io-index"
620checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
621
622[[package]]
623name = "core-foundation"
624version = "0.9.4"
625source = "registry+https://github.com/rust-lang/crates.io-index"
626checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
627dependencies = [
628 "core-foundation-sys",
629 "libc",
630]
631
632[[package]]
633name = "core-foundation-sys"
634version = "0.8.6"
635source = "registry+https://github.com/rust-lang/crates.io-index"
636checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
637
638[[package]]
639name = "cpufeatures"
640version = "0.2.12"
641source = "registry+https://github.com/rust-lang/crates.io-index"
642checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
643dependencies = [
644 "libc",
645]
646
647[[package]]
648name = "crc32c"
649version = "0.6.4"
650source = "registry+https://github.com/rust-lang/crates.io-index"
651checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74"
652dependencies = [
653 "rustc_version",
654]
655
656[[package]]
657name = "crc32fast"
658version = "1.3.2"
659source = "registry+https://github.com/rust-lang/crates.io-index"
660checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
661dependencies = [
662 "cfg-if",
663]
664
665[[package]]
666name = "crypto-bigint"
667version = "0.4.9"
668source = "registry+https://github.com/rust-lang/crates.io-index"
669checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef"
670dependencies = [
671 "generic-array",
672 "rand_core",
673 "subtle",
674 "zeroize",
675]
676
677[[package]]
678name = "crypto-bigint"
679version = "0.5.5"
680source = "registry+https://github.com/rust-lang/crates.io-index"
681checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
682dependencies = [
683 "rand_core",
684 "subtle",
685]
686
687[[package]]
688name = "crypto-common"
689version = "0.1.6"
690source = "registry+https://github.com/rust-lang/crates.io-index"
691checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
692dependencies = [
693 "generic-array",
694 "typenum",
695]
696
697[[package]]
698name = "der"
699version = "0.6.1"
700source = "registry+https://github.com/rust-lang/crates.io-index"
701checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
702dependencies = [
703 "const-oid",
704 "zeroize",
705]
706
707[[package]]
708name = "deranged"
709version = "0.3.11"
710source = "registry+https://github.com/rust-lang/crates.io-index"
711checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
712dependencies = [
713 "powerfmt",
714]
715
716[[package]]
717name = "digest"
718version = "0.10.7"
719source = "registry+https://github.com/rust-lang/crates.io-index"
720checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
721dependencies = [
722 "block-buffer",
723 "crypto-common",
724 "subtle",
725]
726
727[[package]]
728name = "ecdsa"
729version = "0.14.8"
730source = "registry+https://github.com/rust-lang/crates.io-index"
731checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c"
732dependencies = [
733 "der",
734 "elliptic-curve",
735 "rfc6979",
736 "signature",
737]
738
739[[package]]
740name = "either"
741version = "1.9.0"
742source = "registry+https://github.com/rust-lang/crates.io-index"
743checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
744
745[[package]]
746name = "elliptic-curve"
747version = "0.12.3"
748source = "registry+https://github.com/rust-lang/crates.io-index"
749checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3"
750dependencies = [
751 "base16ct",
752 "crypto-bigint 0.4.9",
753 "der",
754 "digest",
755 "ff",
756 "generic-array",
757 "group",
758 "pkcs8",
759 "rand_core",
760 "sec1",
761 "subtle",
762 "zeroize",
763]
764
765[[package]]
766name = "equivalent"
767version = "1.0.1"
768source = "registry+https://github.com/rust-lang/crates.io-index"
769checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
770
771[[package]]
772name = "fastrand"
773version = "2.0.1"
774source = "registry+https://github.com/rust-lang/crates.io-index"
775checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
776
777[[package]]
778name = "ff"
779version = "0.12.1"
780source = "registry+https://github.com/rust-lang/crates.io-index"
781checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160"
782dependencies = [
783 "rand_core",
784 "subtle",
785]
786
787[[package]]
788name = "fnv"
789version = "1.0.7"
790source = "registry+https://github.com/rust-lang/crates.io-index"
791checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
792
793[[package]]
794name = "form_urlencoded"
795version = "1.2.1"
796source = "registry+https://github.com/rust-lang/crates.io-index"
797checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
798dependencies = [
799 "percent-encoding",
800]
801
802[[package]]
803name = "futures-channel"
804version = "0.3.30"
805source = "registry+https://github.com/rust-lang/crates.io-index"
806checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
807dependencies = [
808 "futures-core",
809]
810
811[[package]]
812name = "futures-core"
813version = "0.3.30"
814source = "registry+https://github.com/rust-lang/crates.io-index"
815checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
816
817[[package]]
818name = "futures-sink"
819version = "0.3.30"
820source = "registry+https://github.com/rust-lang/crates.io-index"
821checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
822
823[[package]]
824name = "futures-task"
825version = "0.3.30"
826source = "registry+https://github.com/rust-lang/crates.io-index"
827checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
828
829[[package]]
830name = "futures-util"
831version = "0.3.30"
832source = "registry+https://github.com/rust-lang/crates.io-index"
833checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
834dependencies = [
835 "futures-core",
836 "futures-task",
837 "pin-project-lite",
838 "pin-utils",
839]
840
841[[package]]
842name = "generic-array"
843version = "0.14.7"
844source = "registry+https://github.com/rust-lang/crates.io-index"
845checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
846dependencies = [
847 "typenum",
848 "version_check",
849]
850
851[[package]]
852name = "getrandom"
853version = "0.2.12"
854source = "registry+https://github.com/rust-lang/crates.io-index"
855checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
856dependencies = [
857 "cfg-if",
858 "libc",
859 "wasi",
860]
861
862[[package]]
863name = "gimli"
864version = "0.28.1"
865source = "registry+https://github.com/rust-lang/crates.io-index"
866checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
867
868[[package]]
869name = "git-lfs-authenticate"
870version = "0.1.0"
871dependencies = [
872 "chrono",
873 "common",
874]
875
876[[package]]
877name = "group"
878version = "0.12.1"
879source = "registry+https://github.com/rust-lang/crates.io-index"
880checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7"
881dependencies = [
882 "ff",
883 "rand_core",
884 "subtle",
885]
886
887[[package]]
888name = "h2"
889version = "0.3.24"
890source = "registry+https://github.com/rust-lang/crates.io-index"
891checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
892dependencies = [
893 "bytes",
894 "fnv",
895 "futures-core",
896 "futures-sink",
897 "futures-util",
898 "http 0.2.11",
899 "indexmap",
900 "slab",
901 "tokio",
902 "tokio-util",
903 "tracing",
904]
905
906[[package]]
907name = "h2"
908version = "0.4.2"
909source = "registry+https://github.com/rust-lang/crates.io-index"
910checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943"
911dependencies = [
912 "bytes",
913 "fnv",
914 "futures-core",
915 "futures-sink",
916 "futures-util",
917 "http 1.0.0",
918 "indexmap",
919 "slab",
920 "tokio",
921 "tokio-util",
922 "tracing",
923]
924
925[[package]]
926name = "hashbrown"
927version = "0.14.3"
928source = "registry+https://github.com/rust-lang/crates.io-index"
929checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
930
931[[package]]
932name = "hermit-abi"
933version = "0.3.3"
934source = "registry+https://github.com/rust-lang/crates.io-index"
935checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
936
937[[package]]
938name = "hex"
939version = "0.4.3"
940source = "registry+https://github.com/rust-lang/crates.io-index"
941checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
942
943[[package]]
944name = "hmac"
945version = "0.12.1"
946source = "registry+https://github.com/rust-lang/crates.io-index"
947checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
948dependencies = [
949 "digest",
950]
951
952[[package]]
953name = "hmac-sha256"
954version = "1.1.7"
955source = "registry+https://github.com/rust-lang/crates.io-index"
956checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735"
957
958[[package]]
959name = "http"
960version = "0.2.11"
961source = "registry+https://github.com/rust-lang/crates.io-index"
962checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
963dependencies = [
964 "bytes",
965 "fnv",
966 "itoa",
967]
968
969[[package]]
970name = "http"
971version = "1.0.0"
972source = "registry+https://github.com/rust-lang/crates.io-index"
973checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
974dependencies = [
975 "bytes",
976 "fnv",
977 "itoa",
978]
979
980[[package]]
981name = "http-body"
982version = "0.4.6"
983source = "registry+https://github.com/rust-lang/crates.io-index"
984checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
985dependencies = [
986 "bytes",
987 "http 0.2.11",
988 "pin-project-lite",
989]
990
991[[package]]
992name = "http-body"
993version = "1.0.0"
994source = "registry+https://github.com/rust-lang/crates.io-index"
995checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
996dependencies = [
997 "bytes",
998 "http 1.0.0",
999]
1000
1001[[package]]
1002name = "http-body-util"
1003version = "0.1.0"
1004source = "registry+https://github.com/rust-lang/crates.io-index"
1005checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840"
1006dependencies = [
1007 "bytes",
1008 "futures-util",
1009 "http 1.0.0",
1010 "http-body 1.0.0",
1011 "pin-project-lite",
1012]
1013
1014[[package]]
1015name = "httparse"
1016version = "1.8.0"
1017source = "registry+https://github.com/rust-lang/crates.io-index"
1018checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
1019
1020[[package]]
1021name = "httpdate"
1022version = "1.0.3"
1023source = "registry+https://github.com/rust-lang/crates.io-index"
1024checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
1025
1026[[package]]
1027name = "hyper"
1028version = "0.14.28"
1029source = "registry+https://github.com/rust-lang/crates.io-index"
1030checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
1031dependencies = [
1032 "bytes",
1033 "futures-channel",
1034 "futures-core",
1035 "futures-util",
1036 "h2 0.3.24",
1037 "http 0.2.11",
1038 "http-body 0.4.6",
1039 "httparse",
1040 "httpdate",
1041 "itoa",
1042 "pin-project-lite",
1043 "socket2",
1044 "tokio",
1045 "tower-service",
1046 "tracing",
1047 "want",
1048]
1049
1050[[package]]
1051name = "hyper"
1052version = "1.1.0"
1053source = "registry+https://github.com/rust-lang/crates.io-index"
1054checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75"
1055dependencies = [
1056 "bytes",
1057 "futures-channel",
1058 "futures-util",
1059 "h2 0.4.2",
1060 "http 1.0.0",
1061 "http-body 1.0.0",
1062 "httparse",
1063 "httpdate",
1064 "itoa",
1065 "pin-project-lite",
1066 "tokio",
1067]
1068
1069[[package]]
1070name = "hyper-rustls"
1071version = "0.24.2"
1072source = "registry+https://github.com/rust-lang/crates.io-index"
1073checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
1074dependencies = [
1075 "futures-util",
1076 "http 0.2.11",
1077 "hyper 0.14.28",
1078 "log",
1079 "rustls",
1080 "rustls-native-certs",
1081 "tokio",
1082 "tokio-rustls",
1083]
1084
1085[[package]]
1086name = "hyper-util"
1087version = "0.1.2"
1088source = "registry+https://github.com/rust-lang/crates.io-index"
1089checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67"
1090dependencies = [
1091 "bytes",
1092 "futures-channel",
1093 "futures-util",
1094 "http 1.0.0",
1095 "http-body 1.0.0",
1096 "hyper 1.1.0",
1097 "pin-project-lite",
1098 "socket2",
1099 "tokio",
1100 "tracing",
1101]
1102
1103[[package]]
1104name = "iana-time-zone"
1105version = "0.1.59"
1106source = "registry+https://github.com/rust-lang/crates.io-index"
1107checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
1108dependencies = [
1109 "android_system_properties",
1110 "core-foundation-sys",
1111 "iana-time-zone-haiku",
1112 "js-sys",
1113 "wasm-bindgen",
1114 "windows-core",
1115]
1116
1117[[package]]
1118name = "iana-time-zone-haiku"
1119version = "0.1.2"
1120source = "registry+https://github.com/rust-lang/crates.io-index"
1121checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
1122dependencies = [
1123 "cc",
1124]
1125
1126[[package]]
1127name = "idna"
1128version = "0.5.0"
1129source = "registry+https://github.com/rust-lang/crates.io-index"
1130checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
1131dependencies = [
1132 "unicode-bidi",
1133 "unicode-normalization",
1134]
1135
1136[[package]]
1137name = "indexmap"
1138version = "2.1.0"
1139source = "registry+https://github.com/rust-lang/crates.io-index"
1140checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
1141dependencies = [
1142 "equivalent",
1143 "hashbrown",
1144]
1145
1146[[package]]
1147name = "itoa"
1148version = "1.0.10"
1149source = "registry+https://github.com/rust-lang/crates.io-index"
1150checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
1151
1152[[package]]
1153name = "js-sys"
1154version = "0.3.67"
1155source = "registry+https://github.com/rust-lang/crates.io-index"
1156checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
1157dependencies = [
1158 "wasm-bindgen",
1159]
1160
1161[[package]]
1162name = "lazy_static"
1163version = "1.4.0"
1164source = "registry+https://github.com/rust-lang/crates.io-index"
1165checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
1166
1167[[package]]
1168name = "libc"
1169version = "0.2.152"
1170source = "registry+https://github.com/rust-lang/crates.io-index"
1171checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
1172
1173[[package]]
1174name = "lock_api"
1175version = "0.4.11"
1176source = "registry+https://github.com/rust-lang/crates.io-index"
1177checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
1178dependencies = [
1179 "autocfg",
1180 "scopeguard",
1181]
1182
1183[[package]]
1184name = "log"
1185version = "0.4.20"
1186source = "registry+https://github.com/rust-lang/crates.io-index"
1187checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
1188
1189[[package]]
1190name = "matchers"
1191version = "0.1.0"
1192source = "registry+https://github.com/rust-lang/crates.io-index"
1193checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
1194dependencies = [
1195 "regex-automata 0.1.10",
1196]
1197
1198[[package]]
1199name = "matchit"
1200version = "0.7.3"
1201source = "registry+https://github.com/rust-lang/crates.io-index"
1202checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
1203
1204[[package]]
1205name = "md-5"
1206version = "0.10.6"
1207source = "registry+https://github.com/rust-lang/crates.io-index"
1208checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
1209dependencies = [
1210 "cfg-if",
1211 "digest",
1212]
1213
1214[[package]]
1215name = "memchr"
1216version = "2.7.1"
1217source = "registry+https://github.com/rust-lang/crates.io-index"
1218checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
1219
1220[[package]]
1221name = "mime"
1222version = "0.3.17"
1223source = "registry+https://github.com/rust-lang/crates.io-index"
1224checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
1225
1226[[package]]
1227name = "miniz_oxide"
1228version = "0.7.1"
1229source = "registry+https://github.com/rust-lang/crates.io-index"
1230checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
1231dependencies = [
1232 "adler",
1233]
1234
1235[[package]]
1236name = "mio"
1237version = "0.8.10"
1238source = "registry+https://github.com/rust-lang/crates.io-index"
1239checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
1240dependencies = [
1241 "libc",
1242 "wasi",
1243 "windows-sys 0.48.0",
1244]
1245
1246[[package]]
1247name = "nu-ansi-term"
1248version = "0.46.0"
1249source = "registry+https://github.com/rust-lang/crates.io-index"
1250checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
1251dependencies = [
1252 "overload",
1253 "winapi",
1254]
1255
1256[[package]]
1257name = "num-integer"
1258version = "0.1.45"
1259source = "registry+https://github.com/rust-lang/crates.io-index"
1260checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
1261dependencies = [
1262 "autocfg",
1263 "num-traits",
1264]
1265
1266[[package]]
1267name = "num-traits"
1268version = "0.2.17"
1269source = "registry+https://github.com/rust-lang/crates.io-index"
1270checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
1271dependencies = [
1272 "autocfg",
1273]
1274
1275[[package]]
1276name = "num_cpus"
1277version = "1.16.0"
1278source = "registry+https://github.com/rust-lang/crates.io-index"
1279checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
1280dependencies = [
1281 "hermit-abi",
1282 "libc",
1283]
1284
1285[[package]]
1286name = "object"
1287version = "0.32.2"
1288source = "registry+https://github.com/rust-lang/crates.io-index"
1289checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
1290dependencies = [
1291 "memchr",
1292]
1293
1294[[package]]
1295name = "once_cell"
1296version = "1.19.0"
1297source = "registry+https://github.com/rust-lang/crates.io-index"
1298checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
1299
1300[[package]]
1301name = "openssl-probe"
1302version = "0.1.5"
1303source = "registry+https://github.com/rust-lang/crates.io-index"
1304checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
1305
1306[[package]]
1307name = "outref"
1308version = "0.5.1"
1309source = "registry+https://github.com/rust-lang/crates.io-index"
1310checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a"
1311
1312[[package]]
1313name = "overload"
1314version = "0.1.1"
1315source = "registry+https://github.com/rust-lang/crates.io-index"
1316checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
1317
1318[[package]]
1319name = "p256"
1320version = "0.11.1"
1321source = "registry+https://github.com/rust-lang/crates.io-index"
1322checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594"
1323dependencies = [
1324 "ecdsa",
1325 "elliptic-curve",
1326 "sha2",
1327]
1328
1329[[package]]
1330name = "parking_lot"
1331version = "0.12.1"
1332source = "registry+https://github.com/rust-lang/crates.io-index"
1333checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
1334dependencies = [
1335 "lock_api",
1336 "parking_lot_core",
1337]
1338
1339[[package]]
1340name = "parking_lot_core"
1341version = "0.9.9"
1342source = "registry+https://github.com/rust-lang/crates.io-index"
1343checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
1344dependencies = [
1345 "cfg-if",
1346 "libc",
1347 "redox_syscall",
1348 "smallvec",
1349 "windows-targets 0.48.5",
1350]
1351
1352[[package]]
1353name = "percent-encoding"
1354version = "2.3.1"
1355source = "registry+https://github.com/rust-lang/crates.io-index"
1356checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
1357
1358[[package]]
1359name = "pin-project"
1360version = "1.1.3"
1361source = "registry+https://github.com/rust-lang/crates.io-index"
1362checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
1363dependencies = [
1364 "pin-project-internal",
1365]
1366
1367[[package]]
1368name = "pin-project-internal"
1369version = "1.1.3"
1370source = "registry+https://github.com/rust-lang/crates.io-index"
1371checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
1372dependencies = [
1373 "proc-macro2",
1374 "quote",
1375 "syn",
1376]
1377
1378[[package]]
1379name = "pin-project-lite"
1380version = "0.2.13"
1381source = "registry+https://github.com/rust-lang/crates.io-index"
1382checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
1383
1384[[package]]
1385name = "pin-utils"
1386version = "0.1.0"
1387source = "registry+https://github.com/rust-lang/crates.io-index"
1388checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
1389
1390[[package]]
1391name = "pkcs8"
1392version = "0.9.0"
1393source = "registry+https://github.com/rust-lang/crates.io-index"
1394checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
1395dependencies = [
1396 "der",
1397 "spki",
1398]
1399
1400[[package]]
1401name = "powerfmt"
1402version = "0.2.0"
1403source = "registry+https://github.com/rust-lang/crates.io-index"
1404checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
1405
1406[[package]]
1407name = "proc-macro2"
1408version = "1.0.76"
1409source = "registry+https://github.com/rust-lang/crates.io-index"
1410checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
1411dependencies = [
1412 "unicode-ident",
1413]
1414
1415[[package]]
1416name = "quote"
1417version = "1.0.35"
1418source = "registry+https://github.com/rust-lang/crates.io-index"
1419checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
1420dependencies = [
1421 "proc-macro2",
1422]
1423
1424[[package]]
1425name = "rand_core"
1426version = "0.6.4"
1427source = "registry+https://github.com/rust-lang/crates.io-index"
1428checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
1429dependencies = [
1430 "getrandom",
1431]
1432
1433[[package]]
1434name = "redox_syscall"
1435version = "0.4.1"
1436source = "registry+https://github.com/rust-lang/crates.io-index"
1437checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
1438dependencies = [
1439 "bitflags",
1440]
1441
1442[[package]]
1443name = "regex"
1444version = "1.10.3"
1445source = "registry+https://github.com/rust-lang/crates.io-index"
1446checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
1447dependencies = [
1448 "aho-corasick",
1449 "memchr",
1450 "regex-automata 0.4.4",
1451 "regex-syntax 0.8.2",
1452]
1453
1454[[package]]
1455name = "regex-automata"
1456version = "0.1.10"
1457source = "registry+https://github.com/rust-lang/crates.io-index"
1458checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
1459dependencies = [
1460 "regex-syntax 0.6.29",
1461]
1462
1463[[package]]
1464name = "regex-automata"
1465version = "0.4.4"
1466source = "registry+https://github.com/rust-lang/crates.io-index"
1467checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a"
1468dependencies = [
1469 "aho-corasick",
1470 "memchr",
1471 "regex-syntax 0.8.2",
1472]
1473
1474[[package]]
1475name = "regex-lite"
1476version = "0.1.5"
1477source = "registry+https://github.com/rust-lang/crates.io-index"
1478checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e"
1479
1480[[package]]
1481name = "regex-syntax"
1482version = "0.6.29"
1483source = "registry+https://github.com/rust-lang/crates.io-index"
1484checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
1485
1486[[package]]
1487name = "regex-syntax"
1488version = "0.8.2"
1489source = "registry+https://github.com/rust-lang/crates.io-index"
1490checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
1491
1492[[package]]
1493name = "rfc6979"
1494version = "0.3.1"
1495source = "registry+https://github.com/rust-lang/crates.io-index"
1496checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb"
1497dependencies = [
1498 "crypto-bigint 0.4.9",
1499 "hmac",
1500 "zeroize",
1501]
1502
1503[[package]]
1504name = "ring"
1505version = "0.17.7"
1506source = "registry+https://github.com/rust-lang/crates.io-index"
1507checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
1508dependencies = [
1509 "cc",
1510 "getrandom",
1511 "libc",
1512 "spin",
1513 "untrusted",
1514 "windows-sys 0.48.0",
1515]
1516
1517[[package]]
1518name = "rustc-demangle"
1519version = "0.1.23"
1520source = "registry+https://github.com/rust-lang/crates.io-index"
1521checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
1522
1523[[package]]
1524name = "rustc_version"
1525version = "0.4.0"
1526source = "registry+https://github.com/rust-lang/crates.io-index"
1527checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
1528dependencies = [
1529 "semver",
1530]
1531
1532[[package]]
1533name = "rustls"
1534version = "0.21.10"
1535source = "registry+https://github.com/rust-lang/crates.io-index"
1536checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
1537dependencies = [
1538 "log",
1539 "ring",
1540 "rustls-webpki",
1541 "sct",
1542]
1543
1544[[package]]
1545name = "rustls-native-certs"
1546version = "0.6.3"
1547source = "registry+https://github.com/rust-lang/crates.io-index"
1548checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
1549dependencies = [
1550 "openssl-probe",
1551 "rustls-pemfile",
1552 "schannel",
1553 "security-framework",
1554]
1555
1556[[package]]
1557name = "rustls-pemfile"
1558version = "1.0.4"
1559source = "registry+https://github.com/rust-lang/crates.io-index"
1560checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
1561dependencies = [
1562 "base64",
1563]
1564
1565[[package]]
1566name = "rustls-webpki"
1567version = "0.101.7"
1568source = "registry+https://github.com/rust-lang/crates.io-index"
1569checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
1570dependencies = [
1571 "ring",
1572 "untrusted",
1573]
1574
1575[[package]]
1576name = "rustversion"
1577version = "1.0.14"
1578source = "registry+https://github.com/rust-lang/crates.io-index"
1579checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
1580
1581[[package]]
1582name = "ryu"
1583version = "1.0.16"
1584source = "registry+https://github.com/rust-lang/crates.io-index"
1585checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
1586
1587[[package]]
1588name = "schannel"
1589version = "0.1.23"
1590source = "registry+https://github.com/rust-lang/crates.io-index"
1591checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
1592dependencies = [
1593 "windows-sys 0.52.0",
1594]
1595
1596[[package]]
1597name = "scopeguard"
1598version = "1.2.0"
1599source = "registry+https://github.com/rust-lang/crates.io-index"
1600checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1601
1602[[package]]
1603name = "sct"
1604version = "0.7.1"
1605source = "registry+https://github.com/rust-lang/crates.io-index"
1606checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
1607dependencies = [
1608 "ring",
1609 "untrusted",
1610]
1611
1612[[package]]
1613name = "sec1"
1614version = "0.3.0"
1615source = "registry+https://github.com/rust-lang/crates.io-index"
1616checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928"
1617dependencies = [
1618 "base16ct",
1619 "der",
1620 "generic-array",
1621 "pkcs8",
1622 "subtle",
1623 "zeroize",
1624]
1625
1626[[package]]
1627name = "security-framework"
1628version = "2.9.2"
1629source = "registry+https://github.com/rust-lang/crates.io-index"
1630checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
1631dependencies = [
1632 "bitflags",
1633 "core-foundation",
1634 "core-foundation-sys",
1635 "libc",
1636 "security-framework-sys",
1637]
1638
1639[[package]]
1640name = "security-framework-sys"
1641version = "2.9.1"
1642source = "registry+https://github.com/rust-lang/crates.io-index"
1643checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
1644dependencies = [
1645 "core-foundation-sys",
1646 "libc",
1647]
1648
1649[[package]]
1650name = "semver"
1651version = "1.0.21"
1652source = "registry+https://github.com/rust-lang/crates.io-index"
1653checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
1654
1655[[package]]
1656name = "serde"
1657version = "1.0.195"
1658source = "registry+https://github.com/rust-lang/crates.io-index"
1659checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
1660dependencies = [
1661 "serde_derive",
1662]
1663
1664[[package]]
1665name = "serde_derive"
1666version = "1.0.195"
1667source = "registry+https://github.com/rust-lang/crates.io-index"
1668checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
1669dependencies = [
1670 "proc-macro2",
1671 "quote",
1672 "syn",
1673]
1674
1675[[package]]
1676name = "serde_json"
1677version = "1.0.111"
1678source = "registry+https://github.com/rust-lang/crates.io-index"
1679checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
1680dependencies = [
1681 "itoa",
1682 "ryu",
1683 "serde",
1684]
1685
1686[[package]]
1687name = "serde_path_to_error"
1688version = "0.1.15"
1689source = "registry+https://github.com/rust-lang/crates.io-index"
1690checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c"
1691dependencies = [
1692 "itoa",
1693 "serde",
1694]
1695
1696[[package]]
1697name = "serde_urlencoded"
1698version = "0.7.1"
1699source = "registry+https://github.com/rust-lang/crates.io-index"
1700checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
1701dependencies = [
1702 "form_urlencoded",
1703 "itoa",
1704 "ryu",
1705 "serde",
1706]
1707
1708[[package]]
1709name = "server"
1710version = "0.1.0"
1711dependencies = [
1712 "aws-config",
1713 "aws-sdk-s3",
1714 "axum",
1715 "base64",
1716 "chrono",
1717 "common",
1718 "mime",
1719 "serde",
1720 "serde_json",
1721 "tokio",
1722 "tokio-util",
1723 "tower",
1724 "tracing-subscriber",
1725]
1726
1727[[package]]
1728name = "sha1"
1729version = "0.10.6"
1730source = "registry+https://github.com/rust-lang/crates.io-index"
1731checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
1732dependencies = [
1733 "cfg-if",
1734 "cpufeatures",
1735 "digest",
1736]
1737
1738[[package]]
1739name = "sha2"
1740version = "0.10.8"
1741source = "registry+https://github.com/rust-lang/crates.io-index"
1742checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
1743dependencies = [
1744 "cfg-if",
1745 "cpufeatures",
1746 "digest",
1747]
1748
1749[[package]]
1750name = "sharded-slab"
1751version = "0.1.7"
1752source = "registry+https://github.com/rust-lang/crates.io-index"
1753checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1754dependencies = [
1755 "lazy_static",
1756]
1757
1758[[package]]
1759name = "shell"
1760version = "0.1.0"
1761
1762[[package]]
1763name = "signal-hook-registry"
1764version = "1.4.1"
1765source = "registry+https://github.com/rust-lang/crates.io-index"
1766checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
1767dependencies = [
1768 "libc",
1769]
1770
1771[[package]]
1772name = "signature"
1773version = "1.6.4"
1774source = "registry+https://github.com/rust-lang/crates.io-index"
1775checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c"
1776dependencies = [
1777 "digest",
1778 "rand_core",
1779]
1780
1781[[package]]
1782name = "slab"
1783version = "0.4.9"
1784source = "registry+https://github.com/rust-lang/crates.io-index"
1785checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
1786dependencies = [
1787 "autocfg",
1788]
1789
1790[[package]]
1791name = "smallvec"
1792version = "1.11.2"
1793source = "registry+https://github.com/rust-lang/crates.io-index"
1794checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
1795
1796[[package]]
1797name = "socket2"
1798version = "0.5.5"
1799source = "registry+https://github.com/rust-lang/crates.io-index"
1800checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
1801dependencies = [
1802 "libc",
1803 "windows-sys 0.48.0",
1804]
1805
1806[[package]]
1807name = "spin"
1808version = "0.9.8"
1809source = "registry+https://github.com/rust-lang/crates.io-index"
1810checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
1811
1812[[package]]
1813name = "spki"
1814version = "0.6.0"
1815source = "registry+https://github.com/rust-lang/crates.io-index"
1816checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
1817dependencies = [
1818 "base64ct",
1819 "der",
1820]
1821
1822[[package]]
1823name = "subtle"
1824version = "2.5.0"
1825source = "registry+https://github.com/rust-lang/crates.io-index"
1826checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
1827
1828[[package]]
1829name = "syn"
1830version = "2.0.48"
1831source = "registry+https://github.com/rust-lang/crates.io-index"
1832checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
1833dependencies = [
1834 "proc-macro2",
1835 "quote",
1836 "unicode-ident",
1837]
1838
1839[[package]]
1840name = "sync_wrapper"
1841version = "0.1.2"
1842source = "registry+https://github.com/rust-lang/crates.io-index"
1843checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
1844
1845[[package]]
1846name = "thread_local"
1847version = "1.1.7"
1848source = "registry+https://github.com/rust-lang/crates.io-index"
1849checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
1850dependencies = [
1851 "cfg-if",
1852 "once_cell",
1853]
1854
1855[[package]]
1856name = "time"
1857version = "0.3.31"
1858source = "registry+https://github.com/rust-lang/crates.io-index"
1859checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
1860dependencies = [
1861 "deranged",
1862 "powerfmt",
1863 "serde",
1864 "time-core",
1865 "time-macros",
1866]
1867
1868[[package]]
1869name = "time-core"
1870version = "0.1.2"
1871source = "registry+https://github.com/rust-lang/crates.io-index"
1872checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
1873
1874[[package]]
1875name = "time-macros"
1876version = "0.2.16"
1877source = "registry+https://github.com/rust-lang/crates.io-index"
1878checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
1879dependencies = [
1880 "time-core",
1881]
1882
1883[[package]]
1884name = "tinyvec"
1885version = "1.6.0"
1886source = "registry+https://github.com/rust-lang/crates.io-index"
1887checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
1888dependencies = [
1889 "tinyvec_macros",
1890]
1891
1892[[package]]
1893name = "tinyvec_macros"
1894version = "0.1.1"
1895source = "registry+https://github.com/rust-lang/crates.io-index"
1896checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1897
1898[[package]]
1899name = "tokio"
1900version = "1.35.1"
1901source = "registry+https://github.com/rust-lang/crates.io-index"
1902checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
1903dependencies = [
1904 "backtrace",
1905 "bytes",
1906 "libc",
1907 "mio",
1908 "num_cpus",
1909 "parking_lot",
1910 "pin-project-lite",
1911 "signal-hook-registry",
1912 "socket2",
1913 "tokio-macros",
1914 "windows-sys 0.48.0",
1915]
1916
1917[[package]]
1918name = "tokio-macros"
1919version = "2.2.0"
1920source = "registry+https://github.com/rust-lang/crates.io-index"
1921checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
1922dependencies = [
1923 "proc-macro2",
1924 "quote",
1925 "syn",
1926]
1927
1928[[package]]
1929name = "tokio-rustls"
1930version = "0.24.1"
1931source = "registry+https://github.com/rust-lang/crates.io-index"
1932checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
1933dependencies = [
1934 "rustls",
1935 "tokio",
1936]
1937
1938[[package]]
1939name = "tokio-util"
1940version = "0.7.10"
1941source = "registry+https://github.com/rust-lang/crates.io-index"
1942checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
1943dependencies = [
1944 "bytes",
1945 "futures-core",
1946 "futures-sink",
1947 "pin-project-lite",
1948 "tokio",
1949 "tracing",
1950]
1951
1952[[package]]
1953name = "tower"
1954version = "0.4.13"
1955source = "registry+https://github.com/rust-lang/crates.io-index"
1956checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
1957dependencies = [
1958 "futures-core",
1959 "futures-util",
1960 "pin-project",
1961 "pin-project-lite",
1962 "tokio",
1963 "tower-layer",
1964 "tower-service",
1965 "tracing",
1966]
1967
1968[[package]]
1969name = "tower-layer"
1970version = "0.3.2"
1971source = "registry+https://github.com/rust-lang/crates.io-index"
1972checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
1973
1974[[package]]
1975name = "tower-service"
1976version = "0.3.2"
1977source = "registry+https://github.com/rust-lang/crates.io-index"
1978checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
1979
1980[[package]]
1981name = "tracing"
1982version = "0.1.40"
1983source = "registry+https://github.com/rust-lang/crates.io-index"
1984checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
1985dependencies = [
1986 "log",
1987 "pin-project-lite",
1988 "tracing-attributes",
1989 "tracing-core",
1990]
1991
1992[[package]]
1993name = "tracing-attributes"
1994version = "0.1.27"
1995source = "registry+https://github.com/rust-lang/crates.io-index"
1996checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
1997dependencies = [
1998 "proc-macro2",
1999 "quote",
2000 "syn",
2001]
2002
2003[[package]]
2004name = "tracing-core"
2005version = "0.1.32"
2006source = "registry+https://github.com/rust-lang/crates.io-index"
2007checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
2008dependencies = [
2009 "once_cell",
2010 "valuable",
2011]
2012
2013[[package]]
2014name = "tracing-log"
2015version = "0.2.0"
2016source = "registry+https://github.com/rust-lang/crates.io-index"
2017checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
2018dependencies = [
2019 "log",
2020 "once_cell",
2021 "tracing-core",
2022]
2023
2024[[package]]
2025name = "tracing-subscriber"
2026version = "0.3.18"
2027source = "registry+https://github.com/rust-lang/crates.io-index"
2028checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
2029dependencies = [
2030 "matchers",
2031 "nu-ansi-term",
2032 "once_cell",
2033 "regex",
2034 "sharded-slab",
2035 "smallvec",
2036 "thread_local",
2037 "tracing",
2038 "tracing-core",
2039 "tracing-log",
2040]
2041
2042[[package]]
2043name = "try-lock"
2044version = "0.2.5"
2045source = "registry+https://github.com/rust-lang/crates.io-index"
2046checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
2047
2048[[package]]
2049name = "typenum"
2050version = "1.17.0"
2051source = "registry+https://github.com/rust-lang/crates.io-index"
2052checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
2053
2054[[package]]
2055name = "unicode-bidi"
2056version = "0.3.15"
2057source = "registry+https://github.com/rust-lang/crates.io-index"
2058checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
2059
2060[[package]]
2061name = "unicode-ident"
2062version = "1.0.12"
2063source = "registry+https://github.com/rust-lang/crates.io-index"
2064checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
2065
2066[[package]]
2067name = "unicode-normalization"
2068version = "0.1.22"
2069source = "registry+https://github.com/rust-lang/crates.io-index"
2070checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
2071dependencies = [
2072 "tinyvec",
2073]
2074
2075[[package]]
2076name = "untrusted"
2077version = "0.9.0"
2078source = "registry+https://github.com/rust-lang/crates.io-index"
2079checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
2080
2081[[package]]
2082name = "url"
2083version = "2.5.0"
2084source = "registry+https://github.com/rust-lang/crates.io-index"
2085checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
2086dependencies = [
2087 "form_urlencoded",
2088 "idna",
2089 "percent-encoding",
2090]
2091
2092[[package]]
2093name = "urlencoding"
2094version = "2.1.3"
2095source = "registry+https://github.com/rust-lang/crates.io-index"
2096checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
2097
2098[[package]]
2099name = "uuid"
2100version = "1.6.1"
2101source = "registry+https://github.com/rust-lang/crates.io-index"
2102checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560"
2103
2104[[package]]
2105name = "valuable"
2106version = "0.1.0"
2107source = "registry+https://github.com/rust-lang/crates.io-index"
2108checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
2109
2110[[package]]
2111name = "version_check"
2112version = "0.9.4"
2113source = "registry+https://github.com/rust-lang/crates.io-index"
2114checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
2115
2116[[package]]
2117name = "vsimd"
2118version = "0.8.0"
2119source = "registry+https://github.com/rust-lang/crates.io-index"
2120checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64"
2121
2122[[package]]
2123name = "want"
2124version = "0.3.1"
2125source = "registry+https://github.com/rust-lang/crates.io-index"
2126checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
2127dependencies = [
2128 "try-lock",
2129]
2130
2131[[package]]
2132name = "wasi"
2133version = "0.11.0+wasi-snapshot-preview1"
2134source = "registry+https://github.com/rust-lang/crates.io-index"
2135checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
2136
2137[[package]]
2138name = "wasm-bindgen"
2139version = "0.2.90"
2140source = "registry+https://github.com/rust-lang/crates.io-index"
2141checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
2142dependencies = [
2143 "cfg-if",
2144 "wasm-bindgen-macro",
2145]
2146
2147[[package]]
2148name = "wasm-bindgen-backend"
2149version = "0.2.90"
2150source = "registry+https://github.com/rust-lang/crates.io-index"
2151checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
2152dependencies = [
2153 "bumpalo",
2154 "log",
2155 "once_cell",
2156 "proc-macro2",
2157 "quote",
2158 "syn",
2159 "wasm-bindgen-shared",
2160]
2161
2162[[package]]
2163name = "wasm-bindgen-macro"
2164version = "0.2.90"
2165source = "registry+https://github.com/rust-lang/crates.io-index"
2166checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
2167dependencies = [
2168 "quote",
2169 "wasm-bindgen-macro-support",
2170]
2171
2172[[package]]
2173name = "wasm-bindgen-macro-support"
2174version = "0.2.90"
2175source = "registry+https://github.com/rust-lang/crates.io-index"
2176checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
2177dependencies = [
2178 "proc-macro2",
2179 "quote",
2180 "syn",
2181 "wasm-bindgen-backend",
2182 "wasm-bindgen-shared",
2183]
2184
2185[[package]]
2186name = "wasm-bindgen-shared"
2187version = "0.2.90"
2188source = "registry+https://github.com/rust-lang/crates.io-index"
2189checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
2190
2191[[package]]
2192name = "winapi"
2193version = "0.3.9"
2194source = "registry+https://github.com/rust-lang/crates.io-index"
2195checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
2196dependencies = [
2197 "winapi-i686-pc-windows-gnu",
2198 "winapi-x86_64-pc-windows-gnu",
2199]
2200
2201[[package]]
2202name = "winapi-i686-pc-windows-gnu"
2203version = "0.4.0"
2204source = "registry+https://github.com/rust-lang/crates.io-index"
2205checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
2206
2207[[package]]
2208name = "winapi-x86_64-pc-windows-gnu"
2209version = "0.4.0"
2210source = "registry+https://github.com/rust-lang/crates.io-index"
2211checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
2212
2213[[package]]
2214name = "windows-core"
2215version = "0.52.0"
2216source = "registry+https://github.com/rust-lang/crates.io-index"
2217checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
2218dependencies = [
2219 "windows-targets 0.52.0",
2220]
2221
2222[[package]]
2223name = "windows-sys"
2224version = "0.48.0"
2225source = "registry+https://github.com/rust-lang/crates.io-index"
2226checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
2227dependencies = [
2228 "windows-targets 0.48.5",
2229]
2230
2231[[package]]
2232name = "windows-sys"
2233version = "0.52.0"
2234source = "registry+https://github.com/rust-lang/crates.io-index"
2235checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
2236dependencies = [
2237 "windows-targets 0.52.0",
2238]
2239
2240[[package]]
2241name = "windows-targets"
2242version = "0.48.5"
2243source = "registry+https://github.com/rust-lang/crates.io-index"
2244checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
2245dependencies = [
2246 "windows_aarch64_gnullvm 0.48.5",
2247 "windows_aarch64_msvc 0.48.5",
2248 "windows_i686_gnu 0.48.5",
2249 "windows_i686_msvc 0.48.5",
2250 "windows_x86_64_gnu 0.48.5",
2251 "windows_x86_64_gnullvm 0.48.5",
2252 "windows_x86_64_msvc 0.48.5",
2253]
2254
2255[[package]]
2256name = "windows-targets"
2257version = "0.52.0"
2258source = "registry+https://github.com/rust-lang/crates.io-index"
2259checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
2260dependencies = [
2261 "windows_aarch64_gnullvm 0.52.0",
2262 "windows_aarch64_msvc 0.52.0",
2263 "windows_i686_gnu 0.52.0",
2264 "windows_i686_msvc 0.52.0",
2265 "windows_x86_64_gnu 0.52.0",
2266 "windows_x86_64_gnullvm 0.52.0",
2267 "windows_x86_64_msvc 0.52.0",
2268]
2269
2270[[package]]
2271name = "windows_aarch64_gnullvm"
2272version = "0.48.5"
2273source = "registry+https://github.com/rust-lang/crates.io-index"
2274checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
2275
2276[[package]]
2277name = "windows_aarch64_gnullvm"
2278version = "0.52.0"
2279source = "registry+https://github.com/rust-lang/crates.io-index"
2280checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
2281
2282[[package]]
2283name = "windows_aarch64_msvc"
2284version = "0.48.5"
2285source = "registry+https://github.com/rust-lang/crates.io-index"
2286checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
2287
2288[[package]]
2289name = "windows_aarch64_msvc"
2290version = "0.52.0"
2291source = "registry+https://github.com/rust-lang/crates.io-index"
2292checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
2293
2294[[package]]
2295name = "windows_i686_gnu"
2296version = "0.48.5"
2297source = "registry+https://github.com/rust-lang/crates.io-index"
2298checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
2299
2300[[package]]
2301name = "windows_i686_gnu"
2302version = "0.52.0"
2303source = "registry+https://github.com/rust-lang/crates.io-index"
2304checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
2305
2306[[package]]
2307name = "windows_i686_msvc"
2308version = "0.48.5"
2309source = "registry+https://github.com/rust-lang/crates.io-index"
2310checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
2311
2312[[package]]
2313name = "windows_i686_msvc"
2314version = "0.52.0"
2315source = "registry+https://github.com/rust-lang/crates.io-index"
2316checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
2317
2318[[package]]
2319name = "windows_x86_64_gnu"
2320version = "0.48.5"
2321source = "registry+https://github.com/rust-lang/crates.io-index"
2322checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
2323
2324[[package]]
2325name = "windows_x86_64_gnu"
2326version = "0.52.0"
2327source = "registry+https://github.com/rust-lang/crates.io-index"
2328checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
2329
2330[[package]]
2331name = "windows_x86_64_gnullvm"
2332version = "0.48.5"
2333source = "registry+https://github.com/rust-lang/crates.io-index"
2334checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
2335
2336[[package]]
2337name = "windows_x86_64_gnullvm"
2338version = "0.52.0"
2339source = "registry+https://github.com/rust-lang/crates.io-index"
2340checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
2341
2342[[package]]
2343name = "windows_x86_64_msvc"
2344version = "0.48.5"
2345source = "registry+https://github.com/rust-lang/crates.io-index"
2346checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
2347
2348[[package]]
2349name = "windows_x86_64_msvc"
2350version = "0.52.0"
2351source = "registry+https://github.com/rust-lang/crates.io-index"
2352checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
2353
2354[[package]]
2355name = "xmlparser"
2356version = "0.13.6"
2357source = "registry+https://github.com/rust-lang/crates.io-index"
2358checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
2359
2360[[package]]
2361name = "zeroize"
2362version = "1.7.0"
2363source = "registry+https://github.com/rust-lang/crates.io-index"
2364checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/rs/Cargo.toml b/rs/Cargo.toml
deleted file mode 100644
index 6439e6b..0000000
--- a/rs/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
1[workspace]
2resolver = "2"
3members = [
4 "common",
5 "git-lfs-authenticate",
6 "server",
7 "shell",
8]
diff --git a/rs/common/Cargo.toml b/rs/common/Cargo.toml
deleted file mode 100644
index 20d9bdd..0000000
--- a/rs/common/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
1[package]
2name = "common"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7chrono = "0.4"
8hmac-sha256 = "1.1"
9subtle = "2.5"
10serde = { version = "1", features = ["derive"] }
diff --git a/rs/common/src/lib.rs b/rs/common/src/lib.rs
deleted file mode 100644
index 995352d..0000000
--- a/rs/common/src/lib.rs
+++ /dev/null
@@ -1,368 +0,0 @@
1use chrono::{DateTime, Utc};
2use serde::de;
3use serde::{Deserialize, Serialize};
4use std::fmt::Write;
5use std::ops;
6use std::{fmt, str::FromStr};
7use subtle::ConstantTimeEq;
8
9#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
10#[repr(u8)]
11pub enum Operation {
12 #[serde(rename = "download")]
13 Download = 1,
14 #[serde(rename = "upload")]
15 Upload = 2,
16}
17
18#[derive(Debug, PartialEq, Eq, Copy, Clone)]
19pub struct ParseOperationError;
20
21impl fmt::Display for ParseOperationError {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 write!(f, "operation should be 'download' or 'upload'")
24 }
25}
26
27impl FromStr for Operation {
28 type Err = ParseOperationError;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 match s {
32 "upload" => Ok(Self::Upload),
33 "download" => Ok(Self::Download),
34 _ => Err(ParseOperationError),
35 }
36 }
37}
38
39#[repr(u8)]
40enum AuthType {
41 BatchApi = 1,
42 Download = 2,
43}
44
45/// None means out of range.
46fn decode_nibble(c: u8) -> Option<u8> {
47 if c.is_ascii_digit() {
48 Some(c - b'0')
49 } else if (b'a'..=b'f').contains(&c) {
50 Some(c - b'a' + 10)
51 } else if (b'A'..=b'F').contains(&c) {
52 Some(c - b'A' + 10)
53 } else {
54 None
55 }
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
59pub struct HexByte(pub u8);
60
61impl<'de> Deserialize<'de> for HexByte {
62 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
63 where
64 D: serde::Deserializer<'de>,
65 {
66 let str = <&str>::deserialize(deserializer)?;
67 let &[b1, b2] = str.as_bytes() else {
68 return Err(de::Error::invalid_length(
69 str.len(),
70 &"two hexadecimal characters",
71 ));
72 };
73 let (Some(b1), Some(b2)) = (decode_nibble(b1), decode_nibble(b2)) else {
74 return Err(de::Error::invalid_value(
75 de::Unexpected::Str(str),
76 &"two hexadecimal characters",
77 ));
78 };
79 Ok(HexByte((b1 << 4) | b2))
80 }
81}
82
83impl fmt::Display for HexByte {
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 let &HexByte(b) = self;
86 HexFmt(&[b]).fmt(f)
87 }
88}
89
90#[derive(Debug, PartialEq, Eq, Copy, Clone)]
91pub enum ParseHexError {
92 UnevenNibbles,
93 InvalidCharacter,
94 TooShort,
95 TooLong,
96}
97
98impl fmt::Display for ParseHexError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 Self::UnevenNibbles => {
102 write!(f, "uneven amount of nibbles (chars in range [a-zA-Z0-9])")
103 }
104 Self::InvalidCharacter => write!(f, "non-hex character encountered"),
105 Self::TooShort => write!(f, "unexpected end of hex sequence"),
106 Self::TooLong => write!(f, "longer hex sequence than expected"),
107 }
108 }
109}
110
111#[derive(Debug)]
112pub enum ReadHexError {
113 Io(std::io::Error),
114 Format(ParseHexError),
115}
116
117impl fmt::Display for ReadHexError {
118 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119 match self {
120 Self::Io(e) => e.fmt(f),
121 Self::Format(e) => e.fmt(f),
122 }
123 }
124}
125
126fn parse_hex_exact(value: &str, buf: &mut [u8]) -> Result<(), ParseHexError> {
127 if value.bytes().len() % 2 == 1 {
128 return Err(ParseHexError::UnevenNibbles);
129 }
130 if value.bytes().len() < 2 * buf.len() {
131 return Err(ParseHexError::TooShort);
132 }
133 if value.bytes().len() > 2 * buf.len() {
134 return Err(ParseHexError::TooLong);
135 }
136 for (i, c) in value.bytes().enumerate() {
137 if let Some(b) = decode_nibble(c) {
138 if i % 2 == 0 {
139 buf[i / 2] = b << 4;
140 } else {
141 buf[i / 2] |= b;
142 }
143 } else {
144 return Err(ParseHexError::InvalidCharacter);
145 }
146 }
147 Ok(())
148}
149
150pub struct SafeByteArray<const N: usize> {
151 inner: [u8; N],
152}
153
154impl<const N: usize> SafeByteArray<N> {
155 pub fn new() -> Self {
156 Self { inner: [0; N] }
157 }
158}
159
160impl<const N: usize> Default for SafeByteArray<N> {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166impl<const N: usize> AsRef<[u8]> for SafeByteArray<N> {
167 fn as_ref(&self) -> &[u8] {
168 &self.inner
169 }
170}
171
172impl<const N: usize> AsMut<[u8]> for SafeByteArray<N> {
173 fn as_mut(&mut self) -> &mut [u8] {
174 &mut self.inner
175 }
176}
177
178impl<const N: usize> Drop for SafeByteArray<N> {
179 fn drop(&mut self) {
180 self.inner.fill(0)
181 }
182}
183
184impl<const N: usize> FromStr for SafeByteArray<N> {
185 type Err = ParseHexError;
186
187 fn from_str(value: &str) -> Result<Self, Self::Err> {
188 let mut sba = Self { inner: [0u8; N] };
189 parse_hex_exact(value, &mut sba.inner)?;
190 Ok(sba)
191 }
192}
193
194pub type Oid = Digest<32>;
195
196#[derive(Debug, Copy, Clone)]
197pub enum SpecificClaims {
198 BatchApi(Operation),
199 Download(Oid),
200}
201
202#[derive(Debug, Copy, Clone)]
203pub struct Claims<'a> {
204 pub specific_claims: SpecificClaims,
205 pub repo_path: &'a str,
206 pub expires_at: DateTime<Utc>,
207}
208
209/// Returns None if the claims are invalid. Repo path length may be no more than 100 bytes.
210pub fn generate_tag(claims: Claims, key: impl AsRef<[u8]>) -> Option<Digest<32>> {
211 if claims.repo_path.len() > 100 {
212 return None;
213 }
214
215 let mut hmac = hmac_sha256::HMAC::new(key);
216 match claims.specific_claims {
217 SpecificClaims::BatchApi(operation) => {
218 hmac.update([AuthType::BatchApi as u8]);
219 hmac.update([operation as u8]);
220 }
221 SpecificClaims::Download(oid) => {
222 hmac.update([AuthType::Download as u8]);
223 hmac.update(oid.as_bytes());
224 }
225 }
226 hmac.update([claims.repo_path.len() as u8]);
227 hmac.update(claims.repo_path.as_bytes());
228 hmac.update(claims.expires_at.timestamp().to_be_bytes());
229 Some(hmac.finalize().into())
230}
231
232pub struct HexFmt<B: AsRef<[u8]>>(pub B);
233
234impl<B: AsRef<[u8]>> fmt::Display for HexFmt<B> {
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 let HexFmt(buf) = self;
237 for b in buf.as_ref() {
238 let (high, low) = (b >> 4, b & 0xF);
239 let highc = if high < 10 {
240 high + b'0'
241 } else {
242 high - 10 + b'a'
243 };
244 let lowc = if low < 10 {
245 low + b'0'
246 } else {
247 low - 10 + b'a'
248 };
249 f.write_char(highc as char)?;
250 f.write_char(lowc as char)?;
251 }
252 Ok(())
253 }
254}
255
256pub struct EscJsonFmt<'a>(pub &'a str);
257
258impl<'a> fmt::Display for EscJsonFmt<'a> {
259 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260 let EscJsonFmt(buf) = self;
261 for c in buf.chars() {
262 match c {
263 '"' => f.write_str("\\\"")?, // quote
264 '\\' => f.write_str("\\\\")?, // backslash
265 '\x08' => f.write_str("\\b")?, // backspace
266 '\x0C' => f.write_str("\\f")?, // form feed
267 '\n' => f.write_str("\\n")?, // line feed
268 '\r' => f.write_str("\\r")?, // carriage return
269 '\t' => f.write_str("\\t")?, // horizontal tab
270 _ => f.write_char(c)?,
271 };
272 }
273 Ok(())
274 }
275}
276
277#[derive(Debug, Copy, Clone)]
278pub struct Digest<const N: usize> {
279 inner: [u8; N],
280}
281
282impl<const N: usize> ops::Index<usize> for Digest<N> {
283 type Output = u8;
284
285 fn index(&self, index: usize) -> &Self::Output {
286 &self.inner[index]
287 }
288}
289
290impl<const N: usize> Digest<N> {
291 pub fn as_bytes(&self) -> &[u8; N] {
292 &self.inner
293 }
294}
295
296impl<const N: usize> fmt::Display for Digest<N> {
297 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
298 HexFmt(&self.inner).fmt(f)
299 }
300}
301
302impl<const N: usize> Digest<N> {
303 pub fn new(data: [u8; N]) -> Self {
304 Self { inner: data }
305 }
306}
307
308impl<const N: usize> From<[u8; N]> for Digest<N> {
309 fn from(value: [u8; N]) -> Self {
310 Self::new(value)
311 }
312}
313
314impl<const N: usize> From<Digest<N>> for [u8; N] {
315 fn from(val: Digest<N>) -> Self {
316 val.inner
317 }
318}
319
320impl<const N: usize> FromStr for Digest<N> {
321 type Err = ParseHexError;
322
323 fn from_str(value: &str) -> Result<Self, Self::Err> {
324 let mut buf = [0u8; N];
325 parse_hex_exact(value, &mut buf)?;
326 Ok(buf.into())
327 }
328}
329
330impl<const N: usize> ConstantTimeEq for Digest<N> {
331 fn ct_eq(&self, other: &Self) -> subtle::Choice {
332 self.inner.ct_eq(&other.inner)
333 }
334}
335
336impl<const N: usize> PartialEq for Digest<N> {
337 fn eq(&self, other: &Self) -> bool {
338 self.ct_eq(other).into()
339 }
340}
341
342impl<const N: usize> Eq for Digest<N> {}
343
344impl<'de, const N: usize> Deserialize<'de> for Digest<N> {
345 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
346 where
347 D: serde::Deserializer<'de>,
348 {
349 let hex = <&str>::deserialize(deserializer)?;
350 Digest::from_str(hex).map_err(de::Error::custom)
351 }
352}
353
354impl<const N: usize> Serialize for Digest<N> {
355 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
356 where
357 S: serde::Serializer,
358 {
359 serializer.serialize_str(&format!("{self}"))
360 }
361}
362
363pub type Key = SafeByteArray<64>;
364
365pub fn load_key(path: &str) -> Result<Key, ReadHexError> {
366 let key_str = std::fs::read_to_string(path).map_err(ReadHexError::Io)?;
367 key_str.trim().parse().map_err(ReadHexError::Format)
368}
diff --git a/rs/git-lfs-authenticate/Cargo.toml b/rs/git-lfs-authenticate/Cargo.toml
deleted file mode 100644
index 217250f..0000000
--- a/rs/git-lfs-authenticate/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
1[package]
2name = "git-lfs-authenticate"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7chrono = "0.4"
8common = { path = "../common" }
diff --git a/rs/git-lfs-authenticate/src/main.rs b/rs/git-lfs-authenticate/src/main.rs
deleted file mode 100644
index 36d7818..0000000
--- a/rs/git-lfs-authenticate/src/main.rs
+++ /dev/null
@@ -1,236 +0,0 @@
1use std::{fmt, process::ExitCode, time::Duration};
2
3use chrono::Utc;
4use common::{Operation, ParseOperationError};
5
6fn help() {
7 eprintln!("Usage: git-lfs-authenticate <REPO> upload/download");
8}
9
10#[derive(Debug, Eq, PartialEq, Copy, Clone)]
11enum RepoNameError {
12 TooLong,
13 UnresolvedPath,
14 AbsolutePath,
15 MissingGitSuffix,
16}
17
18impl fmt::Display for RepoNameError {
19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20 match self {
21 Self::TooLong => write!(f, "too long (more than 100 characters)"),
22 Self::UnresolvedPath => {
23 write!(f, "contains path one or more path elements '.' and '..'")
24 }
25 Self::AbsolutePath => {
26 write!(f, "starts with '/', which is not allowed")
27 }
28 Self::MissingGitSuffix => write!(f, "misses '.git' suffix"),
29 }
30 }
31}
32
33// Using `Result<(), E>` here instead of `Option<E>` because `None` typically signifies some error
34// state with no further details provided. If we were to return an `Option` type, the user would
35// have to first transform it into a `Result` type in order to use the `?` operator, meaning that
36// they would have to the following operation to get the type into the right shape:
37// `validate_repo_path(path).map_or(Ok(()), Err)`. That would not be very ergonomic.
38fn validate_repo_path(path: &str) -> Result<(), RepoNameError> {
39 if path.len() > 100 {
40 return Err(RepoNameError::TooLong);
41 }
42 if path.contains("//")
43 || path.contains("/./")
44 || path.contains("/../")
45 || path.starts_with("./")
46 || path.starts_with("../")
47 {
48 return Err(RepoNameError::UnresolvedPath);
49 }
50 if path.starts_with('/') {
51 return Err(RepoNameError::AbsolutePath);
52 }
53 if !path.ends_with(".git") {
54 return Err(RepoNameError::MissingGitSuffix);
55 }
56 Ok(())
57}
58
59#[derive(Debug, Eq, PartialEq, Copy, Clone)]
60enum ParseCmdlineError {
61 UnknownOperation(ParseOperationError),
62 InvalidRepoName(RepoNameError),
63 UnexpectedArgCount(ArgCountError),
64}
65
66impl From<RepoNameError> for ParseCmdlineError {
67 fn from(value: RepoNameError) -> Self {
68 Self::InvalidRepoName(value)
69 }
70}
71
72impl From<ParseOperationError> for ParseCmdlineError {
73 fn from(value: ParseOperationError) -> Self {
74 Self::UnknownOperation(value)
75 }
76}
77
78impl From<ArgCountError> for ParseCmdlineError {
79 fn from(value: ArgCountError) -> Self {
80 Self::UnexpectedArgCount(value)
81 }
82}
83
84impl fmt::Display for ParseCmdlineError {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 match self {
87 Self::UnknownOperation(e) => write!(f, "unknown operation: {e}"),
88 Self::InvalidRepoName(e) => write!(f, "invalid repository name: {e}"),
89 Self::UnexpectedArgCount(e) => e.fmt(f),
90 }
91 }
92}
93
94#[derive(Debug, Eq, PartialEq, Copy, Clone)]
95struct ArgCountError {
96 provided: usize,
97 expected: usize,
98}
99
100impl fmt::Display for ArgCountError {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 write!(
103 f,
104 "got {} argument(s), expected {}",
105 self.provided, self.expected
106 )
107 }
108}
109
110fn get_cmdline_args<const N: usize>() -> Result<[String; N], ArgCountError> {
111 let args = std::env::args();
112 if args.len() - 1 != N {
113 return Err(ArgCountError {
114 provided: args.len() - 1,
115 expected: N,
116 });
117 }
118
119 // Does not allocate.
120 const EMPTY_STRING: String = String::new();
121 let mut values = [EMPTY_STRING; N];
122
123 // Skip the first element; we do not care about the program name.
124 for (i, arg) in args.skip(1).enumerate() {
125 values[i] = arg
126 }
127 Ok(values)
128}
129
130fn parse_cmdline() -> Result<(String, Operation), ParseCmdlineError> {
131 let [repo_path, op_str] = get_cmdline_args::<2>()?;
132 let op: Operation = op_str.parse()?;
133 validate_repo_path(&repo_path)?;
134 Ok((repo_path.to_string(), op))
135}
136
137fn repo_exists(name: &str) -> bool {
138 match std::fs::metadata(name) {
139 Ok(metadata) => metadata.is_dir(),
140 _ => false,
141 }
142}
143
144struct Config {
145 href_base: String,
146 key_path: String,
147}
148
149#[derive(Debug, Eq, PartialEq, Copy, Clone)]
150enum LoadConfigError {
151 BaseUrlNotProvided,
152 BaseUrlSlashSuffixMissing,
153 KeyPathNotProvided,
154}
155
156impl fmt::Display for LoadConfigError {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::BaseUrlNotProvided => write!(f, "base URL not provided"),
160 Self::BaseUrlSlashSuffixMissing => write!(f, "base URL does not end with slash"),
161 Self::KeyPathNotProvided => write!(f, "key path not provided"),
162 }
163 }
164}
165
166fn load_config() -> Result<Config, LoadConfigError> {
167 let Ok(href_base) = std::env::var("GITOLFS3_HREF_BASE") else {
168 return Err(LoadConfigError::BaseUrlNotProvided);
169 };
170 if !href_base.ends_with('/') {
171 return Err(LoadConfigError::BaseUrlSlashSuffixMissing);
172 }
173 let Ok(key_path) = std::env::var("GITOLFS3_KEY_PATH") else {
174 return Err(LoadConfigError::KeyPathNotProvided);
175 };
176 Ok(Config {
177 href_base,
178 key_path,
179 })
180}
181
182fn main() -> ExitCode {
183 let config = match load_config() {
184 Ok(config) => config,
185 Err(e) => {
186 eprintln!("Failed to load config: {e}");
187 return ExitCode::FAILURE;
188 }
189 };
190 let key = match common::load_key(&config.key_path) {
191 Ok(key) => key,
192 Err(e) => {
193 eprintln!("Failed to load key: {e}");
194 return ExitCode::FAILURE;
195 }
196 };
197
198 let (repo_name, operation) = match parse_cmdline() {
199 Ok(args) => args,
200 Err(e) => {
201 eprintln!("Error: {e}\n");
202 help();
203 // Exit code 2 signifies bad usage of CLI.
204 return ExitCode::from(2);
205 }
206 };
207
208 if !repo_exists(&repo_name) {
209 eprintln!("Error: repository does not exist");
210 return ExitCode::FAILURE;
211 }
212
213 let expires_at = Utc::now() + Duration::from_secs(5 * 60);
214 let Some(tag) = common::generate_tag(
215 common::Claims {
216 specific_claims: common::SpecificClaims::BatchApi(operation),
217 repo_path: &repo_name,
218 expires_at,
219 },
220 key,
221 ) else {
222 eprintln!("Failed to generate validation tag");
223 return ExitCode::FAILURE;
224 };
225
226 println!(
227 "{{\"header\":{{\"Authorization\":\"Gitolfs3-Hmac-Sha256 {tag} {}\"}},\
228 \"expires_at\":\"{}\",\"href\":\"{}{}/info/lfs\"}}",
229 expires_at.timestamp(),
230 common::EscJsonFmt(&expires_at.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)),
231 common::EscJsonFmt(&config.href_base),
232 common::EscJsonFmt(&repo_name),
233 );
234
235 ExitCode::SUCCESS
236}
diff --git a/rs/server/Cargo.toml b/rs/server/Cargo.toml
deleted file mode 100644
index edb76d8..0000000
--- a/rs/server/Cargo.toml
+++ /dev/null
@@ -1,19 +0,0 @@
1[package]
2name = "server"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7aws-config = { version = "1.1.2" }
8aws-sdk-s3 = "1.12.0"
9axum = "0.7"
10base64 = "0.21"
11chrono = { version = "0.4", features = ["serde"] }
12common = { path = "../common" }
13mime = "0.3"
14serde = { version = "1", features = ["derive"] }
15serde_json = "1"
16tokio = { version = "1.35", features = ["full"] }
17tokio-util = "0.7"
18tower = "0.4"
19tracing-subscriber = { version = "0.3", features = ["env-filter"] }
diff --git a/rs/server/src/main.rs b/rs/server/src/main.rs
deleted file mode 100644
index 8baa0d6..0000000
--- a/rs/server/src/main.rs
+++ /dev/null
@@ -1,1028 +0,0 @@
1use std::collections::HashMap;
2use std::collections::HashSet;
3use std::process::ExitCode;
4use std::sync::Arc;
5
6use aws_sdk_s3::error::SdkError;
7use aws_sdk_s3::operation::head_object::HeadObjectOutput;
8use axum::extract::rejection;
9use axum::extract::FromRequest;
10use axum::extract::Path;
11use axum::extract::State;
12use axum::http::header;
13use axum::http::HeaderMap;
14use axum::http::HeaderValue;
15use axum::response::Response;
16use axum::Json;
17use axum::ServiceExt;
18use base64::prelude::*;
19use chrono::DateTime;
20use chrono::Utc;
21use common::HexByte;
22use serde::de;
23use serde::de::DeserializeOwned;
24use serde::Deserialize;
25use serde::Serialize;
26use tower::Layer;
27
28use axum::{
29 async_trait,
30 extract::{FromRequestParts, OriginalUri, Request},
31 http::{request::Parts, StatusCode, Uri},
32 response::IntoResponse,
33 routing::{get, post},
34 Extension, Router,
35};
36
37#[derive(Clone)]
38struct RepositoryName(String);
39
40struct RepositoryNameRejection;
41
42impl IntoResponse for RepositoryNameRejection {
43 fn into_response(self) -> Response {
44 (StatusCode::INTERNAL_SERVER_ERROR, "Missing repository name").into_response()
45 }
46}
47
48#[async_trait]
49impl<S: Send + Sync> FromRequestParts<S> for RepositoryName {
50 type Rejection = RepositoryNameRejection;
51
52 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
53 let Ok(Extension(repo_name)) = Extension::<Self>::from_request_parts(parts, state).await
54 else {
55 return Err(RepositoryNameRejection);
56 };
57 Ok(repo_name)
58 }
59}
60
61async fn rewrite_url<B>(
62 mut req: axum::http::Request<B>,
63) -> Result<axum::http::Request<B>, StatusCode> {
64 let uri = req.uri();
65 let original_uri = OriginalUri(uri.clone());
66
67 let Some(path_and_query) = uri.path_and_query() else {
68 // L @ no path & query
69 return Err(StatusCode::BAD_REQUEST);
70 };
71 let Some((repo, path)) = path_and_query.path().split_once("/info/lfs/objects") else {
72 return Err(StatusCode::NOT_FOUND);
73 };
74 let repo = repo
75 .trim_start_matches('/')
76 .trim_end_matches('/')
77 .to_string();
78 if !path.starts_with('/') || !repo.ends_with(".git") {
79 return Err(StatusCode::NOT_FOUND);
80 }
81
82 let mut parts = uri.clone().into_parts();
83 parts.path_and_query = match path_and_query.query() {
84 None => path.try_into().ok(),
85 Some(q) => format!("{path}?{q}").try_into().ok(),
86 };
87 let Ok(new_uri) = Uri::from_parts(parts) else {
88 return Err(StatusCode::INTERNAL_SERVER_ERROR);
89 };
90
91 *req.uri_mut() = new_uri;
92 req.extensions_mut().insert(original_uri);
93 req.extensions_mut().insert(RepositoryName(repo));
94
95 Ok(req)
96}
97
98struct AppState {
99 s3_client: aws_sdk_s3::Client,
100 s3_bucket: String,
101 authz_conf: AuthorizationConfig,
102 // Should not end with a slash.
103 base_url: String,
104}
105
106struct Env {
107 s3_access_key_id: String,
108 s3_secret_access_key: String,
109 s3_bucket: String,
110 s3_region: String,
111 s3_endpoint: String,
112 base_url: String,
113 key_path: String,
114 listen_host: String,
115 listen_port: String,
116 trusted_forwarded_hosts: String,
117}
118
119fn require_env(name: &str) -> Result<String, String> {
120 std::env::var(name)
121 .map_err(|_| format!("environment variable {name} should be defined and valid"))
122}
123
124impl Env {
125 fn load() -> Result<Env, String> {
126 Ok(Env {
127 s3_secret_access_key: require_env("GITOLFS3_S3_SECRET_ACCESS_KEY_FILE")?,
128 s3_access_key_id: require_env("GITOLFS3_S3_ACCESS_KEY_ID_FILE")?,
129 s3_region: require_env("GITOLFS3_S3_REGION")?,
130 s3_endpoint: require_env("GITOLFS3_S3_ENDPOINT")?,
131 s3_bucket: require_env("GITOLFS3_S3_BUCKET")?,
132 base_url: require_env("GITOLFS3_BASE_URL")?,
133 key_path: require_env("GITOLFS3_KEY_PATH")?,
134 listen_host: require_env("GITOLFS3_LISTEN_HOST")?,
135 listen_port: require_env("GITOLFS3_LISTEN_PORT")?,
136 trusted_forwarded_hosts: std::env::var("GITOLFS3_TRUSTED_FORWARDED_HOSTS")
137 .unwrap_or_default(),
138 })
139 }
140}
141
142fn get_s3_client(env: &Env) -> Result<aws_sdk_s3::Client, std::io::Error> {
143 let access_key_id = std::fs::read_to_string(&env.s3_access_key_id)?;
144 let secret_access_key = std::fs::read_to_string(&env.s3_secret_access_key)?;
145
146 let credentials = aws_sdk_s3::config::Credentials::new(
147 access_key_id,
148 secret_access_key,
149 None,
150 None,
151 "gitolfs3-env",
152 );
153 let config = aws_config::SdkConfig::builder()
154 .behavior_version(aws_config::BehaviorVersion::latest())
155 .region(aws_config::Region::new(env.s3_region.clone()))
156 .endpoint_url(&env.s3_endpoint)
157 .credentials_provider(aws_sdk_s3::config::SharedCredentialsProvider::new(
158 credentials,
159 ))
160 .build();
161 Ok(aws_sdk_s3::Client::new(&config))
162}
163
164#[tokio::main]
165async fn main() -> ExitCode {
166 tracing_subscriber::fmt::init();
167
168 let env = match Env::load() {
169 Ok(env) => env,
170 Err(e) => {
171 println!("Failed to load configuration: {e}");
172 return ExitCode::from(2);
173 }
174 };
175
176 let s3_client = match get_s3_client(&env) {
177 Ok(s3_client) => s3_client,
178 Err(e) => {
179 println!("Failed to create S3 client: {e}");
180 return ExitCode::FAILURE;
181 }
182 };
183 let key = match common::load_key(&env.key_path) {
184 Ok(key) => key,
185 Err(e) => {
186 println!("Failed to load Gitolfs3 key: {e}");
187 return ExitCode::FAILURE;
188 }
189 };
190
191 let trusted_forwarded_hosts: HashSet<String> = env
192 .trusted_forwarded_hosts
193 .split(',')
194 .map(|s| s.to_owned())
195 .filter(|s| !s.is_empty())
196 .collect();
197 let base_url = env.base_url.trim_end_matches('/').to_string();
198
199 let authz_conf = AuthorizationConfig {
200 key,
201 trusted_forwarded_hosts,
202 };
203
204 let shared_state = Arc::new(AppState {
205 s3_client,
206 s3_bucket: env.s3_bucket,
207 authz_conf,
208 base_url,
209 });
210 let app = Router::new()
211 .route("/batch", post(batch))
212 .route("/:oid0/:oid1/:oid", get(obj_download))
213 .with_state(shared_state);
214
215 let middleware = axum::middleware::map_request(rewrite_url);
216 let app_with_middleware = middleware.layer(app);
217
218 let Ok(listen_port): Result<u16, _> = env.listen_port.parse() else {
219 println!("Configured LISTEN_PORT should be an unsigned integer no higher than 65535");
220 return ExitCode::from(2);
221 };
222 let addr: (String, u16) = (env.listen_host, listen_port);
223 let listener = match tokio::net::TcpListener::bind(addr).await {
224 Ok(listener) => listener,
225 Err(e) => {
226 println!("Failed to listen: {e}");
227 return ExitCode::FAILURE;
228 }
229 };
230
231 match axum::serve(listener, app_with_middleware.into_make_service()).await {
232 Ok(_) => ExitCode::SUCCESS,
233 Err(e) => {
234 println!("Error serving: {e}");
235 ExitCode::FAILURE
236 }
237 }
238}
239
240#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
241enum TransferAdapter {
242 #[serde(rename = "basic")]
243 Basic,
244 #[serde(other)]
245 Unknown,
246}
247
248#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
249enum HashAlgo {
250 #[serde(rename = "sha256")]
251 Sha256,
252 #[serde(other)]
253 Unknown,
254}
255
256impl Default for HashAlgo {
257 fn default() -> Self {
258 Self::Sha256
259 }
260}
261
262#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
263struct BatchRequestObject {
264 oid: common::Oid,
265 size: i64,
266}
267
268#[derive(Debug, Serialize, Deserialize, Clone)]
269struct BatchRef {
270 name: String,
271}
272
273fn default_transfers() -> Vec<TransferAdapter> {
274 vec![TransferAdapter::Basic]
275}
276
277#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
278struct BatchRequest {
279 operation: common::Operation,
280 #[serde(default = "default_transfers")]
281 transfers: Vec<TransferAdapter>,
282 objects: Vec<BatchRequestObject>,
283 #[serde(default)]
284 hash_algo: HashAlgo,
285}
286
287#[derive(Debug, Clone)]
288struct GitLfsJson<T>(Json<T>);
289
290const LFS_MIME: &str = "application/vnd.git-lfs+json";
291
292enum GitLfsJsonRejection {
293 Json(rejection::JsonRejection),
294 MissingGitLfsJsonContentType,
295}
296
297impl IntoResponse for GitLfsJsonRejection {
298 fn into_response(self) -> Response {
299 match self {
300 Self::Json(rej) => rej.into_response(),
301 Self::MissingGitLfsJsonContentType => make_error_resp(
302 StatusCode::UNSUPPORTED_MEDIA_TYPE,
303 &format!("Expected request with `Content-Type: {LFS_MIME}`"),
304 )
305 .into_response(),
306 }
307 }
308}
309
310fn is_git_lfs_json_mimetype(mimetype: &str) -> bool {
311 let Ok(mime) = mimetype.parse::<mime::Mime>() else {
312 return false;
313 };
314 if mime.type_() != mime::APPLICATION
315 || mime.subtype() != "vnd.git-lfs"
316 || mime.suffix() != Some(mime::JSON)
317 {
318 return false;
319 }
320 match mime.get_param(mime::CHARSET) {
321 Some(mime::UTF_8) | None => true,
322 Some(_) => false,
323 }
324}
325
326fn has_git_lfs_json_content_type(req: &Request) -> bool {
327 let Some(content_type) = req.headers().get(header::CONTENT_TYPE) else {
328 return false;
329 };
330 let Ok(content_type) = content_type.to_str() else {
331 return false;
332 };
333 is_git_lfs_json_mimetype(content_type)
334}
335
336#[async_trait]
337impl<T, S> FromRequest<S> for GitLfsJson<T>
338where
339 T: DeserializeOwned,
340 S: Send + Sync,
341{
342 type Rejection = GitLfsJsonRejection;
343
344 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
345 if !has_git_lfs_json_content_type(&req) {
346 return Err(GitLfsJsonRejection::MissingGitLfsJsonContentType);
347 }
348 Json::<T>::from_request(req, state)
349 .await
350 .map(GitLfsJson)
351 .map_err(GitLfsJsonRejection::Json)
352 }
353}
354
355impl<T: Serialize> IntoResponse for GitLfsJson<T> {
356 fn into_response(self) -> Response {
357 let GitLfsJson(json) = self;
358 let mut resp = json.into_response();
359 resp.headers_mut().insert(
360 header::CONTENT_TYPE,
361 HeaderValue::from_static("application/vnd.git-lfs+json; charset=utf-8"),
362 );
363 resp
364 }
365}
366
367#[derive(Debug, Serialize)]
368struct GitLfsErrorData<'a> {
369 message: &'a str,
370}
371
372type GitLfsErrorResponse<'a> = (StatusCode, GitLfsJson<GitLfsErrorData<'a>>);
373
374const fn make_error_resp(code: StatusCode, message: &str) -> GitLfsErrorResponse {
375 (code, GitLfsJson(Json(GitLfsErrorData { message })))
376}
377
378#[derive(Debug, Serialize, Clone)]
379struct BatchResponseObjectAction {
380 href: String,
381 #[serde(skip_serializing_if = "HashMap::is_empty")]
382 header: HashMap<String, String>,
383 expires_at: DateTime<Utc>,
384}
385
386#[derive(Default, Debug, Serialize, Clone)]
387struct BatchResponseObjectActions {
388 #[serde(skip_serializing_if = "Option::is_none")]
389 upload: Option<BatchResponseObjectAction>,
390 #[serde(skip_serializing_if = "Option::is_none")]
391 download: Option<BatchResponseObjectAction>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 verify: Option<BatchResponseObjectAction>,
394}
395
396#[derive(Debug, Clone, Serialize)]
397struct BatchResponseObjectError {
398 code: u16,
399 message: String,
400}
401
402#[derive(Debug, Serialize, Clone)]
403struct BatchResponseObject {
404 oid: common::Oid,
405 size: i64,
406 #[serde(skip_serializing_if = "Option::is_none")]
407 authenticated: Option<bool>,
408 actions: BatchResponseObjectActions,
409 #[serde(skip_serializing_if = "Option::is_none")]
410 error: Option<BatchResponseObjectError>,
411}
412
413impl BatchResponseObject {
414 fn error(obj: &BatchRequestObject, code: StatusCode, message: String) -> BatchResponseObject {
415 BatchResponseObject {
416 oid: obj.oid,
417 size: obj.size,
418 authenticated: None,
419 actions: Default::default(),
420 error: Some(BatchResponseObjectError {
421 code: code.as_u16(),
422 message,
423 }),
424 }
425 }
426}
427
428#[derive(Debug, Serialize, Clone)]
429struct BatchResponse {
430 transfer: TransferAdapter,
431 objects: Vec<BatchResponseObject>,
432 hash_algo: HashAlgo,
433}
434
435fn validate_checksum(oid: common::Oid, obj: &HeadObjectOutput) -> bool {
436 if let Some(checksum) = obj.checksum_sha256() {
437 if let Ok(checksum) = BASE64_STANDARD.decode(checksum) {
438 if let Ok(checksum32b) = TryInto::<[u8; 32]>::try_into(checksum) {
439 return common::Oid::from(checksum32b) == oid;
440 }
441 }
442 }
443 true
444}
445
446fn validate_size(expected: i64, obj: &HeadObjectOutput) -> bool {
447 if let Some(length) = obj.content_length() {
448 return length == expected;
449 }
450 true
451}
452
453async fn handle_upload_object(
454 state: &AppState,
455 repo: &str,
456 obj: &BatchRequestObject,
457) -> Option<BatchResponseObject> {
458 let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1]));
459 let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid);
460
461 match state
462 .s3_client
463 .head_object()
464 .bucket(&state.s3_bucket)
465 .key(full_path.clone())
466 .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled)
467 .send()
468 .await
469 {
470 Ok(result) => {
471 if validate_size(obj.size, &result) && validate_checksum(obj.oid, &result) {
472 return None;
473 }
474 }
475 Err(SdkError::ServiceError(e)) if e.err().is_not_found() => {}
476 Err(e) => {
477 println!("Failed to HeadObject (repo {repo}, OID {}): {e}", obj.oid);
478 return Some(BatchResponseObject::error(
479 obj,
480 StatusCode::INTERNAL_SERVER_ERROR,
481 "Failed to query object information".to_string(),
482 ));
483 }
484 };
485
486 let expires_in = std::time::Duration::from_secs(5 * 60);
487 let expires_at = Utc::now() + expires_in;
488
489 let Ok(config) = aws_sdk_s3::presigning::PresigningConfig::expires_in(expires_in) else {
490 return Some(BatchResponseObject::error(
491 obj,
492 StatusCode::INTERNAL_SERVER_ERROR,
493 "Failed to generate upload URL".to_string(),
494 ));
495 };
496 let Ok(presigned) = state
497 .s3_client
498 .put_object()
499 .bucket(&state.s3_bucket)
500 .key(full_path)
501 .checksum_sha256(obj.oid.to_string())
502 .content_length(obj.size)
503 .presigned(config)
504 .await
505 else {
506 return Some(BatchResponseObject::error(
507 obj,
508 StatusCode::INTERNAL_SERVER_ERROR,
509 "Failed to generate upload URL".to_string(),
510 ));
511 };
512 Some(BatchResponseObject {
513 oid: obj.oid,
514 size: obj.size,
515 authenticated: Some(true),
516 actions: BatchResponseObjectActions {
517 upload: Some(BatchResponseObjectAction {
518 header: presigned
519 .headers()
520 .map(|(k, v)| (k.to_owned(), v.to_owned()))
521 .collect(),
522 expires_at,
523 href: presigned.uri().to_string(),
524 }),
525 ..Default::default()
526 },
527 error: None,
528 })
529}
530
531async fn handle_download_object(
532 state: &AppState,
533 repo: &str,
534 obj: &BatchRequestObject,
535 trusted: bool,
536) -> BatchResponseObject {
537 let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1]));
538 let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid);
539
540 let result = match state
541 .s3_client
542 .head_object()
543 .bucket(&state.s3_bucket)
544 .key(&full_path)
545 .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled)
546 .send()
547 .await
548 {
549 Ok(result) => result,
550 Err(e) => {
551 println!("Failed to HeadObject (repo {repo}, OID {}): {e}", obj.oid);
552 return BatchResponseObject::error(
553 obj,
554 StatusCode::INTERNAL_SERVER_ERROR,
555 "Failed to query object information".to_string(),
556 );
557 }
558 };
559
560 // Scaleway actually doesn't provide SHA256 suport, but maybe in the future :)
561 if !validate_checksum(obj.oid, &result) {
562 return BatchResponseObject::error(
563 obj,
564 StatusCode::UNPROCESSABLE_ENTITY,
565 "Object corrupted".to_string(),
566 );
567 }
568 if !validate_size(obj.size, &result) {
569 return BatchResponseObject::error(
570 obj,
571 StatusCode::UNPROCESSABLE_ENTITY,
572 "Incorrect size specified (or object corrupted)".to_string(),
573 );
574 }
575
576 let expires_in = std::time::Duration::from_secs(5 * 60);
577 let expires_at = Utc::now() + expires_in;
578
579 if trusted {
580 let Ok(config) = aws_sdk_s3::presigning::PresigningConfig::expires_in(expires_in) else {
581 return BatchResponseObject::error(
582 obj,
583 StatusCode::INTERNAL_SERVER_ERROR,
584 "Failed to generate upload URL".to_string(),
585 );
586 };
587 let Ok(presigned) = state
588 .s3_client
589 .get_object()
590 .bucket(&state.s3_bucket)
591 .key(full_path)
592 .presigned(config)
593 .await
594 else {
595 return BatchResponseObject::error(
596 obj,
597 StatusCode::INTERNAL_SERVER_ERROR,
598 "Failed to generate upload URL".to_string(),
599 );
600 };
601 return BatchResponseObject {
602 oid: obj.oid,
603 size: obj.size,
604 authenticated: Some(true),
605 actions: BatchResponseObjectActions {
606 download: Some(BatchResponseObjectAction {
607 header: presigned
608 .headers()
609 .map(|(k, v)| (k.to_owned(), v.to_owned()))
610 .collect(),
611 expires_at,
612 href: presigned.uri().to_string(),
613 }),
614 ..Default::default()
615 },
616 error: None,
617 };
618 }
619
620 let Some(tag) = common::generate_tag(
621 common::Claims {
622 specific_claims: common::SpecificClaims::Download(obj.oid),
623 repo_path: repo,
624 expires_at,
625 },
626 &state.authz_conf.key,
627 ) else {
628 return BatchResponseObject::error(
629 obj,
630 StatusCode::INTERNAL_SERVER_ERROR,
631 "Internal server error".to_string(),
632 );
633 };
634
635 let upload_path = format!(
636 "{repo}/info/lfs/objects/{}/{}/{}",
637 HexByte(obj.oid[0]),
638 HexByte(obj.oid[1]),
639 obj.oid,
640 );
641
642 BatchResponseObject {
643 oid: obj.oid,
644 size: obj.size,
645 authenticated: Some(true),
646 actions: BatchResponseObjectActions {
647 download: Some(BatchResponseObjectAction {
648 header: {
649 let mut map = HashMap::new();
650 map.insert(
651 "Authorization".to_string(),
652 format!("Gitolfs3-Hmac-Sha256 {tag} {}", expires_at.timestamp()),
653 );
654 map
655 },
656 expires_at,
657 href: format!("{}/{upload_path}", state.base_url),
658 }),
659 ..Default::default()
660 },
661 error: None,
662 }
663}
664
665struct AuthorizationConfig {
666 trusted_forwarded_hosts: HashSet<String>,
667 key: common::Key,
668}
669
670struct Trusted(bool);
671
672fn forwarded_for_trusted_host(
673 headers: &HeaderMap,
674 trusted: &HashSet<String>,
675) -> Result<bool, GitLfsErrorResponse<'static>> {
676 if let Some(forwarded_for) = headers.get("X-Forwarded-For") {
677 if let Ok(forwarded_for) = forwarded_for.to_str() {
678 if trusted.contains(forwarded_for) {
679 return Ok(true);
680 }
681 } else {
682 return Err(make_error_resp(
683 StatusCode::NOT_FOUND,
684 "Invalid X-Forwarded-For header",
685 ));
686 }
687 }
688 Ok(false)
689}
690const REPO_NOT_FOUND: GitLfsErrorResponse =
691 make_error_resp(StatusCode::NOT_FOUND, "Repository not found");
692
693fn authorize_batch(
694 conf: &AuthorizationConfig,
695 repo_path: &str,
696 public: bool,
697 operation: common::Operation,
698 headers: &HeaderMap,
699) -> Result<Trusted, GitLfsErrorResponse<'static>> {
700 // - No authentication required for downloading exported repos
701 // - When authenticated:
702 // - Download / upload over presigned URLs
703 // - When accessing over Tailscale:
704 // - No authentication required for downloading from any repo
705
706 let claims = VerifyClaimsInput {
707 specific_claims: common::SpecificClaims::BatchApi(operation),
708 repo_path,
709 };
710 if verify_claims(conf, &claims, headers)? {
711 return Ok(Trusted(true));
712 }
713
714 let trusted = forwarded_for_trusted_host(headers, &conf.trusted_forwarded_hosts)?;
715 if operation != common::Operation::Download {
716 if trusted {
717 return Err(make_error_resp(
718 StatusCode::FORBIDDEN,
719 "Authentication required to upload",
720 ));
721 }
722 return Err(REPO_NOT_FOUND);
723 }
724 if trusted {
725 return Ok(Trusted(true));
726 }
727
728 if public {
729 Ok(Trusted(false))
730 } else {
731 Err(REPO_NOT_FOUND)
732 }
733}
734
735fn repo_exists(name: &str) -> bool {
736 let Ok(metadata) = std::fs::metadata(name) else {
737 return false;
738 };
739 metadata.is_dir()
740}
741
742fn is_repo_public(name: &str) -> Option<bool> {
743 if !repo_exists(name) {
744 return None;
745 }
746 std::fs::metadata(format!("{name}/git-daemon-export-ok"))
747 .ok()?
748 .is_file()
749 .into()
750}
751
752async fn batch(
753 State(state): State<Arc<AppState>>,
754 headers: HeaderMap,
755 RepositoryName(repo): RepositoryName,
756 GitLfsJson(Json(payload)): GitLfsJson<BatchRequest>,
757) -> Response {
758 let Some(public) = is_repo_public(&repo) else {
759 return REPO_NOT_FOUND.into_response();
760 };
761 let Trusted(trusted) = match authorize_batch(
762 &state.authz_conf,
763 &repo,
764 public,
765 payload.operation,
766 &headers,
767 ) {
768 Ok(authn) => authn,
769 Err(e) => return e.into_response(),
770 };
771
772 if !headers
773 .get_all("Accept")
774 .iter()
775 .filter_map(|v| v.to_str().ok())
776 .any(is_git_lfs_json_mimetype)
777 {
778 let message = format!("Expected `{LFS_MIME}` in list of acceptable response media types");
779 return make_error_resp(StatusCode::NOT_ACCEPTABLE, &message).into_response();
780 }
781
782 if payload.hash_algo != HashAlgo::Sha256 {
783 let message = "Unsupported hashing algorithm specified";
784 return make_error_resp(StatusCode::CONFLICT, message).into_response();
785 }
786 if !payload.transfers.is_empty() && !payload.transfers.contains(&TransferAdapter::Basic) {
787 let message = "Unsupported transfer adapter specified (supported: basic)";
788 return make_error_resp(StatusCode::CONFLICT, message).into_response();
789 }
790
791 let mut resp = BatchResponse {
792 transfer: TransferAdapter::Basic,
793 objects: vec![],
794 hash_algo: HashAlgo::Sha256,
795 };
796 for obj in payload.objects {
797 match payload.operation {
798 common::Operation::Download => resp
799 .objects
800 .push(handle_download_object(&state, &repo, &obj, trusted).await),
801 common::Operation::Upload => {
802 if let Some(obj_resp) = handle_upload_object(&state, &repo, &obj).await {
803 resp.objects.push(obj_resp);
804 }
805 }
806 };
807 }
808 GitLfsJson(Json(resp)).into_response()
809}
810
811#[derive(Deserialize, Copy, Clone)]
812#[serde(remote = "Self")]
813struct FileParams {
814 oid0: HexByte,
815 oid1: HexByte,
816 oid: common::Oid,
817}
818
819impl<'de> Deserialize<'de> for FileParams {
820 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
821 where
822 D: serde::Deserializer<'de>,
823 {
824 let unchecked @ FileParams {
825 oid0: HexByte(oid0),
826 oid1: HexByte(oid1),
827 oid,
828 } = FileParams::deserialize(deserializer)?;
829 if oid0 != oid.as_bytes()[0] {
830 return Err(de::Error::custom(
831 "first OID path part does not match first byte of full OID",
832 ));
833 }
834 if oid1 != oid.as_bytes()[1] {
835 return Err(de::Error::custom(
836 "second OID path part does not match first byte of full OID",
837 ));
838 }
839 Ok(unchecked)
840 }
841}
842
843pub struct VerifyClaimsInput<'a> {
844 pub specific_claims: common::SpecificClaims,
845 pub repo_path: &'a str,
846}
847
848fn verify_claims(
849 conf: &AuthorizationConfig,
850 claims: &VerifyClaimsInput,
851 headers: &HeaderMap,
852) -> Result<bool, GitLfsErrorResponse<'static>> {
853 const INVALID_AUTHZ_HEADER: GitLfsErrorResponse =
854 make_error_resp(StatusCode::BAD_REQUEST, "Invalid authorization header");
855
856 if let Some(authz) = headers.get(header::AUTHORIZATION) {
857 if let Ok(authz) = authz.to_str() {
858 if let Some(val) = authz.strip_prefix("Gitolfs3-Hmac-Sha256 ") {
859 let (tag, expires_at) = val.split_once(' ').ok_or(INVALID_AUTHZ_HEADER)?;
860 let tag: common::Digest<32> = tag.parse().map_err(|_| INVALID_AUTHZ_HEADER)?;
861 let expires_at: i64 = expires_at.parse().map_err(|_| INVALID_AUTHZ_HEADER)?;
862 let expires_at =
863 DateTime::<Utc>::from_timestamp(expires_at, 0).ok_or(INVALID_AUTHZ_HEADER)?;
864 let Some(expected_tag) = common::generate_tag(
865 common::Claims {
866 specific_claims: claims.specific_claims,
867 repo_path: claims.repo_path,
868 expires_at,
869 },
870 &conf.key,
871 ) else {
872 return Err(make_error_resp(
873 StatusCode::INTERNAL_SERVER_ERROR,
874 "Internal server error",
875 ));
876 };
877 if tag == expected_tag {
878 return Ok(true);
879 }
880 }
881 }
882 return Err(INVALID_AUTHZ_HEADER);
883 }
884 Ok(false)
885}
886
887fn authorize_get(
888 conf: &AuthorizationConfig,
889 repo_path: &str,
890 oid: common::Oid,
891 headers: &HeaderMap,
892) -> Result<(), GitLfsErrorResponse<'static>> {
893 let claims = VerifyClaimsInput {
894 specific_claims: common::SpecificClaims::Download(oid),
895 repo_path,
896 };
897 if !verify_claims(conf, &claims, headers)? {
898 return Err(make_error_resp(
899 StatusCode::UNAUTHORIZED,
900 "Repository not found",
901 ));
902 }
903 Ok(())
904}
905
906async fn obj_download(
907 State(state): State<Arc<AppState>>,
908 headers: HeaderMap,
909 RepositoryName(repo): RepositoryName,
910 Path(FileParams { oid0, oid1, oid }): Path<FileParams>,
911) -> Response {
912 if let Err(e) = authorize_get(&state.authz_conf, &repo, oid, &headers) {
913 return e.into_response();
914 }
915
916 let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, oid);
917 let result = match state
918 .s3_client
919 .get_object()
920 .bucket(&state.s3_bucket)
921 .key(full_path)
922 .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled)
923 .send()
924 .await
925 {
926 Ok(result) => result,
927 Err(e) => {
928 println!("Failed to GetObject (repo {repo}, OID {oid}): {e}");
929 return (
930 StatusCode::INTERNAL_SERVER_ERROR,
931 "Failed to query object information",
932 )
933 .into_response();
934 }
935 };
936
937 let mut headers = header::HeaderMap::new();
938 if let Some(content_type) = result.content_type {
939 let Ok(header_value) = content_type.try_into() else {
940 return (
941 StatusCode::INTERNAL_SERVER_ERROR,
942 "Object has invalid content type",
943 )
944 .into_response();
945 };
946 headers.insert(header::CONTENT_TYPE, header_value);
947 }
948 if let Some(content_length) = result.content_length {
949 headers.insert(header::CONTENT_LENGTH, content_length.into());
950 }
951
952 let async_read = result.body.into_async_read();
953 let stream = tokio_util::io::ReaderStream::new(async_read);
954 let body = axum::body::Body::from_stream(stream);
955
956 (headers, body).into_response()
957}
958
959#[test]
960fn test_mimetype() {
961 assert!(is_git_lfs_json_mimetype("application/vnd.git-lfs+json"));
962 assert!(!is_git_lfs_json_mimetype("application/vnd.git-lfs"));
963 assert!(!is_git_lfs_json_mimetype("application/json"));
964 assert!(is_git_lfs_json_mimetype(
965 "application/vnd.git-lfs+json; charset=utf-8"
966 ));
967 assert!(is_git_lfs_json_mimetype(
968 "application/vnd.git-lfs+json; charset=UTF-8"
969 ));
970 assert!(!is_git_lfs_json_mimetype(
971 "application/vnd.git-lfs+json; charset=ISO-8859-1"
972 ));
973}
974
975#[test]
976fn test_deserialize() {
977 let json = r#"{"operation":"upload","objects":[{"oid":"8f4123f9a7181f488c5e111d82cefd992e461ae5df01fd2254399e6e670b2d3c","size":170904}],
978 "transfers":["lfs-standalone-file","basic","ssh"],"ref":{"name":"refs/heads/main"},"hash_algo":"sha256"}"#;
979 let expected = BatchRequest {
980 operation: common::Operation::Upload,
981 objects: vec![BatchRequestObject {
982 oid: "8f4123f9a7181f488c5e111d82cefd992e461ae5df01fd2254399e6e670b2d3c"
983 .parse()
984 .unwrap(),
985 size: 170904,
986 }],
987 transfers: vec![
988 TransferAdapter::Unknown,
989 TransferAdapter::Basic,
990 TransferAdapter::Unknown,
991 ],
992 hash_algo: HashAlgo::Sha256,
993 };
994 assert_eq!(
995 serde_json::from_str::<BatchRequest>(json).unwrap(),
996 expected
997 );
998}
999
1000#[test]
1001fn test_validate_claims() {
1002 let key = "00232f7a019bd34e3921ee6c5f04caf48a4489d1be5d1999038950a7054e0bfea369ce2becc0f13fd3c69f8af2384a25b7ac2d52eb52c33722f3c00c50d4c9c2";
1003 let key: common::Key = key.parse().unwrap();
1004
1005 let claims = common::Claims {
1006 expires_at: Utc::now() + std::time::Duration::from_secs(5 * 60),
1007 repo_path: "lfs-test.git",
1008 specific_claims: common::SpecificClaims::BatchApi(common::Operation::Download),
1009 };
1010 let tag = common::generate_tag(claims, &key).unwrap();
1011 let header_value = format!(
1012 "Gitolfs3-Hmac-Sha256 {tag} {}",
1013 claims.expires_at.timestamp()
1014 );
1015
1016 let conf = AuthorizationConfig {
1017 key,
1018 trusted_forwarded_hosts: HashSet::new(),
1019 };
1020 let verification_claims = VerifyClaimsInput {
1021 repo_path: claims.repo_path,
1022 specific_claims: claims.specific_claims,
1023 };
1024 let mut headers = HeaderMap::new();
1025 headers.insert(header::AUTHORIZATION, header_value.try_into().unwrap());
1026
1027 assert!(verify_claims(&conf, &verification_claims, &headers).unwrap());
1028}
diff --git a/rs/shell/Cargo.toml b/rs/shell/Cargo.toml
deleted file mode 100644
index 0dcb6d6..0000000
--- a/rs/shell/Cargo.toml
+++ /dev/null
@@ -1,6 +0,0 @@
1[package]
2name = "shell"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
diff --git a/rs/shell/src/main.rs b/rs/shell/src/main.rs
deleted file mode 100644
index ef0ef48..0000000
--- a/rs/shell/src/main.rs
+++ /dev/null
@@ -1,143 +0,0 @@
1use std::{os::unix::process::CommandExt, process::ExitCode};
2
3fn parse_sq(s: &str) -> Option<(String, &str)> {
4 #[derive(PartialEq, Eq)]
5 enum SqState {
6 Quoted,
7 Unquoted { may_escape: bool },
8 UnquotedEscaped,
9 }
10
11 let mut result = String::new();
12 let mut state = SqState::Unquoted { may_escape: false };
13 let mut remaining = "";
14 for (i, c) in s.char_indices() {
15 match state {
16 SqState::Unquoted { may_escape: false } => {
17 if c != '\'' {
18 return None;
19 }
20 state = SqState::Quoted
21 }
22 SqState::Quoted => {
23 if c == '\'' {
24 state = SqState::Unquoted { may_escape: true };
25 continue;
26 }
27 result.push(c);
28 }
29 SqState::Unquoted { may_escape: true } => {
30 if is_posix_space(c) {
31 remaining = &s[i..];
32 break;
33 }
34 if c != '\\' {
35 return None;
36 }
37 state = SqState::UnquotedEscaped;
38 }
39 SqState::UnquotedEscaped => {
40 if c != '\\' && c != '!' {
41 return None;
42 }
43 result.push(c);
44 state = SqState::Unquoted { may_escape: false };
45 }
46 }
47 }
48
49 if state != (SqState::Unquoted { may_escape: true }) {
50 return None;
51 }
52 Some((result, remaining))
53}
54
55fn parse_cmd(mut cmd: &str) -> Option<Vec<String>> {
56 let mut args = Vec::<String>::new();
57
58 cmd = cmd.trim_matches(is_posix_space);
59 while cmd != "" {
60 if cmd.starts_with('\'') {
61 let (arg, remaining) = parse_sq(cmd)?;
62 args.push(arg);
63 cmd = remaining.trim_start_matches(is_posix_space);
64 } else if let Some((arg, remaining)) = cmd.split_once(is_posix_space) {
65 args.push(arg.to_owned());
66 cmd = remaining.trim_start_matches(is_posix_space);
67 } else {
68 args.push(cmd.to_owned());
69 cmd = "";
70 }
71 }
72
73 Some(args)
74}
75
76fn is_posix_space(c: char) -> bool {
77 // Form feed: 0x0c
78 // Vertical tab: 0x0b
79 c == ' ' || c == '\x0c' || c == '\n' || c == '\r' || c == '\t' || c == '\x0b'
80}
81
82fn main() -> ExitCode {
83 let bad_usage = ExitCode::from(2);
84
85 let mut args = std::env::args().skip(1);
86 if args.next() != Some("-c".to_string()) {
87 eprintln!("Expected usage: shell -c <argument>");
88 return bad_usage;
89 }
90 let Some(cmd) = args.next() else {
91 eprintln!("Missing argument for argument '-c'");
92 return bad_usage;
93 };
94 if args.next() != None {
95 eprintln!("Too many arguments passed");
96 return bad_usage;
97 }
98
99 let Some(mut cmd) = parse_cmd(&cmd) else {
100 eprintln!("Bad command");
101 return bad_usage;
102 };
103
104 let Some(mut program) = cmd.drain(0..1).next() else {
105 eprintln!("Bad command");
106 return bad_usage;
107 };
108 if program == "git" {
109 let Some(subcommand) = cmd.drain(0..1).next() else {
110 eprintln!("Bad command");
111 return bad_usage;
112 };
113 program.push('-');
114 program.push_str(&subcommand);
115 }
116
117 let mut args = Vec::new();
118
119 let git_cmds = ["git-receive-pack", "git-upload-archive", "git-upload-pack"];
120 if git_cmds.contains(&program.as_str()) {
121 if cmd.len() != 1 {
122 eprintln!("Bad command");
123 return bad_usage;
124 }
125 let repository = cmd[0].trim_start_matches('/');
126 args.push(repository);
127 } else if program == "git-lfs-authenticate" {
128 if cmd.len() != 2 {
129 eprintln!("Bad command");
130 return bad_usage;
131 }
132 let repository = cmd[0].trim_start_matches('/');
133 args.push(repository);
134 args.push(&cmd[1]);
135 } else {
136 eprintln!("Unknown command");
137 return bad_usage;
138 }
139
140 let e = std::process::Command::new(program).args(args).exec();
141 eprintln!("Error: {e}");
142 ExitCode::FAILURE
143}