Commit
+36 -4 +/-2 browse
1 | diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml |
2 | index 40dfd6b..802c290 100644 |
3 | --- a/.github/workflows/ci.yml |
4 | +++ b/.github/workflows/ci.yml |
5 | @@ -77,6 +77,8 @@ jobs: |
6 | - uses: actions-rs/clippy-check@v1 |
7 | with: |
8 | token: ${{ secrets.GITHUB_TOKEN }} |
9 | + args: --all-features -- --allow dead_code |
10 | + |
11 | |
12 | publish_crate: |
13 | name: Publish Crate |
14 | diff --git a/src/app.rs b/src/app.rs |
15 | index c881d5e..42b8de4 100644 |
16 | --- a/src/app.rs |
17 | +++ b/src/app.rs |
18 | @@ -17,6 +17,7 @@ |
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
21 | // SOFTWARE. |
22 | + use std::collections::BTreeMap; |
23 | use std::fmt; |
24 | use std::io; |
25 | |
26 | @@ -27,6 +28,7 @@ use futures::{ |
27 | future::{self, BoxFuture}, |
28 | stream::TryStreamExt, |
29 | }; |
30 | + use http::HeaderMap; |
31 | use http::{self, header, StatusCode, Uri}; |
32 | use hyper::{self, body::Body, service::Service, Method, Request, Response}; |
33 | use serde::{Deserialize, Serialize}; |
34 | @@ -248,6 +250,7 @@ where |
35 | ) -> Result<Response<Body>, Error> { |
36 | // Get the host name and scheme. |
37 | let uri = req.base_uri().path_and_query("/").build().unwrap(); |
38 | + let headers = req.headers().clone(); |
39 | |
40 | match from_json::<lfs::BatchRequest>(req.into_body()).await { |
41 | Ok(val) => { |
42 | @@ -264,7 +267,8 @@ where |
43 | |
44 | let (namespace, _) = key.into_parts(); |
45 | Ok(basic_response( |
46 | - uri, &storage, object, operation, size, namespace, |
47 | + uri, &headers, &storage, object, operation, size, |
48 | + namespace, |
49 | ) |
50 | .await) |
51 | } |
52 | @@ -298,6 +302,7 @@ where |
53 | |
54 | async fn basic_response<E, S>( |
55 | uri: Uri, |
56 | + headers: &HeaderMap, |
57 | storage: &S, |
58 | object: lfs::RequestObject, |
59 | op: lfs::Operation, |
60 | @@ -389,7 +394,7 @@ where |
61 | uri, namespace, object.oid |
62 | ) |
63 | }), |
64 | - header: None, |
65 | + header: extract_auth_header(headers), |
66 | expires_in: Some(upload_expiry_secs), |
67 | expires_at: None, |
68 | }), |
69 | @@ -398,7 +403,7 @@ where |
70 | "{}api/{}/objects/verify", |
71 | uri, namespace |
72 | ), |
73 | - header: None, |
74 | + header: extract_auth_header(headers), |
75 | expires_in: None, |
76 | expires_at: None, |
77 | }), |
78 | @@ -428,7 +433,7 @@ where |
79 | uri, namespace, object.oid |
80 | ) |
81 | }), |
82 | - header: None, |
83 | + header: extract_auth_header(headers), |
84 | expires_in: None, |
85 | expires_at: None, |
86 | }), |
87 | @@ -451,6 +456,31 @@ where |
88 | } |
89 | } |
90 | |
91 | + /// Extracts the authorization headers so that they can be reflected back to the |
92 | + /// `git-lfs` client. If we're behind a reverse proxy that provides |
93 | + /// authentication, the `git-lfs` client will send an `Authorization` header on |
94 | + /// the first connection, however in order for subsequent requests to also be |
95 | + /// authenticated, the `header` field in the `lfs::ResponseObject` must be |
96 | + /// populated. |
97 | + fn extract_auth_header( |
98 | + headers: &HeaderMap, |
99 | + ) -> Option<BTreeMap<String, String>> { |
100 | + let headers = headers.iter().filter_map(|(k, v)| { |
101 | + if k == http::header::AUTHORIZATION { |
102 | + let value = String::from_utf8_lossy(v.as_bytes()).to_string(); |
103 | + Some((k.to_string(), value)) |
104 | + } else { |
105 | + None |
106 | + } |
107 | + }); |
108 | + let map = BTreeMap::from_iter(headers); |
109 | + if map.is_empty() { |
110 | + None |
111 | + } else { |
112 | + Some(map) |
113 | + } |
114 | + } |
115 | + |
116 | impl<S> Service<Request<Body>> for App<S> |
117 | where |
118 | S: Storage + Clone + Send + Sync + 'static, |