mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-24 13:25:01 +09:00
Merge branch 'main' into copilot/add-acme-profile-settings
This commit is contained in:
+10
-5
@@ -45,15 +45,20 @@ func UpdateBlockingNote(ctx context.Context, id int64, note string) error {
|
||||
}
|
||||
|
||||
func IsUserBlockedBy(ctx context.Context, blockee *User, blockerIDs ...int64) bool {
|
||||
if len(blockerIDs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if blockee.IsAdmin {
|
||||
return false
|
||||
}
|
||||
|
||||
cond := builder.Eq{"user_blocking.blockee_id": blockee.ID}.
|
||||
return HasBlocking(ctx, blockee.ID, blockerIDs...)
|
||||
}
|
||||
|
||||
// HasBlocking reports whether a blocking relationship exists regardless of the blockee's admin status.
|
||||
func HasBlocking(ctx context.Context, blockeeID int64, blockerIDs ...int64) bool {
|
||||
if len(blockerIDs) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
cond := builder.Eq{"user_blocking.blockee_id": blockeeID}.
|
||||
And(builder.In("user_blocking.blocker_id", blockerIDs))
|
||||
|
||||
has, _ := db.GetEngine(ctx).Where(cond).Exist(&Blocking{})
|
||||
|
||||
@@ -31,8 +31,9 @@ func (err ErrUserAlreadyExist) Unwrap() error {
|
||||
|
||||
// ErrUserNotExist represents a "UserNotExist" kind of error.
|
||||
type ErrUserNotExist struct {
|
||||
UID int64
|
||||
Name string
|
||||
UID int64
|
||||
Name string
|
||||
ExtraMsg string
|
||||
}
|
||||
|
||||
// IsErrUserNotExist checks if an error is a ErrUserNotExist.
|
||||
@@ -42,7 +43,11 @@ func IsErrUserNotExist(err error) bool {
|
||||
}
|
||||
|
||||
func (err ErrUserNotExist) Error() string {
|
||||
return fmt.Sprintf("user does not exist [uid: %d, name: %s]", err.UID, err.Name)
|
||||
ret := fmt.Sprintf("user does not exist [uid: %d, name: %s]", err.UID, err.Name)
|
||||
if err.ExtraMsg != "" {
|
||||
ret += ": " + err.ExtraMsg
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Unwrap unwraps this error as a ErrNotExist error
|
||||
|
||||
@@ -29,6 +29,8 @@ type FastImportCommit struct {
|
||||
Ref string
|
||||
Message string
|
||||
Files []FastImportFile
|
||||
|
||||
Author, Committer *Signature
|
||||
}
|
||||
|
||||
// ForceFastImportWithInit is for mainly for testing purpose
|
||||
@@ -48,15 +50,32 @@ func ForceFastImportWithInit(ctx context.Context, repoLocalPath string, commits
|
||||
|
||||
// ForceFastImport is for mainly for testing purpose
|
||||
func ForceFastImport(ctx context.Context, repo RepositoryFacade, commits []FastImportCommit) error {
|
||||
var buf bytes.Buffer
|
||||
buf := &bytes.Buffer{}
|
||||
for i, c := range commits {
|
||||
msg := util.IfZero(c.Message, fmt.Sprintf("commit %d", i+1))
|
||||
_, _ = fmt.Fprintf(&buf, "reset %s\n", c.Ref)
|
||||
_, _ = fmt.Fprintf(&buf, "commit %s\nmark :%d\ncommitter Gitea <gitea@example.com> 1500000000 +0000\n", c.Ref, i+1)
|
||||
_, _ = fmt.Fprintf(&buf, "data %d\n%s\n", len(msg), msg)
|
||||
_, _ = fmt.Fprintf(buf, "reset %s\n", c.Ref)
|
||||
_, _ = fmt.Fprintf(buf, "commit %s\n", c.Ref)
|
||||
_, _ = fmt.Fprintf(buf, "mark :%d\n", i+1)
|
||||
|
||||
if c.Author != nil {
|
||||
buf.WriteString("author ")
|
||||
_ = c.Author.Encode(buf)
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
if c.Committer != nil {
|
||||
buf.WriteString("committer ")
|
||||
_ = c.Committer.Encode(buf)
|
||||
buf.WriteByte('\n')
|
||||
} else {
|
||||
// "committer" is required, so we use a default one if not provided
|
||||
buf.WriteString("committer Gitea <gitea@example.com> 1500000000 +0000\n")
|
||||
}
|
||||
|
||||
msg := util.IfZero(c.Message, fmt.Sprintf("test commit %d", i+1))
|
||||
_, _ = fmt.Fprintf(buf, "data %d\n%s\n", len(msg), msg)
|
||||
|
||||
for _, f := range c.Files {
|
||||
mode := util.IfZero(f.Mode, EntryModeBlob)
|
||||
_, _ = fmt.Fprintf(&buf, "M %s inline %s\ndata %d\n%s\n", mode.String(), f.Path, len(f.Content), f.Content)
|
||||
_, _ = fmt.Fprintf(buf, "M %s inline %s\ndata %d\n%s\n", mode.String(), f.Path, len(f.Content), f.Content)
|
||||
}
|
||||
}
|
||||
buf.WriteString("done\n")
|
||||
|
||||
@@ -8,6 +8,7 @@ package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/util"
|
||||
@@ -24,7 +25,13 @@ func (s *Signature) String() string {
|
||||
return fmt.Sprintf("%s <%s>", s.Name, s.Email)
|
||||
}
|
||||
|
||||
// Decode decodes a byte array representing a signature to signature
|
||||
// Encode writes the signature for git commit object (same as gogit's object.Signature Encode method)
|
||||
func (s *Signature) Encode(w io.Writer) error {
|
||||
_, err := fmt.Fprintf(w, "%s <%s> %d %s", s.Name, s.Email, max(0, s.When.Unix()), s.When.Format("-0700"))
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode parses the signature for git commit object (same as gogit's object.Signature Decode method)
|
||||
func (s *Signature) Decode(b []byte) {
|
||||
*s = *parseSignatureFromCommitLine(util.UnsafeBytesToString(b))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/util"
|
||||
)
|
||||
|
||||
// Scheme describes protocol types
|
||||
@@ -274,9 +275,7 @@ func loadServerFrom(rootCfg ConfigProvider) {
|
||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||
RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
|
||||
if len(StaticRootPath) == 0 {
|
||||
StaticRootPath = AppWorkPath
|
||||
}
|
||||
StaticRootPath = util.IfZero(StaticRootPath, AppWorkPath)
|
||||
StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(StaticRootPath)
|
||||
StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
|
||||
AppDataPath = sec.Key("APP_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data"))
|
||||
|
||||
@@ -64,7 +64,7 @@ func ParseJSONRedirect(buf []byte) (ret struct {
|
||||
}
|
||||
|
||||
func IsNormalPageCompleted(s string) bool {
|
||||
return strings.Contains(s, `<footer class="page-footer"`) && strings.Contains(s, `</html>`)
|
||||
return strings.Contains(s, `<footer class="page-footer"`) && strings.HasSuffix(strings.TrimSpace(s), `</html>`)
|
||||
}
|
||||
|
||||
func MockVariableValue[T any](p *T, v ...T) (reset func()) {
|
||||
|
||||
@@ -26,7 +26,7 @@ const (
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
setting.StaticRootPath = "../../"
|
||||
setting.SetupGiteaTestEnv()
|
||||
setting.Names = []string{"english"}
|
||||
setting.Langs = []string{"en-US"}
|
||||
// setup
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
"confirm_delete_artifact": "您确定要删除产物「%s」吗?",
|
||||
"archived": "已归档",
|
||||
"concept_system_global": "全局",
|
||||
"concept_user_user": "发送者帐号",
|
||||
"concept_user_user": "用户",
|
||||
"concept_code_repository": "仓库",
|
||||
"concept_user_organization": "组织",
|
||||
"concept_user_bot": "机器人",
|
||||
|
||||
@@ -21,7 +21,11 @@ import (
|
||||
"gitea.dev/services/context"
|
||||
)
|
||||
|
||||
const tplStatus500 templates.TplName = "status/500"
|
||||
const (
|
||||
tplStatus500 templates.TplName = "status/500"
|
||||
|
||||
PageInternalServerErrorMark = "status-page-500"
|
||||
)
|
||||
|
||||
func renderServerErrorPage(w http.ResponseWriter, req *http.Request, respCode int, tmpl templates.TplName, ctxData map[string]any, plainMsg string) {
|
||||
acceptsHTML := false
|
||||
@@ -68,7 +72,7 @@ func renderPanicErrorPage(w http.ResponseWriter, req *http.Request, recovered an
|
||||
// This recovery handler could be called without Gitea's web context, so we shouldn't touch that context too much.
|
||||
// Otherwise, the 500-page may cause new panics, eg: cache.GetContextWithData, it makes the developer&users couldn't find the original panic.
|
||||
user, _ := ctxData[middleware.ContextDataKeySignedUser].(*user_model.User)
|
||||
if !setting.IsProd || (user != nil && user.IsAdmin) {
|
||||
if !setting.IsProd || setting.IsInTesting || (user != nil && user.IsAdmin) {
|
||||
plainMsg = "PANIC: " + combinedErr.Error()
|
||||
ctxData["ErrorMsg"] = plainMsg
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRenderPanicErrorPage(t *testing.T) {
|
||||
t.Run("HTML", func(t *testing.T) {
|
||||
func TestRenderErrorPage(t *testing.T) {
|
||||
t.Run("PanicHTML", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{URL: &url.URL{}, Header: http.Header{"Accept": []string{"text/html"}}}
|
||||
req = req.WithContext(reqctx.NewRequestContextForTest(t))
|
||||
@@ -33,7 +33,7 @@ func TestRenderPanicErrorPage(t *testing.T) {
|
||||
// the different "footer" is the only way to know whether a page is fully rendered without error.
|
||||
assert.False(t, test.IsNormalPageCompleted(respContent))
|
||||
})
|
||||
t.Run("Plain", func(t *testing.T) {
|
||||
t.Run("ServiceUnavailablePlain", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{URL: &url.URL{}}
|
||||
req = req.WithContext(reqctx.NewRequestContextForTest(t))
|
||||
|
||||
+76
-52
@@ -7,9 +7,9 @@ package repo
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/git/gitcmd"
|
||||
"gitea.dev/modules/git/gitrepo"
|
||||
"gitea.dev/modules/httplib"
|
||||
"gitea.dev/modules/log"
|
||||
repo_module "gitea.dev/modules/repository"
|
||||
"gitea.dev/modules/setting"
|
||||
@@ -232,8 +233,8 @@ func httpBase(ctx *context.Context, optGitService ...string) *serviceHandler {
|
||||
|
||||
repo, err = repo_service.PushCreateRepo(ctx, ctx.Doer, owner, repoName)
|
||||
if err != nil {
|
||||
log.Error("pushCreateRepo: %v", err)
|
||||
ctx.Status(http.StatusNotFound)
|
||||
log.Debug("PushCreateRepo: %v", err)
|
||||
ctx.Status(http.StatusNotFound) // TODO: need to refactor PushCreateRepo and its returned errors
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -281,7 +282,7 @@ func dummyInfoRefs(ctx *context.Context) {
|
||||
WithDir(tmpEmptyRepoDir).
|
||||
RunStdBytes(ctx)
|
||||
if err != nil {
|
||||
log.Error(fmt.Sprintf("%v - %s", err, string(refs)))
|
||||
log.Error("Failed to prepare git-receive-pack cache: %v", err)
|
||||
}
|
||||
|
||||
log.Debug("populating infoRefsCache: \n%s", string(refs))
|
||||
@@ -292,9 +293,9 @@ func dummyInfoRefs(ctx *context.Context) {
|
||||
ctx.RespHeader().Set("Pragma", "no-cache")
|
||||
ctx.RespHeader().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
|
||||
ctx.RespHeader().Set("Content-Type", "application/x-git-receive-pack-advertisement")
|
||||
_, _ = ctx.Write(packetWrite("# service=git-receive-pack\n"))
|
||||
_, _ = ctx.Write([]byte("0000"))
|
||||
_, _ = ctx.Write(infoRefsCache)
|
||||
_ = pktLineWriteText(ctx.Resp, "# service=git-receive-pack")
|
||||
_ = pktLineWriteFlush(ctx.Resp)
|
||||
_, _ = ctx.Resp.Write(infoRefsCache)
|
||||
}
|
||||
|
||||
type serviceHandler struct {
|
||||
@@ -326,29 +327,27 @@ func setHeaderCacheForever(ctx *context.Context) {
|
||||
ctx.Resp.Header().Set("Cache-Control", "public, max-age=31536000")
|
||||
}
|
||||
|
||||
func containsParentDirectorySeparator(v string) bool {
|
||||
if !strings.Contains(v, "..") {
|
||||
return false
|
||||
}
|
||||
return slices.Contains(strings.FieldsFunc(v, isSlashRune), "..")
|
||||
}
|
||||
|
||||
func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
|
||||
|
||||
func (h *serviceHandler) sendFile(ctx *context.Context, contentType, file string) {
|
||||
if containsParentDirectorySeparator(file) {
|
||||
log.Debug("request file path contains invalid path: %v", file)
|
||||
ctx.Resp.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
fs := gitrepo.RepoLocalFS(h.getStorageRepo())
|
||||
ctx.Resp.Header().Set("Content-Type", contentType)
|
||||
http.ServeFileFS(ctx.Resp, ctx.Req, fs, path.Clean(file))
|
||||
relPath := util.PathJoinRelX(file)
|
||||
http.ServeFileFS(ctx.Resp, ctx.Req, fs, relPath)
|
||||
}
|
||||
|
||||
// one or more key=value pairs separated by colons
|
||||
var safeGitProtocolHeader = regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`)
|
||||
var safeGitProtocolHeader = sync.OnceValue(func() *regexp.Regexp {
|
||||
return regexp.MustCompile(`^[0-9a-zA-Z]+=[0-9a-zA-Z]+(:[0-9a-zA-Z]+=[0-9a-zA-Z]+)*$`)
|
||||
})
|
||||
|
||||
func prepareGitCmdEnvs(ctx *context.Context, h *serviceHandler, more ...string) []string {
|
||||
envs := slices.Clone(os.Environ())
|
||||
envs = append(envs, h.environ...)
|
||||
envs = append(envs, more...)
|
||||
if protocol := ctx.Req.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader().MatchString(protocol) {
|
||||
envs = append(envs, "GIT_PROTOCOL="+protocol)
|
||||
}
|
||||
return envs
|
||||
}
|
||||
|
||||
func prepareGitCmdWithAllowedService(service string, allowedServices []string) *gitcmd.Command {
|
||||
if !slices.Contains(allowedServices, service) {
|
||||
@@ -404,23 +403,15 @@ func serviceRPC(ctx *context.Context, service string) {
|
||||
}
|
||||
}
|
||||
|
||||
// set this for allow pre-receive and post-receive execute
|
||||
h.environ = append(h.environ, "SSH_ORIGINAL_COMMAND="+service)
|
||||
|
||||
if protocol := ctx.Req.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) {
|
||||
h.environ = append(h.environ, "GIT_PROTOCOL="+protocol)
|
||||
}
|
||||
|
||||
// set SSH_ORIGINAL_COMMAND to allow pre-receive and post-receive hooks
|
||||
gitCmdEnvs := prepareGitCmdEnvs(ctx, h, "SSH_ORIGINAL_COMMAND="+service)
|
||||
err := cmd.AddArguments(".").
|
||||
WithRepo(h.getStorageRepo()).WithEnv(append(os.Environ(), h.environ...)).
|
||||
WithRepo(h.getStorageRepo()).WithEnv(gitCmdEnvs).
|
||||
WithStdinCopy(reqBody).
|
||||
WithStdoutCopy(ctx.Resp).
|
||||
RunWithStderr(ctx)
|
||||
if err != nil {
|
||||
if !gitcmd.IsErrorCanceledOrKilled(err) {
|
||||
repoLogName := h.repo.FullName() + util.Iif(h.isWiki, ".wiki", "")
|
||||
log.Error("Fail to serve RPC(%s) for repo %s: %v", service, repoLogName, err)
|
||||
}
|
||||
if err != nil && !gitcmd.IsErrorCanceledOrKilled(err) && !httplib.IsClientOrNetworkError(ctx, err) {
|
||||
log.Error("Fail to serve RPC(%s) for repo %s: %v", service, h.getStorageRepo().LogString(), err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,25 +435,43 @@ func ServiceUploadArchive(ctx *context.Context) {
|
||||
serviceRPC(ctx, ServiceTypeUploadArchive)
|
||||
}
|
||||
|
||||
func packetWrite(str string) []byte {
|
||||
s := strconv.FormatInt(int64(len(str)+4), 16)
|
||||
if len(s)%4 != 0 {
|
||||
s = strings.Repeat("0", 4-len(s)%4) + s
|
||||
func pktLineWriteText(w io.Writer, str string) error {
|
||||
// https://git-scm.com/docs/gitprotocol-common
|
||||
prefix := strconv.FormatInt(int64(len(str)+4+1), 16)
|
||||
if len(prefix)%4 != 0 {
|
||||
prefix = "0000" + prefix
|
||||
prefix = prefix[len(prefix)-4:]
|
||||
}
|
||||
return []byte(s + str)
|
||||
if _, err := io.WriteString(w, prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.WriteString(w, str); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := io.WriteString(w, "\n")
|
||||
return err
|
||||
}
|
||||
|
||||
func pktLineWriteFlush(w io.Writer) error {
|
||||
_, err := io.WriteString(w, "0000")
|
||||
return err
|
||||
}
|
||||
|
||||
// GetInfoRefs implements Git dumb HTTP
|
||||
// ref: https://git-scm.com/docs/gitprotocol-http , https://git-scm.com/docs/gitprotocol-v2
|
||||
func GetInfoRefs(ctx *context.Context) {
|
||||
h := httpBase(ctx, ctx.FormString("service")) // git http protocol: "?service=git-<service>"
|
||||
if h == nil {
|
||||
return
|
||||
}
|
||||
|
||||
repo := h.getStorageRepo()
|
||||
setHeaderNoCache(ctx)
|
||||
|
||||
if h.serviceType == "" {
|
||||
// it's said that some legacy git clients will send requests to "/info/refs" without "service" parameter,
|
||||
// although there should be no such case client in the modern days. TODO: not quite sure why we need this UpdateServerInfo logic
|
||||
if err := git.UpdateServerInfo(ctx, h.getStorageRepo()); err != nil {
|
||||
if err := git.UpdateServerInfo(ctx, repo); err != nil {
|
||||
ctx.ServerError("UpdateServerInfo", err)
|
||||
return
|
||||
}
|
||||
@@ -470,28 +479,43 @@ func GetInfoRefs(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
gitCmdEnvs := prepareGitCmdEnvs(ctx, h)
|
||||
cmd := prepareGitCmdWithAllowedService(h.serviceType, []string{ServiceTypeUploadPack, ServiceTypeReceivePack})
|
||||
if cmd == nil {
|
||||
ctx.Resp.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if protocol := ctx.Req.Header.Get("Git-Protocol"); protocol != "" && safeGitProtocolHeader.MatchString(protocol) {
|
||||
h.environ = append(h.environ, "GIT_PROTOCOL="+protocol)
|
||||
}
|
||||
h.environ = append(os.Environ(), h.environ...)
|
||||
ctx.Resp.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-advertisement", h.serviceType))
|
||||
|
||||
cmd = cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").WithEnv(h.environ)
|
||||
refs, _, err := cmd.WithRepo(h.getStorageRepo()).RunStdBytes(ctx)
|
||||
repoExists, err := git.IsRepositoryExist(ctx, repo)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsRepositoryExist", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !repoExists {
|
||||
ctx.Resp.WriteHeader(http.StatusOK)
|
||||
// error-line = PKT-LINE("ERR" SP explanation-text)
|
||||
errMsg := "repository doesn't exist"
|
||||
if h.isWiki {
|
||||
errMsg = "wiki doesn't exist, please initialize the wiki by creating a new page first"
|
||||
}
|
||||
_ = pktLineWriteText(ctx.Resp, "ERR "+errMsg)
|
||||
return
|
||||
}
|
||||
|
||||
cmd = cmd.AddArguments("--stateless-rpc", "--advertise-refs", ".").WithEnv(gitCmdEnvs)
|
||||
refs, _, err := cmd.WithRepo(repo).RunStdBytes(ctx)
|
||||
if err != nil {
|
||||
ctx.ServerError("RunGitServiceAdvertiseRefs", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Set("Content-Type", fmt.Sprintf("application/x-git-%s-advertisement", h.serviceType))
|
||||
// https://git-scm.com/docs/gitprotocol-pack
|
||||
ctx.Resp.WriteHeader(http.StatusOK)
|
||||
_, _ = ctx.Resp.Write(packetWrite("# service=git-" + h.serviceType + "\n"))
|
||||
_, _ = ctx.Resp.Write([]byte("0000"))
|
||||
_ = pktLineWriteText(ctx.Resp, "# service=git-"+h.serviceType)
|
||||
_ = pktLineWriteFlush(ctx.Resp)
|
||||
_, _ = ctx.Resp.Write(refs)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestContainsParentDirectorySeparator(t *testing.T) {
|
||||
tests := []struct {
|
||||
v string
|
||||
b bool
|
||||
}{
|
||||
{
|
||||
v: `user2/repo1/info/refs`,
|
||||
b: false,
|
||||
},
|
||||
{
|
||||
v: `user2/repo1/HEAD`,
|
||||
b: false,
|
||||
},
|
||||
{
|
||||
v: `user2/repo1/some.../strange_file...mp3`,
|
||||
b: false,
|
||||
},
|
||||
{
|
||||
v: `user2/repo1/../../custom/conf/app.ini`,
|
||||
b: true,
|
||||
},
|
||||
{
|
||||
v: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`,
|
||||
b: true,
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
assert.Equal(t, tests[i].b, containsParentDirectorySeparator(tests[i].v))
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
issues_model "gitea.dev/models/issues"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/templates"
|
||||
"gitea.dev/routers/common"
|
||||
"gitea.dev/services/context"
|
||||
"gitea.dev/services/contexttest"
|
||||
"gitea.dev/services/pull"
|
||||
@@ -78,7 +79,7 @@ func TestRenderConversation(t *testing.T) {
|
||||
ctx.Data["ShowOutdatedComments"] = true
|
||||
renderConversation(ctx, preparedComment, "diff")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
assert.NotContains(t, resp.Body.String(), `status-page-500`)
|
||||
assert.NotContains(t, resp.Body.String(), common.PageInternalServerErrorMark)
|
||||
})
|
||||
run("timeline non-existing review", func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder) {
|
||||
err := db.TruncateBeans(t.Context(), &issues_model.Review{})
|
||||
@@ -86,6 +87,6 @@ func TestRenderConversation(t *testing.T) {
|
||||
ctx.Data["ShowOutdatedComments"] = true
|
||||
renderConversation(ctx, preparedComment, "timeline")
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
assert.NotContains(t, resp.Body.String(), `status-page-500`)
|
||||
assert.NotContains(t, resp.Body.String(), common.PageInternalServerErrorMark)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u
|
||||
}
|
||||
sr := source.SearchEntry(loginName, password, source.AuthSource.Type == auth.DLDAP)
|
||||
if sr == nil {
|
||||
// User not in LDAP, do nothing
|
||||
return nil, user_model.ErrUserNotExist{Name: loginName}
|
||||
// User is not in LDAP database, or password is invalid (direct bind)
|
||||
return nil, user_model.ErrUserNotExist{Name: loginName, ExtraMsg: "not in LDAP database or invalid password"}
|
||||
}
|
||||
// Fallback.
|
||||
// FIXME: this fallback would cause problems when the "Username" attribute is not set and a user inputs their email.
|
||||
|
||||
@@ -155,7 +155,7 @@ func (ctx *Context) notFoundInternal(skip int, logMsg string, logErr error) {
|
||||
|
||||
func (ctx *Context) buildUserErrorMessage(msg string, err error) (userErrorMsg string) {
|
||||
// it's safe to show internal error to admin users, and it helps
|
||||
if !setting.IsProd || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
|
||||
if !setting.IsProd || setting.IsInTesting || (ctx.Doer != nil && ctx.Doer.IsAdmin) {
|
||||
userErrorMsg = msg
|
||||
if err != nil {
|
||||
userErrorMsg += ", error: " + err.Error()
|
||||
|
||||
@@ -50,7 +50,7 @@ func CanUnblockUser(ctx context.Context, doer, blocker, blockee *user_model.User
|
||||
return false
|
||||
}
|
||||
|
||||
if !user_model.IsUserBlockedBy(ctx, blockee, blocker.ID) {
|
||||
if !user_model.HasBlocking(ctx, blockee.ID, blocker.ID) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -62,4 +62,8 @@ func TestCanUnblockUser(t *testing.T) {
|
||||
assert.True(t, CanUnblockUser(t.Context(), user1, user2, user29))
|
||||
assert.True(t, CanUnblockUser(t.Context(), user2, user2, user29))
|
||||
assert.True(t, CanUnblockUser(t.Context(), user1, org17, user28))
|
||||
// Existing block can still be removed after the blockee becomes an admin.
|
||||
user29.IsAdmin = true
|
||||
assert.False(t, user_model.IsUserBlockedBy(t.Context(), user29, user2.ID))
|
||||
assert.True(t, CanUnblockUser(t.Context(), user2, user2, user29))
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
<span class="flex-text-inline tw-text-text-light">{{ctx.Locale.Tr "repo.diff.committed_at" $authors $committedAt}}</span>
|
||||
{{else}}
|
||||
{{$committerAvatar := ""}}{{$committerDisplayName := ""}}
|
||||
{{if .Verification.CommittingUser}}
|
||||
{{if and .Verification .Verification.CommittingUser}}
|
||||
{{$committerAvatar = ctx.AvatarUtils.Avatar .Verification.CommittingUser 20}}
|
||||
{{$committerDisplayName = HTMLFormat `%s%s` .Verification.CommittingUser.GetShortDisplayNameLinkHTML (ctx.RenderUtils.UserTypeLabel .Verification.CommittingUser)}}
|
||||
{{else}}
|
||||
|
||||
@@ -907,7 +907,6 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{template "repo/settings/layout_footer" .}}
|
||||
|
||||
{{if $.CanManagerDangerZone}}
|
||||
{{if .Repository.IsMirror}}
|
||||
@@ -1083,3 +1082,5 @@
|
||||
{{end}}
|
||||
|
||||
{{template "repo/settings/push_mirror_sync_modal" .}}
|
||||
|
||||
{{template "repo/settings/layout_footer" .}}
|
||||
|
||||
@@ -34,9 +34,10 @@ func TestGitSmartHTTP(t *testing.T) {
|
||||
}
|
||||
|
||||
func testGitSmartHTTP(t *testing.T, u *url.URL) {
|
||||
kases := []struct {
|
||||
cases := []struct {
|
||||
method, path string
|
||||
code int
|
||||
contains string
|
||||
}{
|
||||
{
|
||||
path: "user2/repo1/info/refs",
|
||||
@@ -51,6 +52,11 @@ func testGitSmartHTTP(t *testing.T, u *url.URL) {
|
||||
path: "user2/repo1/HEAD",
|
||||
code: http.StatusOK,
|
||||
},
|
||||
{
|
||||
path: "user2/repo2.wiki/info/refs?service=git-upload-pack",
|
||||
code: http.StatusOK,
|
||||
contains: "ERR wiki doesn't exist",
|
||||
},
|
||||
{
|
||||
path: "user2/repo1/objects/info/alternates",
|
||||
code: http.StatusNotFound,
|
||||
@@ -73,17 +79,20 @@ func testGitSmartHTTP(t *testing.T, u *url.URL) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, kase := range kases {
|
||||
t.Run(kase.path, func(t *testing.T) {
|
||||
req, err := http.NewRequest(util.IfZero(kase.method, "GET"), u.String()+kase.path, nil)
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.path, func(t *testing.T) {
|
||||
req, err := http.NewRequest(util.IfZero(tc.method, "GET"), u.String()+tc.path, nil)
|
||||
require.NoError(t, err)
|
||||
req.SetBasicAuth("user2", userPassword)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
assert.Equal(t, kase.code, resp.StatusCode)
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
assert.Equal(t, tc.code, resp.StatusCode)
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
if tc.contains != "" {
|
||||
assert.Contains(t, string(respBody), tc.contains)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"gitea.dev/modules/web"
|
||||
"gitea.dev/modules/web/middleware"
|
||||
"gitea.dev/routers"
|
||||
"gitea.dev/routers/common"
|
||||
gitea_context "gitea.dev/services/context"
|
||||
"gitea.dev/tests"
|
||||
|
||||
@@ -289,6 +290,9 @@ func MakeRequest(t testing.TB, rw *RequestWrapper, expectedStatus int) *httptest
|
||||
// don't use "require" which exits the test case and makes "wait group" wait forever
|
||||
assert.Equal(t, expectedStatus, recorder.Code, "Request: %s %s", req.Method, req.URL.String())
|
||||
}
|
||||
if expectedStatus != http.StatusInternalServerError {
|
||||
assert.NotContains(t, recorder.Body.String(), common.PageInternalServerErrorMark, "Request: %s %s, response should not contain internal server error", req.Method, req.URL.String())
|
||||
}
|
||||
}
|
||||
return recorder
|
||||
}
|
||||
|
||||
@@ -14,7 +14,10 @@ import (
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
git_model "gitea.dev/models/git"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
"gitea.dev/modules/commitstatus"
|
||||
"gitea.dev/modules/git"
|
||||
"gitea.dev/modules/json"
|
||||
"gitea.dev/modules/setting"
|
||||
api "gitea.dev/modules/structs"
|
||||
@@ -77,7 +80,6 @@ func TestRepoCommits(t *testing.T) {
|
||||
const (
|
||||
commitID = "5099b81332712fe655e34e8dd63574f503f61811"
|
||||
expectedCommitterTime = "2017-08-06T19:56:13+02:00"
|
||||
authorTime = "2017-08-06T19:55:01+02:00"
|
||||
)
|
||||
|
||||
req := NewRequest(t, "GET", "/user2/repo16/commits/branch/master")
|
||||
@@ -104,6 +106,24 @@ func TestRepoCommits(t *testing.T) {
|
||||
authorElem := doc.doc.Find(".latest-commit .avatar-stack-names")
|
||||
assert.Equal(t, "6543", strings.TrimSpace(authorElem.Text()))
|
||||
})
|
||||
|
||||
t.Run("CommitterIsNotAuthor", func(t *testing.T) {
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
err := git.ForceFastImport(t.Context(), repo1, []git.FastImportCommit{
|
||||
{
|
||||
Ref: "refs/heads/test-branch-committer",
|
||||
Files: []git.FastImportFile{{Path: "dummy-file.txt", Content: "dummy-content"}},
|
||||
Author: &git.Signature{Name: "real-commit-author", Email: "dummy-email1@example.com"},
|
||||
Committer: &git.Signature{Name: "non-author-committer", Email: "dummy-email2@example.com"},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
commitID, err := git.GetBranchCommitID(t.Context(), repo1, "test-branch-committer")
|
||||
require.NoError(t, err)
|
||||
req := NewRequest(t, "GET", "/user2/repo1/commit/"+commitID)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Contains(t, resp.Body.String(), "non-author-committer")
|
||||
})
|
||||
}
|
||||
|
||||
func TestRepoCommitsWithStatus(t *testing.T) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export async function initMarkupCodeMath(elMarkup: HTMLElement): Promise<void> {
|
||||
import('katex/dist/katex.css'),
|
||||
]);
|
||||
|
||||
const MAX_CHARS = 1000;
|
||||
const MAX_CHARS = 10000;
|
||||
const MAX_SIZE = 25;
|
||||
const MAX_EXPAND = 1000;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user