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

.

parent a462b6e9
......@@ -51,7 +51,7 @@ type AuthCacheEntry struct {
// Authorization reply cache
// {} project -> AuthCacheEntry
//
// XXX should be not only project (privateToken etc...)
// XXX should be not only project (private_token etc...)
type AuthCache struct {
mu sync.RWMutex // guards .cached
cached map[string]*AuthCacheEntry
......@@ -166,7 +166,8 @@ func askAuthBackend(u *upstream, project string) AuthReply {
// Request to auth backend to verify whether download is possible via
// 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)
if err != nil {
fail500(authReply.RawReply, "GET git-upload-pack", err)
......@@ -196,23 +197,18 @@ func verifyDownloadAccess(u *upstream, project string) AuthReply {
}
// HTTP handler for `.../raw/<ref>/path`
var projectRe = regexp.MustCompile(`^/[\w\.-]+/[\w\.-]+/`)
var rawRe = regexp.MustCompile(`/raw/`)
func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) {
// Extract project & refpath
// <project>/raw/branch/file -> <project>, branch/file
project := projectRe.FindString(r.Request.URL.Path)
if project == "" || project[len(project)-1] != '/' {
rawLoc := rawRe.FindStringIndex(r.Request.URL.Path)
if rawLoc == nil {
fail500(w, "extract project name", nil) // XXX err=nil
return
}
refpath := r.Request.URL.Path[len(project):]
if refpath[:4] != "raw/" {
fail500(w, "refpath != raw/...", nil) // XXX err=nil
return
}
project = project[:len(project)-1] // strip '.../'
refpath = refpath[4:] // strip 'raw/...'
project := r.Request.URL.Path[:rawLoc[0]]
refpath := r.Request.URL.Path[rawLoc[1]:]
// Query download access auth for this project
authReply := verifyDownloadAccess(r.u, project)
......@@ -224,7 +220,13 @@ func handleGetBlobRaw(w http.ResponseWriter, r *gitRequest) {
w.Header()[k] = v
}
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
}
......
......@@ -2,6 +2,7 @@ package main
import (
"bytes"
"crypto/sha1"
"encoding/json"
"fmt"
"io/ioutil"
......@@ -453,3 +454,71 @@ func deniedXSendfileDownload(t *testing.T, contentFilename string, filePath stri
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{
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(`/uploads/`), handleSendFile},
gitService{"GET", regexp.MustCompile(`/raw/.+\z`), handleGetBlobRaw},
gitService{"GET", regexp.MustCompile(`/raw/`), handleGetBlobRaw},
// Git LFS
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