Commit 4b3b9a15 authored by Kirill Smelkov's avatar Kirill Smelkov

.

parent a462b6e9
...@@ -51,7 +51,7 @@ type AuthCacheEntry struct { ...@@ -51,7 +51,7 @@ type AuthCacheEntry struct {
// Authorization reply cache // Authorization reply cache
// {} project -> AuthCacheEntry // {} project -> AuthCacheEntry
// //
// XXX should be not only project (privateToken etc...) // XXX should be not only project (private_token etc...)
type AuthCache struct { type AuthCache struct {
mu sync.RWMutex // guards .cached mu sync.RWMutex // guards .cached
cached map[string]*AuthCacheEntry cached map[string]*AuthCacheEntry
...@@ -166,7 +166,8 @@ func askAuthBackend(u *upstream, project string) AuthReply { ...@@ -166,7 +166,8 @@ func askAuthBackend(u *upstream, project string) AuthReply {
// Request to auth backend to verify whether download is possible via // Request to auth backend to verify whether download is possible via
// asking as `git fetch` would do. // asking as `git fetch` would do.
// XXX privateToken not propagated, etc ... // XXX private_token not propagated, etc ...
// XXX PRIVATE-TOKEN (if header)
reqDownloadAccess, err := http.NewRequest("GET", project+".git/info/refs?service=git-upload-pack", nil) reqDownloadAccess, err := http.NewRequest("GET", project+".git/info/refs?service=git-upload-pack", nil)
if err != nil { if err != nil {
fail500(authReply.RawReply, "GET git-upload-pack", err) fail500(authReply.RawReply, "GET git-upload-pack", err)
...@@ -196,23 +197,18 @@ func verifyDownloadAccess(u *upstream, project string) AuthReply { ...@@ -196,23 +197,18 @@ func verifyDownloadAccess(u *upstream, project string) AuthReply {
} }
// HTTP handler for `.../raw/<ref>/path` // HTTP handler for `.../raw/<ref>/path`
var projectRe = regexp.MustCompile(`^/[\w\.-]+/[\w\.-]+/`) var rawRe = regexp.MustCompile(`/raw/`)
func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) { func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) {
// Extract project & refpath // Extract project & refpath
// <project>/raw/branch/file -> <project>, branch/file // <project>/raw/branch/file -> <project>, branch/file
project := projectRe.FindString(r.Request.URL.Path) rawLoc := rawRe.FindStringIndex(r.Request.URL.Path)
if project == "" || project[len(project)-1] != '/' { if rawLoc == nil {
fail500(w, "extract project name", nil) // XXX err=nil fail500(w, "extract project name", nil) // XXX err=nil
return return
} }
refpath := r.Request.URL.Path[len(project):] project := r.Request.URL.Path[:rawLoc[0]]
if refpath[:4] != "raw/" { refpath := r.Request.URL.Path[rawLoc[1]:]
fail500(w, "refpath != raw/...", nil) // XXX err=nil
return
}
project = project[:len(project)-1] // strip '.../'
refpath = refpath[4:] // strip 'raw/...'
// Query download access auth for this project // Query download access auth for this project
authReply := verifyDownloadAccess(r.u, project) authReply := verifyDownloadAccess(r.u, project)
...@@ -224,7 +220,13 @@ func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) { ...@@ -224,7 +220,13 @@ func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) {
w.Header()[k] = v w.Header()[k] = v
} }
w.WriteHeader(authReply.RawReply.Code) w.WriteHeader(authReply.RawReply.Code)
io.Copy(w, authReply.RawReply.Body) // NOTE do not consume authReply.RawReply.Body with io.Copy -
// this way it will be read one time only and next reads will
// be empty.
_, err := w.Write(authReply.RawReply.Body.Bytes())
if err != nil {
logContext("writing authReply.RawReply.Body", err)
}
return return
} }
......
...@@ -2,6 +2,7 @@ package main ...@@ -2,6 +2,7 @@ package main
import ( import (
"bytes" "bytes"
"crypto/sha1"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
...@@ -453,3 +454,71 @@ func deniedXSendfileDownload(t *testing.T, contentFilename string, filePath stri ...@@ -453,3 +454,71 @@ func deniedXSendfileDownload(t *testing.T, contentFilename string, filePath stri
t.Fatal("Unexpected file contents in download") t.Fatal("Unexpected file contents in download")
} }
} }
// sha1(data) as human-readable string
func sha1s(data []byte) string {
return fmt.Sprintf("%x", sha1.Sum(data))
}
func download(t *testing.T, url string) (*http.Response, []byte) {
resp, err := http.Get(url)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
return resp, body
}
func TestAllowedBlobDownload(t *testing.T) {
// Prepare test server and backend
ts := testAuthServer(nil, 200, gitOkBody(t))
defer ts.Close()
ws := startWorkhorseServer(ts.URL)
defer ws.Close()
downloadRaw := func (refpath string) (*http.Response, []byte) {
return download(t, fmt.Sprintf("%s/%s/raw/%s", ws.URL, testProject, refpath))
}
downloadAndExpectSha1 := func (refpath, expectSha1 string) {
_, out := downloadRaw(refpath)
outSha1 := sha1s(out)
if outSha1 != expectSha1 {
t.Fatal("Unexpected content in blob download")
}
}
downloadAndExpect := func (refpath, expect string) {
downloadAndExpectSha1(refpath, sha1s([]byte(expect)))
}
downloadAndExpect("5f923865/README.md", "testme\n======\n\nSample repo for testing gitlab features\n")
downloadAndExpectSha1("5f923865/files/ruby/popen.rb", "68990cc20fa74383358797a27967fa2b45d7d8f6")
downloadAndExpectSha1("874797c3/files/ruby/popen.rb", "4c266708f2bfd7ca3fed3f7ec74253f92ff3fe73")
resp, _ := downloadRaw("master/non-existing-file")
if resp.StatusCode != 404 {
t.Fatalf("Unexpected status code (expected 404, got %v)", resp.StatusCode)
}
}
func TestDeniedBlobDownload(t *testing.T) {
// Prepare test server and backend
ts := testAuthServer(nil, 403, "Access denied")
defer ts.Close()
ws := startWorkhorseServer(ts.URL)
defer ws.Close()
downloadAndExpect := func (refpath string, code int) {
resp, _ := download(t, fmt.Sprintf("%s/%s/raw/%s", ws.URL, testProject, refpath))
if resp.StatusCode != code {
t.Fatalf("Unexpected status code (expected %v, got %v)", code, resp.StatusCode)
}
}
downloadAndExpect("5f923865/README.md", 403)
downloadAndExpect("5f923865/files/ruby/popen.rb", 403)
downloadAndExpect("874797c3/files/ruby/popen.rb", 403)
downloadAndExpect("master/non-existing-file", 403)
}
...@@ -75,7 +75,7 @@ var gitServices = [...]gitService{ ...@@ -75,7 +75,7 @@ var gitServices = [...]gitService{
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), repoPreAuthorizeHandler(handleGetArchive)}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.gz\z`), repoPreAuthorizeHandler(handleGetArchive)},
gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), repoPreAuthorizeHandler(handleGetArchive)}, gitService{"GET", regexp.MustCompile(`/repository/archive.tar.bz2\z`), repoPreAuthorizeHandler(handleGetArchive)},
gitService{"GET", regexp.MustCompile(`/uploads/`), handleSendFile}, gitService{"GET", regexp.MustCompile(`/uploads/`), handleSendFile},
gitService{"GET", regexp.MustCompile(`/raw/.+\z`), handleGetBlobRaw}, gitService{"GET", regexp.MustCompile(`/raw/`), handleGetBlobRaw},
// Git LFS // Git LFS
gitService{"PUT", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`), lfsAuthorizeHandler(handleStoreLfsObject)}, gitService{"PUT", regexp.MustCompile(`/gitlab-lfs/objects/([0-9a-f]{64})/([0-9]+)\z`), lfsAuthorizeHandler(handleStoreLfsObject)},
......
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