Skip to content

lfsapi: re-authenticate HTTP redirects when needed#3028

Merged
ttaylorr merged 4 commits intomasterfrom
lfsapi-authenticate-redirects
May 29, 2018
Merged

lfsapi: re-authenticate HTTP redirects when needed#3028
ttaylorr merged 4 commits intomasterfrom
lfsapi-authenticate-redirects

Conversation

@ttaylorr
Copy link
Contributor

@ttaylorr ttaylorr commented May 25, 2018

This pull request teaches package lfsapi to re-authenticate HTTP requests when the response indicates a redirection.

When an HTTP request is sent through package lfsapi, there are a few paths that we can take down into do() and doWithRedirects(). The one that we're concerned with involves one of two outcomes:

  1. We send an HTTP request that is responded to with a 301 or 302 code. If we're within our retry limit, we construct a new request sent to the new host/path, and copy over the headers from the old request [1]. Notably, we do not copy the Authorization header iff the new host differs from the old one, as a security concern. We retry the request.

  2. Same setup as (1), except we have exceeded our retry count. We complain and fail [2].

In case (1) this security measure has the opportunity to fail when the redirected host differs from the requested host and the redirected host requires authentication. Because we recur early on doWithRedirects(), and not DoWithAuth(), we don't re-authenticate the request, and instead devolve into a case like #3025.

But, recurring through DoWithAuth() would throw away our state via, allowing authenticated request to flow through different pairs of hosts infinitely. That's no good.

Instead, let's try this:

  • 1a3a19c: set up do() to take via []*http.Request, the list of requests that have lead up to this one (redirects), or nil if there are no such requests.
  • b1c2b42: keep track of the remote associated with a given request, or "" if no remote matches, or we don't care which remote is associated.
  • 5257e58: branch through doWithAuth and override the via list such that we can re-authenticate requests across different hosts.

One tricky thing in 5257e58 is that we don't actually know whether the original request was designated to be authenticated or not. An additional tricky thing is that we might never authenticate the original request, and instead load from http.[$url.]extraHeaders, such that Authorization header is present, but Git LFS didn't put it there.

In all of those cases, we reduce the problem to checking whether the original request contained an Authorization header.

Closes: #3025.

/cc @git-lfs/core #3025

}

func (c *Client) doWithCreds(req *http.Request, credHelper CredentialHelper, creds Creds, credsURL *url.URL, access Access) (*http.Response, error) {
func (c *Client) doWithCreds(req *http.Request, credHelper CredentialHelper, creds Creds, credsURL *url.URL, access Access, via []*http.Request) (*http.Response, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Monster arg list!

@ashmckenzie
Copy link

Looks good! I started to prepare a fix but found myself putting remote in a lot of places and bailed.

Challenging one to test!

@ttaylorr
Copy link
Contributor Author

Challenging one to test!

Thanks for the reminder. We test package lfsapi from Go, mostly as a consequence of not having established ways to run "fake" HTTP servers from the integration tests. I added a test exercising this new behavior here: 8715e89.

@ttaylorr
Copy link
Contributor Author

Windows failures are unrelated.

@ttaylorr ttaylorr merged commit 37bc00a into master May 29, 2018
@ttaylorr ttaylorr deleted the lfsapi-authenticate-redirects branch May 29, 2018 01:35
@dosire
Copy link

dosire commented Jul 17, 2018

Thanks for fixing this @ttaylorr

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants