Commit 015c6f5f authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent 86b2a17f
Pipeline #563 failed with stage
......@@ -43,6 +43,7 @@ type AuthCacheEntry struct {
// Entries are keyed by project + credentials
type AuthCacheKey struct {
project string
userinfo string // user[:password] or ""
query string // e.g. with passing in private_token=...
header string // request header url-encoded, e.g. PRIVATE-TOKEN=...
}
......@@ -63,7 +64,13 @@ func NewAuthCache(a *API) *AuthCache {
// Verify that download access is ok or not.
// first we try to use the cache; if information is not there -> ask auth backend
// download is ok if AuthReply.RepoPath != ""
func (c *AuthCache) VerifyDownloadAccess(project string, query string, header http.Header) AuthReply {
func (c *AuthCache) VerifyDownloadAccess(project string, userinfo *url.Userinfo, query string, header http.Header) AuthReply {
// In addition to userinfo:
u := ""
if userinfo != nil {
u = userinfo.String()
}
// Use only tokens from query/header and selected cookies to minimize cache and avoid
// creating redundant cache entries because of e.g. unrelated headers.
queryValues, _ := url.ParseQuery(query) // this is what URL.Query() does
......@@ -95,7 +102,7 @@ func (c *AuthCache) VerifyDownloadAccess(project string, query string, header ht
h["Cookie"] = []string{hc}
}
key := AuthCacheKey{project, q.Encode(), h.Encode()}
key := AuthCacheKey{project, u, q.Encode(), h.Encode()}
return c.verifyDownloadAccess(key)
}
......@@ -194,6 +201,18 @@ func (c *AuthCache) refreshEntry(auth *AuthCacheEntry, key AuthCacheKey) {
// Ask auth backend about cache key
func (c *AuthCache) askAuthBackend(key AuthCacheKey) AuthReply {
// key.userinfo -> url.Userinfo
var user *url.Userinfo
if key.userinfo != "" {
u, err := url.Parse(key.userinfo + "@/")
// url prepared-to-parse userinfo must be valid
panic(err)
if (u.User == nil) {
panic(fmt.Errorf("userinfo parse: `%s` -> empty", key.userinfo))
}
user = u.User
}
// key.header -> url.Values -> http.Header
hv, err := url.ParseQuery(key.header)
if err != nil {
......@@ -206,7 +225,7 @@ func (c *AuthCache) askAuthBackend(key AuthCacheKey) AuthReply {
header[k] = v
}
return c.a.verifyDownloadAccess(key.project, key.query, header)
return c.a.verifyDownloadAccess(key.project, user, key.query, header)
}
// Ask auth backend about whether download is ok for a project.
......@@ -215,11 +234,11 @@ func (c *AuthCache) askAuthBackend(key AuthCacheKey) AuthReply {
//
// Replies from authentication backend are cached for 30 seconds as each
// request to Rails code is heavy and slow.
func (a *API) VerifyDownloadAccess(project, query string, header http.Header) AuthReply {
return a.authCache.VerifyDownloadAccess(project, query, header)
func (a *API) VerifyDownloadAccess(project string, user *url.Userinfo, query string, header http.Header) AuthReply {
return a.authCache.VerifyDownloadAccess(project, user, query, header)
}
func (a *API) verifyDownloadAccess(project, query string, header http.Header) AuthReply {
func (a *API) verifyDownloadAccess(project string, user *url.Userinfo, query string, header http.Header) AuthReply {
authReply := AuthReply{
RawReply: httptest.NewRecorder(),
}
......@@ -230,7 +249,12 @@ func (a *API) verifyDownloadAccess(project, query string, header http.Header) Au
// - that's why we auth backend to authenticate as if it was request to
// get repo archive and propagate request query and header.
// url := project + ".git/info/refs?service=git-upload-pack"
url := project + "/repository/archive.zip"
url := ""
if user != nil {
url += fmt.Sprintf("%s@", user)
}
url += project + "/repository/archive.zip"
if query != "" {
url += "?" + query
}
......
......@@ -40,7 +40,7 @@ func handleGetBlobRaw(a *api.API, w http.ResponseWriter, r *http.Request) {
refpath := u.Path[rawLoc[1]:]
// Query download access auth for this project
authReply := a.VerifyDownloadAccess(project, u.RawQuery, r.Header)
authReply := a.VerifyDownloadAccess(project, u.User, u.RawQuery, r.Header)
if authReply.RepoPath == "" {
// access denied - copy auth reply to client in full -
// there are HTTP code and other headers / body relevant for
......
......@@ -815,7 +815,13 @@ func TestPrivateBlobDownload(t *testing.T) {
token_ok2 := r.Header.Get("BBB-TOKEN") == "TOKEN-4BBB"
cookie, _ := r.Cookie("_gitlab_session")
cookie_ok3 := (cookie != nil && cookie.Value == "COOKIE-CCC")
if !(token_ok1 || token_ok2 || cookie_ok3) {
user := r.URL.User
user_ok4 := false
if user != nil {
password, _ := user.Password()
user_ok4 = (user.Username() == "user-ddd" && password == "password-eee")
}
if !(token_ok1 || token_ok2 || cookie_ok3 || user_ok4) {
w.WriteHeader(403)
fmt.Fprintf(w, "Access denied")
return
......@@ -854,4 +860,13 @@ func TestPrivateBlobDownload(t *testing.T) {
dl.Header.Set("Cookie", "alpha=1; _gitlab_session=COOKIE-CCC; beta=2")
dl.ExpectCode("/5f923865/README.md", 200)
dl.ExpectSha1("/5f923865/README.md", "5f7af35c185a9e5face2f4afb6d7c4f00328d04c")
dl.Header = make(http.Header) // clear
dl.ExpectCode("/5f923865/README.md", 403)
dlBaseUrl := dl.urlPrefix
dl.urlPrefix = "user-aaa:password-bbb@" + dlBaseUrl
dl.ExpectCode("/5f923865/README.md", 403)
dl.urlPrefix = "user-ddd:password-eee@" + dlBaseUrl
dl.ExpectCode("/5f923865/README.md", 200)
dl.ExpectSha1("/5f923865/README.md", "5f7af35c185a9e5face2f4afb6d7c4f00328d04c")
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment