Commit 81bcc097 authored by Kamil Trzciński's avatar Kamil Trzciński

Prevent uploading two files as artifacts in single request

parent ebb62ecc
...@@ -136,7 +136,8 @@ func TestUploadHandlerSendingToExternalStorage(t *testing.T) { ...@@ -136,7 +136,8 @@ func TestUploadHandlerSendingToExternalStorage(t *testing.T) {
contentBuffer, contentType := createTestMultipartForm(t, archiveData) contentBuffer, contentType := createTestMultipartForm(t, archiveData)
response := testUploadArtifacts(contentType, &contentBuffer, t, ts) response := testUploadArtifacts(contentType, &contentBuffer, t, ts)
testhelper.AssertResponseCode(t, response, 200) testhelper.AssertResponseCode(t, response, http.StatusOK)
testhelper.AssertResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderPresent)
assert.Equal(t, 1, storeServerCalled, "store should be called only once") assert.Equal(t, 1, storeServerCalled, "store should be called only once")
assert.Equal(t, 1, responseProcessorCalled, "response processor should be called only once") assert.Equal(t, 1, responseProcessorCalled, "response processor should be called only once")
}) })
...@@ -166,7 +167,7 @@ func TestUploadHandlerSendingToExternalStorageAndStorageServerUnreachable(t *tes ...@@ -166,7 +167,7 @@ func TestUploadHandlerSendingToExternalStorageAndStorageServerUnreachable(t *tes
defer ts.Close() defer ts.Close()
response := testUploadArtifactsFromTestZip(t, ts) response := testUploadArtifactsFromTestZip(t, ts)
testhelper.AssertResponseCode(t, response, 500) testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
} }
func TestUploadHandlerSendingToExternalStorageAndInvalidURLIsUsed(t *testing.T) { func TestUploadHandlerSendingToExternalStorageAndInvalidURLIsUsed(t *testing.T) {
...@@ -192,7 +193,7 @@ func TestUploadHandlerSendingToExternalStorageAndInvalidURLIsUsed(t *testing.T) ...@@ -192,7 +193,7 @@ func TestUploadHandlerSendingToExternalStorageAndInvalidURLIsUsed(t *testing.T)
defer ts.Close() defer ts.Close()
response := testUploadArtifactsFromTestZip(t, ts) response := testUploadArtifactsFromTestZip(t, ts)
testhelper.AssertResponseCode(t, response, 500) testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
} }
func TestUploadHandlerSendingToExternalStorageAndItReturnsAnError(t *testing.T) { func TestUploadHandlerSendingToExternalStorageAndItReturnsAnError(t *testing.T) {
...@@ -230,7 +231,7 @@ func TestUploadHandlerSendingToExternalStorageAndItReturnsAnError(t *testing.T) ...@@ -230,7 +231,7 @@ func TestUploadHandlerSendingToExternalStorageAndItReturnsAnError(t *testing.T)
defer ts.Close() defer ts.Close()
response := testUploadArtifactsFromTestZip(t, ts) response := testUploadArtifactsFromTestZip(t, ts)
testhelper.AssertResponseCode(t, response, 500) testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
assert.Equal(t, 1, putCalledTimes, "upload should be called only once") assert.Equal(t, 1, putCalledTimes, "upload should be called only once")
} }
...@@ -271,7 +272,7 @@ func TestUploadHandlerSendingToExternalStorageAndSupportRequestTimeout(t *testin ...@@ -271,7 +272,7 @@ func TestUploadHandlerSendingToExternalStorageAndSupportRequestTimeout(t *testin
defer ts.Close() defer ts.Close()
response := testUploadArtifactsFromTestZip(t, ts) response := testUploadArtifactsFromTestZip(t, ts)
testhelper.AssertResponseCode(t, response, 500) testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
assert.Equal(t, 1, putCalledTimes, "upload should be called only once") assert.Equal(t, 1, putCalledTimes, "upload should be called only once")
} }
......
...@@ -19,7 +19,6 @@ import ( ...@@ -19,7 +19,6 @@ import (
type artifactsUploadProcessor struct { type artifactsUploadProcessor struct {
opts *filestore.SaveFileOpts opts *filestore.SaveFileOpts
metadataFile string
stored bool stored bool
} }
...@@ -80,9 +79,10 @@ func (a *artifactsUploadProcessor) ProcessFile(ctx context.Context, formName str ...@@ -80,9 +79,10 @@ func (a *artifactsUploadProcessor) ProcessFile(ctx context.Context, formName str
if formName != "file" { if formName != "file" {
return fmt.Errorf("Invalid form field: %q", formName) return fmt.Errorf("Invalid form field: %q", formName)
} }
if a.metadataFile != "" { if a.stored {
return fmt.Errorf("Artifacts request contains more than one file!") return fmt.Errorf("Artifacts request contains more than one file")
} }
a.stored = true
select { select {
case <-ctx.Done(): case <-ctx.Done():
......
...@@ -23,6 +23,12 @@ import ( ...@@ -23,6 +23,12 @@ import (
"gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts" "gitlab.com/gitlab-org/gitlab-workhorse/internal/zipartifacts"
) )
const (
MetadataHeaderKey = "Metadata-Status"
MetadataHeaderPresent = "present"
MetadataHeaderMissing = "missing"
)
func testArtifactsUploadServer(t *testing.T, authResponse api.Response, bodyProcessor func(w http.ResponseWriter, r *http.Request)) *httptest.Server { func testArtifactsUploadServer(t *testing.T, authResponse api.Response, bodyProcessor func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/url/path/authorize", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/url/path/authorize", func(w http.ResponseWriter, r *http.Request) {
...@@ -46,52 +52,54 @@ func testArtifactsUploadServer(t *testing.T, authResponse api.Response, bodyProc ...@@ -46,52 +52,54 @@ func testArtifactsUploadServer(t *testing.T, authResponse api.Response, bodyProc
} }
if opts.IsLocal() { if opts.IsLocal() {
if r.FormValue("file.path") == "" { if r.FormValue("file.path") == "" {
w.WriteHeader(501) t.Fatal("Expected file to be present")
return return
} }
_, err := ioutil.ReadFile(r.FormValue("file.path")) _, err := ioutil.ReadFile(r.FormValue("file.path"))
if err != nil { if err != nil {
w.WriteHeader(404) t.Fatal("Expected file to be readable")
return return
} }
} } else if opts.IsRemote() {
if r.FormValue("file.remote_url") == "" {
if opts.IsRemote() && r.FormValue("file.remote_url") == "" { t.Fatal("Expected file to be remote accessible")
w.WriteHeader(501)
return return
} }
if r.FormValue("metadata.path") == "" {
w.WriteHeader(502)
return
} }
if r.FormValue("metadata.path") != "" {
metadata, err := ioutil.ReadFile(r.FormValue("metadata.path")) metadata, err := ioutil.ReadFile(r.FormValue("metadata.path"))
if err != nil { if err != nil {
w.WriteHeader(404) t.Fatal("Expected metadata to be readable")
return return
} }
gz, err := gzip.NewReader(bytes.NewReader(metadata)) gz, err := gzip.NewReader(bytes.NewReader(metadata))
if err != nil { if err != nil {
w.WriteHeader(405) t.Fatal("Expected metadata to be valid gzip")
return return
} }
defer gz.Close() defer gz.Close()
metadata, err = ioutil.ReadAll(gz) metadata, err = ioutil.ReadAll(gz)
if err != nil { if err != nil {
w.WriteHeader(404) t.Fatal("Expected metadata to be valid")
return return
} }
if !bytes.HasPrefix(metadata, []byte(zipartifacts.MetadataHeaderPrefix+zipartifacts.MetadataHeader)) { if !bytes.HasPrefix(metadata, []byte(zipartifacts.MetadataHeaderPrefix+zipartifacts.MetadataHeader)) {
w.WriteHeader(400) t.Fatal("Expected metadata to be of valid format")
return return
} }
w.Header().Set(MetadataHeaderKey, MetadataHeaderPresent)
} else {
w.Header().Set(MetadataHeaderKey, MetadataHeaderMissing)
}
w.WriteHeader(http.StatusOK)
if bodyProcessor != nil { if bodyProcessor != nil {
bodyProcessor(w, r) bodyProcessor(w, r)
} else {
w.WriteHeader(200)
} }
}) })
return testhelper.TestServerWithHandler(nil, mux.ServeHTTP) return testhelper.TestServerWithHandler(nil, mux.ServeHTTP)
...@@ -141,7 +149,8 @@ func TestUploadHandlerAddingMetadata(t *testing.T) { ...@@ -141,7 +149,8 @@ func TestUploadHandlerAddingMetadata(t *testing.T) {
writer.Close() writer.Close()
response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts) response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts)
testhelper.AssertResponseCode(t, response, 200) testhelper.AssertResponseCode(t, response, http.StatusOK)
testhelper.AssertResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderPresent)
} }
func TestUploadHandlerForUnsupportedArchive(t *testing.T) { func TestUploadHandlerForUnsupportedArchive(t *testing.T) {
...@@ -164,8 +173,37 @@ func TestUploadHandlerForUnsupportedArchive(t *testing.T) { ...@@ -164,8 +173,37 @@ func TestUploadHandlerForUnsupportedArchive(t *testing.T) {
writer.Close() writer.Close()
response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts) response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts)
// 502 is a custom response code from the mock server in testUploadArtifacts testhelper.AssertResponseCode(t, response, http.StatusOK)
testhelper.AssertResponseCode(t, response, 502) testhelper.AssertResponseHeader(t, response, MetadataHeaderKey, MetadataHeaderMissing)
}
func TestUploadHandlerForMultipleFiles(t *testing.T) {
tempPath, err := ioutil.TempDir("", "uploads")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tempPath)
ts := testArtifactsUploadServer(t, api.Response{TempPath: tempPath}, nil)
defer ts.Close()
var buffer bytes.Buffer
writer := multipart.NewWriter(&buffer)
file, err := writer.CreateFormFile("file", "my.file")
if err != nil {
t.Fatal(err)
}
fmt.Fprint(file, "test")
file, err = writer.CreateFormFile("file", "my.file")
if err != nil {
t.Fatal(err)
}
fmt.Fprint(file, "test")
writer.Close()
response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts)
testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
} }
func TestUploadFormProcessing(t *testing.T) { func TestUploadFormProcessing(t *testing.T) {
...@@ -188,5 +226,5 @@ func TestUploadFormProcessing(t *testing.T) { ...@@ -188,5 +226,5 @@ func TestUploadFormProcessing(t *testing.T) {
writer.Close() writer.Close()
response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts) response := testUploadArtifacts(writer.FormDataContentType(), &buffer, t, ts)
testhelper.AssertResponseCode(t, response, 500) testhelper.AssertResponseCode(t, response, http.StatusInternalServerError)
} }
...@@ -78,8 +78,20 @@ func AssertResponseWriterHeader(t *testing.T, w http.ResponseWriter, header stri ...@@ -78,8 +78,20 @@ func AssertResponseWriterHeader(t *testing.T, w http.ResponseWriter, header stri
assertHeaderExists(t, header, actual, expected) assertHeaderExists(t, header, actual, expected)
} }
func AssertResponseHeader(t *testing.T, w *http.Response, header string, expected ...string) { func AssertResponseHeader(t *testing.T, w interface{}, header string, expected ...string) {
actual := w.Header[http.CanonicalHeaderKey(header)] var actual []string
header = http.CanonicalHeaderKey(header)
if resp, ok := w.(*http.Response); ok {
actual = resp.Header[header]
} else if resp, ok := w.(http.ResponseWriter); ok {
actual = resp.Header()[header]
} else if resp, ok := w.(*httptest.ResponseRecorder); ok {
actual = resp.Header()[header]
} else {
t.Fatalf("invalid type of w passed AssertResponseHeader")
}
assertHeaderExists(t, header, actual, expected) assertHeaderExists(t, header, actual, expected)
} }
......
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