mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 20:36:07 +01:00 
			
		
		
		
	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:
		
				
					committed by
					
						
						Lauris BH
					
				
			
			
				
	
			
			
			
						parent
						
							6a5a2f493a
						
					
				
				
					commit
					8b24073713
				
			@@ -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
 | 
				
			||||||
 | 
					  download_count: 0
 | 
				
			||||||
 | 
					  created_unix: 946684800
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 11
 | 
				
			||||||
 | 
					  uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21
 | 
				
			||||||
 | 
					  release_id: 2
 | 
				
			||||||
  name: attach1
 | 
					  name: attach1
 | 
				
			||||||
  download_count: 0
 | 
					  download_count: 0
 | 
				
			||||||
  created_unix: 946684800
 | 
					  created_unix: 946684800
 | 
				
			||||||
@@ -12,3 +12,18 @@
 | 
				
			|||||||
  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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -473,3 +473,9 @@
 | 
				
			|||||||
  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
 | 
					  created_unix: 946684810
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 69
 | 
				
			||||||
 | 
					  repo_id: 2
 | 
				
			||||||
 | 
					  type: 2
 | 
				
			||||||
 | 
					  config: "{}"
 | 
				
			||||||
 | 
					  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) {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user