diff options
author | Rutger Broekhoff | 2024-01-19 15:45:48 +0100 |
---|---|---|
committer | Rutger Broekhoff | 2024-01-19 15:45:48 +0100 |
commit | efe9d55ea6c01c718d3c53dbcee6b48899b48267 (patch) | |
tree | 97ff392c814290f12023e9c581078f9a10c4174c | |
parent | 0956476fda305f48644e53305e12ae46cb67a32b (diff) | |
download | gitolfs3-efe9d55ea6c01c718d3c53dbcee6b48899b48267.tar.gz gitolfs3-efe9d55ea6c01c718d3c53dbcee6b48899b48267.zip |
Implement downloading objects for trusted forwarded hosts
-rw-r--r-- | rs/server/src/main.rs | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/rs/server/src/main.rs b/rs/server/src/main.rs index aebeb88..0266f61 100644 --- a/rs/server/src/main.rs +++ b/rs/server/src/main.rs | |||
@@ -299,7 +299,7 @@ struct BatchResponseObjectAction { | |||
299 | expires_at: DateTime<Utc>, | 299 | expires_at: DateTime<Utc>, |
300 | } | 300 | } |
301 | 301 | ||
302 | #[derive(Debug, Serialize, Clone)] | 302 | #[derive(Default, Debug, Serialize, Clone)] |
303 | struct BatchResponseObjectActions { | 303 | struct BatchResponseObjectActions { |
304 | #[serde(skip_serializing_if = "Option::is_none")] | 304 | #[serde(skip_serializing_if = "Option::is_none")] |
305 | upload: Option<BatchResponseObjectAction>, | 305 | upload: Option<BatchResponseObjectAction>, |
@@ -315,6 +315,7 @@ struct BatchResponseObject { | |||
315 | size: i64, | 315 | size: i64, |
316 | #[serde(skip_serializing_if = "Option::is_none")] | 316 | #[serde(skip_serializing_if = "Option::is_none")] |
317 | authenticated: Option<bool>, | 317 | authenticated: Option<bool>, |
318 | actions: BatchResponseObjectActions, | ||
318 | } | 319 | } |
319 | 320 | ||
320 | #[derive(Debug, Serialize, Clone)] | 321 | #[derive(Debug, Serialize, Clone)] |
@@ -342,7 +343,7 @@ fn validate_size(expected: i64, obj: &HeadObjectOutput) -> bool { | |||
342 | true | 343 | true |
343 | } | 344 | } |
344 | 345 | ||
345 | async fn handle_download_object(state: &AppState, repo: &str, obj: &BatchRequestObject) { | 346 | async fn handle_download_object(state: &AppState, repo: &str, obj: &BatchRequestObject, trusted: bool) -> BatchResponseObject { |
346 | let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1])); | 347 | let (oid0, oid1) = (HexByte(obj.oid[0]), HexByte(obj.oid[1])); |
347 | let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid); | 348 | let full_path = format!("{repo}/lfs/objects/{}/{}/{}", oid0, oid1, obj.oid); |
348 | 349 | ||
@@ -354,16 +355,36 @@ async fn handle_download_object(state: &AppState, repo: &str, obj: &BatchRequest | |||
354 | .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled) | 355 | .checksum_mode(aws_sdk_s3::types::ChecksumMode::Enabled) |
355 | .send() | 356 | .send() |
356 | .await | 357 | .await |
357 | .unwrap(); | 358 | .unwrap(); // TODO: don't unwrap() |
358 | // Scaleway actually doesn't provide SHA256 suport, but maybe in the future :) | 359 | // Scaleway actually doesn't provide SHA256 suport, but maybe in the future :) |
359 | if !validate_checksum(obj.oid, &result) { | 360 | if !validate_checksum(obj.oid, &result) { |
360 | unreachable!(); | 361 | todo!(); |
361 | } | 362 | } |
362 | if !validate_size(obj.size, &result) { | 363 | if !validate_size(obj.size, &result) { |
363 | unreachable!(); | 364 | todo!(); |
364 | } | 365 | } |
365 | 366 | ||
366 | let expires_at = Utc::now() + Duration::seconds(5 * 60); | 367 | let expires_in = std::time::Duration::from_secs(5 * 60); |
368 | let expires_at = Utc::now() + expires_in; | ||
369 | |||
370 | if trusted { | ||
371 | let config = aws_sdk_s3::presigning::PresigningConfig::expires_in(expires_in).unwrap(); | ||
372 | let presigned = state.s3_client.get_object().presigned(config).await.unwrap(); | ||
373 | return BatchResponseObject{ | ||
374 | oid: obj.oid, | ||
375 | size: obj.size, | ||
376 | authenticated: Some(true), | ||
377 | actions: BatchResponseObjectActions { | ||
378 | download: Some(BatchResponseObjectAction{ | ||
379 | header: presigned.headers().map(|(k, v)| (k.to_owned(), v.to_owned())).collect(), | ||
380 | expires_at, | ||
381 | href: presigned.uri().to_string(), | ||
382 | }), | ||
383 | ..Default::default() | ||
384 | } | ||
385 | }; | ||
386 | } | ||
387 | todo!(); | ||
367 | } | 388 | } |
368 | 389 | ||
369 | struct AuthorizationConfig { | 390 | struct AuthorizationConfig { |
@@ -496,7 +517,7 @@ async fn batch( | |||
496 | let Some(public) = is_repo_public(&repo) else { | 517 | let Some(public) = is_repo_public(&repo) else { |
497 | return REPO_NOT_FOUND.into_response(); | 518 | return REPO_NOT_FOUND.into_response(); |
498 | }; | 519 | }; |
499 | let authn = match authorize( | 520 | let Trusted(trusted) = match authorize( |
500 | &state.authz_conf, | 521 | &state.authz_conf, |
501 | &headers, | 522 | &headers, |
502 | &repo, | 523 | &repo, |
@@ -528,7 +549,7 @@ async fn batch( | |||
528 | 549 | ||
529 | let resp: BatchResponse; | 550 | let resp: BatchResponse; |
530 | for obj in payload.objects { | 551 | for obj in payload.objects { |
531 | handle_download_object(&state, &repo, &obj).await; | 552 | handle_download_object(&state, &repo, &obj, trusted).await; |
532 | // match payload.operation { | 553 | // match payload.operation { |
533 | // Operation::Download => resp.objects.push(handle_download_object(repo, obj));, | 554 | // Operation::Download => resp.objects.push(handle_download_object(repo, obj));, |
534 | // Operation::Upload => resp.objects.push(handle_upload_object(repo, obj)), | 555 | // Operation::Upload => resp.objects.push(handle_upload_object(repo, obj)), |