Skip to content

Commit

Permalink
[clone] http basic auth support for all kinds of calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Aug 29, 2020
1 parent c5b2d04 commit 572fb54
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 13 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Please see _'Development Status'_ for a listing of all crates and their capabili
* [x] V2 handshake
* [x] send command request, receive response with sideband support
* [x] http(s)://<service>
* [ ] pass in (or set) basic auth
* [x] set identity for basic authentication
* [x] V1 handshake
* [x] send values + receive data with sidebands
* [x] V2 handshake
Expand Down
40 changes: 28 additions & 12 deletions git-transport/src/client/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,26 @@ impl<H: Http> Transport<H> {
}
Ok(())
}

fn add_basic_auth_if_present(&self, headers: &mut Vec<Cow<str>>) -> Result<(), client::Error> {
if let Some(identity) = &self.identity {
match identity {
client::Identity::Account { username, password } => {
#[cfg(not(debug_assertions))]
if self.url.starts_with("http://") {
return Err(client::Error::AuthenticationRefused(
"Will not send credentials in clear text over http",
));
}
headers.push(Cow::Owned(format!(
"Authorization: Basic {}",
base64::encode(format!("{}:{}", username, password))
)))
}
}
}
Ok(())
}
}

fn append_url(base: &str, suffix: &str) -> String {
Expand All @@ -75,14 +95,7 @@ impl<H: Http> client::Transport for Transport<H> {
if self.version != Protocol::V1 {
dynamic_headers.push(Cow::Owned(format!("Git-Protocol: version={}", self.version as usize)));
}
if let Some(identity) = &self.identity {
match identity {
client::Identity::Account { username, password } => dynamic_headers.push(Cow::Owned(format!(
"Authorization: Basic {}",
base64::encode(format!("{}:{}", username, password))
))),
}
}
self.add_basic_auth_if_present(&mut dynamic_headers)?;
let GetResponse { headers, body } = self.http.get(&url, static_headers.iter().chain(&dynamic_headers))?;
<Transport<H>>::check_content_type(service, "advertisement", headers)?;

Expand Down Expand Up @@ -122,16 +135,19 @@ impl<H: Http> client::Transport for Transport<H> {
) -> Result<client::RequestWriter, client::Error> {
let service = self.service.expect("handshake() must have been called first");
let url = append_url(&self.url, service.as_str());
let headers = &[
format!("Content-Type: application/x-{}-request", service.as_str()),
format!("Accept: application/x-{}-result", service.as_str()),
let static_headers = &[
Cow::Owned(format!("Content-Type: application/x-{}-request", service.as_str())),
format!("Accept: application/x-{}-result", service.as_str()).into(),
"Expect:".into(), // needed to avoid sending Expect: 100-continue, which adds another response and only CURL wants that
];
let mut dynamic_headers = Vec::new();
self.add_basic_auth_if_present(&mut dynamic_headers)?;

let PostResponse {
headers,
body,
post_body,
} = self.http.post(&url, headers)?;
} = self.http.post(&url, static_headers.iter().chain(&dynamic_headers))?;
let line_provider = self
.line_provider
.as_mut()
Expand Down
2 changes: 2 additions & 0 deletions git-transport/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum Error {
ExpectedDataLine,
#[error("The transport layer does not support authentication")]
AuthenticationUnsupported,
#[error("The transport layer refuses to use a given identity: {0}")]
AuthenticationRefused(&'static str),
#[error(transparent)]
Http(#[from] HttpError),
}
Expand Down
22 changes: 22 additions & 0 deletions git-transport/tests/client/http/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,28 @@ Authorization: Basic dXNlcjpwYXNzd29yZA==
.collect::<Vec<_>>()
);

server.next_read_and_respond_with(fixture_bytes("v1/http-handshake.response"));
client.request(client::WriteMode::Binary, Vec::new())?;

assert_eq!(
server.received_as_string().lines().collect::<Vec<_>>(),
format!(
"POST /path/not-important/git-upload-pack HTTP/1.1
Host: 127.0.0.1:{}
Transfer-Encoding: chunked
Content-Type: application/x-git-upload-pack-request
Accept: application/x-git-upload-pack-result
Authorization: Basic dXNlcjpwYXNzd29yZA==
0
",
server.addr.port(),
)
.lines()
.collect::<Vec<_>>()
);

Ok(())
}

Expand Down

0 comments on commit 572fb54

Please sign in to comment.