diff options
author | Rutger Broekhoff | 2024-07-12 00:29:57 +0200 |
---|---|---|
committer | Rutger Broekhoff | 2024-07-12 00:29:57 +0200 |
commit | bc709f0f23be345a1e2ccd06acd36bd5dac40bde (patch) | |
tree | 4ffe66b1ac246e0a9eab4a2649a7db5bb3a1ff0a /gitolfs3-server/src/authz.rs | |
parent | 3e67a3486eed22522f4352503ef7067ca81a8050 (diff) | |
download | gitolfs3-main.tar.gz gitolfs3-main.zip |
Diffstat (limited to 'gitolfs3-server/src/authz.rs')
-rw-r--r-- | gitolfs3-server/src/authz.rs | 80 |
1 files changed, 45 insertions, 35 deletions
diff --git a/gitolfs3-server/src/authz.rs b/gitolfs3-server/src/authz.rs index 0674cef..8a5f21f 100644 --- a/gitolfs3-server/src/authz.rs +++ b/gitolfs3-server/src/authz.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use std::collections::HashSet; | 1 | use std::collections::HashSet; |
2 | 2 | ||
3 | use axum::http::{header, HeaderMap, StatusCode}; | 3 | use axum::http; |
4 | use chrono::{DateTime, Utc}; | 4 | use chrono::{DateTime, Utc}; |
5 | use gitolfs3_common::{generate_tag, Claims, Digest, Oid, Operation, SpecificClaims}; | 5 | use gitolfs3_common::{generate_tag, Claims, Digest, Oid, Operation, SpecificClaims}; |
6 | 6 | ||
@@ -11,31 +11,12 @@ use crate::{ | |||
11 | 11 | ||
12 | pub struct Trusted(pub bool); | 12 | pub struct Trusted(pub bool); |
13 | 13 | ||
14 | fn forwarded_from_trusted_host( | ||
15 | headers: &HeaderMap, | ||
16 | trusted: &HashSet<String>, | ||
17 | ) -> Result<bool, GitLfsErrorResponse<'static>> { | ||
18 | if let Some(forwarded_host) = headers.get("X-Forwarded-Host") { | ||
19 | if let Ok(forwarded_host) = forwarded_host.to_str() { | ||
20 | if trusted.contains(forwarded_host) { | ||
21 | return Ok(true); | ||
22 | } | ||
23 | } else { | ||
24 | return Err(make_error_resp( | ||
25 | StatusCode::NOT_FOUND, | ||
26 | "Invalid X-Forwarded-Host header", | ||
27 | )); | ||
28 | } | ||
29 | } | ||
30 | Ok(false) | ||
31 | } | ||
32 | |||
33 | pub fn authorize_batch( | 14 | pub fn authorize_batch( |
34 | conf: &AuthorizationConfig, | 15 | conf: &AuthorizationConfig, |
35 | repo_path: &str, | 16 | repo_path: &str, |
36 | public: bool, | 17 | public: bool, |
37 | operation: Operation, | 18 | operation: Operation, |
38 | headers: &HeaderMap, | 19 | headers: &http::HeaderMap, |
39 | ) -> Result<Trusted, GitLfsErrorResponse<'static>> { | 20 | ) -> Result<Trusted, GitLfsErrorResponse<'static>> { |
40 | // - No authentication required for downloading exported repos | 21 | // - No authentication required for downloading exported repos |
41 | // - When authenticated: | 22 | // - When authenticated: |
@@ -57,7 +38,7 @@ fn authorize_batch_unauthenticated( | |||
57 | conf: &AuthorizationConfig, | 38 | conf: &AuthorizationConfig, |
58 | public: bool, | 39 | public: bool, |
59 | operation: Operation, | 40 | operation: Operation, |
60 | headers: &HeaderMap, | 41 | headers: &http::HeaderMap, |
61 | ) -> Result<Trusted, GitLfsErrorResponse<'static>> { | 42 | ) -> Result<Trusted, GitLfsErrorResponse<'static>> { |
62 | let trusted = forwarded_from_trusted_host(headers, &conf.trusted_forwarded_hosts)?; | 43 | let trusted = forwarded_from_trusted_host(headers, &conf.trusted_forwarded_hosts)?; |
63 | match operation { | 44 | match operation { |
@@ -71,7 +52,7 @@ fn authorize_batch_unauthenticated( | |||
71 | return Err(REPO_NOT_FOUND); | 52 | return Err(REPO_NOT_FOUND); |
72 | } | 53 | } |
73 | Err(make_error_resp( | 54 | Err(make_error_resp( |
74 | StatusCode::FORBIDDEN, | 55 | http::StatusCode::FORBIDDEN, |
75 | "Authentication required to upload", | 56 | "Authentication required to upload", |
76 | )) | 57 | )) |
77 | } | 58 | } |
@@ -94,7 +75,7 @@ pub fn authorize_get( | |||
94 | conf: &AuthorizationConfig, | 75 | conf: &AuthorizationConfig, |
95 | repo_path: &str, | 76 | repo_path: &str, |
96 | oid: Oid, | 77 | oid: Oid, |
97 | headers: &HeaderMap, | 78 | headers: &http::HeaderMap, |
98 | ) -> Result<(), GitLfsErrorResponse<'static>> { | 79 | ) -> Result<(), GitLfsErrorResponse<'static>> { |
99 | let claims = VerifyClaimsInput { | 80 | let claims = VerifyClaimsInput { |
100 | specific_claims: SpecificClaims::Download(oid), | 81 | specific_claims: SpecificClaims::Download(oid), |
@@ -102,27 +83,48 @@ pub fn authorize_get( | |||
102 | }; | 83 | }; |
103 | if !verify_claims(conf, &claims, headers)? { | 84 | if !verify_claims(conf, &claims, headers)? { |
104 | return Err(make_error_resp( | 85 | return Err(make_error_resp( |
105 | StatusCode::UNAUTHORIZED, | 86 | http::StatusCode::UNAUTHORIZED, |
106 | "Repository not found", | 87 | "Repository not found", |
107 | )); | 88 | )); |
108 | } | 89 | } |
109 | Ok(()) | 90 | Ok(()) |
110 | } | 91 | } |
111 | 92 | ||
112 | pub struct VerifyClaimsInput<'a> { | 93 | fn forwarded_from_trusted_host( |
113 | pub specific_claims: SpecificClaims, | 94 | headers: &http::HeaderMap, |
114 | pub repo_path: &'a str, | 95 | trusted: &HashSet<String>, |
96 | ) -> Result<bool, GitLfsErrorResponse<'static>> { | ||
97 | if let Some(forwarded_host) = headers.get("X-Forwarded-Host") { | ||
98 | if let Ok(forwarded_host) = forwarded_host.to_str() { | ||
99 | if trusted.contains(forwarded_host) { | ||
100 | return Ok(true); | ||
101 | } | ||
102 | } else { | ||
103 | return Err(make_error_resp( | ||
104 | http::StatusCode::NOT_FOUND, | ||
105 | "Invalid X-Forwarded-Host header", | ||
106 | )); | ||
107 | } | ||
108 | } | ||
109 | Ok(false) | ||
110 | } | ||
111 | |||
112 | struct VerifyClaimsInput<'a> { | ||
113 | specific_claims: SpecificClaims, | ||
114 | repo_path: &'a str, | ||
115 | } | 115 | } |
116 | 116 | ||
117 | fn verify_claims( | 117 | fn verify_claims( |
118 | conf: &AuthorizationConfig, | 118 | conf: &AuthorizationConfig, |
119 | claims: &VerifyClaimsInput, | 119 | claims: &VerifyClaimsInput, |
120 | headers: &HeaderMap, | 120 | headers: &http::HeaderMap, |
121 | ) -> Result<bool, GitLfsErrorResponse<'static>> { | 121 | ) -> Result<bool, GitLfsErrorResponse<'static>> { |
122 | const INVALID_AUTHZ_HEADER: GitLfsErrorResponse = | 122 | const INVALID_AUTHZ_HEADER: GitLfsErrorResponse = make_error_resp( |
123 | make_error_resp(StatusCode::BAD_REQUEST, "Invalid authorization header"); | 123 | http::StatusCode::BAD_REQUEST, |
124 | "Invalid authorization header", | ||
125 | ); | ||
124 | 126 | ||
125 | let Some(authz) = headers.get(header::AUTHORIZATION) else { | 127 | let Some(authz) = headers.get(http::header::AUTHORIZATION) else { |
126 | return Ok(false); | 128 | return Ok(false); |
127 | }; | 129 | }; |
128 | let authz = authz.to_str().map_err(|_| INVALID_AUTHZ_HEADER)?; | 130 | let authz = authz.to_str().map_err(|_| INVALID_AUTHZ_HEADER)?; |
@@ -141,7 +143,12 @@ fn verify_claims( | |||
141 | }, | 143 | }, |
142 | &conf.key, | 144 | &conf.key, |
143 | ) | 145 | ) |
144 | .ok_or_else(|| make_error_resp(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"))?; | 146 | .ok_or_else(|| { |
147 | make_error_resp( | ||
148 | http::StatusCode::INTERNAL_SERVER_ERROR, | ||
149 | "Internal server error", | ||
150 | ) | ||
151 | })?; | ||
145 | if tag != expected_tag { | 152 | if tag != expected_tag { |
146 | return Err(INVALID_AUTHZ_HEADER); | 153 | return Err(INVALID_AUTHZ_HEADER); |
147 | } | 154 | } |
@@ -175,8 +182,11 @@ fn test_validate_claims() { | |||
175 | repo_path: claims.repo_path, | 182 | repo_path: claims.repo_path, |
176 | specific_claims: claims.specific_claims, | 183 | specific_claims: claims.specific_claims, |
177 | }; | 184 | }; |
178 | let mut headers = HeaderMap::new(); | 185 | let mut headers = http::HeaderMap::new(); |
179 | headers.insert(header::AUTHORIZATION, header_value.try_into().unwrap()); | 186 | headers.insert( |
187 | http::header::AUTHORIZATION, | ||
188 | header_value.try_into().unwrap(), | ||
189 | ); | ||
180 | 190 | ||
181 | assert!(verify_claims(&conf, &verification_claims, &headers).unwrap()); | 191 | assert!(verify_claims(&conf, &verification_claims, &headers).unwrap()); |
182 | } | 192 | } |