mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-13 02:02:53 +09:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7d8181a70 | ||
|
|
548ae3eb98 | ||
|
|
2c383d812d | ||
|
|
ef12b8de80 | ||
|
|
dd1ba34ee5 | ||
|
|
1fbdf96c34 | ||
|
|
5159055278 | ||
|
|
06da10b9a1 | ||
|
|
175ebc6f88 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -4,6 +4,17 @@ This changelog goes through all the changes that have been made in each release
|
||||
without substantial changes to our git log; to see the highlights of what has
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.15.6](https://github.com/go-gitea/gitea/releases/tag/v1.15.6) - 2021-10-28
|
||||
|
||||
* BUGFIXES
|
||||
* Prevent panic in serv.go with Deploy Keys (#17434) (#17435)
|
||||
* Fix CSV render error (#17406) (#17431)
|
||||
* Read expected buffer size (#17409) (#17430)
|
||||
* Ensure that restricted users can access repos for which they are members (#17460) (#17464)
|
||||
* Make commit-statuses popup show correctly (#17447) (#17466)
|
||||
* TESTING
|
||||
* Add integration tests for private.NoServCommand and private.ServCommand (#17456) (#17463)
|
||||
|
||||
## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
|
||||
|
||||
* SECURITY
|
||||
|
||||
154
integrations/api_private_serv_test.go
Normal file
154
integrations/api_private_serv_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2021 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 (
|
||||
"context"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAPIPrivateNoServ(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
key, user, err := private.ServNoCommand(ctx, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), user.ID)
|
||||
assert.Equal(t, "user2", user.Name)
|
||||
assert.Equal(t, int64(1), key.ID)
|
||||
assert.Equal(t, "user2@localhost", key.Name)
|
||||
|
||||
deployKey, err := models.AddDeployKey(1, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
key, user, err = private.ServNoCommand(ctx, deployKey.KeyID)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, user)
|
||||
assert.Equal(t, deployKey.KeyID, key.ID)
|
||||
assert.Equal(t, "test-deploy", key.Name)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIPrivateServ(t *testing.T) {
|
||||
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Can push to a repo we own
|
||||
results, err := private.ServCommand(ctx, 1, "user2", "repo1", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
assert.Equal(t, int64(2), results.UserID)
|
||||
assert.Equal(t, "user2", results.OwnerName)
|
||||
assert.Equal(t, "repo1", results.RepoName)
|
||||
assert.Equal(t, int64(1), results.RepoID)
|
||||
|
||||
// Cannot push to a private repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Cannot pull from a private repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Can pull from a public repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
assert.Equal(t, int64(2), results.UserID)
|
||||
assert.Equal(t, "user15", results.OwnerName)
|
||||
assert.Equal(t, "big_test_public_1", results.RepoName)
|
||||
assert.Equal(t, int64(17), results.RepoID)
|
||||
|
||||
// Cannot push to a public repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Add reading deploy key
|
||||
deployKey, err := models.AddDeployKey(19, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Can pull from repo we're a deploy key for
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
assert.Equal(t, int64(15), results.UserID)
|
||||
assert.Equal(t, "user15", results.OwnerName)
|
||||
assert.Equal(t, "big_test_private_1", results.RepoName)
|
||||
assert.Equal(t, int64(19), results.RepoID)
|
||||
|
||||
// Cannot push to a private repo with reading key
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Cannot pull from a private repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Cannot pull from a public repo we're not associated with
|
||||
results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Add writing deploy key
|
||||
deployKey, err = models.AddDeployKey(20, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Cannot push to a private repo with reading key
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, results)
|
||||
|
||||
// Can pull from repo we're a writing deploy key for
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
assert.Equal(t, int64(15), results.UserID)
|
||||
assert.Equal(t, "user15", results.OwnerName)
|
||||
assert.Equal(t, "big_test_private_2", results.RepoName)
|
||||
assert.Equal(t, int64(20), results.RepoID)
|
||||
|
||||
// Can push to repo we're a writing deploy key for
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
assert.Equal(t, int64(15), results.UserID)
|
||||
assert.Equal(t, "user15", results.OwnerName)
|
||||
assert.Equal(t, "big_test_private_2", results.RepoName)
|
||||
assert.Equal(t, int64(20), results.RepoID)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
@@ -5,10 +5,12 @@
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -110,3 +112,64 @@ func TestPrivateOrg(t *testing.T) {
|
||||
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestOrgRestrictedUser(t *testing.T) {
|
||||
defer prepareTestEnv(t)()
|
||||
|
||||
// privated_org is a private org who has id 23
|
||||
orgName := "privated_org"
|
||||
|
||||
// public_repo_on_private_org is a public repo on privated_org
|
||||
repoName := "public_repo_on_private_org"
|
||||
|
||||
// user29 is a restricted user who is not a member of the organization
|
||||
restrictedUser := "user29"
|
||||
|
||||
// #17003 reports a bug whereby adding a restricted user to a read-only team doesn't work
|
||||
|
||||
// assert restrictedUser cannot see the org or the public repo
|
||||
restrictedSession := loginUser(t, restrictedUser)
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
|
||||
restrictedSession.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
|
||||
restrictedSession.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Therefore create a read-only team
|
||||
adminSession := loginUser(t, "user1")
|
||||
token := getTokenForLoggedInUser(t, adminSession)
|
||||
|
||||
teamToCreate := &api.CreateTeamOption{
|
||||
Name: "codereader",
|
||||
Description: "Code Reader",
|
||||
IncludesAllRepositories: true,
|
||||
Permission: "read",
|
||||
Units: []string{"repo.code"},
|
||||
}
|
||||
|
||||
req = NewRequestWithJSON(t, "POST",
|
||||
fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", orgName, token), teamToCreate)
|
||||
|
||||
var apiTeam api.Team
|
||||
|
||||
resp := adminSession.MakeRequest(t, req, http.StatusCreated)
|
||||
DecodeJSON(t, resp, &apiTeam)
|
||||
checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
|
||||
teamToCreate.Permission, teamToCreate.Units)
|
||||
checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
|
||||
teamToCreate.Permission, teamToCreate.Units)
|
||||
//teamID := apiTeam.ID
|
||||
|
||||
// Now we need to add the restricted user to the team
|
||||
req = NewRequest(t, "PUT",
|
||||
fmt.Sprintf("/api/v1/teams/%d/members/%s?token=%s", apiTeam.ID, restrictedUser, token))
|
||||
_ = adminSession.MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
// Now we need to check if the restrictedUser can access the repo
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
|
||||
restrictedSession.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
|
||||
restrictedSession.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
@@ -568,7 +568,7 @@
|
||||
-
|
||||
id: 40
|
||||
owner_id: 23
|
||||
owner_name: limited_org
|
||||
owner_name: privated_org
|
||||
lower_name: public_repo_on_private_org
|
||||
name: public_repo_on_private_org
|
||||
is_private: false
|
||||
@@ -581,7 +581,7 @@
|
||||
-
|
||||
id: 41
|
||||
owner_id: 23
|
||||
owner_name: limited_org
|
||||
owner_name: privated_org
|
||||
lower_name: private_repo_on_private_org
|
||||
name: private_repo_on_private_org
|
||||
is_private: true
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/gogs/chardet"
|
||||
"golang.org/x/net/html/charset"
|
||||
@@ -26,9 +27,9 @@ var UTF8BOM = []byte{'\xef', '\xbb', '\xbf'}
|
||||
// ToUTF8WithFallbackReader detects the encoding of content and coverts to UTF-8 reader if possible
|
||||
func ToUTF8WithFallbackReader(rd io.Reader) io.Reader {
|
||||
var buf = make([]byte, 2048)
|
||||
n, err := rd.Read(buf)
|
||||
n, err := util.ReadAtMost(rd, buf)
|
||||
if err != nil {
|
||||
return rd
|
||||
return io.MultiReader(bytes.NewReader(RemoveBOMIfPresent(buf[:n])), rd)
|
||||
}
|
||||
|
||||
charsetLabel, err := DetectEncoding(buf[:n])
|
||||
|
||||
@@ -345,7 +345,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
|
||||
}
|
||||
|
||||
// Check access.
|
||||
if ctx.Repo.Permission.AccessMode == models.AccessModeNone {
|
||||
if !ctx.Repo.Permission.HasAccess() {
|
||||
if ctx.Query("go-get") == "1" {
|
||||
EarlyResponseForGoGetMeta(ctx)
|
||||
return
|
||||
|
||||
@@ -28,35 +28,24 @@ func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader {
|
||||
}
|
||||
|
||||
// CreateReaderAndGuessDelimiter tries to guess the field delimiter from the content and creates a csv.Reader.
|
||||
// Reads at most 10k bytes.
|
||||
func CreateReaderAndGuessDelimiter(rd io.Reader) (*stdcsv.Reader, error) {
|
||||
var data = make([]byte, 1e4)
|
||||
size, err := rd.Read(data)
|
||||
size, err := util.ReadAtMost(rd, data)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return CreateReader(bytes.NewReader([]byte{}), rune(',')), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delimiter := guessDelimiter(data[:size])
|
||||
|
||||
var newInput io.Reader
|
||||
if size < 1e4 {
|
||||
newInput = bytes.NewReader(data[:size])
|
||||
} else {
|
||||
newInput = io.MultiReader(bytes.NewReader(data), rd)
|
||||
}
|
||||
|
||||
return CreateReader(newInput, delimiter), nil
|
||||
return CreateReader(
|
||||
io.MultiReader(bytes.NewReader(data[:size]), rd),
|
||||
guessDelimiter(data[:size]),
|
||||
), nil
|
||||
}
|
||||
|
||||
// guessDelimiter scores the input CSV data against delimiters, and returns the best match.
|
||||
// Reads at most 10k bytes & 10 lines.
|
||||
func guessDelimiter(data []byte) rune {
|
||||
maxLines := 10
|
||||
maxBytes := util.Min(len(data), 1e4)
|
||||
text := string(data[:maxBytes])
|
||||
text = quoteRegexp.ReplaceAllLiteralString(text, "")
|
||||
text := quoteRegexp.ReplaceAllLiteralString(string(data), "")
|
||||
lines := strings.SplitN(text, "\n", maxLines+1)
|
||||
lines = lines[:util.Min(maxLines, len(lines))]
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"io/ioutil"
|
||||
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// This file contains common functions between the gogit and !gogit variants for git Blobs
|
||||
@@ -29,7 +30,7 @@ func (b *Blob) GetBlobContent() (string, error) {
|
||||
}
|
||||
defer dataRc.Close()
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := dataRc.Read(buf)
|
||||
n, _ := util.ReadAtMost(dataRc, buf)
|
||||
buf = buf[:n]
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ func isLinkStr(link string) bool {
|
||||
func getIssueFullPattern() *regexp.Regexp {
|
||||
if issueFullPattern == nil {
|
||||
issueFullPattern = regexp.MustCompile(regexp.QuoteMeta(setting.AppURL) +
|
||||
`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#]\S+.(\S+)?)?\b`)
|
||||
`\w+/\w+/(?:issues|pulls)/((?:\w{1,10}-)?[1-9][0-9]*)([\?|#](\S+)?)?\b`)
|
||||
}
|
||||
return issueFullPattern
|
||||
}
|
||||
|
||||
@@ -265,6 +265,10 @@ func TestRender_FullIssueURLs(t *testing.T) {
|
||||
`<a href="http://localhost:3000/person/repo/issues/4#issuecomment-1234" class="ref-issue">person/repo#4</a>`)
|
||||
test("http://localhost:3000/gogits/gogs/issues/4",
|
||||
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a>`)
|
||||
test("http://localhost:3000/gogits/gogs/issues/4 test",
|
||||
`<a href="http://localhost:3000/gogits/gogs/issues/4" class="ref-issue">#4</a> test`)
|
||||
test("http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123 test",
|
||||
`<a href="http://localhost:3000/gogits/gogs/issues/4?a=1&b=2#comment-123" class="ref-issue">#4</a> test`)
|
||||
}
|
||||
|
||||
func TestRegExp_sha1CurrentPattern(t *testing.T) {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
repo_module "code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
stdcharset "golang.org/x/net/html/charset"
|
||||
"golang.org/x/text/transform"
|
||||
@@ -61,7 +62,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string
|
||||
}
|
||||
defer reader.Close()
|
||||
buf := make([]byte, 1024)
|
||||
n, err := reader.Read(buf)
|
||||
n, err := util.ReadAtMost(reader, buf)
|
||||
if err != nil {
|
||||
// return default
|
||||
return "UTF-8", false
|
||||
@@ -84,7 +85,7 @@ func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string
|
||||
}
|
||||
defer dataRc.Close()
|
||||
buf = make([]byte, 1024)
|
||||
n, err = dataRc.Read(buf)
|
||||
n, err = util.ReadAtMost(dataRc, buf)
|
||||
if err != nil {
|
||||
// return default
|
||||
return "UTF-8", false
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// Use at most this many bytes to determine Content Type.
|
||||
@@ -86,8 +88,8 @@ func DetectContentType(data []byte) SniffedType {
|
||||
// DetectContentTypeFromReader guesses the content type contained in the reader.
|
||||
func DetectContentTypeFromReader(r io.Reader) (SniffedType, error) {
|
||||
buf := make([]byte, sniffLen)
|
||||
n, err := r.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
n, err := util.ReadAtMost(r, buf)
|
||||
if err != nil {
|
||||
return SniffedType{}, fmt.Errorf("DetectContentTypeFromReader io error: %w", err)
|
||||
}
|
||||
buf = buf[:n]
|
||||
|
||||
20
modules/util/io.go
Normal file
20
modules/util/io.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2021 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 util
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// ReadAtMost reads at most len(buf) bytes from r into buf.
|
||||
// It returns the number of bytes copied. n is only less then len(buf) if r provides fewer bytes.
|
||||
// If EOF occurs while reading, err will be nil.
|
||||
func ReadAtMost(r io.Reader, buf []byte) (n int, err error) {
|
||||
n, err = io.ReadFull(r, buf)
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func ListReleases(ctx *context.APIContext) {
|
||||
|
||||
opts := models.FindReleasesOptions{
|
||||
ListOptions: listOptions,
|
||||
IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite,
|
||||
IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite || ctx.Repo.UnitAccessMode(models.UnitTypeReleases) >= models.AccessModeWrite,
|
||||
IncludeTags: false,
|
||||
IsDraft: ctx.QueryOptionalBool("draft"),
|
||||
IsPreRelease: ctx.QueryOptionalBool("pre-release"),
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ServeBlob download a git.Blob
|
||||
@@ -42,8 +43,8 @@ func ServeBlob(ctx *context.Context, blob *git.Blob) error {
|
||||
// ServeData download file from io.Reader
|
||||
func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error {
|
||||
buf := make([]byte, 1024)
|
||||
n, err := reader.Read(buf)
|
||||
if err != nil && err != io.EOF {
|
||||
n, err := util.ReadAtMost(reader, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n >= 0 {
|
||||
|
||||
@@ -282,7 +282,7 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
(mode > models.AccessModeRead ||
|
||||
repo.IsPrivate ||
|
||||
owner.Visibility.IsPrivate() ||
|
||||
user.IsRestricted ||
|
||||
(user != nil && user.IsRestricted) || // user will be nil if the key is a deploykey
|
||||
setting.Service.RequireSignInView) {
|
||||
if key.Type == models.KeyTypeDeploy {
|
||||
if deployKey.Mode < mode {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/upload"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/routers/common"
|
||||
)
|
||||
|
||||
@@ -43,10 +44,8 @@ func uploadAttachment(ctx *context.Context, allowedTypes string) {
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := file.Read(buf)
|
||||
if n > 0 {
|
||||
buf = buf[:n]
|
||||
}
|
||||
n, _ := util.ReadAtMost(file, buf)
|
||||
buf = buf[:n]
|
||||
|
||||
err = upload.Verify(buf, header.Filename, allowedTypes)
|
||||
if err != nil {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -104,30 +105,36 @@ func setCsvCompareContext(ctx *context.Context) {
|
||||
|
||||
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
|
||||
|
||||
csvReaderFromCommit := func(c *git.Commit) (*csv.Reader, error) {
|
||||
csvReaderFromCommit := func(c *git.Commit) (*csv.Reader, io.Closer, error) {
|
||||
blob, err := c.GetBlobByPath(diffFile.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
|
||||
return nil, errTooLarge
|
||||
return nil, nil, errTooLarge
|
||||
}
|
||||
|
||||
reader, err := blob.DataAsync()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
return csv_module.CreateReaderAndGuessDelimiter(charset.ToUTF8WithFallbackReader(reader))
|
||||
csvReader, err := csv_module.CreateReaderAndGuessDelimiter(charset.ToUTF8WithFallbackReader(reader))
|
||||
return csvReader, reader, err
|
||||
}
|
||||
|
||||
baseReader, err := csvReaderFromCommit(baseCommit)
|
||||
baseReader, baseBlobCloser, err := csvReaderFromCommit(baseCommit)
|
||||
if baseBlobCloser != nil {
|
||||
defer baseBlobCloser.Close()
|
||||
}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
headReader, err := csvReaderFromCommit(headCommit)
|
||||
headReader, headBlobCloser, err := csvReaderFromCommit(headCommit)
|
||||
if headBlobCloser != nil {
|
||||
defer headBlobCloser.Close()
|
||||
}
|
||||
if err == errTooLarge {
|
||||
return CsvDiffResult{nil, err.Error()}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func editFile(ctx *context.Context, isNewFile bool) {
|
||||
ctx.Data["FileName"] = blob.Name()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := dataRc.Read(buf)
|
||||
n, _ := util.ReadAtMost(dataRc, buf)
|
||||
buf = buf[:n]
|
||||
|
||||
// Only some file types are editable online as text.
|
||||
@@ -747,7 +747,7 @@ func UploadFileToServer(ctx *context.Context) {
|
||||
defer file.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := file.Read(buf)
|
||||
n, _ := util.ReadAtMost(file, buf)
|
||||
if n > 0 {
|
||||
buf = buf[:n]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -272,7 +273,7 @@ func LFSFileGet(ctx *context.Context) {
|
||||
}
|
||||
defer dataRc.Close()
|
||||
buf := make([]byte, 1024)
|
||||
n, err := dataRc.Read(buf)
|
||||
n, err := util.ReadAtMost(dataRc, buf)
|
||||
if err != nil {
|
||||
ctx.ServerError("Data", err)
|
||||
return
|
||||
@@ -297,10 +298,10 @@ func LFSFileGet(ctx *context.Context) {
|
||||
break
|
||||
}
|
||||
|
||||
buf := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
|
||||
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc))
|
||||
|
||||
// Building code view blocks with line number on server side.
|
||||
fileContent, _ := ioutil.ReadAll(buf)
|
||||
fileContent, _ := ioutil.ReadAll(rd)
|
||||
|
||||
var output bytes.Buffer
|
||||
lines := strings.Split(string(fileContent), "\n")
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -264,7 +265,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||
defer dataRc.Close()
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := dataRc.Read(buf)
|
||||
n, _ := util.ReadAtMost(dataRc, buf)
|
||||
buf = buf[:n]
|
||||
|
||||
st := typesniffer.DetectContentType(buf)
|
||||
@@ -299,7 +300,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||
defer dataRc.Close()
|
||||
|
||||
buf = make([]byte, 1024)
|
||||
n, err = dataRc.Read(buf)
|
||||
n, err = util.ReadAtMost(dataRc, buf)
|
||||
if err != nil {
|
||||
ctx.ServerError("Data", err)
|
||||
return
|
||||
@@ -413,7 +414,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
ctx.Data["RawFileLink"] = rawLink + "/" + ctx.Repo.TreePath
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := dataRc.Read(buf)
|
||||
n, _ := util.ReadAtMost(dataRc, buf)
|
||||
buf = buf[:n]
|
||||
|
||||
st := typesniffer.DetectContentType(buf)
|
||||
@@ -445,10 +446,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
|
||||
defer dataRc.Close()
|
||||
|
||||
buf = make([]byte, 1024)
|
||||
n, err = dataRc.Read(buf)
|
||||
// Error EOF don't mean there is an error, it just means we read to
|
||||
// the end
|
||||
if err != nil && err != io.EOF {
|
||||
n, err = util.ReadAtMost(dataRc, buf)
|
||||
if err != nil {
|
||||
ctx.ServerError("Data", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -788,10 +788,13 @@ async function initRepository() {
|
||||
|
||||
// Commit statuses
|
||||
$('.commit-statuses-trigger').each(function () {
|
||||
const positionRight = $('.repository.file.list').length > 0 || $('.repository.diff').length > 0;
|
||||
const popupPosition = positionRight ? 'right center' : 'left center';
|
||||
$(this)
|
||||
.popup({
|
||||
on: 'click',
|
||||
position: ($('.repository.file.list').length > 0 ? 'right center' : 'left center'),
|
||||
lastResort: popupPosition, // prevent error message "Popup does not fit within the boundaries of the viewport"
|
||||
position: popupPosition,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
.repository {
|
||||
.commit-statuses .list > .item {
|
||||
line-height: 2;
|
||||
.popup.commit-statuses {
|
||||
// we had better limit the max size of the popup, and add scroll bars if the content size is too large.
|
||||
// otherwise some part of the popup will be hidden by viewport boundary
|
||||
max-height: 45vh;
|
||||
max-width: 60vw;
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
|
||||
.list {
|
||||
padding: .8em; // to make the scrollbar align to the border, we move the padding from outer `.popup` to this inside `.list`
|
||||
|
||||
> .item {
|
||||
line-height: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.repo-header {
|
||||
|
||||
@@ -158,9 +158,18 @@
|
||||
|
||||
.task-list-item {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
line-height: 1.5rem;
|
||||
min-height: 1.5rem; // to render a checkbox list without content `- [ ]`, we need this min-height to make sure the <li> can be visible
|
||||
|
||||
input[type="checkbox"] {
|
||||
margin: 0 6px .25em -1.6em;
|
||||
position: absolute;
|
||||
top: .25em;
|
||||
left: -1.6em;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user