aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/git-lfs-authenticate/main.c13
-rw-r--r--flake.lock20
-rw-r--r--flake.nix9
-rw-r--r--rs/.gitignore1
-rw-r--r--rs/Cargo.lock2022
-rw-r--r--rs/Cargo.toml7
-rw-r--r--rs/common/Cargo.toml10
-rw-r--r--rs/common/src/lib.rs337
-rw-r--r--rs/git-lfs-authenticate/Cargo.toml8
-rw-r--r--rs/git-lfs-authenticate/src/main.rs239
-rw-r--r--rs/server/Cargo.toml15
-rw-r--r--rs/server/src/main.rs347
12 files changed, 3020 insertions, 8 deletions
diff --git a/cmd/git-lfs-authenticate/main.c b/cmd/git-lfs-authenticate/main.c
index 71481e9..a7ec031 100644
--- a/cmd/git-lfs-authenticate/main.c
+++ b/cmd/git-lfs-authenticate/main.c
@@ -65,8 +65,6 @@ void checkrepopath(const char *path) {
65 die("Bad repository name: Is unresolved path"); 65 die("Bad repository name: Is unresolved path");
66 if (strlen(path) > 100) 66 if (strlen(path) > 100)
67 die("Bad repository name: Longer than 100 characters"); 67 die("Bad repository name: Longer than 100 characters");
68 if (hassuffix(path, "/"))
69 die("Bad repositry name: Unexpected trailing slash");
70 if (hasprefix(path, "/")) 68 if (hasprefix(path, "/"))
71 die("Bad repository name: Unexpected absolute path"); 69 die("Bad repository name: Unexpected absolute path");
72 if (!hassuffix(path, ".git")) 70 if (!hassuffix(path, ".git"))
@@ -152,6 +150,10 @@ void u64tobe(uint64_t x, uint8_t b[8]) {
152 b[7] = (uint8_t)(x >> 0); 150 b[7] = (uint8_t)(x >> 0);
153} 151}
154 152
153void *memcat(void *dest, const void *src, size_t n) {
154 return memcpy(dest, src, n) + n;
155}
156
155#define MAX_TAG_SIZE EVP_MAX_MD_SIZE 157#define MAX_TAG_SIZE EVP_MAX_MD_SIZE
156 158
157typedef struct taginfo { 159typedef struct taginfo {
@@ -161,10 +163,6 @@ typedef struct taginfo {
161 const int64_t expiresat_s; 163 const int64_t expiresat_s;
162} taginfo_t; 164} taginfo_t;
163 165
164void *memcat(void *dest, const void *src, size_t n) {
165 return memcpy(dest, src, n) + n;
166}
167
168void maketag(const taginfo_t info, uint8_t key[KEYSIZE], uint8_t dest[MAX_TAG_SIZE], uint32_t *len) { 166void maketag(const taginfo_t info, uint8_t key[KEYSIZE], uint8_t dest[MAX_TAG_SIZE], uint32_t *len) {
169 uint8_t expiresat_b[8]; 167 uint8_t expiresat_b[8];
170 u64tobe(info.expiresat_s, expiresat_b); 168 u64tobe(info.expiresat_s, expiresat_b);
@@ -247,7 +245,8 @@ int main(int argc, char *argv[]) {
247 char hextag[MAX_HEXTAG_STRLEN + 1]; 245 char hextag[MAX_HEXTAG_STRLEN + 1];
248 makehextag(taginfo, key, hextag); 246 makehextag(taginfo, key, hextag);
249 247
250 printf("{\"header\":{\"Authorization\":\"Gitolfs3-Hmac-Sha256 %s\"},\"expires_in\":%ld,\"href\":\"", hextag, expiresin_s); 248 printf("{\"header\":{\"Authorization\":\"Gitolfs3-Hmac-Sha256 %s\"},"
249 "\"expires_in\":%ld,\"href\":\"", hextag, expiresin_s);
251 printescjson(hrefbase); 250 printescjson(hrefbase);
252 printescjson(repopath); 251 printescjson(repopath);
253 printf("/info/lfs?p=1&te=%ld\"}\n", expiresat_s); 252 printf("/info/lfs?p=1&te=%ld\"}\n", expiresat_s);
diff --git a/flake.lock b/flake.lock
index eb759fc..369d5eb 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,5 +1,24 @@
1{ 1{
2 "nodes": { 2 "nodes": {
3 "crane": {
4 "inputs": {
5 "nixpkgs": [
6 "nixpkgs"
7 ]
8 },
9 "locked": {
10 "lastModified": 1701384536,
11 "narHash": "sha256-PdYjpXmkn4FYJU7uvmCa54b0PPXBgDZHJaJpSEWR1Ek=",
12 "rev": "07c531adf572dafd494db0672cdac00e48216171",
13 "revCount": 469,
14 "type": "tarball",
15 "url": "https://api.flakehub.com/f/pinned/ipetkov/crane/0.15.1/018c226e-9985-758b-9809-17419c4ebd2d/source.tar.gz"
16 },
17 "original": {
18 "type": "tarball",
19 "url": "https://flakehub.com/f/ipetkov/crane/0.15.1.tar.gz"
20 }
21 },
3 "flake-utils": { 22 "flake-utils": {
4 "inputs": { 23 "inputs": {
5 "systems": "systems" 24 "systems": "systems"
@@ -33,6 +52,7 @@
33 }, 52 },
34 "root": { 53 "root": {
35 "inputs": { 54 "inputs": {
55 "crane": "crane",
36 "flake-utils": "flake-utils", 56 "flake-utils": "flake-utils",
37 "nixpkgs": "nixpkgs" 57 "nixpkgs": "nixpkgs"
38 } 58 }
diff --git a/flake.nix b/flake.nix
index 63bce9e..ebcc991 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,9 +2,11 @@
2 inputs = { 2 inputs = {
3 nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2311.*.tar.gz"; 3 nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2311.*.tar.gz";
4 flake-utils.url = "https://flakehub.com/f/numtide/flake-utils/0.1.88.tar.gz"; 4 flake-utils.url = "https://flakehub.com/f/numtide/flake-utils/0.1.88.tar.gz";
5 crane.url = "https://flakehub.com/f/ipetkov/crane/0.15.1.tar.gz";
6 crane.inputs.nixpkgs.follows = "nixpkgs";
5 }; 7 };
6 8
7 outputs = { self, nixpkgs, flake-utils, ... }@inputs: 9 outputs = { self, crane, nixpkgs, flake-utils, ... }@inputs:
8 flake-utils.lib.eachDefaultSystem 10 flake-utils.lib.eachDefaultSystem
9 (system: 11 (system:
10 let 12 let
@@ -13,6 +15,8 @@
13 overlays = [ ]; 15 overlays = [ ];
14 }; 16 };
15 17
18 craneLib = crane.lib.${system};
19
16 gitolfs3 = pkgs.buildGoModule { 20 gitolfs3 = pkgs.buildGoModule {
17 name = "gitolfs3"; 21 name = "gitolfs3";
18 src = ./.; 22 src = ./.;
@@ -21,6 +25,9 @@
21 in 25 in
22 { 26 {
23 packages.gitolfs3 = gitolfs3; 27 packages.gitolfs3 = gitolfs3;
28 packages.gitolfs3-rs = craneLib.buildPackage {
29 src = craneLib.cleanCargoSource (craneLib.path ./rs);
30 };
24 packages.default = self.packages.${system}.gitolfs3; 31 packages.default = self.packages.${system}.gitolfs3;
25 32
26 devShells.default = pkgs.mkShell { 33 devShells.default = pkgs.mkShell {
diff --git a/rs/.gitignore b/rs/.gitignore
new file mode 100644
index 0000000..b83d222
--- /dev/null
+++ b/rs/.gitignore
@@ -0,0 +1 @@
/target/
diff --git a/rs/Cargo.lock b/rs/Cargo.lock
new file mode 100644
index 0000000..6069bcb
--- /dev/null
+++ b/rs/Cargo.lock
@@ -0,0 +1,2022 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 3
4
5[[package]]
6name = "addr2line"
7version = "0.21.0"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
10dependencies = [
11 "gimli",
12]
13
14[[package]]
15name = "adler"
16version = "1.0.2"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
19
20[[package]]
21name = "ahash"
22version = "0.7.7"
23source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
25dependencies = [
26 "getrandom",
27 "once_cell",
28 "version_check",
29]
30
31[[package]]
32name = "android-tzdata"
33version = "0.1.1"
34source = "registry+https://github.com/rust-lang/crates.io-index"
35checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
36
37[[package]]
38name = "android_system_properties"
39version = "0.1.5"
40source = "registry+https://github.com/rust-lang/crates.io-index"
41checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
42dependencies = [
43 "libc",
44]
45
46[[package]]
47name = "async-trait"
48version = "0.1.77"
49source = "registry+https://github.com/rust-lang/crates.io-index"
50checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
51dependencies = [
52 "proc-macro2",
53 "quote",
54 "syn 2.0.48",
55]
56
57[[package]]
58name = "attohttpc"
59version = "0.22.0"
60source = "registry+https://github.com/rust-lang/crates.io-index"
61checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7"
62dependencies = [
63 "http 0.2.11",
64 "log",
65 "native-tls",
66 "serde",
67 "serde_json",
68 "url",
69]
70
71[[package]]
72name = "autocfg"
73version = "1.1.0"
74source = "registry+https://github.com/rust-lang/crates.io-index"
75checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
76
77[[package]]
78name = "aws-creds"
79version = "0.34.1"
80source = "registry+https://github.com/rust-lang/crates.io-index"
81checksum = "3776743bb68d4ad02ba30ba8f64373f1be4e082fe47651767171ce75bb2f6cf5"
82dependencies = [
83 "attohttpc",
84 "dirs",
85 "log",
86 "quick-xml",
87 "rust-ini",
88 "serde",
89 "thiserror",
90 "time",
91 "url",
92]
93
94[[package]]
95name = "aws-region"
96version = "0.25.4"
97source = "registry+https://github.com/rust-lang/crates.io-index"
98checksum = "42fed2b9fca70f2908268d057a607f2a906f47edbf856ea8587de9038d264e22"
99dependencies = [
100 "thiserror",
101]
102
103[[package]]
104name = "axum"
105version = "0.7.4"
106source = "registry+https://github.com/rust-lang/crates.io-index"
107checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e"
108dependencies = [
109 "async-trait",
110 "axum-core",
111 "bytes",
112 "futures-util",
113 "http 1.0.0",
114 "http-body 1.0.0",
115 "http-body-util",
116 "hyper 1.1.0",
117 "hyper-util",
118 "itoa",
119 "matchit",
120 "memchr",
121 "mime",
122 "percent-encoding",
123 "pin-project-lite",
124 "rustversion",
125 "serde",
126 "serde_json",
127 "serde_path_to_error",
128 "serde_urlencoded",
129 "sync_wrapper",
130 "tokio",
131 "tower",
132 "tower-layer",
133 "tower-service",
134 "tracing",
135]
136
137[[package]]
138name = "axum-core"
139version = "0.4.3"
140source = "registry+https://github.com/rust-lang/crates.io-index"
141checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
142dependencies = [
143 "async-trait",
144 "bytes",
145 "futures-util",
146 "http 1.0.0",
147 "http-body 1.0.0",
148 "http-body-util",
149 "mime",
150 "pin-project-lite",
151 "rustversion",
152 "sync_wrapper",
153 "tower-layer",
154 "tower-service",
155 "tracing",
156]
157
158[[package]]
159name = "backtrace"
160version = "0.3.69"
161source = "registry+https://github.com/rust-lang/crates.io-index"
162checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
163dependencies = [
164 "addr2line",
165 "cc",
166 "cfg-if",
167 "libc",
168 "miniz_oxide",
169 "object",
170 "rustc-demangle",
171]
172
173[[package]]
174name = "base64"
175version = "0.13.1"
176source = "registry+https://github.com/rust-lang/crates.io-index"
177checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
178
179[[package]]
180name = "base64"
181version = "0.21.7"
182source = "registry+https://github.com/rust-lang/crates.io-index"
183checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
184
185[[package]]
186name = "bitflags"
187version = "1.3.2"
188source = "registry+https://github.com/rust-lang/crates.io-index"
189checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
190
191[[package]]
192name = "bitflags"
193version = "2.4.2"
194source = "registry+https://github.com/rust-lang/crates.io-index"
195checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
196
197[[package]]
198name = "block-buffer"
199version = "0.10.4"
200source = "registry+https://github.com/rust-lang/crates.io-index"
201checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
202dependencies = [
203 "generic-array",
204]
205
206[[package]]
207name = "bumpalo"
208version = "3.14.0"
209source = "registry+https://github.com/rust-lang/crates.io-index"
210checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
211
212[[package]]
213name = "bytes"
214version = "1.5.0"
215source = "registry+https://github.com/rust-lang/crates.io-index"
216checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
217
218[[package]]
219name = "cc"
220version = "1.0.83"
221source = "registry+https://github.com/rust-lang/crates.io-index"
222checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
223dependencies = [
224 "libc",
225]
226
227[[package]]
228name = "cfg-if"
229version = "1.0.0"
230source = "registry+https://github.com/rust-lang/crates.io-index"
231checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
232
233[[package]]
234name = "chrono"
235version = "0.4.31"
236source = "registry+https://github.com/rust-lang/crates.io-index"
237checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
238dependencies = [
239 "android-tzdata",
240 "iana-time-zone",
241 "js-sys",
242 "num-traits",
243 "serde",
244 "wasm-bindgen",
245 "windows-targets 0.48.5",
246]
247
248[[package]]
249name = "common"
250version = "0.1.0"
251dependencies = [
252 "chrono",
253 "hmac-sha256",
254 "serde",
255 "subtle",
256]
257
258[[package]]
259name = "core-foundation"
260version = "0.9.4"
261source = "registry+https://github.com/rust-lang/crates.io-index"
262checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
263dependencies = [
264 "core-foundation-sys",
265 "libc",
266]
267
268[[package]]
269name = "core-foundation-sys"
270version = "0.8.6"
271source = "registry+https://github.com/rust-lang/crates.io-index"
272checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
273
274[[package]]
275name = "cpufeatures"
276version = "0.2.12"
277source = "registry+https://github.com/rust-lang/crates.io-index"
278checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
279dependencies = [
280 "libc",
281]
282
283[[package]]
284name = "crypto-common"
285version = "0.1.6"
286source = "registry+https://github.com/rust-lang/crates.io-index"
287checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
288dependencies = [
289 "generic-array",
290 "typenum",
291]
292
293[[package]]
294name = "deranged"
295version = "0.3.11"
296source = "registry+https://github.com/rust-lang/crates.io-index"
297checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
298dependencies = [
299 "powerfmt",
300 "serde",
301]
302
303[[package]]
304name = "digest"
305version = "0.10.7"
306source = "registry+https://github.com/rust-lang/crates.io-index"
307checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
308dependencies = [
309 "block-buffer",
310 "crypto-common",
311 "subtle",
312]
313
314[[package]]
315name = "dirs"
316version = "4.0.0"
317source = "registry+https://github.com/rust-lang/crates.io-index"
318checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
319dependencies = [
320 "dirs-sys",
321]
322
323[[package]]
324name = "dirs-sys"
325version = "0.3.7"
326source = "registry+https://github.com/rust-lang/crates.io-index"
327checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
328dependencies = [
329 "libc",
330 "redox_users",
331 "winapi",
332]
333
334[[package]]
335name = "dlv-list"
336version = "0.3.0"
337source = "registry+https://github.com/rust-lang/crates.io-index"
338checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257"
339
340[[package]]
341name = "encoding_rs"
342version = "0.8.33"
343source = "registry+https://github.com/rust-lang/crates.io-index"
344checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
345dependencies = [
346 "cfg-if",
347]
348
349[[package]]
350name = "equivalent"
351version = "1.0.1"
352source = "registry+https://github.com/rust-lang/crates.io-index"
353checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
354
355[[package]]
356name = "errno"
357version = "0.3.8"
358source = "registry+https://github.com/rust-lang/crates.io-index"
359checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
360dependencies = [
361 "libc",
362 "windows-sys 0.52.0",
363]
364
365[[package]]
366name = "fastrand"
367version = "2.0.1"
368source = "registry+https://github.com/rust-lang/crates.io-index"
369checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
370
371[[package]]
372name = "fnv"
373version = "1.0.7"
374source = "registry+https://github.com/rust-lang/crates.io-index"
375checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
376
377[[package]]
378name = "foreign-types"
379version = "0.3.2"
380source = "registry+https://github.com/rust-lang/crates.io-index"
381checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
382dependencies = [
383 "foreign-types-shared",
384]
385
386[[package]]
387name = "foreign-types-shared"
388version = "0.1.1"
389source = "registry+https://github.com/rust-lang/crates.io-index"
390checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
391
392[[package]]
393name = "form_urlencoded"
394version = "1.2.1"
395source = "registry+https://github.com/rust-lang/crates.io-index"
396checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
397dependencies = [
398 "percent-encoding",
399]
400
401[[package]]
402name = "futures"
403version = "0.3.30"
404source = "registry+https://github.com/rust-lang/crates.io-index"
405checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
406dependencies = [
407 "futures-channel",
408 "futures-core",
409 "futures-executor",
410 "futures-io",
411 "futures-sink",
412 "futures-task",
413 "futures-util",
414]
415
416[[package]]
417name = "futures-channel"
418version = "0.3.30"
419source = "registry+https://github.com/rust-lang/crates.io-index"
420checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
421dependencies = [
422 "futures-core",
423 "futures-sink",
424]
425
426[[package]]
427name = "futures-core"
428version = "0.3.30"
429source = "registry+https://github.com/rust-lang/crates.io-index"
430checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
431
432[[package]]
433name = "futures-executor"
434version = "0.3.30"
435source = "registry+https://github.com/rust-lang/crates.io-index"
436checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
437dependencies = [
438 "futures-core",
439 "futures-task",
440 "futures-util",
441]
442
443[[package]]
444name = "futures-io"
445version = "0.3.30"
446source = "registry+https://github.com/rust-lang/crates.io-index"
447checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
448
449[[package]]
450name = "futures-macro"
451version = "0.3.30"
452source = "registry+https://github.com/rust-lang/crates.io-index"
453checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
454dependencies = [
455 "proc-macro2",
456 "quote",
457 "syn 2.0.48",
458]
459
460[[package]]
461name = "futures-sink"
462version = "0.3.30"
463source = "registry+https://github.com/rust-lang/crates.io-index"
464checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
465
466[[package]]
467name = "futures-task"
468version = "0.3.30"
469source = "registry+https://github.com/rust-lang/crates.io-index"
470checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
471
472[[package]]
473name = "futures-util"
474version = "0.3.30"
475source = "registry+https://github.com/rust-lang/crates.io-index"
476checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
477dependencies = [
478 "futures-channel",
479 "futures-core",
480 "futures-io",
481 "futures-macro",
482 "futures-sink",
483 "futures-task",
484 "memchr",
485 "pin-project-lite",
486 "pin-utils",
487 "slab",
488]
489
490[[package]]
491name = "generic-array"
492version = "0.14.7"
493source = "registry+https://github.com/rust-lang/crates.io-index"
494checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
495dependencies = [
496 "typenum",
497 "version_check",
498]
499
500[[package]]
501name = "getrandom"
502version = "0.2.12"
503source = "registry+https://github.com/rust-lang/crates.io-index"
504checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
505dependencies = [
506 "cfg-if",
507 "libc",
508 "wasi",
509]
510
511[[package]]
512name = "gimli"
513version = "0.28.1"
514source = "registry+https://github.com/rust-lang/crates.io-index"
515checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
516
517[[package]]
518name = "git-lfs-authenticate"
519version = "0.1.0"
520dependencies = [
521 "chrono",
522 "common",
523]
524
525[[package]]
526name = "h2"
527version = "0.3.24"
528source = "registry+https://github.com/rust-lang/crates.io-index"
529checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
530dependencies = [
531 "bytes",
532 "fnv",
533 "futures-core",
534 "futures-sink",
535 "futures-util",
536 "http 0.2.11",
537 "indexmap",
538 "slab",
539 "tokio",
540 "tokio-util",
541 "tracing",
542]
543
544[[package]]
545name = "h2"
546version = "0.4.2"
547source = "registry+https://github.com/rust-lang/crates.io-index"
548checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943"
549dependencies = [
550 "bytes",
551 "fnv",
552 "futures-core",
553 "futures-sink",
554 "futures-util",
555 "http 1.0.0",
556 "indexmap",
557 "slab",
558 "tokio",
559 "tokio-util",
560 "tracing",
561]
562
563[[package]]
564name = "hashbrown"
565version = "0.12.3"
566source = "registry+https://github.com/rust-lang/crates.io-index"
567checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
568dependencies = [
569 "ahash",
570]
571
572[[package]]
573name = "hashbrown"
574version = "0.14.3"
575source = "registry+https://github.com/rust-lang/crates.io-index"
576checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
577
578[[package]]
579name = "hermit-abi"
580version = "0.3.3"
581source = "registry+https://github.com/rust-lang/crates.io-index"
582checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
583
584[[package]]
585name = "hex"
586version = "0.4.3"
587source = "registry+https://github.com/rust-lang/crates.io-index"
588checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
589
590[[package]]
591name = "hmac"
592version = "0.12.1"
593source = "registry+https://github.com/rust-lang/crates.io-index"
594checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
595dependencies = [
596 "digest",
597]
598
599[[package]]
600name = "hmac-sha256"
601version = "1.1.7"
602source = "registry+https://github.com/rust-lang/crates.io-index"
603checksum = "3688e69b38018fec1557254f64c8dc2cc8ec502890182f395dbb0aa997aa5735"
604
605[[package]]
606name = "http"
607version = "0.2.11"
608source = "registry+https://github.com/rust-lang/crates.io-index"
609checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb"
610dependencies = [
611 "bytes",
612 "fnv",
613 "itoa",
614]
615
616[[package]]
617name = "http"
618version = "1.0.0"
619source = "registry+https://github.com/rust-lang/crates.io-index"
620checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea"
621dependencies = [
622 "bytes",
623 "fnv",
624 "itoa",
625]
626
627[[package]]
628name = "http-body"
629version = "0.4.6"
630source = "registry+https://github.com/rust-lang/crates.io-index"
631checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
632dependencies = [
633 "bytes",
634 "http 0.2.11",
635 "pin-project-lite",
636]
637
638[[package]]
639name = "http-body"
640version = "1.0.0"
641source = "registry+https://github.com/rust-lang/crates.io-index"
642checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
643dependencies = [
644 "bytes",
645 "http 1.0.0",
646]
647
648[[package]]
649name = "http-body-util"
650version = "0.1.0"
651source = "registry+https://github.com/rust-lang/crates.io-index"
652checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840"
653dependencies = [
654 "bytes",
655 "futures-util",
656 "http 1.0.0",
657 "http-body 1.0.0",
658 "pin-project-lite",
659]
660
661[[package]]
662name = "httparse"
663version = "1.8.0"
664source = "registry+https://github.com/rust-lang/crates.io-index"
665checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
666
667[[package]]
668name = "httpdate"
669version = "1.0.3"
670source = "registry+https://github.com/rust-lang/crates.io-index"
671checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
672
673[[package]]
674name = "hyper"
675version = "0.14.28"
676source = "registry+https://github.com/rust-lang/crates.io-index"
677checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
678dependencies = [
679 "bytes",
680 "futures-channel",
681 "futures-core",
682 "futures-util",
683 "h2 0.3.24",
684 "http 0.2.11",
685 "http-body 0.4.6",
686 "httparse",
687 "httpdate",
688 "itoa",
689 "pin-project-lite",
690 "socket2",
691 "tokio",
692 "tower-service",
693 "tracing",
694 "want",
695]
696
697[[package]]
698name = "hyper"
699version = "1.1.0"
700source = "registry+https://github.com/rust-lang/crates.io-index"
701checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75"
702dependencies = [
703 "bytes",
704 "futures-channel",
705 "futures-util",
706 "h2 0.4.2",
707 "http 1.0.0",
708 "http-body 1.0.0",
709 "httparse",
710 "httpdate",
711 "itoa",
712 "pin-project-lite",
713 "tokio",
714]
715
716[[package]]
717name = "hyper-tls"
718version = "0.5.0"
719source = "registry+https://github.com/rust-lang/crates.io-index"
720checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
721dependencies = [
722 "bytes",
723 "hyper 0.14.28",
724 "native-tls",
725 "tokio",
726 "tokio-native-tls",
727]
728
729[[package]]
730name = "hyper-util"
731version = "0.1.2"
732source = "registry+https://github.com/rust-lang/crates.io-index"
733checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67"
734dependencies = [
735 "bytes",
736 "futures-channel",
737 "futures-util",
738 "http 1.0.0",
739 "http-body 1.0.0",
740 "hyper 1.1.0",
741 "pin-project-lite",
742 "socket2",
743 "tokio",
744 "tracing",
745]
746
747[[package]]
748name = "iana-time-zone"
749version = "0.1.59"
750source = "registry+https://github.com/rust-lang/crates.io-index"
751checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539"
752dependencies = [
753 "android_system_properties",
754 "core-foundation-sys",
755 "iana-time-zone-haiku",
756 "js-sys",
757 "wasm-bindgen",
758 "windows-core",
759]
760
761[[package]]
762name = "iana-time-zone-haiku"
763version = "0.1.2"
764source = "registry+https://github.com/rust-lang/crates.io-index"
765checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
766dependencies = [
767 "cc",
768]
769
770[[package]]
771name = "idna"
772version = "0.5.0"
773source = "registry+https://github.com/rust-lang/crates.io-index"
774checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
775dependencies = [
776 "unicode-bidi",
777 "unicode-normalization",
778]
779
780[[package]]
781name = "indexmap"
782version = "2.1.0"
783source = "registry+https://github.com/rust-lang/crates.io-index"
784checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
785dependencies = [
786 "equivalent",
787 "hashbrown 0.14.3",
788]
789
790[[package]]
791name = "ipnet"
792version = "2.9.0"
793source = "registry+https://github.com/rust-lang/crates.io-index"
794checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
795
796[[package]]
797name = "itoa"
798version = "1.0.10"
799source = "registry+https://github.com/rust-lang/crates.io-index"
800checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
801
802[[package]]
803name = "js-sys"
804version = "0.3.67"
805source = "registry+https://github.com/rust-lang/crates.io-index"
806checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
807dependencies = [
808 "wasm-bindgen",
809]
810
811[[package]]
812name = "lazy_static"
813version = "1.4.0"
814source = "registry+https://github.com/rust-lang/crates.io-index"
815checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
816
817[[package]]
818name = "libc"
819version = "0.2.152"
820source = "registry+https://github.com/rust-lang/crates.io-index"
821checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
822
823[[package]]
824name = "libredox"
825version = "0.0.1"
826source = "registry+https://github.com/rust-lang/crates.io-index"
827checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
828dependencies = [
829 "bitflags 2.4.2",
830 "libc",
831 "redox_syscall",
832]
833
834[[package]]
835name = "linux-raw-sys"
836version = "0.4.13"
837source = "registry+https://github.com/rust-lang/crates.io-index"
838checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
839
840[[package]]
841name = "lock_api"
842version = "0.4.11"
843source = "registry+https://github.com/rust-lang/crates.io-index"
844checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
845dependencies = [
846 "autocfg",
847 "scopeguard",
848]
849
850[[package]]
851name = "log"
852version = "0.4.20"
853source = "registry+https://github.com/rust-lang/crates.io-index"
854checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
855
856[[package]]
857name = "matchit"
858version = "0.7.3"
859source = "registry+https://github.com/rust-lang/crates.io-index"
860checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
861
862[[package]]
863name = "maybe-async"
864version = "0.2.7"
865source = "registry+https://github.com/rust-lang/crates.io-index"
866checksum = "0f1b8c13cb1f814b634a96b2c725449fe7ed464a7b8781de8688be5ffbd3f305"
867dependencies = [
868 "proc-macro2",
869 "quote",
870 "syn 1.0.109",
871]
872
873[[package]]
874name = "md5"
875version = "0.7.0"
876source = "registry+https://github.com/rust-lang/crates.io-index"
877checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
878
879[[package]]
880name = "memchr"
881version = "2.7.1"
882source = "registry+https://github.com/rust-lang/crates.io-index"
883checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
884
885[[package]]
886name = "mime"
887version = "0.3.17"
888source = "registry+https://github.com/rust-lang/crates.io-index"
889checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
890
891[[package]]
892name = "minidom"
893version = "0.15.2"
894source = "registry+https://github.com/rust-lang/crates.io-index"
895checksum = "f45614075738ce1b77a1768912a60c0227525971b03e09122a05b8a34a2a6278"
896dependencies = [
897 "rxml",
898]
899
900[[package]]
901name = "miniz_oxide"
902version = "0.7.1"
903source = "registry+https://github.com/rust-lang/crates.io-index"
904checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
905dependencies = [
906 "adler",
907]
908
909[[package]]
910name = "mio"
911version = "0.8.10"
912source = "registry+https://github.com/rust-lang/crates.io-index"
913checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
914dependencies = [
915 "libc",
916 "wasi",
917 "windows-sys 0.48.0",
918]
919
920[[package]]
921name = "native-tls"
922version = "0.2.11"
923source = "registry+https://github.com/rust-lang/crates.io-index"
924checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
925dependencies = [
926 "lazy_static",
927 "libc",
928 "log",
929 "openssl",
930 "openssl-probe",
931 "openssl-sys",
932 "schannel",
933 "security-framework",
934 "security-framework-sys",
935 "tempfile",
936]
937
938[[package]]
939name = "num-traits"
940version = "0.2.17"
941source = "registry+https://github.com/rust-lang/crates.io-index"
942checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
943dependencies = [
944 "autocfg",
945]
946
947[[package]]
948name = "num_cpus"
949version = "1.16.0"
950source = "registry+https://github.com/rust-lang/crates.io-index"
951checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
952dependencies = [
953 "hermit-abi",
954 "libc",
955]
956
957[[package]]
958name = "object"
959version = "0.32.2"
960source = "registry+https://github.com/rust-lang/crates.io-index"
961checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
962dependencies = [
963 "memchr",
964]
965
966[[package]]
967name = "once_cell"
968version = "1.19.0"
969source = "registry+https://github.com/rust-lang/crates.io-index"
970checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
971
972[[package]]
973name = "openssl"
974version = "0.10.62"
975source = "registry+https://github.com/rust-lang/crates.io-index"
976checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
977dependencies = [
978 "bitflags 2.4.2",
979 "cfg-if",
980 "foreign-types",
981 "libc",
982 "once_cell",
983 "openssl-macros",
984 "openssl-sys",
985]
986
987[[package]]
988name = "openssl-macros"
989version = "0.1.1"
990source = "registry+https://github.com/rust-lang/crates.io-index"
991checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
992dependencies = [
993 "proc-macro2",
994 "quote",
995 "syn 2.0.48",
996]
997
998[[package]]
999name = "openssl-probe"
1000version = "0.1.5"
1001source = "registry+https://github.com/rust-lang/crates.io-index"
1002checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
1003
1004[[package]]
1005name = "openssl-sys"
1006version = "0.9.98"
1007source = "registry+https://github.com/rust-lang/crates.io-index"
1008checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
1009dependencies = [
1010 "cc",
1011 "libc",
1012 "pkg-config",
1013 "vcpkg",
1014]
1015
1016[[package]]
1017name = "ordered-multimap"
1018version = "0.4.3"
1019source = "registry+https://github.com/rust-lang/crates.io-index"
1020checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a"
1021dependencies = [
1022 "dlv-list",
1023 "hashbrown 0.12.3",
1024]
1025
1026[[package]]
1027name = "parking_lot"
1028version = "0.12.1"
1029source = "registry+https://github.com/rust-lang/crates.io-index"
1030checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
1031dependencies = [
1032 "lock_api",
1033 "parking_lot_core",
1034]
1035
1036[[package]]
1037name = "parking_lot_core"
1038version = "0.9.9"
1039source = "registry+https://github.com/rust-lang/crates.io-index"
1040checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
1041dependencies = [
1042 "cfg-if",
1043 "libc",
1044 "redox_syscall",
1045 "smallvec",
1046 "windows-targets 0.48.5",
1047]
1048
1049[[package]]
1050name = "percent-encoding"
1051version = "2.3.1"
1052source = "registry+https://github.com/rust-lang/crates.io-index"
1053checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
1054
1055[[package]]
1056name = "pin-project"
1057version = "1.1.3"
1058source = "registry+https://github.com/rust-lang/crates.io-index"
1059checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422"
1060dependencies = [
1061 "pin-project-internal",
1062]
1063
1064[[package]]
1065name = "pin-project-internal"
1066version = "1.1.3"
1067source = "registry+https://github.com/rust-lang/crates.io-index"
1068checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
1069dependencies = [
1070 "proc-macro2",
1071 "quote",
1072 "syn 2.0.48",
1073]
1074
1075[[package]]
1076name = "pin-project-lite"
1077version = "0.2.13"
1078source = "registry+https://github.com/rust-lang/crates.io-index"
1079checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
1080
1081[[package]]
1082name = "pin-utils"
1083version = "0.1.0"
1084source = "registry+https://github.com/rust-lang/crates.io-index"
1085checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
1086
1087[[package]]
1088name = "pkg-config"
1089version = "0.3.29"
1090source = "registry+https://github.com/rust-lang/crates.io-index"
1091checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
1092
1093[[package]]
1094name = "powerfmt"
1095version = "0.2.0"
1096source = "registry+https://github.com/rust-lang/crates.io-index"
1097checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
1098
1099[[package]]
1100name = "proc-macro2"
1101version = "1.0.76"
1102source = "registry+https://github.com/rust-lang/crates.io-index"
1103checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
1104dependencies = [
1105 "unicode-ident",
1106]
1107
1108[[package]]
1109name = "quick-xml"
1110version = "0.26.0"
1111source = "registry+https://github.com/rust-lang/crates.io-index"
1112checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
1113dependencies = [
1114 "memchr",
1115 "serde",
1116]
1117
1118[[package]]
1119name = "quote"
1120version = "1.0.35"
1121source = "registry+https://github.com/rust-lang/crates.io-index"
1122checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
1123dependencies = [
1124 "proc-macro2",
1125]
1126
1127[[package]]
1128name = "redox_syscall"
1129version = "0.4.1"
1130source = "registry+https://github.com/rust-lang/crates.io-index"
1131checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
1132dependencies = [
1133 "bitflags 1.3.2",
1134]
1135
1136[[package]]
1137name = "redox_users"
1138version = "0.4.4"
1139source = "registry+https://github.com/rust-lang/crates.io-index"
1140checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
1141dependencies = [
1142 "getrandom",
1143 "libredox",
1144 "thiserror",
1145]
1146
1147[[package]]
1148name = "reqwest"
1149version = "0.11.23"
1150source = "registry+https://github.com/rust-lang/crates.io-index"
1151checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
1152dependencies = [
1153 "base64 0.21.7",
1154 "bytes",
1155 "encoding_rs",
1156 "futures-core",
1157 "futures-util",
1158 "h2 0.3.24",
1159 "http 0.2.11",
1160 "http-body 0.4.6",
1161 "hyper 0.14.28",
1162 "hyper-tls",
1163 "ipnet",
1164 "js-sys",
1165 "log",
1166 "mime",
1167 "native-tls",
1168 "once_cell",
1169 "percent-encoding",
1170 "pin-project-lite",
1171 "serde",
1172 "serde_json",
1173 "serde_urlencoded",
1174 "system-configuration",
1175 "tokio",
1176 "tokio-native-tls",
1177 "tokio-util",
1178 "tower-service",
1179 "url",
1180 "wasm-bindgen",
1181 "wasm-bindgen-futures",
1182 "wasm-streams",
1183 "web-sys",
1184 "winreg",
1185]
1186
1187[[package]]
1188name = "rust-ini"
1189version = "0.18.0"
1190source = "registry+https://github.com/rust-lang/crates.io-index"
1191checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
1192dependencies = [
1193 "cfg-if",
1194 "ordered-multimap",
1195]
1196
1197[[package]]
1198name = "rust-s3"
1199version = "0.33.0"
1200source = "registry+https://github.com/rust-lang/crates.io-index"
1201checksum = "1b2ac5ff6acfbe74226fa701b5ef793aaa054055c13ebb7060ad36942956e027"
1202dependencies = [
1203 "async-trait",
1204 "aws-creds",
1205 "aws-region",
1206 "base64 0.13.1",
1207 "bytes",
1208 "cfg-if",
1209 "futures",
1210 "hex",
1211 "hmac",
1212 "http 0.2.11",
1213 "log",
1214 "maybe-async",
1215 "md5",
1216 "minidom",
1217 "percent-encoding",
1218 "quick-xml",
1219 "reqwest",
1220 "serde",
1221 "serde_derive",
1222 "sha2",
1223 "thiserror",
1224 "time",
1225 "tokio",
1226 "tokio-stream",
1227 "url",
1228]
1229
1230[[package]]
1231name = "rustc-demangle"
1232version = "0.1.23"
1233source = "registry+https://github.com/rust-lang/crates.io-index"
1234checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
1235
1236[[package]]
1237name = "rustix"
1238version = "0.38.30"
1239source = "registry+https://github.com/rust-lang/crates.io-index"
1240checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca"
1241dependencies = [
1242 "bitflags 2.4.2",
1243 "errno",
1244 "libc",
1245 "linux-raw-sys",
1246 "windows-sys 0.52.0",
1247]
1248
1249[[package]]
1250name = "rustversion"
1251version = "1.0.14"
1252source = "registry+https://github.com/rust-lang/crates.io-index"
1253checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
1254
1255[[package]]
1256name = "rxml"
1257version = "0.9.1"
1258source = "registry+https://github.com/rust-lang/crates.io-index"
1259checksum = "a98f186c7a2f3abbffb802984b7f1dfd65dac8be1aafdaabbca4137f53f0dff7"
1260dependencies = [
1261 "bytes",
1262 "rxml_validation",
1263 "smartstring",
1264]
1265
1266[[package]]
1267name = "rxml_validation"
1268version = "0.9.1"
1269source = "registry+https://github.com/rust-lang/crates.io-index"
1270checksum = "22a197350ece202f19a166d1ad6d9d6de145e1d2a8ef47db299abe164dbd7530"
1271
1272[[package]]
1273name = "ryu"
1274version = "1.0.16"
1275source = "registry+https://github.com/rust-lang/crates.io-index"
1276checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
1277
1278[[package]]
1279name = "schannel"
1280version = "0.1.23"
1281source = "registry+https://github.com/rust-lang/crates.io-index"
1282checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
1283dependencies = [
1284 "windows-sys 0.52.0",
1285]
1286
1287[[package]]
1288name = "scopeguard"
1289version = "1.2.0"
1290source = "registry+https://github.com/rust-lang/crates.io-index"
1291checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1292
1293[[package]]
1294name = "security-framework"
1295version = "2.9.2"
1296source = "registry+https://github.com/rust-lang/crates.io-index"
1297checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
1298dependencies = [
1299 "bitflags 1.3.2",
1300 "core-foundation",
1301 "core-foundation-sys",
1302 "libc",
1303 "security-framework-sys",
1304]
1305
1306[[package]]
1307name = "security-framework-sys"
1308version = "2.9.1"
1309source = "registry+https://github.com/rust-lang/crates.io-index"
1310checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
1311dependencies = [
1312 "core-foundation-sys",
1313 "libc",
1314]
1315
1316[[package]]
1317name = "serde"
1318version = "1.0.195"
1319source = "registry+https://github.com/rust-lang/crates.io-index"
1320checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
1321dependencies = [
1322 "serde_derive",
1323]
1324
1325[[package]]
1326name = "serde_derive"
1327version = "1.0.195"
1328source = "registry+https://github.com/rust-lang/crates.io-index"
1329checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
1330dependencies = [
1331 "proc-macro2",
1332 "quote",
1333 "syn 2.0.48",
1334]
1335
1336[[package]]
1337name = "serde_json"
1338version = "1.0.111"
1339source = "registry+https://github.com/rust-lang/crates.io-index"
1340checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
1341dependencies = [
1342 "itoa",
1343 "ryu",
1344 "serde",
1345]
1346
1347[[package]]
1348name = "serde_path_to_error"
1349version = "0.1.15"
1350source = "registry+https://github.com/rust-lang/crates.io-index"
1351checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c"
1352dependencies = [
1353 "itoa",
1354 "serde",
1355]
1356
1357[[package]]
1358name = "serde_urlencoded"
1359version = "0.7.1"
1360source = "registry+https://github.com/rust-lang/crates.io-index"
1361checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
1362dependencies = [
1363 "form_urlencoded",
1364 "itoa",
1365 "ryu",
1366 "serde",
1367]
1368
1369[[package]]
1370name = "server"
1371version = "0.1.0"
1372dependencies = [
1373 "aws-creds",
1374 "axum",
1375 "chrono",
1376 "common",
1377 "mime",
1378 "rust-s3",
1379 "serde",
1380 "tokio",
1381 "tower-service",
1382]
1383
1384[[package]]
1385name = "sha2"
1386version = "0.10.8"
1387source = "registry+https://github.com/rust-lang/crates.io-index"
1388checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
1389dependencies = [
1390 "cfg-if",
1391 "cpufeatures",
1392 "digest",
1393]
1394
1395[[package]]
1396name = "signal-hook-registry"
1397version = "1.4.1"
1398source = "registry+https://github.com/rust-lang/crates.io-index"
1399checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
1400dependencies = [
1401 "libc",
1402]
1403
1404[[package]]
1405name = "slab"
1406version = "0.4.9"
1407source = "registry+https://github.com/rust-lang/crates.io-index"
1408checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
1409dependencies = [
1410 "autocfg",
1411]
1412
1413[[package]]
1414name = "smallvec"
1415version = "1.11.2"
1416source = "registry+https://github.com/rust-lang/crates.io-index"
1417checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
1418
1419[[package]]
1420name = "smartstring"
1421version = "1.0.1"
1422source = "registry+https://github.com/rust-lang/crates.io-index"
1423checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29"
1424dependencies = [
1425 "autocfg",
1426 "static_assertions",
1427 "version_check",
1428]
1429
1430[[package]]
1431name = "socket2"
1432version = "0.5.5"
1433source = "registry+https://github.com/rust-lang/crates.io-index"
1434checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
1435dependencies = [
1436 "libc",
1437 "windows-sys 0.48.0",
1438]
1439
1440[[package]]
1441name = "static_assertions"
1442version = "1.1.0"
1443source = "registry+https://github.com/rust-lang/crates.io-index"
1444checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1445
1446[[package]]
1447name = "subtle"
1448version = "2.5.0"
1449source = "registry+https://github.com/rust-lang/crates.io-index"
1450checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
1451
1452[[package]]
1453name = "syn"
1454version = "1.0.109"
1455source = "registry+https://github.com/rust-lang/crates.io-index"
1456checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
1457dependencies = [
1458 "proc-macro2",
1459 "quote",
1460 "unicode-ident",
1461]
1462
1463[[package]]
1464name = "syn"
1465version = "2.0.48"
1466source = "registry+https://github.com/rust-lang/crates.io-index"
1467checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
1468dependencies = [
1469 "proc-macro2",
1470 "quote",
1471 "unicode-ident",
1472]
1473
1474[[package]]
1475name = "sync_wrapper"
1476version = "0.1.2"
1477source = "registry+https://github.com/rust-lang/crates.io-index"
1478checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
1479
1480[[package]]
1481name = "system-configuration"
1482version = "0.5.1"
1483source = "registry+https://github.com/rust-lang/crates.io-index"
1484checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
1485dependencies = [
1486 "bitflags 1.3.2",
1487 "core-foundation",
1488 "system-configuration-sys",
1489]
1490
1491[[package]]
1492name = "system-configuration-sys"
1493version = "0.5.0"
1494source = "registry+https://github.com/rust-lang/crates.io-index"
1495checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
1496dependencies = [
1497 "core-foundation-sys",
1498 "libc",
1499]
1500
1501[[package]]
1502name = "tempfile"
1503version = "3.9.0"
1504source = "registry+https://github.com/rust-lang/crates.io-index"
1505checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
1506dependencies = [
1507 "cfg-if",
1508 "fastrand",
1509 "redox_syscall",
1510 "rustix",
1511 "windows-sys 0.52.0",
1512]
1513
1514[[package]]
1515name = "thiserror"
1516version = "1.0.56"
1517source = "registry+https://github.com/rust-lang/crates.io-index"
1518checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
1519dependencies = [
1520 "thiserror-impl",
1521]
1522
1523[[package]]
1524name = "thiserror-impl"
1525version = "1.0.56"
1526source = "registry+https://github.com/rust-lang/crates.io-index"
1527checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
1528dependencies = [
1529 "proc-macro2",
1530 "quote",
1531 "syn 2.0.48",
1532]
1533
1534[[package]]
1535name = "time"
1536version = "0.3.31"
1537source = "registry+https://github.com/rust-lang/crates.io-index"
1538checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
1539dependencies = [
1540 "deranged",
1541 "itoa",
1542 "powerfmt",
1543 "serde",
1544 "time-core",
1545 "time-macros",
1546]
1547
1548[[package]]
1549name = "time-core"
1550version = "0.1.2"
1551source = "registry+https://github.com/rust-lang/crates.io-index"
1552checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
1553
1554[[package]]
1555name = "time-macros"
1556version = "0.2.16"
1557source = "registry+https://github.com/rust-lang/crates.io-index"
1558checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
1559dependencies = [
1560 "time-core",
1561]
1562
1563[[package]]
1564name = "tinyvec"
1565version = "1.6.0"
1566source = "registry+https://github.com/rust-lang/crates.io-index"
1567checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
1568dependencies = [
1569 "tinyvec_macros",
1570]
1571
1572[[package]]
1573name = "tinyvec_macros"
1574version = "0.1.1"
1575source = "registry+https://github.com/rust-lang/crates.io-index"
1576checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1577
1578[[package]]
1579name = "tokio"
1580version = "1.35.1"
1581source = "registry+https://github.com/rust-lang/crates.io-index"
1582checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
1583dependencies = [
1584 "backtrace",
1585 "bytes",
1586 "libc",
1587 "mio",
1588 "num_cpus",
1589 "parking_lot",
1590 "pin-project-lite",
1591 "signal-hook-registry",
1592 "socket2",
1593 "tokio-macros",
1594 "windows-sys 0.48.0",
1595]
1596
1597[[package]]
1598name = "tokio-macros"
1599version = "2.2.0"
1600source = "registry+https://github.com/rust-lang/crates.io-index"
1601checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
1602dependencies = [
1603 "proc-macro2",
1604 "quote",
1605 "syn 2.0.48",
1606]
1607
1608[[package]]
1609name = "tokio-native-tls"
1610version = "0.3.1"
1611source = "registry+https://github.com/rust-lang/crates.io-index"
1612checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
1613dependencies = [
1614 "native-tls",
1615 "tokio",
1616]
1617
1618[[package]]
1619name = "tokio-stream"
1620version = "0.1.14"
1621source = "registry+https://github.com/rust-lang/crates.io-index"
1622checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842"
1623dependencies = [
1624 "futures-core",
1625 "pin-project-lite",
1626 "tokio",
1627]
1628
1629[[package]]
1630name = "tokio-util"
1631version = "0.7.10"
1632source = "registry+https://github.com/rust-lang/crates.io-index"
1633checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
1634dependencies = [
1635 "bytes",
1636 "futures-core",
1637 "futures-sink",
1638 "pin-project-lite",
1639 "tokio",
1640 "tracing",
1641]
1642
1643[[package]]
1644name = "tower"
1645version = "0.4.13"
1646source = "registry+https://github.com/rust-lang/crates.io-index"
1647checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
1648dependencies = [
1649 "futures-core",
1650 "futures-util",
1651 "pin-project",
1652 "pin-project-lite",
1653 "tokio",
1654 "tower-layer",
1655 "tower-service",
1656 "tracing",
1657]
1658
1659[[package]]
1660name = "tower-layer"
1661version = "0.3.2"
1662source = "registry+https://github.com/rust-lang/crates.io-index"
1663checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
1664
1665[[package]]
1666name = "tower-service"
1667version = "0.3.2"
1668source = "registry+https://github.com/rust-lang/crates.io-index"
1669checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
1670
1671[[package]]
1672name = "tracing"
1673version = "0.1.40"
1674source = "registry+https://github.com/rust-lang/crates.io-index"
1675checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
1676dependencies = [
1677 "log",
1678 "pin-project-lite",
1679 "tracing-core",
1680]
1681
1682[[package]]
1683name = "tracing-core"
1684version = "0.1.32"
1685source = "registry+https://github.com/rust-lang/crates.io-index"
1686checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
1687dependencies = [
1688 "once_cell",
1689]
1690
1691[[package]]
1692name = "try-lock"
1693version = "0.2.5"
1694source = "registry+https://github.com/rust-lang/crates.io-index"
1695checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
1696
1697[[package]]
1698name = "typenum"
1699version = "1.17.0"
1700source = "registry+https://github.com/rust-lang/crates.io-index"
1701checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
1702
1703[[package]]
1704name = "unicode-bidi"
1705version = "0.3.15"
1706source = "registry+https://github.com/rust-lang/crates.io-index"
1707checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
1708
1709[[package]]
1710name = "unicode-ident"
1711version = "1.0.12"
1712source = "registry+https://github.com/rust-lang/crates.io-index"
1713checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
1714
1715[[package]]
1716name = "unicode-normalization"
1717version = "0.1.22"
1718source = "registry+https://github.com/rust-lang/crates.io-index"
1719checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
1720dependencies = [
1721 "tinyvec",
1722]
1723
1724[[package]]
1725name = "url"
1726version = "2.5.0"
1727source = "registry+https://github.com/rust-lang/crates.io-index"
1728checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
1729dependencies = [
1730 "form_urlencoded",
1731 "idna",
1732 "percent-encoding",
1733]
1734
1735[[package]]
1736name = "vcpkg"
1737version = "0.2.15"
1738source = "registry+https://github.com/rust-lang/crates.io-index"
1739checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
1740
1741[[package]]
1742name = "version_check"
1743version = "0.9.4"
1744source = "registry+https://github.com/rust-lang/crates.io-index"
1745checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
1746
1747[[package]]
1748name = "want"
1749version = "0.3.1"
1750source = "registry+https://github.com/rust-lang/crates.io-index"
1751checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
1752dependencies = [
1753 "try-lock",
1754]
1755
1756[[package]]
1757name = "wasi"
1758version = "0.11.0+wasi-snapshot-preview1"
1759source = "registry+https://github.com/rust-lang/crates.io-index"
1760checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1761
1762[[package]]
1763name = "wasm-bindgen"
1764version = "0.2.90"
1765source = "registry+https://github.com/rust-lang/crates.io-index"
1766checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
1767dependencies = [
1768 "cfg-if",
1769 "wasm-bindgen-macro",
1770]
1771
1772[[package]]
1773name = "wasm-bindgen-backend"
1774version = "0.2.90"
1775source = "registry+https://github.com/rust-lang/crates.io-index"
1776checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
1777dependencies = [
1778 "bumpalo",
1779 "log",
1780 "once_cell",
1781 "proc-macro2",
1782 "quote",
1783 "syn 2.0.48",
1784 "wasm-bindgen-shared",
1785]
1786
1787[[package]]
1788name = "wasm-bindgen-futures"
1789version = "0.4.40"
1790source = "registry+https://github.com/rust-lang/crates.io-index"
1791checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461"
1792dependencies = [
1793 "cfg-if",
1794 "js-sys",
1795 "wasm-bindgen",
1796 "web-sys",
1797]
1798
1799[[package]]
1800name = "wasm-bindgen-macro"
1801version = "0.2.90"
1802source = "registry+https://github.com/rust-lang/crates.io-index"
1803checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
1804dependencies = [
1805 "quote",
1806 "wasm-bindgen-macro-support",
1807]
1808
1809[[package]]
1810name = "wasm-bindgen-macro-support"
1811version = "0.2.90"
1812source = "registry+https://github.com/rust-lang/crates.io-index"
1813checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
1814dependencies = [
1815 "proc-macro2",
1816 "quote",
1817 "syn 2.0.48",
1818 "wasm-bindgen-backend",
1819 "wasm-bindgen-shared",
1820]
1821
1822[[package]]
1823name = "wasm-bindgen-shared"
1824version = "0.2.90"
1825source = "registry+https://github.com/rust-lang/crates.io-index"
1826checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
1827
1828[[package]]
1829name = "wasm-streams"
1830version = "0.3.0"
1831source = "registry+https://github.com/rust-lang/crates.io-index"
1832checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
1833dependencies = [
1834 "futures-util",
1835 "js-sys",
1836 "wasm-bindgen",
1837 "wasm-bindgen-futures",
1838 "web-sys",
1839]
1840
1841[[package]]
1842name = "web-sys"
1843version = "0.3.67"
1844source = "registry+https://github.com/rust-lang/crates.io-index"
1845checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed"
1846dependencies = [
1847 "js-sys",
1848 "wasm-bindgen",
1849]
1850
1851[[package]]
1852name = "winapi"
1853version = "0.3.9"
1854source = "registry+https://github.com/rust-lang/crates.io-index"
1855checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1856dependencies = [
1857 "winapi-i686-pc-windows-gnu",
1858 "winapi-x86_64-pc-windows-gnu",
1859]
1860
1861[[package]]
1862name = "winapi-i686-pc-windows-gnu"
1863version = "0.4.0"
1864source = "registry+https://github.com/rust-lang/crates.io-index"
1865checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1866
1867[[package]]
1868name = "winapi-x86_64-pc-windows-gnu"
1869version = "0.4.0"
1870source = "registry+https://github.com/rust-lang/crates.io-index"
1871checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1872
1873[[package]]
1874name = "windows-core"
1875version = "0.52.0"
1876source = "registry+https://github.com/rust-lang/crates.io-index"
1877checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
1878dependencies = [
1879 "windows-targets 0.52.0",
1880]
1881
1882[[package]]
1883name = "windows-sys"
1884version = "0.48.0"
1885source = "registry+https://github.com/rust-lang/crates.io-index"
1886checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
1887dependencies = [
1888 "windows-targets 0.48.5",
1889]
1890
1891[[package]]
1892name = "windows-sys"
1893version = "0.52.0"
1894source = "registry+https://github.com/rust-lang/crates.io-index"
1895checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
1896dependencies = [
1897 "windows-targets 0.52.0",
1898]
1899
1900[[package]]
1901name = "windows-targets"
1902version = "0.48.5"
1903source = "registry+https://github.com/rust-lang/crates.io-index"
1904checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
1905dependencies = [
1906 "windows_aarch64_gnullvm 0.48.5",
1907 "windows_aarch64_msvc 0.48.5",
1908 "windows_i686_gnu 0.48.5",
1909 "windows_i686_msvc 0.48.5",
1910 "windows_x86_64_gnu 0.48.5",
1911 "windows_x86_64_gnullvm 0.48.5",
1912 "windows_x86_64_msvc 0.48.5",
1913]
1914
1915[[package]]
1916name = "windows-targets"
1917version = "0.52.0"
1918source = "registry+https://github.com/rust-lang/crates.io-index"
1919checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
1920dependencies = [
1921 "windows_aarch64_gnullvm 0.52.0",
1922 "windows_aarch64_msvc 0.52.0",
1923 "windows_i686_gnu 0.52.0",
1924 "windows_i686_msvc 0.52.0",
1925 "windows_x86_64_gnu 0.52.0",
1926 "windows_x86_64_gnullvm 0.52.0",
1927 "windows_x86_64_msvc 0.52.0",
1928]
1929
1930[[package]]
1931name = "windows_aarch64_gnullvm"
1932version = "0.48.5"
1933source = "registry+https://github.com/rust-lang/crates.io-index"
1934checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
1935
1936[[package]]
1937name = "windows_aarch64_gnullvm"
1938version = "0.52.0"
1939source = "registry+https://github.com/rust-lang/crates.io-index"
1940checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
1941
1942[[package]]
1943name = "windows_aarch64_msvc"
1944version = "0.48.5"
1945source = "registry+https://github.com/rust-lang/crates.io-index"
1946checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
1947
1948[[package]]
1949name = "windows_aarch64_msvc"
1950version = "0.52.0"
1951source = "registry+https://github.com/rust-lang/crates.io-index"
1952checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
1953
1954[[package]]
1955name = "windows_i686_gnu"
1956version = "0.48.5"
1957source = "registry+https://github.com/rust-lang/crates.io-index"
1958checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
1959
1960[[package]]
1961name = "windows_i686_gnu"
1962version = "0.52.0"
1963source = "registry+https://github.com/rust-lang/crates.io-index"
1964checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
1965
1966[[package]]
1967name = "windows_i686_msvc"
1968version = "0.48.5"
1969source = "registry+https://github.com/rust-lang/crates.io-index"
1970checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
1971
1972[[package]]
1973name = "windows_i686_msvc"
1974version = "0.52.0"
1975source = "registry+https://github.com/rust-lang/crates.io-index"
1976checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
1977
1978[[package]]
1979name = "windows_x86_64_gnu"
1980version = "0.48.5"
1981source = "registry+https://github.com/rust-lang/crates.io-index"
1982checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
1983
1984[[package]]
1985name = "windows_x86_64_gnu"
1986version = "0.52.0"
1987source = "registry+https://github.com/rust-lang/crates.io-index"
1988checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
1989
1990[[package]]
1991name = "windows_x86_64_gnullvm"
1992version = "0.48.5"
1993source = "registry+https://github.com/rust-lang/crates.io-index"
1994checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
1995
1996[[package]]
1997name = "windows_x86_64_gnullvm"
1998version = "0.52.0"
1999source = "registry+https://github.com/rust-lang/crates.io-index"
2000checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
2001
2002[[package]]
2003name = "windows_x86_64_msvc"
2004version = "0.48.5"
2005source = "registry+https://github.com/rust-lang/crates.io-index"
2006checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
2007
2008[[package]]
2009name = "windows_x86_64_msvc"
2010version = "0.52.0"
2011source = "registry+https://github.com/rust-lang/crates.io-index"
2012checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
2013
2014[[package]]
2015name = "winreg"
2016version = "0.50.0"
2017source = "registry+https://github.com/rust-lang/crates.io-index"
2018checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
2019dependencies = [
2020 "cfg-if",
2021 "windows-sys 0.48.0",
2022]
diff --git a/rs/Cargo.toml b/rs/Cargo.toml
new file mode 100644
index 0000000..ec75eb3
--- /dev/null
+++ b/rs/Cargo.toml
@@ -0,0 +1,7 @@
1[workspace]
2resolver = "2"
3members = [
4 "common",
5 "git-lfs-authenticate",
6 "server",
7]
diff --git a/rs/common/Cargo.toml b/rs/common/Cargo.toml
new file mode 100644
index 0000000..20d9bdd
--- /dev/null
+++ b/rs/common/Cargo.toml
@@ -0,0 +1,10 @@
1[package]
2name = "common"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7chrono = "0.4"
8hmac-sha256 = "1.1"
9subtle = "2.5"
10serde = { version = "1", features = ["derive"] }
diff --git a/rs/common/src/lib.rs b/rs/common/src/lib.rs
new file mode 100644
index 0000000..aafe7f1
--- /dev/null
+++ b/rs/common/src/lib.rs
@@ -0,0 +1,337 @@
1use chrono::{DateTime, Utc};
2use serde::de;
3use serde::{Deserialize, Serialize};
4use std::fmt::Write;
5use std::ops;
6use std::{fmt, str::FromStr};
7use subtle::ConstantTimeEq;
8
9#[derive(Debug, Eq, PartialEq, Copy, Clone, Serialize, Deserialize)]
10#[repr(u8)]
11pub enum Operation {
12 #[serde(rename = "download")]
13 Download = 1,
14 #[serde(rename = "upload")]
15 Upload = 2,
16}
17
18#[derive(Debug, PartialEq, Eq, Copy, Clone)]
19pub struct ParseOperationError;
20
21impl fmt::Display for ParseOperationError {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 write!(f, "operation should be 'download' or 'upload'")
24 }
25}
26
27impl FromStr for Operation {
28 type Err = ParseOperationError;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 match s {
32 "upload" => Ok(Self::Upload),
33 "download" => Ok(Self::Download),
34 _ => Err(ParseOperationError),
35 }
36 }
37}
38
39#[repr(u8)]
40pub enum AuthType {
41 GitLfsAuthenticate = 1,
42}
43
44/// None means out of range.
45fn decode_nibble(c: u8) -> Option<u8> {
46 if c.is_ascii_digit() {
47 Some(c - b'0')
48 } else if (b'a'..=b'f').contains(&c) {
49 Some(c - b'a' + 10)
50 } else if (b'A'..=b'F').contains(&c) {
51 Some(c - b'A' + 10)
52 } else {
53 None
54 }
55}
56
57#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
58pub struct HexByte(pub u8);
59
60impl<'de> Deserialize<'de> for HexByte {
61 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62 where
63 D: serde::Deserializer<'de>,
64 {
65 let str = <&str>::deserialize(deserializer)?;
66 let &[b1, b2] = str.as_bytes() else {
67 return Err(de::Error::invalid_length(
68 str.len(),
69 &"two hexadecimal characters",
70 ));
71 };
72 let (Some(b1), Some(b2)) = (decode_nibble(b1), decode_nibble(b2)) else {
73 return Err(de::Error::invalid_value(
74 de::Unexpected::Str(str),
75 &"two hexadecimal characters",
76 ));
77 };
78 Ok(HexByte((b1 << 4) | b2))
79 }
80}
81
82impl fmt::Display for HexByte {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 let &HexByte(b) = self;
85 HexFmt(&[b]).fmt(f)
86 }
87}
88
89#[derive(Debug, PartialEq, Eq, Copy, Clone)]
90pub enum ParseHexError {
91 UnevenNibbles,
92 InvalidCharacter,
93 TooShort,
94 TooLong,
95}
96
97impl fmt::Display for ParseHexError {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::UnevenNibbles => {
101 write!(f, "uneven amount of nibbles (chars in range [a-zA-Z0-9])")
102 }
103 Self::InvalidCharacter => write!(f, "non-hex character encountered"),
104 Self::TooShort => write!(f, "unexpected end of hex sequence"),
105 Self::TooLong => write!(f, "longer hex sequence than expected"),
106 }
107 }
108}
109
110#[derive(Debug)]
111pub enum ReadHexError {
112 Io(std::io::Error),
113 Format(ParseHexError),
114}
115
116impl fmt::Display for ReadHexError {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 match self {
119 Self::Io(e) => e.fmt(f),
120 Self::Format(e) => e.fmt(f),
121 }
122 }
123}
124
125fn parse_hex_exact(value: &str, buf: &mut [u8]) -> Result<(), ParseHexError> {
126 if value.bytes().len() % 2 == 1 {
127 return Err(ParseHexError::UnevenNibbles);
128 }
129 if value.bytes().len() < 2 * buf.len() {
130 return Err(ParseHexError::TooShort);
131 }
132 if value.bytes().len() > 2 * buf.len() {
133 return Err(ParseHexError::TooLong);
134 }
135 for (i, c) in value.bytes().enumerate() {
136 if let Some(b) = decode_nibble(c) {
137 if i % 2 == 0 {
138 buf[i / 2] |= b;
139 } else {
140 buf[i / 2] = b << 4;
141 }
142 } else {
143 return Err(ParseHexError::InvalidCharacter);
144 }
145 }
146 Ok(())
147}
148
149pub struct SafeByteArray<const N: usize> {
150 inner: [u8; N],
151}
152
153impl<const N: usize> SafeByteArray<N> {
154 pub fn new() -> Self {
155 Self { inner: [0; N] }
156 }
157}
158
159impl<const N: usize> AsRef<[u8]> for SafeByteArray<N> {
160 fn as_ref(&self) -> &[u8] {
161 &self.inner
162 }
163}
164
165impl<const N: usize> AsMut<[u8]> for SafeByteArray<N> {
166 fn as_mut(&mut self) -> &mut [u8] {
167 &mut self.inner
168 }
169}
170
171impl<const N: usize> Drop for SafeByteArray<N> {
172 fn drop(&mut self) {
173 self.inner.fill(0)
174 }
175}
176
177impl<const N: usize> FromStr for SafeByteArray<N> {
178 type Err = ParseHexError;
179
180 fn from_str(value: &str) -> Result<Self, Self::Err> {
181 let mut sba = Self { inner: [0u8; N] };
182 parse_hex_exact(value, &mut sba.inner)?;
183 Ok(sba)
184 }
185}
186
187pub struct Claims<'a> {
188 pub auth_type: AuthType,
189 pub repo_path: &'a str,
190 pub operation: Operation,
191 pub expires_at: DateTime<Utc>,
192}
193
194/// Returns None if the claims are invalid. Repo path length may be no more than 100 bytes.
195pub fn generate_tag(claims: Claims, key: impl AsRef<[u8]>) -> Option<Digest<32>> {
196 if claims.repo_path.len() > 100 {
197 return None;
198 }
199
200 let mut hmac = hmac_sha256::HMAC::new(key);
201 hmac.update([claims.auth_type as u8]);
202 hmac.update([claims.repo_path.len() as u8]);
203 hmac.update(claims.repo_path.as_bytes());
204 hmac.update([claims.operation as u8]);
205 hmac.update(claims.expires_at.timestamp().to_be_bytes());
206 Some(hmac.finalize().into())
207}
208
209pub struct HexFmt<B: AsRef<[u8]>>(pub B);
210
211impl<B: AsRef<[u8]>> fmt::Display for HexFmt<B> {
212 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213 let HexFmt(buf) = self;
214 for b in buf.as_ref() {
215 let (high, low) = (b >> 4, b & 0xF);
216 let highc = if high < 10 { b'0' + high } else { b'a' + high };
217 let lowc = if low < 10 { b'0' + low } else { b'a' + low };
218 f.write_char(highc as char)?;
219 f.write_char(lowc as char)?;
220 }
221 Ok(())
222 }
223}
224
225pub struct EscJsonFmt<'a>(pub &'a str);
226
227impl<'a> fmt::Display for EscJsonFmt<'a> {
228 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229 let EscJsonFmt(buf) = self;
230 for c in buf.chars() {
231 match c {
232 '"' => f.write_str("\\\"")?, // quote
233 '\\' => f.write_str("\\\\")?, // backslash
234 '\x08' => f.write_str("\\b")?, // backspace
235 '\x0C' => f.write_str("\\f")?, // form feed
236 '\n' => f.write_str("\\n")?, // line feed
237 '\r' => f.write_str("\\r")?, // carriage return
238 '\t' => f.write_str("\\t")?, // horizontal tab
239 _ => f.write_char(c)?,
240 };
241 }
242 Ok(())
243 }
244}
245
246#[derive(Debug, Copy, Clone)]
247pub struct Digest<const N: usize> {
248 inner: [u8; N],
249}
250
251impl<const N: usize> ops::Index<usize> for Digest<N> {
252 type Output = u8;
253
254 fn index(&self, index: usize) -> &Self::Output {
255 &self.inner[index]
256 }
257}
258
259impl<const N: usize> Digest<N> {
260 pub fn as_bytes(&self) -> &[u8; N] {
261 &self.inner
262 }
263}
264
265impl<const N: usize> fmt::Display for Digest<N> {
266 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
267 HexFmt(&self.inner).fmt(f)
268 }
269}
270
271impl<const N: usize> Digest<N> {
272 pub fn new(data: [u8; N]) -> Self {
273 Self { inner: data }
274 }
275}
276
277impl<const N: usize> From<[u8; N]> for Digest<N> {
278 fn from(value: [u8; N]) -> Self {
279 Self::new(value)
280 }
281}
282
283impl<const N: usize> Into<[u8; N]> for Digest<N> {
284 fn into(self) -> [u8; N] {
285 self.inner
286 }
287}
288
289impl<const N: usize> FromStr for Digest<N> {
290 type Err = ParseHexError;
291
292 fn from_str(value: &str) -> Result<Self, Self::Err> {
293 let mut buf = [0u8; N];
294 parse_hex_exact(value, &mut buf)?;
295 Ok(buf.into())
296 }
297}
298
299impl<const N: usize> ConstantTimeEq for Digest<N> {
300 fn ct_eq(&self, other: &Self) -> subtle::Choice {
301 self.inner.ct_eq(&other.inner)
302 }
303}
304
305impl<const N: usize> PartialEq for Digest<N> {
306 fn eq(&self, other: &Self) -> bool {
307 self.ct_eq(&other).into()
308 }
309}
310
311impl<const N: usize> Eq for Digest<N> {}
312
313impl<'de, const N: usize> Deserialize<'de> for Digest<N> {
314 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
315 where
316 D: serde::Deserializer<'de>,
317 {
318 let hex = <&str>::deserialize(deserializer)?;
319 Digest::from_str(hex).map_err(de::Error::custom)
320 }
321}
322
323impl<const N: usize> Serialize for Digest<N> {
324 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
325 where
326 S: serde::Serializer,
327 {
328 serializer.serialize_str(&format!("{self}"))
329 }
330}
331
332pub type Key = SafeByteArray<64>;
333
334pub fn load_key(path: &str) -> Result<Key, ReadHexError> {
335 let key_str = std::fs::read_to_string(path).map_err(ReadHexError::Io)?;
336 key_str.trim().parse().map_err(ReadHexError::Format)
337}
diff --git a/rs/git-lfs-authenticate/Cargo.toml b/rs/git-lfs-authenticate/Cargo.toml
new file mode 100644
index 0000000..217250f
--- /dev/null
+++ b/rs/git-lfs-authenticate/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "git-lfs-authenticate"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7chrono = "0.4"
8common = { path = "../common" }
diff --git a/rs/git-lfs-authenticate/src/main.rs b/rs/git-lfs-authenticate/src/main.rs
new file mode 100644
index 0000000..76f2edd
--- /dev/null
+++ b/rs/git-lfs-authenticate/src/main.rs
@@ -0,0 +1,239 @@
1use std::{fmt, process::ExitCode, time::Duration};
2
3use chrono::Utc;
4use common::{Operation, ParseOperationError};
5
6fn help() {
7 eprintln!("Usage: git-lfs-authenticate <REPO> upload/download");
8}
9
10#[derive(Debug, Eq, PartialEq, Copy, Clone)]
11enum RepoNameError {
12 TooLong,
13 UnresolvedPath,
14 AbsolutePath,
15 MissingGitSuffix,
16}
17
18impl fmt::Display for RepoNameError {
19 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
20 match self {
21 Self::TooLong => write!(f, "too long (more than 100 characters)"),
22 Self::UnresolvedPath => {
23 write!(f, "contains path one or more path elements '.' and '..'")
24 }
25 Self::AbsolutePath => {
26 write!(f, "starts with '/', which is not allowed")
27 }
28 Self::MissingGitSuffix => write!(f, "misses '.git' suffix"),
29 }
30 }
31}
32
33// Using `Result<(), E>` here instead of `Option<E>` because `None` typically signifies some error
34// state with no further details provided. If we were to return an `Option` type, the user would
35// have to first transform it into a `Result` type in order to use the `?` operator, meaning that
36// they would have to the following operation to get the type into the right shape:
37// `validate_repo_path(path).map_or(Ok(()), Err)`. That would not be very ergonomic.
38fn validate_repo_path(path: &str) -> Result<(), RepoNameError> {
39 if path.len() > 100 {
40 return Err(RepoNameError::TooLong);
41 }
42 if path.contains("//")
43 || path.contains("/./")
44 || path.contains("/../")
45 || path.starts_with("./")
46 || path.starts_with("../")
47 {
48 return Err(RepoNameError::UnresolvedPath);
49 }
50 if path.starts_with('/') {
51 return Err(RepoNameError::AbsolutePath);
52 }
53 if !path.ends_with(".git") {
54 return Err(RepoNameError::MissingGitSuffix);
55 }
56 Ok(())
57}
58
59#[derive(Debug, Eq, PartialEq, Copy, Clone)]
60enum ParseCmdlineError {
61 UnknownOperation(ParseOperationError),
62 InvalidRepoName(RepoNameError),
63 UnexpectedArgCount(ArgCountError),
64}
65
66impl From<RepoNameError> for ParseCmdlineError {
67 fn from(value: RepoNameError) -> Self {
68 Self::InvalidRepoName(value)
69 }
70}
71
72impl From<ParseOperationError> for ParseCmdlineError {
73 fn from(value: ParseOperationError) -> Self {
74 Self::UnknownOperation(value)
75 }
76}
77
78impl From<ArgCountError> for ParseCmdlineError {
79 fn from(value: ArgCountError) -> Self {
80 Self::UnexpectedArgCount(value)
81 }
82}
83
84impl fmt::Display for ParseCmdlineError {
85 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86 match self {
87 Self::UnknownOperation(e) => write!(f, "unknown operation: {e}"),
88 Self::InvalidRepoName(e) => write!(f, "invalid repository name: {e}"),
89 Self::UnexpectedArgCount(e) => e.fmt(f),
90 }
91 }
92}
93
94#[derive(Debug, Eq, PartialEq, Copy, Clone)]
95struct ArgCountError {
96 provided: usize,
97 expected: usize,
98}
99
100impl fmt::Display for ArgCountError {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 write!(
103 f,
104 "got {} argument(s), expected {}",
105 self.provided, self.expected
106 )
107 }
108}
109
110fn get_cmdline_args<const N: usize>() -> Result<[String; N], ArgCountError> {
111 let args = std::env::args();
112 if args.len() - 1 != N {
113 return Err(ArgCountError {
114 provided: args.len() - 1,
115 expected: N,
116 });
117 }
118
119 // Does not allocate.
120 const EMPTY_STRING: String = String::new();
121 let mut values = [EMPTY_STRING; N];
122
123 // Skip the first element; we do not care about the program name.
124 for (i, arg) in args.skip(1).enumerate() {
125 values[i] = arg
126 }
127 Ok(values)
128}
129
130fn parse_cmdline() -> Result<(String, Operation), ParseCmdlineError> {
131 let [repo_path, op_str] = get_cmdline_args::<2>()?;
132 let op: Operation = op_str.parse()?;
133 validate_repo_path(&repo_path)?;
134 Ok((repo_path.to_string(), op))
135}
136
137fn repo_exists(name: &str) -> bool {
138 match std::fs::metadata(name) {
139 Ok(metadata) => metadata.is_dir(),
140 _ => false,
141 }
142}
143
144struct Config {
145 href_base: String,
146 key_path: String,
147}
148
149#[derive(Debug, Eq, PartialEq, Copy, Clone)]
150enum LoadConfigError {
151 BaseUrlMissing,
152 BaseUrlSlashSuffixMissing,
153 KeyPathMissing,
154}
155
156impl fmt::Display for LoadConfigError {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::BaseUrlMissing => write!(f, "base URL not provided"),
160 Self::BaseUrlSlashSuffixMissing => write!(f, "base URL does not end with slash"),
161 Self::KeyPathMissing => write!(f, "key path not provided"),
162 }
163 }
164}
165
166fn load_config() -> Result<Config, LoadConfigError> {
167 let Ok(href_base) = std::env::var("GITOLFS3_HREF_BASE") else {
168 return Err(LoadConfigError::BaseUrlMissing);
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::KeyPathMissing);
175 };
176 Ok(Config {
177 href_base,
178 key_path,
179 })
180}
181
182fn main() -> ExitCode {
183 let config = match load_config() {
184 Ok(config) => config,
185 Err(e) => {
186 eprintln!("Failed to load config: {e}");
187 return ExitCode::FAILURE;
188 }
189 };
190 let key = match common::load_key(&config.key_path) {
191 Ok(key) => key,
192 Err(e) => {
193 eprintln!("Failed to load key: {e}");
194 return ExitCode::FAILURE;
195 }
196 };
197
198 let (repo_name, op) = 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_in = Duration::from_secs(5 * 60);
214 let expires_at = Utc::now() + expires_in;
215
216 let Some(tag) = common::generate_tag(
217 common::Claims {
218 auth_type: common::AuthType::GitLfsAuthenticate,
219 repo_path: &repo_name,
220 expires_at,
221 operation: op,
222 },
223 key,
224 ) else {
225 eprintln!("Failed to generate validation tag");
226 return ExitCode::FAILURE;
227 };
228
229 println!(
230 "{{\"header\":{{\"Authorization\":\"Gitolfs3-Hmac-Sha256 {tag}\"}},\
231 \"expires_at\":\"{}\",\"href\":\"{}{}/info/lfs?p=1&te={}\"}}",
232 common::EscJsonFmt(&expires_at.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)),
233 common::EscJsonFmt(&config.href_base),
234 common::EscJsonFmt(&repo_name),
235 expires_at.timestamp()
236 );
237
238 ExitCode::SUCCESS
239}
diff --git a/rs/server/Cargo.toml b/rs/server/Cargo.toml
new file mode 100644
index 0000000..ac571af
--- /dev/null
+++ b/rs/server/Cargo.toml
@@ -0,0 +1,15 @@
1[package]
2name = "server"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7axum = "0.7"
8aws-creds = "0.34"
9chrono = { version = "0.4", features = ["serde"] }
10common = { path = "../common" }
11mime = "0.3"
12rust-s3 = "0.33"
13serde = { version = "1", features = ["derive"] }
14tokio = { version = "1.35", features = ["full"] }
15tower-service = "0.3"
diff --git a/rs/server/src/main.rs b/rs/server/src/main.rs
new file mode 100644
index 0000000..8fe1d16
--- /dev/null
+++ b/rs/server/src/main.rs
@@ -0,0 +1,347 @@
1use std::collections::HashMap;
2
3use awscreds::Credentials;
4use axum::extract::rejection;
5use axum::extract::FromRequest;
6use axum::extract::Path;
7use axum::http::header;
8use axum::http::HeaderMap;
9use axum::http::HeaderValue;
10use axum::response::Response;
11use axum::Json;
12use chrono::DateTime;
13use chrono::Utc;
14use common::HexByte;
15use common::Operation;
16use s3::Bucket;
17use serde::de;
18use serde::de::DeserializeOwned;
19use serde::Deserialize;
20use serde::Serialize;
21use tower_service::Service;
22
23use axum::{
24 async_trait,
25 extract::{FromRequestParts, OriginalUri, Request},
26 http::{request::Parts, StatusCode, Uri},
27 response::IntoResponse,
28 routing::{any, get, post, put},
29 Extension, Router,
30};
31
32#[derive(Clone)]
33struct RepositoryName(String);
34
35struct RepositoryNameRejection;
36
37impl IntoResponse for RepositoryNameRejection {
38 fn into_response(self) -> Response {
39 (StatusCode::INTERNAL_SERVER_ERROR, "Missing repository name").into_response()
40 }
41}
42
43#[async_trait]
44impl<S: Send + Sync> FromRequestParts<S> for RepositoryName {
45 type Rejection = RepositoryNameRejection;
46
47 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
48 let Ok(Extension(repo_name)) = Extension::<Self>::from_request_parts(parts, state).await
49 else {
50 return Err(RepositoryNameRejection);
51 };
52 Ok(repo_name)
53 }
54}
55
56#[tokio::main]
57async fn main() {
58 // run our app with hyper, listening globally on port 3000
59 let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
60 let mut app = Router::new()
61 .route("/batch", post(batch))
62 .route("/:oid0/:oid1/:oid", get(obj_download))
63 .route("/:oid0/:oid1/:oid", put(obj_upload));
64 axum::serve(
65 listener,
66 any(|mut req: Request| async move {
67 let uri = req.uri();
68 let original_uri = OriginalUri(uri.clone());
69
70 let path_and_query = uri.path_and_query().unwrap();
71 let Some((repo, path)) = path_and_query.path().split_once("/info/lfs/objects") else {
72 return Ok(StatusCode::NOT_FOUND.into_response());
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 Ok(StatusCode::NOT_FOUND.into_response());
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 new_uri = Uri::from_parts(parts).unwrap();
88
89 *req.uri_mut() = new_uri;
90 req.extensions_mut().insert(original_uri);
91 req.extensions_mut().insert(RepositoryName(repo));
92
93 app.call(req).await
94 }),
95 )
96 .await
97 .unwrap();
98}
99
100#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
101enum TransferAdapter {
102 #[serde(rename = "basic")]
103 Basic,
104}
105
106#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
107enum HashAlgo {
108 #[serde(rename = "sha256")]
109 Sha256,
110}
111
112impl Default for HashAlgo {
113 fn default() -> Self {
114 Self::Sha256
115 }
116}
117
118type Oid = common::Digest<32>;
119
120#[derive(Debug, Deserialize, Clone)]
121struct BatchRequestObject {
122 oid: Oid,
123 size: i64,
124}
125
126#[derive(Debug, Serialize, Deserialize, Clone)]
127struct BatchRef {
128 name: String,
129}
130
131fn default_transfers() -> Vec<TransferAdapter> {
132 vec![TransferAdapter::Basic]
133}
134
135#[derive(Deserialize)]
136struct BatchRequest {
137 operation: common::Operation,
138 #[serde(default = "default_transfers")]
139 transfers: Vec<TransferAdapter>,
140 #[serde(rename = "ref")]
141 reference: Option<BatchRef>,
142 objects: Vec<BatchRequestObject>,
143 #[serde(default)]
144 hash_algo: HashAlgo,
145}
146
147#[derive(Clone)]
148struct GitLfsJson<T>(Json<T>);
149
150const LFS_MIME: &'static str = "application/vnd.git-lfs+json";
151
152enum GitLfsJsonRejection {
153 Json(rejection::JsonRejection),
154 MissingGitLfsJsonContentType,
155}
156
157impl IntoResponse for GitLfsJsonRejection {
158 fn into_response(self) -> Response {
159 (
160 StatusCode::UNSUPPORTED_MEDIA_TYPE,
161 format!("Expected request with `Content-Type: {LFS_MIME}`"),
162 )
163 .into_response()
164 }
165}
166
167fn is_git_lfs_json_mimetype(mimetype: &str) -> bool {
168 let Ok(mime) = mimetype.parse::<mime::Mime>() else {
169 return false;
170 };
171 if mime.type_() != mime::APPLICATION
172 || mime.subtype() != "vnd.git-lfs"
173 || mime.suffix() != Some(mime::JSON)
174 {
175 return false;
176 }
177 match mime.get_param(mime::CHARSET) {
178 Some(mime::UTF_8) | None => true,
179 Some(_) => false,
180 }
181}
182
183fn has_git_lfs_json_content_type(req: &Request) -> bool {
184 let Some(content_type) = req.headers().get(header::CONTENT_TYPE) else {
185 return false;
186 };
187 let Ok(content_type) = content_type.to_str() else {
188 return false;
189 };
190 return is_git_lfs_json_mimetype(content_type);
191}
192
193#[async_trait]
194impl<T, S> FromRequest<S> for GitLfsJson<T>
195where
196 T: DeserializeOwned,
197 S: Send + Sync,
198{
199 type Rejection = GitLfsJsonRejection;
200
201 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
202 if !has_git_lfs_json_content_type(&req) {
203 return Err(GitLfsJsonRejection::MissingGitLfsJsonContentType);
204 }
205 Json::<T>::from_request(req, state)
206 .await
207 .map(GitLfsJson)
208 .map_err(GitLfsJsonRejection::Json)
209 }
210}
211
212impl<T: Serialize> IntoResponse for GitLfsJson<T> {
213 fn into_response(self) -> Response {
214 let GitLfsJson(json) = self;
215 let mut resp = json.into_response();
216 resp.headers_mut().insert(
217 header::CONTENT_TYPE,
218 HeaderValue::from_static("application/vnd.git-lfs+json"),
219 );
220 resp
221 }
222}
223
224#[derive(Debug, Serialize, Clone)]
225struct BatchResponseObjectAction {
226 href: String,
227 #[serde(skip_serializing_if = "HashMap::is_empty")]
228 header: HashMap<String, String>,
229 expires_at: DateTime<Utc>,
230}
231
232#[derive(Debug, Serialize, Clone)]
233struct BatchResponseObjectActions {
234 #[serde(skip_serializing_if = "Option::is_none")]
235 upload: Option<BatchResponseObjectAction>,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 download: Option<BatchResponseObjectAction>,
238 #[serde(skip_serializing_if = "Option::is_none")]
239 verify: Option<BatchResponseObjectAction>,
240}
241
242#[derive(Debug, Serialize, Clone)]
243struct BatchResponseObject {
244 oid: Oid,
245 size: i64,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 authenticated: Option<bool>,
248}
249
250#[derive(Debug, Serialize, Clone)]
251struct BatchResponse {
252 transfer: TransferAdapter,
253 objects: Vec<BatchResponseObject>,
254 hash_algo: HashAlgo,
255}
256
257//fn handle_download_object(repo: &str, obj: &BatchRequestObject) {
258// let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1]));
259// let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid);
260//
261// let bucket_anme = "asdfasdf";
262// let region = s3::Region::Custom {
263// region: "nl-ams".to_string(),
264// endpoint: "rg.nl-ams.swc.cloud".to_string()
265// };
266// let credentials = Credentials::new(None, None, None, None, None).unwrap();
267// let bucket = Bucket::new(bucket_anme, region, credentials).unwrap();
268//}
269
270async fn batch(
271 header: HeaderMap,
272 RepositoryName(repo): RepositoryName,
273 GitLfsJson(Json(payload)): GitLfsJson<BatchRequest>,
274) -> Response {
275 if !header
276 .get_all("Accept")
277 .iter()
278 .filter_map(|v| v.to_str().ok())
279 .any(is_git_lfs_json_mimetype)
280 {
281 return (
282 StatusCode::NOT_ACCEPTABLE,
283 format!("Expected `{LFS_MIME}` (with UTF-8 charset) in list of acceptable response media types"),
284 ).into_response();
285 }
286
287 if payload.hash_algo != HashAlgo::Sha256 {
288 return (
289 StatusCode::CONFLICT,
290 "Unsupported hashing algorithm speicifed",
291 )
292 .into_response();
293 }
294 if !payload.transfers.is_empty() && !payload.transfers.contains(&TransferAdapter::Basic) {
295 return (
296 StatusCode::CONFLICT,
297 "Unsupported transfer adapter specified (supported: basic)",
298 )
299 .into_response();
300 }
301
302 let resp: BatchResponse;
303 for obj in payload.objects {
304// match payload.operation {
305// Operation::Download => resp.objects.push(handle_download_object(repo, obj));,
306// Operation::Upload => resp.objects.push(handle_upload_object(repo, obj)),
307// };
308 }
309
310 format!("hi from {repo}\n").into_response()
311}
312
313#[derive(Deserialize, Copy, Clone)]
314#[serde(remote = "Self")]
315struct FileParams {
316 oid0: HexByte,
317 oid1: HexByte,
318 oid: Oid,
319}
320
321impl<'de> Deserialize<'de> for FileParams {
322 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
323 where
324 D: serde::Deserializer<'de>,
325 {
326 let unchecked @ FileParams {
327 oid0: HexByte(oid0),
328 oid1: HexByte(oid1),
329 oid,
330 } = FileParams::deserialize(deserializer)?;
331 if oid0 != oid.as_bytes()[0] {
332 return Err(de::Error::custom(
333 "first OID path part does not match first byte of full OID",
334 ));
335 }
336 if oid1 != oid.as_bytes()[1] {
337 return Err(de::Error::custom(
338 "second OID path part does not match first byte of full OID",
339 ));
340 }
341 Ok(unchecked)
342 }
343}
344
345async fn obj_download(Path(FileParams { oid0, oid1, oid }): Path<FileParams>) {}
346
347async fn obj_upload(Path(FileParams { oid0, oid1, oid }): Path<FileParams>) {}