Only serve attachments when linked to issue/release and if accessible by user (#9340)
* test: add current attachement responses * refactor: check if attachement is linked and accessible by user * chore: clean TODO * fix: typo attachement -> attachment * revert un-needed go.sum change * refactor: move models logic to models * fix TestCreateIssueAttachment which was wrongly successful * fix unit tests with unittype added * fix unit tests with changes * use a valid uuid format for pgsql int. test * test: add unit test TestLinkedRepository * refactor: allow uploader to access unlinked attachement * add missing blank line * refactor: move to a separate function repo.GetAttachment * typo * test: remove err test return * refactor: use repo perm for access checking generally + 404 for all reject
This commit is contained in:
		
							parent
							
								
									6a5a2f493a
								
							
						
					
					
						commit
						8b24073713
					
				
					 10 changed files with 279 additions and 124 deletions
				
			
		|  | @ -1,88 +0,0 @@ | ||||||
| // Copyright 2019 The Gitea Authors. All rights reserved.
 |  | ||||||
| // Use of this source code is governed by a MIT-style
 |  | ||||||
| // license that can be found in the LICENSE file.
 |  | ||||||
| 
 |  | ||||||
| package integrations |  | ||||||
| 
 |  | ||||||
| import ( |  | ||||||
| 	"bytes" |  | ||||||
| 	"image" |  | ||||||
| 	"image/png" |  | ||||||
| 	"io" |  | ||||||
| 	"mime/multipart" |  | ||||||
| 	"net/http" |  | ||||||
| 	"testing" |  | ||||||
| 
 |  | ||||||
| 	"code.gitea.io/gitea/modules/test" |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| func generateImg() bytes.Buffer { |  | ||||||
| 	// Generate image
 |  | ||||||
| 	myImage := image.NewRGBA(image.Rect(0, 0, 32, 32)) |  | ||||||
| 	var buff bytes.Buffer |  | ||||||
| 	png.Encode(&buff, myImage) |  | ||||||
| 	return buff |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func createAttachment(t *testing.T, session *TestSession, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string { |  | ||||||
| 	body := &bytes.Buffer{} |  | ||||||
| 
 |  | ||||||
| 	//Setup multi-part
 |  | ||||||
| 	writer := multipart.NewWriter(body) |  | ||||||
| 	part, err := writer.CreateFormFile("file", filename) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	_, err = io.Copy(part, &buff) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	err = writer.Close() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 
 |  | ||||||
| 	csrf := GetCSRF(t, session, repoURL) |  | ||||||
| 
 |  | ||||||
| 	req := NewRequestWithBody(t, "POST", "/attachments", body) |  | ||||||
| 	req.Header.Add("X-Csrf-Token", csrf) |  | ||||||
| 	req.Header.Add("Content-Type", writer.FormDataContentType()) |  | ||||||
| 	resp := session.MakeRequest(t, req, expectedStatus) |  | ||||||
| 
 |  | ||||||
| 	if expectedStatus != http.StatusOK { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	var obj map[string]string |  | ||||||
| 	DecodeJSON(t, resp, &obj) |  | ||||||
| 	return obj["uuid"] |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestCreateAnonymousAttachment(t *testing.T) { |  | ||||||
| 	prepareTestEnv(t) |  | ||||||
| 	session := emptyTestSession(t) |  | ||||||
| 	createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusFound) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestCreateIssueAttachement(t *testing.T) { |  | ||||||
| 	prepareTestEnv(t) |  | ||||||
| 	const repoURL = "user2/repo1" |  | ||||||
| 	session := loginUser(t, "user2") |  | ||||||
| 	uuid := createAttachment(t, session, repoURL, "image.png", generateImg(), http.StatusOK) |  | ||||||
| 
 |  | ||||||
| 	req := NewRequest(t, "GET", repoURL+"/issues/new") |  | ||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) |  | ||||||
| 	htmlDoc := NewHTMLParser(t, resp.Body) |  | ||||||
| 
 |  | ||||||
| 	link, exists := htmlDoc.doc.Find("form").Attr("action") |  | ||||||
| 	assert.True(t, exists, "The template has changed") |  | ||||||
| 
 |  | ||||||
| 	postData := map[string]string{ |  | ||||||
| 		"_csrf":    htmlDoc.GetCSRF(), |  | ||||||
| 		"title":    "New Issue With Attachement", |  | ||||||
| 		"content":  "some content", |  | ||||||
| 		"files[0]": uuid, |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	req = NewRequestWithValues(t, "POST", link, postData) |  | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusFound) |  | ||||||
| 	test.RedirectURL(resp) // check that redirect URL exists
 |  | ||||||
| 
 |  | ||||||
| 	//Validate that attachement is available
 |  | ||||||
| 	req = NewRequest(t, "GET", "/attachments/"+uuid) |  | ||||||
| 	session.MakeRequest(t, req, http.StatusOK) |  | ||||||
| } |  | ||||||
							
								
								
									
										137
									
								
								integrations/attachment_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								integrations/attachment_test.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,137 @@ | ||||||
|  | // Copyright 2019 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package integrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"image" | ||||||
|  | 	"image/png" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"mime/multipart" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
|  | 	"path" | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/models" | ||||||
|  | 	"code.gitea.io/gitea/modules/test" | ||||||
|  | 
 | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func generateImg() bytes.Buffer { | ||||||
|  | 	// Generate image
 | ||||||
|  | 	myImage := image.NewRGBA(image.Rect(0, 0, 32, 32)) | ||||||
|  | 	var buff bytes.Buffer | ||||||
|  | 	png.Encode(&buff, myImage) | ||||||
|  | 	return buff | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func createAttachment(t *testing.T, session *TestSession, repoURL, filename string, buff bytes.Buffer, expectedStatus int) string { | ||||||
|  | 	body := &bytes.Buffer{} | ||||||
|  | 
 | ||||||
|  | 	//Setup multi-part
 | ||||||
|  | 	writer := multipart.NewWriter(body) | ||||||
|  | 	part, err := writer.CreateFormFile("file", filename) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	_, err = io.Copy(part, &buff) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	err = writer.Close() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	csrf := GetCSRF(t, session, repoURL) | ||||||
|  | 
 | ||||||
|  | 	req := NewRequestWithBody(t, "POST", "/attachments", body) | ||||||
|  | 	req.Header.Add("X-Csrf-Token", csrf) | ||||||
|  | 	req.Header.Add("Content-Type", writer.FormDataContentType()) | ||||||
|  | 	resp := session.MakeRequest(t, req, expectedStatus) | ||||||
|  | 
 | ||||||
|  | 	if expectedStatus != http.StatusOK { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	var obj map[string]string | ||||||
|  | 	DecodeJSON(t, resp, &obj) | ||||||
|  | 	return obj["uuid"] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCreateAnonymousAttachment(t *testing.T) { | ||||||
|  | 	prepareTestEnv(t) | ||||||
|  | 	session := emptyTestSession(t) | ||||||
|  | 	createAttachment(t, session, "user2/repo1", "image.png", generateImg(), http.StatusFound) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestCreateIssueAttachment(t *testing.T) { | ||||||
|  | 	prepareTestEnv(t) | ||||||
|  | 	const repoURL = "user2/repo1" | ||||||
|  | 	session := loginUser(t, "user2") | ||||||
|  | 	uuid := createAttachment(t, session, repoURL, "image.png", generateImg(), http.StatusOK) | ||||||
|  | 
 | ||||||
|  | 	req := NewRequest(t, "GET", repoURL+"/issues/new") | ||||||
|  | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
|  | 	htmlDoc := NewHTMLParser(t, resp.Body) | ||||||
|  | 
 | ||||||
|  | 	link, exists := htmlDoc.doc.Find("form").Attr("action") | ||||||
|  | 	assert.True(t, exists, "The template has changed") | ||||||
|  | 
 | ||||||
|  | 	postData := map[string]string{ | ||||||
|  | 		"_csrf":   htmlDoc.GetCSRF(), | ||||||
|  | 		"title":   "New Issue With Attachment", | ||||||
|  | 		"content": "some content", | ||||||
|  | 		"files":   uuid, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	req = NewRequestWithValues(t, "POST", link, postData) | ||||||
|  | 	resp = session.MakeRequest(t, req, http.StatusFound) | ||||||
|  | 	test.RedirectURL(resp) // check that redirect URL exists
 | ||||||
|  | 
 | ||||||
|  | 	//Validate that attachment is available
 | ||||||
|  | 	req = NewRequest(t, "GET", "/attachments/"+uuid) | ||||||
|  | 	session.MakeRequest(t, req, http.StatusOK) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestGetAttachment(t *testing.T) { | ||||||
|  | 	prepareTestEnv(t) | ||||||
|  | 	adminSession := loginUser(t, "user1") | ||||||
|  | 	user2Session := loginUser(t, "user2") | ||||||
|  | 	user8Session := loginUser(t, "user8") | ||||||
|  | 	emptySession := emptyTestSession(t) | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		name       string | ||||||
|  | 		uuid       string | ||||||
|  | 		createFile bool | ||||||
|  | 		session    *TestSession | ||||||
|  | 		want       int | ||||||
|  | 	}{ | ||||||
|  | 		{"LinkedIssueUUID", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", true, user2Session, http.StatusOK}, | ||||||
|  | 		{"LinkedCommentUUID", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17", true, user2Session, http.StatusOK}, | ||||||
|  | 		{"linked_release_uuid", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a19", true, user2Session, http.StatusOK}, | ||||||
|  | 		{"NotExistingUUID", "b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", false, user2Session, http.StatusNotFound}, | ||||||
|  | 		{"FileMissing", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18", false, user2Session, http.StatusInternalServerError}, | ||||||
|  | 		{"NotLinked", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20", true, user2Session, http.StatusNotFound}, | ||||||
|  | 		{"NotLinkedAccessibleByUploader", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20", true, user8Session, http.StatusOK}, | ||||||
|  | 		{"PublicByNonLogged", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", true, emptySession, http.StatusOK}, | ||||||
|  | 		{"PrivateByNonLogged", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, emptySession, http.StatusNotFound}, | ||||||
|  | 		{"PrivateAccessibleByAdmin", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, adminSession, http.StatusOK}, | ||||||
|  | 		{"PrivateAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user2Session, http.StatusOK}, | ||||||
|  | 		{"RepoNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12", true, user8Session, http.StatusNotFound}, | ||||||
|  | 		{"OrgNotAccessibleByUser", "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21", true, user8Session, http.StatusNotFound}, | ||||||
|  | 	} | ||||||
|  | 	for _, tc := range testCases { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			//Write empty file to be available for response
 | ||||||
|  | 			if tc.createFile { | ||||||
|  | 				localPath := models.AttachmentLocalPath(tc.uuid) | ||||||
|  | 				err := os.MkdirAll(path.Dir(localPath), os.ModePerm) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 				err = ioutil.WriteFile(localPath, []byte("hello world"), 0644) | ||||||
|  | 				assert.NoError(t, err) | ||||||
|  | 			} | ||||||
|  | 			//Actual test
 | ||||||
|  | 			req := NewRequest(t, "GET", "/attachments/"+tc.uuid) | ||||||
|  | 			tc.session.MakeRequest(t, req, tc.want) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -71,6 +71,26 @@ func (a *Attachment) DownloadURL() string { | ||||||
| 	return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID) | 	return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // LinkedRepository returns the linked repo if any
 | ||||||
|  | func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) { | ||||||
|  | 	if a.IssueID != 0 { | ||||||
|  | 		iss, err := GetIssueByID(a.IssueID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, UnitTypeIssues, err | ||||||
|  | 		} | ||||||
|  | 		repo, err := GetRepositoryByID(iss.RepoID) | ||||||
|  | 		return repo, UnitTypeIssues, err | ||||||
|  | 	} else if a.ReleaseID != 0 { | ||||||
|  | 		rel, err := GetReleaseByID(a.ReleaseID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, UnitTypeReleases, err | ||||||
|  | 		} | ||||||
|  | 		repo, err := GetRepositoryByID(rel.RepoID) | ||||||
|  | 		return repo, UnitTypeReleases, err | ||||||
|  | 	} | ||||||
|  | 	return nil, -1, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // NewAttachment creates a new attachment object.
 | // NewAttachment creates a new attachment object.
 | ||||||
| func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) { | func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) { | ||||||
| 	attach.UUID = gouuid.NewV4().String() | 	attach.UUID = gouuid.NewV4().String() | ||||||
|  |  | ||||||
|  | @ -61,7 +61,7 @@ func TestGetByCommentOrIssueID(t *testing.T) { | ||||||
| 	// count of attachments from issue ID
 | 	// count of attachments from issue ID
 | ||||||
| 	attachments, err := GetAttachmentsByIssueID(1) | 	attachments, err := GetAttachmentsByIssueID(1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, 2, len(attachments)) | 	assert.Equal(t, 1, len(attachments)) | ||||||
| 
 | 
 | ||||||
| 	attachments, err = GetAttachmentsByCommentID(1) | 	attachments, err = GetAttachmentsByCommentID(1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  | @ -73,7 +73,7 @@ func TestDeleteAttachments(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	count, err := DeleteAttachmentsByIssue(4, false) | 	count, err := DeleteAttachmentsByIssue(4, false) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, 1, count) | 	assert.Equal(t, 2, count) | ||||||
| 
 | 
 | ||||||
| 	count, err = DeleteAttachmentsByComment(2, false) | 	count, err = DeleteAttachmentsByComment(2, false) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  | @ -128,3 +128,31 @@ func TestGetAttachmentsByUUIDs(t *testing.T) { | ||||||
| 	assert.Equal(t, int64(1), attachList[0].IssueID) | 	assert.Equal(t, int64(1), attachList[0].IssueID) | ||||||
| 	assert.Equal(t, int64(5), attachList[1].IssueID) | 	assert.Equal(t, int64(5), attachList[1].IssueID) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestLinkedRepository(t *testing.T) { | ||||||
|  | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		name             string | ||||||
|  | 		attachID         int64 | ||||||
|  | 		expectedRepo     *Repository | ||||||
|  | 		expectedUnitType UnitType | ||||||
|  | 	}{ | ||||||
|  | 		{"LinkedIssue", 1, &Repository{ID: 1}, UnitTypeIssues}, | ||||||
|  | 		{"LinkedComment", 3, &Repository{ID: 1}, UnitTypeIssues}, | ||||||
|  | 		{"LinkedRelease", 9, &Repository{ID: 1}, UnitTypeReleases}, | ||||||
|  | 		{"Notlinked", 10, nil, -1}, | ||||||
|  | 	} | ||||||
|  | 	for _, tc := range testCases { | ||||||
|  | 		t.Run(tc.name, func(t *testing.T) { | ||||||
|  | 			attach, err := GetAttachmentByID(tc.attachID) | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			repo, unitType, err := attach.LinkedRepository() | ||||||
|  | 			assert.NoError(t, err) | ||||||
|  | 			if tc.expectedRepo != nil { | ||||||
|  | 				assert.Equal(t, tc.expectedRepo.ID, repo.ID) | ||||||
|  | 			} | ||||||
|  | 			assert.Equal(t, tc.expectedUnitType, unitType) | ||||||
|  | 
 | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
| - | - | ||||||
|   id: 2 |   id: 2 | ||||||
|   uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12 |   uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12 | ||||||
|   issue_id: 1 |   issue_id: 4 | ||||||
|   comment_id: 0 |   comment_id: 0 | ||||||
|   name: attach2 |   name: attach2 | ||||||
|   download_count: 1 |   download_count: 1 | ||||||
|  | @ -81,6 +81,15 @@ | ||||||
| - | - | ||||||
|   id: 10 |   id: 10 | ||||||
|   uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20 |   uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20 | ||||||
|  |   uploader_id: 8 | ||||||
|   name: attach1 |   name: attach1 | ||||||
|   download_count: 0 |   download_count: 0 | ||||||
|   created_unix: 946684800  |   created_unix: 946684800 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 11 | ||||||
|  |   uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21 | ||||||
|  |   release_id: 2 | ||||||
|  |   name: attach1 | ||||||
|  |   download_count: 0 | ||||||
|  |   created_unix: 946684800 | ||||||
|  |  | ||||||
|  | @ -11,4 +11,19 @@ | ||||||
|   is_draft: false |   is_draft: false | ||||||
|   is_prerelease: false |   is_prerelease: false | ||||||
|   is_tag: false |   is_tag: false | ||||||
|   created_unix: 946684800 |   created_unix: 946684800 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 2 | ||||||
|  |   repo_id: 40 | ||||||
|  |   publisher_id: 2 | ||||||
|  |   tag_name: "v1.1" | ||||||
|  |   lower_tag_name: "v1.1" | ||||||
|  |   target: "master" | ||||||
|  |   title: "testing-release" | ||||||
|  |   sha1: "65f1bf27bc3bf70f64657658635e66094edbcb4d" | ||||||
|  |   num_commits: 10 | ||||||
|  |   is_draft: false | ||||||
|  |   is_prerelease: false | ||||||
|  |   is_tag: false | ||||||
|  |   created_unix: 946684800 | ||||||
|  |  | ||||||
|  | @ -472,4 +472,10 @@ | ||||||
|   repo_id: 48 |   repo_id: 48 | ||||||
|   type: 7 |   type: 7 | ||||||
|   config: "{\"ExternalTrackerURL\":\"https://tracker.com\",\"ExternalTrackerFormat\":\"https://tracker.com/{user}/{repo}/issues/{index}\",\"ExternalTrackerStyle\":\"alphanumeric\"}" |   config: "{\"ExternalTrackerURL\":\"https://tracker.com\",\"ExternalTrackerFormat\":\"https://tracker.com/{user}/{repo}/issues/{index}\",\"ExternalTrackerStyle\":\"alphanumeric\"}" | ||||||
|  |   created_unix: 946684810 | ||||||
|  | - | ||||||
|  |   id: 69 | ||||||
|  |   repo_id: 2 | ||||||
|  |   type: 2 | ||||||
|  |   config: "{}" | ||||||
|   created_unix: 946684810 |   created_unix: 946684810 | ||||||
|  | @ -6,6 +6,8 @@ package repo | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
|  | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | @ -85,3 +87,57 @@ func DeleteAttachment(ctx *context.Context) { | ||||||
| 		"uuid": attach.UUID, | 		"uuid": attach.UUID, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // GetAttachment serve attachements
 | ||||||
|  | func GetAttachment(ctx *context.Context) { | ||||||
|  | 	attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid")) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if models.IsErrAttachmentNotExist(err) { | ||||||
|  | 			ctx.Error(404) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.ServerError("GetAttachmentByUUID", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	repository, unitType, err := attach.LinkedRepository() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("LinkedRepository", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if repository == nil { //If not linked
 | ||||||
|  | 		if !(ctx.IsSigned && attach.UploaderID == ctx.User.ID) { //We block if not the uploader
 | ||||||
|  | 			ctx.Error(http.StatusNotFound) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} else { //If we have the repository we check access
 | ||||||
|  | 		perm, err := models.GetUserRepoPermission(repository, ctx.User) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err.Error()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		if !perm.CanRead(unitType) { | ||||||
|  | 			ctx.Error(http.StatusNotFound) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	//If we have matched and access to release or issue
 | ||||||
|  | 	fr, err := os.Open(attach.LocalPath()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("Open", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	defer fr.Close() | ||||||
|  | 
 | ||||||
|  | 	if err := attach.IncreaseDownloadCount(); err != nil { | ||||||
|  | 		ctx.ServerError("Update", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = ServeData(ctx, attach.Name, fr); err != nil { | ||||||
|  | 		ctx.ServerError("ServeData", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -8,7 +8,6 @@ import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"encoding/gob" | 	"encoding/gob" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"os" |  | ||||||
| 	"path" | 	"path" | ||||||
| 	"text/template" | 	"text/template" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -474,34 +473,7 @@ func RegisterRoutes(m *macaron.Macaron) { | ||||||
| 			m.Get("/following", user.Following) | 			m.Get("/following", user.Following) | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		m.Get("/attachments/:uuid", func(ctx *context.Context) { | 		m.Get("/attachments/:uuid", repo.GetAttachment) | ||||||
| 			attach, err := models.GetAttachmentByUUID(ctx.Params(":uuid")) |  | ||||||
| 			if err != nil { |  | ||||||
| 				if models.IsErrAttachmentNotExist(err) { |  | ||||||
| 					ctx.Error(404) |  | ||||||
| 				} else { |  | ||||||
| 					ctx.ServerError("GetAttachmentByUUID", err) |  | ||||||
| 				} |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			fr, err := os.Open(attach.LocalPath()) |  | ||||||
| 			if err != nil { |  | ||||||
| 				ctx.ServerError("Open", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			defer fr.Close() |  | ||||||
| 
 |  | ||||||
| 			if err := attach.IncreaseDownloadCount(); err != nil { |  | ||||||
| 				ctx.ServerError("Update", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			if err = repo.ServeData(ctx, attach.Name, fr); err != nil { |  | ||||||
| 				ctx.ServerError("ServeData", err) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	}, ignSignIn) | 	}, ignSignIn) | ||||||
| 
 | 
 | ||||||
| 	m.Group("/attachments", func() { | 	m.Group("/attachments", func() { | ||||||
|  |  | ||||||
|  | @ -26,10 +26,10 @@ func TestIssues(t *testing.T) { | ||||||
| 	Issues(ctx) | 	Issues(ctx) | ||||||
| 	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) | 	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) | ||||||
| 
 | 
 | ||||||
| 	assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) | 	assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"]) | ||||||
| 	assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) | 	assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) | ||||||
| 	assert.Len(t, ctx.Data["Issues"], 1) | 	assert.Len(t, ctx.Data["Issues"], 1) | ||||||
| 	assert.Len(t, ctx.Data["Repos"], 1) | 	assert.Len(t, ctx.Data["Repos"], 2) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestMilestones(t *testing.T) { | func TestMilestones(t *testing.T) { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue