diff options
Diffstat (limited to 'rs')
-rw-r--r-- | rs/.gitignore | 1 | ||||
-rw-r--r-- | rs/Cargo.lock | 2364 | ||||
-rw-r--r-- | rs/Cargo.toml | 8 | ||||
-rw-r--r-- | rs/common/Cargo.toml | 10 | ||||
-rw-r--r-- | rs/common/src/lib.rs | 368 | ||||
-rw-r--r-- | rs/git-lfs-authenticate/Cargo.toml | 8 | ||||
-rw-r--r-- | rs/git-lfs-authenticate/src/main.rs | 236 | ||||
-rw-r--r-- | rs/server/Cargo.toml | 19 | ||||
-rw-r--r-- | rs/server/src/main.rs | 1028 | ||||
-rw-r--r-- | rs/shell/Cargo.toml | 6 | ||||
-rw-r--r-- | rs/shell/src/main.rs | 143 |
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. | ||
3 | version = 3 | ||
4 | |||
5 | [[package]] | ||
6 | name = "addr2line" | ||
7 | version = "0.21.0" | ||
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" | ||
10 | dependencies = [ | ||
11 | "gimli", | ||
12 | ] | ||
13 | |||
14 | [[package]] | ||
15 | name = "adler" | ||
16 | version = "1.0.2" | ||
17 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||
19 | |||
20 | [[package]] | ||
21 | name = "aho-corasick" | ||
22 | version = "1.1.2" | ||
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
24 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" | ||
25 | dependencies = [ | ||
26 | "memchr", | ||
27 | ] | ||
28 | |||
29 | [[package]] | ||
30 | name = "android-tzdata" | ||
31 | version = "0.1.1" | ||
32 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
33 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" | ||
34 | |||
35 | [[package]] | ||
36 | name = "android_system_properties" | ||
37 | version = "0.1.5" | ||
38 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
39 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" | ||
40 | dependencies = [ | ||
41 | "libc", | ||
42 | ] | ||
43 | |||
44 | [[package]] | ||
45 | name = "async-trait" | ||
46 | version = "0.1.77" | ||
47 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
48 | checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" | ||
49 | dependencies = [ | ||
50 | "proc-macro2", | ||
51 | "quote", | ||
52 | "syn", | ||
53 | ] | ||
54 | |||
55 | [[package]] | ||
56 | name = "autocfg" | ||
57 | version = "1.1.0" | ||
58 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
59 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" | ||
60 | |||
61 | [[package]] | ||
62 | name = "aws-config" | ||
63 | version = "1.1.2" | ||
64 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
65 | checksum = "7e64b72d4bdbb41a73d27709c65a25b6e4bfc8321bf70fa3a8b19ce7d4eb81b0" | ||
66 | dependencies = [ | ||
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]] | ||
93 | name = "aws-credential-types" | ||
94 | version = "1.1.2" | ||
95 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
96 | checksum = "4a7cb3510b95492bd9014b60e2e3bee3e48bc516e220316f8e6b60df18b47331" | ||
97 | dependencies = [ | ||
98 | "aws-smithy-async", | ||
99 | "aws-smithy-runtime-api", | ||
100 | "aws-smithy-types", | ||
101 | "zeroize", | ||
102 | ] | ||
103 | |||
104 | [[package]] | ||
105 | name = "aws-http" | ||
106 | version = "0.60.2" | ||
107 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
108 | checksum = "a95d41abe4e941399fdb4bc2f54713eac3c839d98151875948bb24e66ab658f2" | ||
109 | dependencies = [ | ||
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]] | ||
121 | name = "aws-runtime" | ||
122 | version = "1.1.2" | ||
123 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
124 | checksum = "233cca219c6705d525ace011d6f9bc51aaf32fce5b4c41661d2d7ff22d9b4d49" | ||
125 | dependencies = [ | ||
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]] | ||
143 | name = "aws-sdk-s3" | ||
144 | version = "1.12.0" | ||
145 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
146 | checksum = "634fbe5b6591ee2e281cd2ba8641e9bd752dbf5bf338924d6ad4bd5a3304fe31" | ||
147 | dependencies = [ | ||
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]] | ||
173 | name = "aws-sdk-sso" | ||
174 | version = "1.10.0" | ||
175 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
176 | checksum = "ee41005e0f3a19ae749c7953d9e1f1ef8d2183f76f64966e346fa41c1ba0ed44" | ||
177 | dependencies = [ | ||
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]] | ||
196 | name = "aws-sdk-ssooidc" | ||
197 | version = "1.10.0" | ||
198 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
199 | checksum = "fa08168f8a27505e7b90f922c32a489feb1f2133878981a15138bebc849ac09c" | ||
200 | dependencies = [ | ||
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]] | ||
219 | name = "aws-sdk-sts" | ||
220 | version = "1.10.0" | ||
221 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
222 | checksum = "29102eff04d50ef70f11a48823db33e33c6cc5f027bfb6ff4864efbd5f1f66f3" | ||
223 | dependencies = [ | ||
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]] | ||
243 | name = "aws-sigv4" | ||
244 | version = "1.1.2" | ||
245 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
246 | checksum = "b92384b39aedb258aa734fe0e7b2ffcd13f33e68227251a72cd2635e0acc8f1a" | ||
247 | dependencies = [ | ||
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]] | ||
271 | name = "aws-smithy-async" | ||
272 | version = "1.1.3" | ||
273 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
274 | checksum = "2eac0bb78e9e2765699999a02d7bfb4e6ad8f13e0962ebb9f5202b1d8cd76006" | ||
275 | dependencies = [ | ||
276 | "futures-util", | ||
277 | "pin-project-lite", | ||
278 | "tokio", | ||
279 | ] | ||
280 | |||
281 | [[package]] | ||
282 | name = "aws-smithy-checksums" | ||
283 | version = "0.60.3" | ||
284 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
285 | checksum = "535a2d5f1e459bc7709580a77152c8d493982db083236c2b1d1c51dc6217e8a3" | ||
286 | dependencies = [ | ||
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]] | ||
303 | name = "aws-smithy-eventstream" | ||
304 | version = "0.60.3" | ||
305 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
306 | checksum = "682371561562d08ab437766903c6bc28f4f95d7ab2ecfb389bda7849dd98aefe" | ||
307 | dependencies = [ | ||
308 | "aws-smithy-types", | ||
309 | "bytes", | ||
310 | "crc32fast", | ||
311 | ] | ||
312 | |||
313 | [[package]] | ||
314 | name = "aws-smithy-http" | ||
315 | version = "0.60.3" | ||
316 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
317 | checksum = "365ca49744b2bda2f1e2dc03b856da3fa5a28ca5b0a41e41d7ff5305a8fae190" | ||
318 | dependencies = [ | ||
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]] | ||
335 | name = "aws-smithy-json" | ||
336 | version = "0.60.3" | ||
337 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
338 | checksum = "733ccdb727ac63370836aa3b3c483d75ad2ef7bc6507db3efe1d01e8d2e50367" | ||
339 | dependencies = [ | ||
340 | "aws-smithy-types", | ||
341 | ] | ||
342 | |||
343 | [[package]] | ||
344 | name = "aws-smithy-query" | ||
345 | version = "0.60.3" | ||
346 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
347 | checksum = "aff02ae2ee7968bbce2983ffb5ce529d24f4848532300f398347bde8c2196974" | ||
348 | dependencies = [ | ||
349 | "aws-smithy-types", | ||
350 | "urlencoding", | ||
351 | ] | ||
352 | |||
353 | [[package]] | ||
354 | name = "aws-smithy-runtime" | ||
355 | version = "1.1.3" | ||
356 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
357 | checksum = "6ab9cb6fee50680af8ceaa293ae79eba32095ca117161cb323f9ee30dd87d139" | ||
358 | dependencies = [ | ||
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]] | ||
379 | name = "aws-smithy-runtime-api" | ||
380 | version = "1.1.3" | ||
381 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
382 | checksum = "02ca2da7619517310bfead6d18abcdde90f1439224d887d608503cfacff46dff" | ||
383 | dependencies = [ | ||
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]] | ||
395 | name = "aws-smithy-types" | ||
396 | version = "1.1.3" | ||
397 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
398 | checksum = "5d4bb944488536cd2fef43212d829bc7e9a8bfc4afa079d21170441e7be8d2d0" | ||
399 | dependencies = [ | ||
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]] | ||
418 | name = "aws-smithy-xml" | ||
419 | version = "0.60.3" | ||
420 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
421 | checksum = "ef796feaf894d7fd03869235237aeffe73ed1b29a3927cceeee2eecadf876eba" | ||
422 | dependencies = [ | ||
423 | "xmlparser", | ||
424 | ] | ||
425 | |||
426 | [[package]] | ||
427 | name = "aws-types" | ||
428 | version = "1.1.2" | ||
429 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
430 | checksum = "8549aa62c5b7db5c57ab915200ee214b4f5d8f19b29a4a8fa0b3ad3bca1380e3" | ||
431 | dependencies = [ | ||
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]] | ||
442 | name = "axum" | ||
443 | version = "0.7.4" | ||
444 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
445 | checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" | ||
446 | dependencies = [ | ||
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]] | ||
476 | name = "axum-core" | ||
477 | version = "0.4.3" | ||
478 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
479 | checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" | ||
480 | dependencies = [ | ||
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]] | ||
497 | name = "backtrace" | ||
498 | version = "0.3.69" | ||
499 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
500 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" | ||
501 | dependencies = [ | ||
502 | "addr2line", | ||
503 | "cc", | ||
504 | "cfg-if", | ||
505 | "libc", | ||
506 | "miniz_oxide", | ||
507 | "object", | ||
508 | "rustc-demangle", | ||
509 | ] | ||
510 | |||
511 | [[package]] | ||
512 | name = "base16ct" | ||
513 | version = "0.1.1" | ||
514 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
515 | checksum = "349a06037c7bf932dd7e7d1f653678b2038b9ad46a74102f1fc7bd7872678cce" | ||
516 | |||
517 | [[package]] | ||
518 | name = "base64" | ||
519 | version = "0.21.7" | ||
520 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
521 | checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" | ||
522 | |||
523 | [[package]] | ||
524 | name = "base64-simd" | ||
525 | version = "0.8.0" | ||
526 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
527 | checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" | ||
528 | dependencies = [ | ||
529 | "outref", | ||
530 | "vsimd", | ||
531 | ] | ||
532 | |||
533 | [[package]] | ||
534 | name = "base64ct" | ||
535 | version = "1.6.0" | ||
536 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
537 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" | ||
538 | |||
539 | [[package]] | ||
540 | name = "bitflags" | ||
541 | version = "1.3.2" | ||
542 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
543 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
544 | |||
545 | [[package]] | ||
546 | name = "block-buffer" | ||
547 | version = "0.10.4" | ||
548 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
549 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" | ||
550 | dependencies = [ | ||
551 | "generic-array", | ||
552 | ] | ||
553 | |||
554 | [[package]] | ||
555 | name = "bumpalo" | ||
556 | version = "3.14.0" | ||
557 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
558 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" | ||
559 | |||
560 | [[package]] | ||
561 | name = "bytes" | ||
562 | version = "1.5.0" | ||
563 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
564 | checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" | ||
565 | |||
566 | [[package]] | ||
567 | name = "bytes-utils" | ||
568 | version = "0.1.4" | ||
569 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
570 | checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" | ||
571 | dependencies = [ | ||
572 | "bytes", | ||
573 | "either", | ||
574 | ] | ||
575 | |||
576 | [[package]] | ||
577 | name = "cc" | ||
578 | version = "1.0.83" | ||
579 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
580 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" | ||
581 | dependencies = [ | ||
582 | "libc", | ||
583 | ] | ||
584 | |||
585 | [[package]] | ||
586 | name = "cfg-if" | ||
587 | version = "1.0.0" | ||
588 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
589 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||
590 | |||
591 | [[package]] | ||
592 | name = "chrono" | ||
593 | version = "0.4.31" | ||
594 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
595 | checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" | ||
596 | dependencies = [ | ||
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]] | ||
607 | name = "common" | ||
608 | version = "0.1.0" | ||
609 | dependencies = [ | ||
610 | "chrono", | ||
611 | "hmac-sha256", | ||
612 | "serde", | ||
613 | "subtle", | ||
614 | ] | ||
615 | |||
616 | [[package]] | ||
617 | name = "const-oid" | ||
618 | version = "0.9.6" | ||
619 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
620 | checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" | ||
621 | |||
622 | [[package]] | ||
623 | name = "core-foundation" | ||
624 | version = "0.9.4" | ||
625 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
626 | checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" | ||
627 | dependencies = [ | ||
628 | "core-foundation-sys", | ||
629 | "libc", | ||
630 | ] | ||
631 | |||
632 | [[package]] | ||
633 | name = "core-foundation-sys" | ||
634 | version = "0.8.6" | ||
635 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
636 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" | ||
637 | |||
638 | [[package]] | ||
639 | name = "cpufeatures" | ||
640 | version = "0.2.12" | ||
641 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
642 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" | ||
643 | dependencies = [ | ||
644 | "libc", | ||
645 | ] | ||
646 | |||
647 | [[package]] | ||
648 | name = "crc32c" | ||
649 | version = "0.6.4" | ||
650 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
651 | checksum = "d8f48d60e5b4d2c53d5c2b1d8a58c849a70ae5e5509b08a48d047e3b65714a74" | ||
652 | dependencies = [ | ||
653 | "rustc_version", | ||
654 | ] | ||
655 | |||
656 | [[package]] | ||
657 | name = "crc32fast" | ||
658 | version = "1.3.2" | ||
659 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
660 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" | ||
661 | dependencies = [ | ||
662 | "cfg-if", | ||
663 | ] | ||
664 | |||
665 | [[package]] | ||
666 | name = "crypto-bigint" | ||
667 | version = "0.4.9" | ||
668 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
669 | checksum = "ef2b4b23cddf68b89b8f8069890e8c270d54e2d5fe1b143820234805e4cb17ef" | ||
670 | dependencies = [ | ||
671 | "generic-array", | ||
672 | "rand_core", | ||
673 | "subtle", | ||
674 | "zeroize", | ||
675 | ] | ||
676 | |||
677 | [[package]] | ||
678 | name = "crypto-bigint" | ||
679 | version = "0.5.5" | ||
680 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
681 | checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" | ||
682 | dependencies = [ | ||
683 | "rand_core", | ||
684 | "subtle", | ||
685 | ] | ||
686 | |||
687 | [[package]] | ||
688 | name = "crypto-common" | ||
689 | version = "0.1.6" | ||
690 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
691 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" | ||
692 | dependencies = [ | ||
693 | "generic-array", | ||
694 | "typenum", | ||
695 | ] | ||
696 | |||
697 | [[package]] | ||
698 | name = "der" | ||
699 | version = "0.6.1" | ||
700 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
701 | checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de" | ||
702 | dependencies = [ | ||
703 | "const-oid", | ||
704 | "zeroize", | ||
705 | ] | ||
706 | |||
707 | [[package]] | ||
708 | name = "deranged" | ||
709 | version = "0.3.11" | ||
710 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
711 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" | ||
712 | dependencies = [ | ||
713 | "powerfmt", | ||
714 | ] | ||
715 | |||
716 | [[package]] | ||
717 | name = "digest" | ||
718 | version = "0.10.7" | ||
719 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
720 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" | ||
721 | dependencies = [ | ||
722 | "block-buffer", | ||
723 | "crypto-common", | ||
724 | "subtle", | ||
725 | ] | ||
726 | |||
727 | [[package]] | ||
728 | name = "ecdsa" | ||
729 | version = "0.14.8" | ||
730 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
731 | checksum = "413301934810f597c1d19ca71c8710e99a3f1ba28a0d2ebc01551a2daeea3c5c" | ||
732 | dependencies = [ | ||
733 | "der", | ||
734 | "elliptic-curve", | ||
735 | "rfc6979", | ||
736 | "signature", | ||
737 | ] | ||
738 | |||
739 | [[package]] | ||
740 | name = "either" | ||
741 | version = "1.9.0" | ||
742 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
743 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" | ||
744 | |||
745 | [[package]] | ||
746 | name = "elliptic-curve" | ||
747 | version = "0.12.3" | ||
748 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
749 | checksum = "e7bb888ab5300a19b8e5bceef25ac745ad065f3c9f7efc6de1b91958110891d3" | ||
750 | dependencies = [ | ||
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]] | ||
766 | name = "equivalent" | ||
767 | version = "1.0.1" | ||
768 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
769 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||
770 | |||
771 | [[package]] | ||
772 | name = "fastrand" | ||
773 | version = "2.0.1" | ||
774 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
775 | checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" | ||
776 | |||
777 | [[package]] | ||
778 | name = "ff" | ||
779 | version = "0.12.1" | ||
780 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
781 | checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" | ||
782 | dependencies = [ | ||
783 | "rand_core", | ||
784 | "subtle", | ||
785 | ] | ||
786 | |||
787 | [[package]] | ||
788 | name = "fnv" | ||
789 | version = "1.0.7" | ||
790 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
791 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" | ||
792 | |||
793 | [[package]] | ||
794 | name = "form_urlencoded" | ||
795 | version = "1.2.1" | ||
796 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
797 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" | ||
798 | dependencies = [ | ||
799 | "percent-encoding", | ||
800 | ] | ||
801 | |||
802 | [[package]] | ||
803 | name = "futures-channel" | ||
804 | version = "0.3.30" | ||
805 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
806 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" | ||
807 | dependencies = [ | ||
808 | "futures-core", | ||
809 | ] | ||
810 | |||
811 | [[package]] | ||
812 | name = "futures-core" | ||
813 | version = "0.3.30" | ||
814 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
815 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" | ||
816 | |||
817 | [[package]] | ||
818 | name = "futures-sink" | ||
819 | version = "0.3.30" | ||
820 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
821 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" | ||
822 | |||
823 | [[package]] | ||
824 | name = "futures-task" | ||
825 | version = "0.3.30" | ||
826 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
827 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" | ||
828 | |||
829 | [[package]] | ||
830 | name = "futures-util" | ||
831 | version = "0.3.30" | ||
832 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
833 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" | ||
834 | dependencies = [ | ||
835 | "futures-core", | ||
836 | "futures-task", | ||
837 | "pin-project-lite", | ||
838 | "pin-utils", | ||
839 | ] | ||
840 | |||
841 | [[package]] | ||
842 | name = "generic-array" | ||
843 | version = "0.14.7" | ||
844 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
845 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" | ||
846 | dependencies = [ | ||
847 | "typenum", | ||
848 | "version_check", | ||
849 | ] | ||
850 | |||
851 | [[package]] | ||
852 | name = "getrandom" | ||
853 | version = "0.2.12" | ||
854 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
855 | checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" | ||
856 | dependencies = [ | ||
857 | "cfg-if", | ||
858 | "libc", | ||
859 | "wasi", | ||
860 | ] | ||
861 | |||
862 | [[package]] | ||
863 | name = "gimli" | ||
864 | version = "0.28.1" | ||
865 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
866 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" | ||
867 | |||
868 | [[package]] | ||
869 | name = "git-lfs-authenticate" | ||
870 | version = "0.1.0" | ||
871 | dependencies = [ | ||
872 | "chrono", | ||
873 | "common", | ||
874 | ] | ||
875 | |||
876 | [[package]] | ||
877 | name = "group" | ||
878 | version = "0.12.1" | ||
879 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
880 | checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" | ||
881 | dependencies = [ | ||
882 | "ff", | ||
883 | "rand_core", | ||
884 | "subtle", | ||
885 | ] | ||
886 | |||
887 | [[package]] | ||
888 | name = "h2" | ||
889 | version = "0.3.24" | ||
890 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
891 | checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" | ||
892 | dependencies = [ | ||
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]] | ||
907 | name = "h2" | ||
908 | version = "0.4.2" | ||
909 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
910 | checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" | ||
911 | dependencies = [ | ||
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]] | ||
926 | name = "hashbrown" | ||
927 | version = "0.14.3" | ||
928 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
929 | checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" | ||
930 | |||
931 | [[package]] | ||
932 | name = "hermit-abi" | ||
933 | version = "0.3.3" | ||
934 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
935 | checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" | ||
936 | |||
937 | [[package]] | ||
938 | name = "hex" | ||
939 | version = "0.4.3" | ||
940 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
941 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" | ||
942 | |||
943 | [[package]] | ||
944 | name = "hmac" | ||
945 | version = "0.12.1" | ||
946 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
947 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" | ||
948 | dependencies = [ | ||
949 | "digest", | ||
950 | ] | ||
951 | |||
952 | [[package]] | ||
953 | name = "hmac-sha256" | ||
954 | version = "1.1.7" | ||
955 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
956 | checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735" | ||
957 | |||
958 | [[package]] | ||
959 | name = "http" | ||
960 | version = "0.2.11" | ||
961 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
962 | checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" | ||
963 | dependencies = [ | ||
964 | "bytes", | ||
965 | "fnv", | ||
966 | "itoa", | ||
967 | ] | ||
968 | |||
969 | [[package]] | ||
970 | name = "http" | ||
971 | version = "1.0.0" | ||
972 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
973 | checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" | ||
974 | dependencies = [ | ||
975 | "bytes", | ||
976 | "fnv", | ||
977 | "itoa", | ||
978 | ] | ||
979 | |||
980 | [[package]] | ||
981 | name = "http-body" | ||
982 | version = "0.4.6" | ||
983 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
984 | checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" | ||
985 | dependencies = [ | ||
986 | "bytes", | ||
987 | "http 0.2.11", | ||
988 | "pin-project-lite", | ||
989 | ] | ||
990 | |||
991 | [[package]] | ||
992 | name = "http-body" | ||
993 | version = "1.0.0" | ||
994 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
995 | checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" | ||
996 | dependencies = [ | ||
997 | "bytes", | ||
998 | "http 1.0.0", | ||
999 | ] | ||
1000 | |||
1001 | [[package]] | ||
1002 | name = "http-body-util" | ||
1003 | version = "0.1.0" | ||
1004 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1005 | checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" | ||
1006 | dependencies = [ | ||
1007 | "bytes", | ||
1008 | "futures-util", | ||
1009 | "http 1.0.0", | ||
1010 | "http-body 1.0.0", | ||
1011 | "pin-project-lite", | ||
1012 | ] | ||
1013 | |||
1014 | [[package]] | ||
1015 | name = "httparse" | ||
1016 | version = "1.8.0" | ||
1017 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1018 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" | ||
1019 | |||
1020 | [[package]] | ||
1021 | name = "httpdate" | ||
1022 | version = "1.0.3" | ||
1023 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1024 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" | ||
1025 | |||
1026 | [[package]] | ||
1027 | name = "hyper" | ||
1028 | version = "0.14.28" | ||
1029 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1030 | checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" | ||
1031 | dependencies = [ | ||
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]] | ||
1051 | name = "hyper" | ||
1052 | version = "1.1.0" | ||
1053 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1054 | checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" | ||
1055 | dependencies = [ | ||
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]] | ||
1070 | name = "hyper-rustls" | ||
1071 | version = "0.24.2" | ||
1072 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1073 | checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" | ||
1074 | dependencies = [ | ||
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]] | ||
1086 | name = "hyper-util" | ||
1087 | version = "0.1.2" | ||
1088 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1089 | checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" | ||
1090 | dependencies = [ | ||
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]] | ||
1104 | name = "iana-time-zone" | ||
1105 | version = "0.1.59" | ||
1106 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1107 | checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" | ||
1108 | dependencies = [ | ||
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]] | ||
1118 | name = "iana-time-zone-haiku" | ||
1119 | version = "0.1.2" | ||
1120 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1121 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" | ||
1122 | dependencies = [ | ||
1123 | "cc", | ||
1124 | ] | ||
1125 | |||
1126 | [[package]] | ||
1127 | name = "idna" | ||
1128 | version = "0.5.0" | ||
1129 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1130 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" | ||
1131 | dependencies = [ | ||
1132 | "unicode-bidi", | ||
1133 | "unicode-normalization", | ||
1134 | ] | ||
1135 | |||
1136 | [[package]] | ||
1137 | name = "indexmap" | ||
1138 | version = "2.1.0" | ||
1139 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1140 | checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" | ||
1141 | dependencies = [ | ||
1142 | "equivalent", | ||
1143 | "hashbrown", | ||
1144 | ] | ||
1145 | |||
1146 | [[package]] | ||
1147 | name = "itoa" | ||
1148 | version = "1.0.10" | ||
1149 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1150 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" | ||
1151 | |||
1152 | [[package]] | ||
1153 | name = "js-sys" | ||
1154 | version = "0.3.67" | ||
1155 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1156 | checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" | ||
1157 | dependencies = [ | ||
1158 | "wasm-bindgen", | ||
1159 | ] | ||
1160 | |||
1161 | [[package]] | ||
1162 | name = "lazy_static" | ||
1163 | version = "1.4.0" | ||
1164 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1165 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||
1166 | |||
1167 | [[package]] | ||
1168 | name = "libc" | ||
1169 | version = "0.2.152" | ||
1170 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1171 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" | ||
1172 | |||
1173 | [[package]] | ||
1174 | name = "lock_api" | ||
1175 | version = "0.4.11" | ||
1176 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1177 | checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" | ||
1178 | dependencies = [ | ||
1179 | "autocfg", | ||
1180 | "scopeguard", | ||
1181 | ] | ||
1182 | |||
1183 | [[package]] | ||
1184 | name = "log" | ||
1185 | version = "0.4.20" | ||
1186 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1187 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" | ||
1188 | |||
1189 | [[package]] | ||
1190 | name = "matchers" | ||
1191 | version = "0.1.0" | ||
1192 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1193 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" | ||
1194 | dependencies = [ | ||
1195 | "regex-automata 0.1.10", | ||
1196 | ] | ||
1197 | |||
1198 | [[package]] | ||
1199 | name = "matchit" | ||
1200 | version = "0.7.3" | ||
1201 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1202 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" | ||
1203 | |||
1204 | [[package]] | ||
1205 | name = "md-5" | ||
1206 | version = "0.10.6" | ||
1207 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1208 | checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" | ||
1209 | dependencies = [ | ||
1210 | "cfg-if", | ||
1211 | "digest", | ||
1212 | ] | ||
1213 | |||
1214 | [[package]] | ||
1215 | name = "memchr" | ||
1216 | version = "2.7.1" | ||
1217 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1218 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" | ||
1219 | |||
1220 | [[package]] | ||
1221 | name = "mime" | ||
1222 | version = "0.3.17" | ||
1223 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1224 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" | ||
1225 | |||
1226 | [[package]] | ||
1227 | name = "miniz_oxide" | ||
1228 | version = "0.7.1" | ||
1229 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1230 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" | ||
1231 | dependencies = [ | ||
1232 | "adler", | ||
1233 | ] | ||
1234 | |||
1235 | [[package]] | ||
1236 | name = "mio" | ||
1237 | version = "0.8.10" | ||
1238 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1239 | checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" | ||
1240 | dependencies = [ | ||
1241 | "libc", | ||
1242 | "wasi", | ||
1243 | "windows-sys 0.48.0", | ||
1244 | ] | ||
1245 | |||
1246 | [[package]] | ||
1247 | name = "nu-ansi-term" | ||
1248 | version = "0.46.0" | ||
1249 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1250 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" | ||
1251 | dependencies = [ | ||
1252 | "overload", | ||
1253 | "winapi", | ||
1254 | ] | ||
1255 | |||
1256 | [[package]] | ||
1257 | name = "num-integer" | ||
1258 | version = "0.1.45" | ||
1259 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1260 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" | ||
1261 | dependencies = [ | ||
1262 | "autocfg", | ||
1263 | "num-traits", | ||
1264 | ] | ||
1265 | |||
1266 | [[package]] | ||
1267 | name = "num-traits" | ||
1268 | version = "0.2.17" | ||
1269 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1270 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" | ||
1271 | dependencies = [ | ||
1272 | "autocfg", | ||
1273 | ] | ||
1274 | |||
1275 | [[package]] | ||
1276 | name = "num_cpus" | ||
1277 | version = "1.16.0" | ||
1278 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1279 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" | ||
1280 | dependencies = [ | ||
1281 | "hermit-abi", | ||
1282 | "libc", | ||
1283 | ] | ||
1284 | |||
1285 | [[package]] | ||
1286 | name = "object" | ||
1287 | version = "0.32.2" | ||
1288 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1289 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" | ||
1290 | dependencies = [ | ||
1291 | "memchr", | ||
1292 | ] | ||
1293 | |||
1294 | [[package]] | ||
1295 | name = "once_cell" | ||
1296 | version = "1.19.0" | ||
1297 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1298 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" | ||
1299 | |||
1300 | [[package]] | ||
1301 | name = "openssl-probe" | ||
1302 | version = "0.1.5" | ||
1303 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1304 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" | ||
1305 | |||
1306 | [[package]] | ||
1307 | name = "outref" | ||
1308 | version = "0.5.1" | ||
1309 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1310 | checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" | ||
1311 | |||
1312 | [[package]] | ||
1313 | name = "overload" | ||
1314 | version = "0.1.1" | ||
1315 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1316 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" | ||
1317 | |||
1318 | [[package]] | ||
1319 | name = "p256" | ||
1320 | version = "0.11.1" | ||
1321 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1322 | checksum = "51f44edd08f51e2ade572f141051021c5af22677e42b7dd28a88155151c33594" | ||
1323 | dependencies = [ | ||
1324 | "ecdsa", | ||
1325 | "elliptic-curve", | ||
1326 | "sha2", | ||
1327 | ] | ||
1328 | |||
1329 | [[package]] | ||
1330 | name = "parking_lot" | ||
1331 | version = "0.12.1" | ||
1332 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1333 | checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" | ||
1334 | dependencies = [ | ||
1335 | "lock_api", | ||
1336 | "parking_lot_core", | ||
1337 | ] | ||
1338 | |||
1339 | [[package]] | ||
1340 | name = "parking_lot_core" | ||
1341 | version = "0.9.9" | ||
1342 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1343 | checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" | ||
1344 | dependencies = [ | ||
1345 | "cfg-if", | ||
1346 | "libc", | ||
1347 | "redox_syscall", | ||
1348 | "smallvec", | ||
1349 | "windows-targets 0.48.5", | ||
1350 | ] | ||
1351 | |||
1352 | [[package]] | ||
1353 | name = "percent-encoding" | ||
1354 | version = "2.3.1" | ||
1355 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1356 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" | ||
1357 | |||
1358 | [[package]] | ||
1359 | name = "pin-project" | ||
1360 | version = "1.1.3" | ||
1361 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1362 | checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" | ||
1363 | dependencies = [ | ||
1364 | "pin-project-internal", | ||
1365 | ] | ||
1366 | |||
1367 | [[package]] | ||
1368 | name = "pin-project-internal" | ||
1369 | version = "1.1.3" | ||
1370 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1371 | checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" | ||
1372 | dependencies = [ | ||
1373 | "proc-macro2", | ||
1374 | "quote", | ||
1375 | "syn", | ||
1376 | ] | ||
1377 | |||
1378 | [[package]] | ||
1379 | name = "pin-project-lite" | ||
1380 | version = "0.2.13" | ||
1381 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1382 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" | ||
1383 | |||
1384 | [[package]] | ||
1385 | name = "pin-utils" | ||
1386 | version = "0.1.0" | ||
1387 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1388 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" | ||
1389 | |||
1390 | [[package]] | ||
1391 | name = "pkcs8" | ||
1392 | version = "0.9.0" | ||
1393 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1394 | checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba" | ||
1395 | dependencies = [ | ||
1396 | "der", | ||
1397 | "spki", | ||
1398 | ] | ||
1399 | |||
1400 | [[package]] | ||
1401 | name = "powerfmt" | ||
1402 | version = "0.2.0" | ||
1403 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1404 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" | ||
1405 | |||
1406 | [[package]] | ||
1407 | name = "proc-macro2" | ||
1408 | version = "1.0.76" | ||
1409 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1410 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" | ||
1411 | dependencies = [ | ||
1412 | "unicode-ident", | ||
1413 | ] | ||
1414 | |||
1415 | [[package]] | ||
1416 | name = "quote" | ||
1417 | version = "1.0.35" | ||
1418 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1419 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" | ||
1420 | dependencies = [ | ||
1421 | "proc-macro2", | ||
1422 | ] | ||
1423 | |||
1424 | [[package]] | ||
1425 | name = "rand_core" | ||
1426 | version = "0.6.4" | ||
1427 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1428 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" | ||
1429 | dependencies = [ | ||
1430 | "getrandom", | ||
1431 | ] | ||
1432 | |||
1433 | [[package]] | ||
1434 | name = "redox_syscall" | ||
1435 | version = "0.4.1" | ||
1436 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1437 | checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" | ||
1438 | dependencies = [ | ||
1439 | "bitflags", | ||
1440 | ] | ||
1441 | |||
1442 | [[package]] | ||
1443 | name = "regex" | ||
1444 | version = "1.10.3" | ||
1445 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1446 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" | ||
1447 | dependencies = [ | ||
1448 | "aho-corasick", | ||
1449 | "memchr", | ||
1450 | "regex-automata 0.4.4", | ||
1451 | "regex-syntax 0.8.2", | ||
1452 | ] | ||
1453 | |||
1454 | [[package]] | ||
1455 | name = "regex-automata" | ||
1456 | version = "0.1.10" | ||
1457 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1458 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" | ||
1459 | dependencies = [ | ||
1460 | "regex-syntax 0.6.29", | ||
1461 | ] | ||
1462 | |||
1463 | [[package]] | ||
1464 | name = "regex-automata" | ||
1465 | version = "0.4.4" | ||
1466 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1467 | checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" | ||
1468 | dependencies = [ | ||
1469 | "aho-corasick", | ||
1470 | "memchr", | ||
1471 | "regex-syntax 0.8.2", | ||
1472 | ] | ||
1473 | |||
1474 | [[package]] | ||
1475 | name = "regex-lite" | ||
1476 | version = "0.1.5" | ||
1477 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1478 | checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" | ||
1479 | |||
1480 | [[package]] | ||
1481 | name = "regex-syntax" | ||
1482 | version = "0.6.29" | ||
1483 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1484 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" | ||
1485 | |||
1486 | [[package]] | ||
1487 | name = "regex-syntax" | ||
1488 | version = "0.8.2" | ||
1489 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1490 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" | ||
1491 | |||
1492 | [[package]] | ||
1493 | name = "rfc6979" | ||
1494 | version = "0.3.1" | ||
1495 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1496 | checksum = "7743f17af12fa0b03b803ba12cd6a8d9483a587e89c69445e3909655c0b9fabb" | ||
1497 | dependencies = [ | ||
1498 | "crypto-bigint 0.4.9", | ||
1499 | "hmac", | ||
1500 | "zeroize", | ||
1501 | ] | ||
1502 | |||
1503 | [[package]] | ||
1504 | name = "ring" | ||
1505 | version = "0.17.7" | ||
1506 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1507 | checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" | ||
1508 | dependencies = [ | ||
1509 | "cc", | ||
1510 | "getrandom", | ||
1511 | "libc", | ||
1512 | "spin", | ||
1513 | "untrusted", | ||
1514 | "windows-sys 0.48.0", | ||
1515 | ] | ||
1516 | |||
1517 | [[package]] | ||
1518 | name = "rustc-demangle" | ||
1519 | version = "0.1.23" | ||
1520 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1521 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" | ||
1522 | |||
1523 | [[package]] | ||
1524 | name = "rustc_version" | ||
1525 | version = "0.4.0" | ||
1526 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1527 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" | ||
1528 | dependencies = [ | ||
1529 | "semver", | ||
1530 | ] | ||
1531 | |||
1532 | [[package]] | ||
1533 | name = "rustls" | ||
1534 | version = "0.21.10" | ||
1535 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1536 | checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" | ||
1537 | dependencies = [ | ||
1538 | "log", | ||
1539 | "ring", | ||
1540 | "rustls-webpki", | ||
1541 | "sct", | ||
1542 | ] | ||
1543 | |||
1544 | [[package]] | ||
1545 | name = "rustls-native-certs" | ||
1546 | version = "0.6.3" | ||
1547 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1548 | checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" | ||
1549 | dependencies = [ | ||
1550 | "openssl-probe", | ||
1551 | "rustls-pemfile", | ||
1552 | "schannel", | ||
1553 | "security-framework", | ||
1554 | ] | ||
1555 | |||
1556 | [[package]] | ||
1557 | name = "rustls-pemfile" | ||
1558 | version = "1.0.4" | ||
1559 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1560 | checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" | ||
1561 | dependencies = [ | ||
1562 | "base64", | ||
1563 | ] | ||
1564 | |||
1565 | [[package]] | ||
1566 | name = "rustls-webpki" | ||
1567 | version = "0.101.7" | ||
1568 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1569 | checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" | ||
1570 | dependencies = [ | ||
1571 | "ring", | ||
1572 | "untrusted", | ||
1573 | ] | ||
1574 | |||
1575 | [[package]] | ||
1576 | name = "rustversion" | ||
1577 | version = "1.0.14" | ||
1578 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1579 | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" | ||
1580 | |||
1581 | [[package]] | ||
1582 | name = "ryu" | ||
1583 | version = "1.0.16" | ||
1584 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1585 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" | ||
1586 | |||
1587 | [[package]] | ||
1588 | name = "schannel" | ||
1589 | version = "0.1.23" | ||
1590 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1591 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" | ||
1592 | dependencies = [ | ||
1593 | "windows-sys 0.52.0", | ||
1594 | ] | ||
1595 | |||
1596 | [[package]] | ||
1597 | name = "scopeguard" | ||
1598 | version = "1.2.0" | ||
1599 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1600 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" | ||
1601 | |||
1602 | [[package]] | ||
1603 | name = "sct" | ||
1604 | version = "0.7.1" | ||
1605 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1606 | checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" | ||
1607 | dependencies = [ | ||
1608 | "ring", | ||
1609 | "untrusted", | ||
1610 | ] | ||
1611 | |||
1612 | [[package]] | ||
1613 | name = "sec1" | ||
1614 | version = "0.3.0" | ||
1615 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1616 | checksum = "3be24c1842290c45df0a7bf069e0c268a747ad05a192f2fd7dcfdbc1cba40928" | ||
1617 | dependencies = [ | ||
1618 | "base16ct", | ||
1619 | "der", | ||
1620 | "generic-array", | ||
1621 | "pkcs8", | ||
1622 | "subtle", | ||
1623 | "zeroize", | ||
1624 | ] | ||
1625 | |||
1626 | [[package]] | ||
1627 | name = "security-framework" | ||
1628 | version = "2.9.2" | ||
1629 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1630 | checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" | ||
1631 | dependencies = [ | ||
1632 | "bitflags", | ||
1633 | "core-foundation", | ||
1634 | "core-foundation-sys", | ||
1635 | "libc", | ||
1636 | "security-framework-sys", | ||
1637 | ] | ||
1638 | |||
1639 | [[package]] | ||
1640 | name = "security-framework-sys" | ||
1641 | version = "2.9.1" | ||
1642 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1643 | checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" | ||
1644 | dependencies = [ | ||
1645 | "core-foundation-sys", | ||
1646 | "libc", | ||
1647 | ] | ||
1648 | |||
1649 | [[package]] | ||
1650 | name = "semver" | ||
1651 | version = "1.0.21" | ||
1652 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1653 | checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" | ||
1654 | |||
1655 | [[package]] | ||
1656 | name = "serde" | ||
1657 | version = "1.0.195" | ||
1658 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1659 | checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" | ||
1660 | dependencies = [ | ||
1661 | "serde_derive", | ||
1662 | ] | ||
1663 | |||
1664 | [[package]] | ||
1665 | name = "serde_derive" | ||
1666 | version = "1.0.195" | ||
1667 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1668 | checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" | ||
1669 | dependencies = [ | ||
1670 | "proc-macro2", | ||
1671 | "quote", | ||
1672 | "syn", | ||
1673 | ] | ||
1674 | |||
1675 | [[package]] | ||
1676 | name = "serde_json" | ||
1677 | version = "1.0.111" | ||
1678 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1679 | checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" | ||
1680 | dependencies = [ | ||
1681 | "itoa", | ||
1682 | "ryu", | ||
1683 | "serde", | ||
1684 | ] | ||
1685 | |||
1686 | [[package]] | ||
1687 | name = "serde_path_to_error" | ||
1688 | version = "0.1.15" | ||
1689 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1690 | checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" | ||
1691 | dependencies = [ | ||
1692 | "itoa", | ||
1693 | "serde", | ||
1694 | ] | ||
1695 | |||
1696 | [[package]] | ||
1697 | name = "serde_urlencoded" | ||
1698 | version = "0.7.1" | ||
1699 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1700 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" | ||
1701 | dependencies = [ | ||
1702 | "form_urlencoded", | ||
1703 | "itoa", | ||
1704 | "ryu", | ||
1705 | "serde", | ||
1706 | ] | ||
1707 | |||
1708 | [[package]] | ||
1709 | name = "server" | ||
1710 | version = "0.1.0" | ||
1711 | dependencies = [ | ||
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]] | ||
1728 | name = "sha1" | ||
1729 | version = "0.10.6" | ||
1730 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1731 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" | ||
1732 | dependencies = [ | ||
1733 | "cfg-if", | ||
1734 | "cpufeatures", | ||
1735 | "digest", | ||
1736 | ] | ||
1737 | |||
1738 | [[package]] | ||
1739 | name = "sha2" | ||
1740 | version = "0.10.8" | ||
1741 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1742 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" | ||
1743 | dependencies = [ | ||
1744 | "cfg-if", | ||
1745 | "cpufeatures", | ||
1746 | "digest", | ||
1747 | ] | ||
1748 | |||
1749 | [[package]] | ||
1750 | name = "sharded-slab" | ||
1751 | version = "0.1.7" | ||
1752 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1753 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" | ||
1754 | dependencies = [ | ||
1755 | "lazy_static", | ||
1756 | ] | ||
1757 | |||
1758 | [[package]] | ||
1759 | name = "shell" | ||
1760 | version = "0.1.0" | ||
1761 | |||
1762 | [[package]] | ||
1763 | name = "signal-hook-registry" | ||
1764 | version = "1.4.1" | ||
1765 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1766 | checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" | ||
1767 | dependencies = [ | ||
1768 | "libc", | ||
1769 | ] | ||
1770 | |||
1771 | [[package]] | ||
1772 | name = "signature" | ||
1773 | version = "1.6.4" | ||
1774 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1775 | checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" | ||
1776 | dependencies = [ | ||
1777 | "digest", | ||
1778 | "rand_core", | ||
1779 | ] | ||
1780 | |||
1781 | [[package]] | ||
1782 | name = "slab" | ||
1783 | version = "0.4.9" | ||
1784 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1785 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" | ||
1786 | dependencies = [ | ||
1787 | "autocfg", | ||
1788 | ] | ||
1789 | |||
1790 | [[package]] | ||
1791 | name = "smallvec" | ||
1792 | version = "1.11.2" | ||
1793 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1794 | checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" | ||
1795 | |||
1796 | [[package]] | ||
1797 | name = "socket2" | ||
1798 | version = "0.5.5" | ||
1799 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1800 | checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" | ||
1801 | dependencies = [ | ||
1802 | "libc", | ||
1803 | "windows-sys 0.48.0", | ||
1804 | ] | ||
1805 | |||
1806 | [[package]] | ||
1807 | name = "spin" | ||
1808 | version = "0.9.8" | ||
1809 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1810 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" | ||
1811 | |||
1812 | [[package]] | ||
1813 | name = "spki" | ||
1814 | version = "0.6.0" | ||
1815 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1816 | checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b" | ||
1817 | dependencies = [ | ||
1818 | "base64ct", | ||
1819 | "der", | ||
1820 | ] | ||
1821 | |||
1822 | [[package]] | ||
1823 | name = "subtle" | ||
1824 | version = "2.5.0" | ||
1825 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1826 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" | ||
1827 | |||
1828 | [[package]] | ||
1829 | name = "syn" | ||
1830 | version = "2.0.48" | ||
1831 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1832 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" | ||
1833 | dependencies = [ | ||
1834 | "proc-macro2", | ||
1835 | "quote", | ||
1836 | "unicode-ident", | ||
1837 | ] | ||
1838 | |||
1839 | [[package]] | ||
1840 | name = "sync_wrapper" | ||
1841 | version = "0.1.2" | ||
1842 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1843 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" | ||
1844 | |||
1845 | [[package]] | ||
1846 | name = "thread_local" | ||
1847 | version = "1.1.7" | ||
1848 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1849 | checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" | ||
1850 | dependencies = [ | ||
1851 | "cfg-if", | ||
1852 | "once_cell", | ||
1853 | ] | ||
1854 | |||
1855 | [[package]] | ||
1856 | name = "time" | ||
1857 | version = "0.3.31" | ||
1858 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1859 | checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" | ||
1860 | dependencies = [ | ||
1861 | "deranged", | ||
1862 | "powerfmt", | ||
1863 | "serde", | ||
1864 | "time-core", | ||
1865 | "time-macros", | ||
1866 | ] | ||
1867 | |||
1868 | [[package]] | ||
1869 | name = "time-core" | ||
1870 | version = "0.1.2" | ||
1871 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1872 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" | ||
1873 | |||
1874 | [[package]] | ||
1875 | name = "time-macros" | ||
1876 | version = "0.2.16" | ||
1877 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1878 | checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" | ||
1879 | dependencies = [ | ||
1880 | "time-core", | ||
1881 | ] | ||
1882 | |||
1883 | [[package]] | ||
1884 | name = "tinyvec" | ||
1885 | version = "1.6.0" | ||
1886 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1887 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" | ||
1888 | dependencies = [ | ||
1889 | "tinyvec_macros", | ||
1890 | ] | ||
1891 | |||
1892 | [[package]] | ||
1893 | name = "tinyvec_macros" | ||
1894 | version = "0.1.1" | ||
1895 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1896 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" | ||
1897 | |||
1898 | [[package]] | ||
1899 | name = "tokio" | ||
1900 | version = "1.35.1" | ||
1901 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1902 | checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" | ||
1903 | dependencies = [ | ||
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]] | ||
1918 | name = "tokio-macros" | ||
1919 | version = "2.2.0" | ||
1920 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1921 | checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" | ||
1922 | dependencies = [ | ||
1923 | "proc-macro2", | ||
1924 | "quote", | ||
1925 | "syn", | ||
1926 | ] | ||
1927 | |||
1928 | [[package]] | ||
1929 | name = "tokio-rustls" | ||
1930 | version = "0.24.1" | ||
1931 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1932 | checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" | ||
1933 | dependencies = [ | ||
1934 | "rustls", | ||
1935 | "tokio", | ||
1936 | ] | ||
1937 | |||
1938 | [[package]] | ||
1939 | name = "tokio-util" | ||
1940 | version = "0.7.10" | ||
1941 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1942 | checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" | ||
1943 | dependencies = [ | ||
1944 | "bytes", | ||
1945 | "futures-core", | ||
1946 | "futures-sink", | ||
1947 | "pin-project-lite", | ||
1948 | "tokio", | ||
1949 | "tracing", | ||
1950 | ] | ||
1951 | |||
1952 | [[package]] | ||
1953 | name = "tower" | ||
1954 | version = "0.4.13" | ||
1955 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1956 | checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" | ||
1957 | dependencies = [ | ||
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]] | ||
1969 | name = "tower-layer" | ||
1970 | version = "0.3.2" | ||
1971 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1972 | checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" | ||
1973 | |||
1974 | [[package]] | ||
1975 | name = "tower-service" | ||
1976 | version = "0.3.2" | ||
1977 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1978 | checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" | ||
1979 | |||
1980 | [[package]] | ||
1981 | name = "tracing" | ||
1982 | version = "0.1.40" | ||
1983 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1984 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" | ||
1985 | dependencies = [ | ||
1986 | "log", | ||
1987 | "pin-project-lite", | ||
1988 | "tracing-attributes", | ||
1989 | "tracing-core", | ||
1990 | ] | ||
1991 | |||
1992 | [[package]] | ||
1993 | name = "tracing-attributes" | ||
1994 | version = "0.1.27" | ||
1995 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1996 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" | ||
1997 | dependencies = [ | ||
1998 | "proc-macro2", | ||
1999 | "quote", | ||
2000 | "syn", | ||
2001 | ] | ||
2002 | |||
2003 | [[package]] | ||
2004 | name = "tracing-core" | ||
2005 | version = "0.1.32" | ||
2006 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2007 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" | ||
2008 | dependencies = [ | ||
2009 | "once_cell", | ||
2010 | "valuable", | ||
2011 | ] | ||
2012 | |||
2013 | [[package]] | ||
2014 | name = "tracing-log" | ||
2015 | version = "0.2.0" | ||
2016 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2017 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" | ||
2018 | dependencies = [ | ||
2019 | "log", | ||
2020 | "once_cell", | ||
2021 | "tracing-core", | ||
2022 | ] | ||
2023 | |||
2024 | [[package]] | ||
2025 | name = "tracing-subscriber" | ||
2026 | version = "0.3.18" | ||
2027 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2028 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" | ||
2029 | dependencies = [ | ||
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]] | ||
2043 | name = "try-lock" | ||
2044 | version = "0.2.5" | ||
2045 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2046 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" | ||
2047 | |||
2048 | [[package]] | ||
2049 | name = "typenum" | ||
2050 | version = "1.17.0" | ||
2051 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2052 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" | ||
2053 | |||
2054 | [[package]] | ||
2055 | name = "unicode-bidi" | ||
2056 | version = "0.3.15" | ||
2057 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2058 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" | ||
2059 | |||
2060 | [[package]] | ||
2061 | name = "unicode-ident" | ||
2062 | version = "1.0.12" | ||
2063 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2064 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" | ||
2065 | |||
2066 | [[package]] | ||
2067 | name = "unicode-normalization" | ||
2068 | version = "0.1.22" | ||
2069 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2070 | checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" | ||
2071 | dependencies = [ | ||
2072 | "tinyvec", | ||
2073 | ] | ||
2074 | |||
2075 | [[package]] | ||
2076 | name = "untrusted" | ||
2077 | version = "0.9.0" | ||
2078 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2079 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" | ||
2080 | |||
2081 | [[package]] | ||
2082 | name = "url" | ||
2083 | version = "2.5.0" | ||
2084 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2085 | checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" | ||
2086 | dependencies = [ | ||
2087 | "form_urlencoded", | ||
2088 | "idna", | ||
2089 | "percent-encoding", | ||
2090 | ] | ||
2091 | |||
2092 | [[package]] | ||
2093 | name = "urlencoding" | ||
2094 | version = "2.1.3" | ||
2095 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2096 | checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" | ||
2097 | |||
2098 | [[package]] | ||
2099 | name = "uuid" | ||
2100 | version = "1.6.1" | ||
2101 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2102 | checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" | ||
2103 | |||
2104 | [[package]] | ||
2105 | name = "valuable" | ||
2106 | version = "0.1.0" | ||
2107 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2108 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" | ||
2109 | |||
2110 | [[package]] | ||
2111 | name = "version_check" | ||
2112 | version = "0.9.4" | ||
2113 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2114 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" | ||
2115 | |||
2116 | [[package]] | ||
2117 | name = "vsimd" | ||
2118 | version = "0.8.0" | ||
2119 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2120 | checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" | ||
2121 | |||
2122 | [[package]] | ||
2123 | name = "want" | ||
2124 | version = "0.3.1" | ||
2125 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2126 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" | ||
2127 | dependencies = [ | ||
2128 | "try-lock", | ||
2129 | ] | ||
2130 | |||
2131 | [[package]] | ||
2132 | name = "wasi" | ||
2133 | version = "0.11.0+wasi-snapshot-preview1" | ||
2134 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2135 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||
2136 | |||
2137 | [[package]] | ||
2138 | name = "wasm-bindgen" | ||
2139 | version = "0.2.90" | ||
2140 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2141 | checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" | ||
2142 | dependencies = [ | ||
2143 | "cfg-if", | ||
2144 | "wasm-bindgen-macro", | ||
2145 | ] | ||
2146 | |||
2147 | [[package]] | ||
2148 | name = "wasm-bindgen-backend" | ||
2149 | version = "0.2.90" | ||
2150 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2151 | checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" | ||
2152 | dependencies = [ | ||
2153 | "bumpalo", | ||
2154 | "log", | ||
2155 | "once_cell", | ||
2156 | "proc-macro2", | ||
2157 | "quote", | ||
2158 | "syn", | ||
2159 | "wasm-bindgen-shared", | ||
2160 | ] | ||
2161 | |||
2162 | [[package]] | ||
2163 | name = "wasm-bindgen-macro" | ||
2164 | version = "0.2.90" | ||
2165 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2166 | checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" | ||
2167 | dependencies = [ | ||
2168 | "quote", | ||
2169 | "wasm-bindgen-macro-support", | ||
2170 | ] | ||
2171 | |||
2172 | [[package]] | ||
2173 | name = "wasm-bindgen-macro-support" | ||
2174 | version = "0.2.90" | ||
2175 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2176 | checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" | ||
2177 | dependencies = [ | ||
2178 | "proc-macro2", | ||
2179 | "quote", | ||
2180 | "syn", | ||
2181 | "wasm-bindgen-backend", | ||
2182 | "wasm-bindgen-shared", | ||
2183 | ] | ||
2184 | |||
2185 | [[package]] | ||
2186 | name = "wasm-bindgen-shared" | ||
2187 | version = "0.2.90" | ||
2188 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2189 | checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" | ||
2190 | |||
2191 | [[package]] | ||
2192 | name = "winapi" | ||
2193 | version = "0.3.9" | ||
2194 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2195 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
2196 | dependencies = [ | ||
2197 | "winapi-i686-pc-windows-gnu", | ||
2198 | "winapi-x86_64-pc-windows-gnu", | ||
2199 | ] | ||
2200 | |||
2201 | [[package]] | ||
2202 | name = "winapi-i686-pc-windows-gnu" | ||
2203 | version = "0.4.0" | ||
2204 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2205 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
2206 | |||
2207 | [[package]] | ||
2208 | name = "winapi-x86_64-pc-windows-gnu" | ||
2209 | version = "0.4.0" | ||
2210 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2211 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
2212 | |||
2213 | [[package]] | ||
2214 | name = "windows-core" | ||
2215 | version = "0.52.0" | ||
2216 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2217 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" | ||
2218 | dependencies = [ | ||
2219 | "windows-targets 0.52.0", | ||
2220 | ] | ||
2221 | |||
2222 | [[package]] | ||
2223 | name = "windows-sys" | ||
2224 | version = "0.48.0" | ||
2225 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2226 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||
2227 | dependencies = [ | ||
2228 | "windows-targets 0.48.5", | ||
2229 | ] | ||
2230 | |||
2231 | [[package]] | ||
2232 | name = "windows-sys" | ||
2233 | version = "0.52.0" | ||
2234 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2235 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | ||
2236 | dependencies = [ | ||
2237 | "windows-targets 0.52.0", | ||
2238 | ] | ||
2239 | |||
2240 | [[package]] | ||
2241 | name = "windows-targets" | ||
2242 | version = "0.48.5" | ||
2243 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2244 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | ||
2245 | dependencies = [ | ||
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]] | ||
2256 | name = "windows-targets" | ||
2257 | version = "0.52.0" | ||
2258 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2259 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" | ||
2260 | dependencies = [ | ||
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]] | ||
2271 | name = "windows_aarch64_gnullvm" | ||
2272 | version = "0.48.5" | ||
2273 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2274 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | ||
2275 | |||
2276 | [[package]] | ||
2277 | name = "windows_aarch64_gnullvm" | ||
2278 | version = "0.52.0" | ||
2279 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2280 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" | ||
2281 | |||
2282 | [[package]] | ||
2283 | name = "windows_aarch64_msvc" | ||
2284 | version = "0.48.5" | ||
2285 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2286 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | ||
2287 | |||
2288 | [[package]] | ||
2289 | name = "windows_aarch64_msvc" | ||
2290 | version = "0.52.0" | ||
2291 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2292 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" | ||
2293 | |||
2294 | [[package]] | ||
2295 | name = "windows_i686_gnu" | ||
2296 | version = "0.48.5" | ||
2297 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2298 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | ||
2299 | |||
2300 | [[package]] | ||
2301 | name = "windows_i686_gnu" | ||
2302 | version = "0.52.0" | ||
2303 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2304 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" | ||
2305 | |||
2306 | [[package]] | ||
2307 | name = "windows_i686_msvc" | ||
2308 | version = "0.48.5" | ||
2309 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2310 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | ||
2311 | |||
2312 | [[package]] | ||
2313 | name = "windows_i686_msvc" | ||
2314 | version = "0.52.0" | ||
2315 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2316 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" | ||
2317 | |||
2318 | [[package]] | ||
2319 | name = "windows_x86_64_gnu" | ||
2320 | version = "0.48.5" | ||
2321 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2322 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | ||
2323 | |||
2324 | [[package]] | ||
2325 | name = "windows_x86_64_gnu" | ||
2326 | version = "0.52.0" | ||
2327 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2328 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" | ||
2329 | |||
2330 | [[package]] | ||
2331 | name = "windows_x86_64_gnullvm" | ||
2332 | version = "0.48.5" | ||
2333 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2334 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | ||
2335 | |||
2336 | [[package]] | ||
2337 | name = "windows_x86_64_gnullvm" | ||
2338 | version = "0.52.0" | ||
2339 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2340 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" | ||
2341 | |||
2342 | [[package]] | ||
2343 | name = "windows_x86_64_msvc" | ||
2344 | version = "0.48.5" | ||
2345 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2346 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | ||
2347 | |||
2348 | [[package]] | ||
2349 | name = "windows_x86_64_msvc" | ||
2350 | version = "0.52.0" | ||
2351 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2352 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" | ||
2353 | |||
2354 | [[package]] | ||
2355 | name = "xmlparser" | ||
2356 | version = "0.13.6" | ||
2357 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2358 | checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" | ||
2359 | |||
2360 | [[package]] | ||
2361 | name = "zeroize" | ||
2362 | version = "1.7.0" | ||
2363 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
2364 | checksum = "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] | ||
2 | resolver = "2" | ||
3 | members = [ | ||
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] | ||
2 | name = "common" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | chrono = "0.4" | ||
8 | hmac-sha256 = "1.1" | ||
9 | subtle = "2.5" | ||
10 | serde = { 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 @@ | |||
1 | use chrono::{DateTime, Utc}; | ||
2 | use serde::de; | ||
3 | use serde::{Deserialize, Serialize}; | ||
4 | use std::fmt::Write; | ||
5 | use std::ops; | ||
6 | use std::{fmt, str::FromStr}; | ||
7 | use subtle::ConstantTimeEq; | ||
8 | |||
9 | #[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)] | ||
10 | #[repr(u8)] | ||
11 | pub 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)] | ||
19 | pub struct ParseOperationError; | ||
20 | |||
21 | impl 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 | |||
27 | impl 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)] | ||
40 | enum AuthType { | ||
41 | BatchApi = 1, | ||
42 | Download = 2, | ||
43 | } | ||
44 | |||
45 | /// None means out of range. | ||
46 | fn 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)] | ||
59 | pub struct HexByte(pub u8); | ||
60 | |||
61 | impl<'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 | |||
83 | impl 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)] | ||
91 | pub enum ParseHexError { | ||
92 | UnevenNibbles, | ||
93 | InvalidCharacter, | ||
94 | TooShort, | ||
95 | TooLong, | ||
96 | } | ||
97 | |||
98 | impl 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)] | ||
112 | pub enum ReadHexError { | ||
113 | Io(std::io::Error), | ||
114 | Format(ParseHexError), | ||
115 | } | ||
116 | |||
117 | impl 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 | |||
126 | fn 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 | |||
150 | pub struct SafeByteArray<const N: usize> { | ||
151 | inner: [u8; N], | ||
152 | } | ||
153 | |||
154 | impl<const N: usize> SafeByteArray<N> { | ||
155 | pub fn new() -> Self { | ||
156 | Self { inner: [0; N] } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | impl<const N: usize> Default for SafeByteArray<N> { | ||
161 | fn default() -> Self { | ||
162 | Self::new() | ||
163 | } | ||
164 | } | ||
165 | |||
166 | impl<const N: usize> AsRef<[u8]> for SafeByteArray<N> { | ||
167 | fn as_ref(&self) -> &[u8] { | ||
168 | &self.inner | ||
169 | } | ||
170 | } | ||
171 | |||
172 | impl<const N: usize> AsMut<[u8]> for SafeByteArray<N> { | ||
173 | fn as_mut(&mut self) -> &mut [u8] { | ||
174 | &mut self.inner | ||
175 | } | ||
176 | } | ||
177 | |||
178 | impl<const N: usize> Drop for SafeByteArray<N> { | ||
179 | fn drop(&mut self) { | ||
180 | self.inner.fill(0) | ||
181 | } | ||
182 | } | ||
183 | |||
184 | impl<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 | |||
194 | pub type Oid = Digest<32>; | ||
195 | |||
196 | #[derive(Debug, Copy, Clone)] | ||
197 | pub enum SpecificClaims { | ||
198 | BatchApi(Operation), | ||
199 | Download(Oid), | ||
200 | } | ||
201 | |||
202 | #[derive(Debug, Copy, Clone)] | ||
203 | pub 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. | ||
210 | pub 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 | |||
232 | pub struct HexFmt<B: AsRef<[u8]>>(pub B); | ||
233 | |||
234 | impl<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 | |||
256 | pub struct EscJsonFmt<'a>(pub &'a str); | ||
257 | |||
258 | impl<'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)] | ||
278 | pub struct Digest<const N: usize> { | ||
279 | inner: [u8; N], | ||
280 | } | ||
281 | |||
282 | impl<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 | |||
290 | impl<const N: usize> Digest<N> { | ||
291 | pub fn as_bytes(&self) -> &[u8; N] { | ||
292 | &self.inner | ||
293 | } | ||
294 | } | ||
295 | |||
296 | impl<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 | |||
302 | impl<const N: usize> Digest<N> { | ||
303 | pub fn new(data: [u8; N]) -> Self { | ||
304 | Self { inner: data } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | impl<const N: usize> From<[u8; N]> for Digest<N> { | ||
309 | fn from(value: [u8; N]) -> Self { | ||
310 | Self::new(value) | ||
311 | } | ||
312 | } | ||
313 | |||
314 | impl<const N: usize> From<Digest<N>> for [u8; N] { | ||
315 | fn from(val: Digest<N>) -> Self { | ||
316 | val.inner | ||
317 | } | ||
318 | } | ||
319 | |||
320 | impl<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 | |||
330 | impl<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 | |||
336 | impl<const N: usize> PartialEq for Digest<N> { | ||
337 | fn eq(&self, other: &Self) -> bool { | ||
338 | self.ct_eq(other).into() | ||
339 | } | ||
340 | } | ||
341 | |||
342 | impl<const N: usize> Eq for Digest<N> {} | ||
343 | |||
344 | impl<'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 | |||
354 | impl<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 | |||
363 | pub type Key = SafeByteArray<64>; | ||
364 | |||
365 | pub 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] | ||
2 | name = "git-lfs-authenticate" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | chrono = "0.4" | ||
8 | common = { 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 @@ | |||
1 | use std::{fmt, process::ExitCode, time::Duration}; | ||
2 | |||
3 | use chrono::Utc; | ||
4 | use common::{Operation, ParseOperationError}; | ||
5 | |||
6 | fn help() { | ||
7 | eprintln!("Usage: git-lfs-authenticate <REPO> upload/download"); | ||
8 | } | ||
9 | |||
10 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
11 | enum RepoNameError { | ||
12 | TooLong, | ||
13 | UnresolvedPath, | ||
14 | AbsolutePath, | ||
15 | MissingGitSuffix, | ||
16 | } | ||
17 | |||
18 | impl 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. | ||
38 | fn 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)] | ||
60 | enum ParseCmdlineError { | ||
61 | UnknownOperation(ParseOperationError), | ||
62 | InvalidRepoName(RepoNameError), | ||
63 | UnexpectedArgCount(ArgCountError), | ||
64 | } | ||
65 | |||
66 | impl From<RepoNameError> for ParseCmdlineError { | ||
67 | fn from(value: RepoNameError) -> Self { | ||
68 | Self::InvalidRepoName(value) | ||
69 | } | ||
70 | } | ||
71 | |||
72 | impl From<ParseOperationError> for ParseCmdlineError { | ||
73 | fn from(value: ParseOperationError) -> Self { | ||
74 | Self::UnknownOperation(value) | ||
75 | } | ||
76 | } | ||
77 | |||
78 | impl From<ArgCountError> for ParseCmdlineError { | ||
79 | fn from(value: ArgCountError) -> Self { | ||
80 | Self::UnexpectedArgCount(value) | ||
81 | } | ||
82 | } | ||
83 | |||
84 | impl 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)] | ||
95 | struct ArgCountError { | ||
96 | provided: usize, | ||
97 | expected: usize, | ||
98 | } | ||
99 | |||
100 | impl 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 | |||
110 | fn 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 | |||
130 | fn 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 | |||
137 | fn repo_exists(name: &str) -> bool { | ||
138 | match std::fs::metadata(name) { | ||
139 | Ok(metadata) => metadata.is_dir(), | ||
140 | _ => false, | ||
141 | } | ||
142 | } | ||
143 | |||
144 | struct Config { | ||
145 | href_base: String, | ||
146 | key_path: String, | ||
147 | } | ||
148 | |||
149 | #[derive(Debug, Eq, PartialEq, Copy, Clone)] | ||
150 | enum LoadConfigError { | ||
151 | BaseUrlNotProvided, | ||
152 | BaseUrlSlashSuffixMissing, | ||
153 | KeyPathNotProvided, | ||
154 | } | ||
155 | |||
156 | impl 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 | |||
166 | fn 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 | |||
182 | fn 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] | ||
2 | name = "server" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | |||
6 | [dependencies] | ||
7 | aws-config = { version = "1.1.2" } | ||
8 | aws-sdk-s3 = "1.12.0" | ||
9 | axum = "0.7" | ||
10 | base64 = "0.21" | ||
11 | chrono = { version = "0.4", features = ["serde"] } | ||
12 | common = { path = "../common" } | ||
13 | mime = "0.3" | ||
14 | serde = { version = "1", features = ["derive"] } | ||
15 | serde_json = "1" | ||
16 | tokio = { version = "1.35", features = ["full"] } | ||
17 | tokio-util = "0.7" | ||
18 | tower = "0.4" | ||
19 | tracing-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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | use std::collections::HashSet; | ||
3 | use std::process::ExitCode; | ||
4 | use std::sync::Arc; | ||
5 | |||
6 | use aws_sdk_s3::error::SdkError; | ||
7 | use aws_sdk_s3::operation::head_object::HeadObjectOutput; | ||
8 | use axum::extract::rejection; | ||
9 | use axum::extract::FromRequest; | ||
10 | use axum::extract::Path; | ||
11 | use axum::extract::State; | ||
12 | use axum::http::header; | ||
13 | use axum::http::HeaderMap; | ||
14 | use axum::http::HeaderValue; | ||
15 | use axum::response::Response; | ||
16 | use axum::Json; | ||
17 | use axum::ServiceExt; | ||
18 | use base64::prelude::*; | ||
19 | use chrono::DateTime; | ||
20 | use chrono::Utc; | ||
21 | use common::HexByte; | ||
22 | use serde::de; | ||
23 | use serde::de::DeserializeOwned; | ||
24 | use serde::Deserialize; | ||
25 | use serde::Serialize; | ||
26 | use tower::Layer; | ||
27 | |||
28 | use 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)] | ||
38 | struct RepositoryName(String); | ||
39 | |||
40 | struct RepositoryNameRejection; | ||
41 | |||
42 | impl 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] | ||
49 | impl<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 | |||
61 | async 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 | |||
98 | struct 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 | |||
106 | struct 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 | |||
119 | fn 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 | |||
124 | impl 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 | |||
142 | fn 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] | ||
165 | async 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)] | ||
241 | enum TransferAdapter { | ||
242 | #[serde(rename = "basic")] | ||
243 | Basic, | ||
244 | #[serde(other)] | ||
245 | Unknown, | ||
246 | } | ||
247 | |||
248 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)] | ||
249 | enum HashAlgo { | ||
250 | #[serde(rename = "sha256")] | ||
251 | Sha256, | ||
252 | #[serde(other)] | ||
253 | Unknown, | ||
254 | } | ||
255 | |||
256 | impl Default for HashAlgo { | ||
257 | fn default() -> Self { | ||
258 | Self::Sha256 | ||
259 | } | ||
260 | } | ||
261 | |||
262 | #[derive(Debug, Deserialize, PartialEq, Eq, Clone)] | ||
263 | struct BatchRequestObject { | ||
264 | oid: common::Oid, | ||
265 | size: i64, | ||
266 | } | ||
267 | |||
268 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
269 | struct BatchRef { | ||
270 | name: String, | ||
271 | } | ||
272 | |||
273 | fn default_transfers() -> Vec<TransferAdapter> { | ||
274 | vec![TransferAdapter::Basic] | ||
275 | } | ||
276 | |||
277 | #[derive(Debug, Deserialize, PartialEq, Eq, Clone)] | ||
278 | struct 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)] | ||
288 | struct GitLfsJson<T>(Json<T>); | ||
289 | |||
290 | const LFS_MIME: &str = "application/vnd.git-lfs+json"; | ||
291 | |||
292 | enum GitLfsJsonRejection { | ||
293 | Json(rejection::JsonRejection), | ||
294 | MissingGitLfsJsonContentType, | ||
295 | } | ||
296 | |||
297 | impl 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 | |||
310 | fn 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 | |||
326 | fn 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] | ||
337 | impl<T, S> FromRequest<S> for GitLfsJson<T> | ||
338 | where | ||
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 | |||
355 | impl<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)] | ||
368 | struct GitLfsErrorData<'a> { | ||
369 | message: &'a str, | ||
370 | } | ||
371 | |||
372 | type GitLfsErrorResponse<'a> = (StatusCode, GitLfsJson<GitLfsErrorData<'a>>); | ||
373 | |||
374 | const fn make_error_resp(code: StatusCode, message: &str) -> GitLfsErrorResponse { | ||
375 | (code, GitLfsJson(Json(GitLfsErrorData { message }))) | ||
376 | } | ||
377 | |||
378 | #[derive(Debug, Serialize, Clone)] | ||
379 | struct 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)] | ||
387 | struct 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)] | ||
397 | struct BatchResponseObjectError { | ||
398 | code: u16, | ||
399 | message: String, | ||
400 | } | ||
401 | |||
402 | #[derive(Debug, Serialize, Clone)] | ||
403 | struct 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 | |||
413 | impl 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)] | ||
429 | struct BatchResponse { | ||
430 | transfer: TransferAdapter, | ||
431 | objects: Vec<BatchResponseObject>, | ||
432 | hash_algo: HashAlgo, | ||
433 | } | ||
434 | |||
435 | fn 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 | |||
446 | fn validate_size(expected: i64, obj: &HeadObjectOutput) -> bool { | ||
447 | if let Some(length) = obj.content_length() { | ||
448 | return length == expected; | ||
449 | } | ||
450 | true | ||
451 | } | ||
452 | |||
453 | async 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 | |||
531 | async 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 | |||
665 | struct AuthorizationConfig { | ||
666 | trusted_forwarded_hosts: HashSet<String>, | ||
667 | key: common::Key, | ||
668 | } | ||
669 | |||
670 | struct Trusted(bool); | ||
671 | |||
672 | fn 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 | } | ||
690 | const REPO_NOT_FOUND: GitLfsErrorResponse = | ||
691 | make_error_resp(StatusCode::NOT_FOUND, "Repository not found"); | ||
692 | |||
693 | fn 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 | |||
735 | fn repo_exists(name: &str) -> bool { | ||
736 | let Ok(metadata) = std::fs::metadata(name) else { | ||
737 | return false; | ||
738 | }; | ||
739 | metadata.is_dir() | ||
740 | } | ||
741 | |||
742 | fn 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 | |||
752 | async 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")] | ||
813 | struct FileParams { | ||
814 | oid0: HexByte, | ||
815 | oid1: HexByte, | ||
816 | oid: common::Oid, | ||
817 | } | ||
818 | |||
819 | impl<'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 | |||
843 | pub struct VerifyClaimsInput<'a> { | ||
844 | pub specific_claims: common::SpecificClaims, | ||
845 | pub repo_path: &'a str, | ||
846 | } | ||
847 | |||
848 | fn 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 | |||
887 | fn 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 | |||
906 | async 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] | ||
960 | fn 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] | ||
976 | fn 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] | ||
1001 | fn 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] | ||
2 | name = "shell" | ||
3 | version = "0.1.0" | ||
4 | edition = "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 @@ | |||
1 | use std::{os::unix::process::CommandExt, process::ExitCode}; | ||
2 | |||
3 | fn 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 | |||
55 | fn 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 | |||
76 | fn 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 | |||
82 | fn 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 | } | ||