mirror of
https://github.com/go-gitea/gitea.git
synced 2025-02-24 21:14:11 +08:00
Refactor context flash msg and global variables (#33375)
1. add `GetSiteCookieFlashMessage` to help to parse flash message 2. clarify `handleRepoHomeFeed` logic 3. remove unnecessary global variables, use `sync.OnceValue` instead 4. add some tests for `IsUsableUsername` and `IsUsableRepoName`
This commit is contained in:
parent
6a516a0d14
commit
2c1ff8701a
@ -11,8 +11,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
|
||||||
|
|
||||||
// ErrNameReserved represents a "reserved name" error.
|
// ErrNameReserved represents a "reserved name" error.
|
||||||
type ErrNameReserved struct {
|
type ErrNameReserved struct {
|
||||||
Name string
|
Name string
|
||||||
@ -79,7 +77,7 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
|
|||||||
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
|
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
|
||||||
name = strings.TrimSpace(strings.ToLower(name))
|
name = strings.TrimSpace(strings.ToLower(name))
|
||||||
if utf8.RuneCountInString(name) == 0 {
|
if utf8.RuneCountInString(name) == 0 {
|
||||||
return ErrNameEmpty
|
return util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range reservedNames {
|
for i := range reservedNames {
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
@ -61,20 +62,30 @@ func (err ErrRepoIsArchived) Error() string {
|
|||||||
return fmt.Sprintf("%s is archived", err.Repo.LogString())
|
return fmt.Sprintf("%s is archived", err.Repo.LogString())
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
type globalVarsStruct struct {
|
||||||
validRepoNamePattern = regexp.MustCompile(`[-.\w]+`)
|
validRepoNamePattern *regexp.Regexp
|
||||||
invalidRepoNamePattern = regexp.MustCompile(`[.]{2,}`)
|
invalidRepoNamePattern *regexp.Regexp
|
||||||
reservedRepoNames = []string{".", "..", "-"}
|
reservedRepoNames []string
|
||||||
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
|
reservedRepoPatterns []string
|
||||||
)
|
}
|
||||||
|
|
||||||
|
var globalVars = sync.OnceValue(func() *globalVarsStruct {
|
||||||
|
return &globalVarsStruct{
|
||||||
|
validRepoNamePattern: regexp.MustCompile(`[-.\w]+`),
|
||||||
|
invalidRepoNamePattern: regexp.MustCompile(`[.]{2,}`),
|
||||||
|
reservedRepoNames: []string{".", "..", "-"},
|
||||||
|
reservedRepoPatterns: []string{"*.git", "*.wiki", "*.rss", "*.atom"},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// IsUsableRepoName returns true when name is usable
|
// IsUsableRepoName returns true when name is usable
|
||||||
func IsUsableRepoName(name string) error {
|
func IsUsableRepoName(name string) error {
|
||||||
if !validRepoNamePattern.MatchString(name) || invalidRepoNamePattern.MatchString(name) {
|
vars := globalVars()
|
||||||
|
if !vars.validRepoNamePattern.MatchString(name) || vars.invalidRepoNamePattern.MatchString(name) {
|
||||||
// Note: usually this error is normally caught up earlier in the UI
|
// Note: usually this error is normally caught up earlier in the UI
|
||||||
return db.ErrNameCharsNotAllowed{Name: name}
|
return db.ErrNameCharsNotAllowed{Name: name}
|
||||||
}
|
}
|
||||||
return db.IsUsableName(reservedRepoNames, reservedRepoPatterns, name)
|
return db.IsUsableName(vars.reservedRepoNames, vars.reservedRepoPatterns, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrustModelType defines the types of trust model for this repository
|
// TrustModelType defines the types of trust model for this repository
|
||||||
|
@ -219,4 +219,5 @@ func TestIsUsableRepoName(t *testing.T) {
|
|||||||
assert.Error(t, IsUsableRepoName("the..repo"))
|
assert.Error(t, IsUsableRepoName("the..repo"))
|
||||||
assert.Error(t, IsUsableRepoName("foo.wiki"))
|
assert.Error(t, IsUsableRepoName("foo.wiki"))
|
||||||
assert.Error(t, IsUsableRepoName("foo.git"))
|
assert.Error(t, IsUsableRepoName("foo.git"))
|
||||||
|
assert.Error(t, IsUsableRepoName("foo.RSS"))
|
||||||
}
|
}
|
||||||
|
@ -502,10 +502,10 @@ func (u *User) IsMailable() bool {
|
|||||||
return u.IsActive
|
return u.IsActive
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsUserExist checks if given user name exist,
|
// IsUserExist checks if given username exist,
|
||||||
// the user name should be noncased unique.
|
// the username should be non-cased unique.
|
||||||
// If uid is presented, then check will rule out that one,
|
// If uid is presented, then check will rule out that one,
|
||||||
// it is used when update a user name in settings page.
|
// it is used when update a username in settings page.
|
||||||
func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
|
func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
|
||||||
if len(name) == 0 {
|
if len(name) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
@ -515,7 +515,7 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
|
|||||||
Get(&User{LowerName: strings.ToLower(name)})
|
Get(&User{LowerName: strings.ToLower(name)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: As of the beginning of 2022, it is recommended to use at least
|
// SaltByteLength as of the beginning of 2022, it is recommended to use at least
|
||||||
// 64 bits of salt, but NIST is already recommending to use to 128 bits.
|
// 64 bits of salt, but NIST is already recommending to use to 128 bits.
|
||||||
// (16 bytes = 16 * 8 = 128 bits)
|
// (16 bytes = 16 * 8 = 128 bits)
|
||||||
const SaltByteLength = 16
|
const SaltByteLength = 16
|
||||||
|
@ -25,6 +25,21 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIsUsableUsername(t *testing.T) {
|
||||||
|
assert.NoError(t, user_model.IsUsableUsername("a"))
|
||||||
|
assert.NoError(t, user_model.IsUsableUsername("foo.wiki"))
|
||||||
|
assert.NoError(t, user_model.IsUsableUsername("foo.git"))
|
||||||
|
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("a--b"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("-1_."))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername(".profile"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("-"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("🌞"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("the..repo"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("foo.RSS"))
|
||||||
|
assert.Error(t, user_model.IsUsableUsername("foo.PnG"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestOAuth2Application_LoadUser(t *testing.T) {
|
func TestOAuth2Application_LoadUser(t *testing.T) {
|
||||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||||
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})
|
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})
|
||||||
|
@ -19,7 +19,10 @@ func getGlobPatternErrorString(pattern string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var globValidationTestCases = []validationTestCase{
|
func Test_GlobPatternValidation(t *testing.T) {
|
||||||
|
AddBindingRules()
|
||||||
|
|
||||||
|
globValidationTestCases := []validationTestCase{
|
||||||
{
|
{
|
||||||
description: "Empty glob pattern",
|
description: "Empty glob pattern",
|
||||||
data: TestForm{
|
data: TestForm{
|
||||||
@ -48,10 +51,7 @@ var globValidationTestCases = []validationTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GlobPatternValidation(t *testing.T) {
|
|
||||||
AddBindingRules()
|
|
||||||
|
|
||||||
for _, testCase := range globValidationTestCases {
|
for _, testCase := range globValidationTestCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
@ -8,13 +8,26 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
|
||||||
"github.com/gobwas/glob"
|
"github.com/gobwas/glob"
|
||||||
)
|
)
|
||||||
|
|
||||||
var externalTrackerRegex = regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`)
|
type globalVarsStruct struct {
|
||||||
|
externalTrackerRegex *regexp.Regexp
|
||||||
|
validUsernamePattern *regexp.Regexp
|
||||||
|
invalidUsernamePattern *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalVars = sync.OnceValue(func() *globalVarsStruct {
|
||||||
|
return &globalVarsStruct{
|
||||||
|
externalTrackerRegex: regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`),
|
||||||
|
validUsernamePattern: regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`),
|
||||||
|
invalidUsernamePattern: regexp.MustCompile(`[-._]{2,}|[-._]$`), // No consecutive or trailing non-alphanumeric chars
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
func isLoopbackIP(ip string) bool {
|
func isLoopbackIP(ip string) bool {
|
||||||
return net.ParseIP(ip).IsLoopback()
|
return net.ParseIP(ip).IsLoopback()
|
||||||
@ -105,9 +118,9 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
|
|||||||
if !IsValidExternalURL(uri) {
|
if !IsValidExternalURL(uri) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
vars := globalVars()
|
||||||
// check for typoed variables like /{index/ or /[repo}
|
// check for typoed variables like /{index/ or /[repo}
|
||||||
for _, match := range externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
|
for _, match := range vars.externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
|
||||||
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
|
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -116,14 +129,10 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
|
|
||||||
invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsValidUsername checks if username is valid
|
// IsValidUsername checks if username is valid
|
||||||
func IsValidUsername(name string) bool {
|
func IsValidUsername(name string) bool {
|
||||||
// It is difficult to find a single pattern that is both readable and effective,
|
// It is difficult to find a single pattern that is both readable and effective,
|
||||||
// but it's easier to use positive and negative checks.
|
// but it's easier to use positive and negative checks.
|
||||||
return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name)
|
vars := globalVars()
|
||||||
|
return vars.validUsernamePattern.MatchString(name) && !vars.invalidUsernamePattern.MatchString(name)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@ import (
|
|||||||
"gitea.com/go-chi/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var gitRefNameValidationTestCases = []validationTestCase{
|
func Test_GitRefNameValidation(t *testing.T) {
|
||||||
|
AddBindingRules()
|
||||||
|
gitRefNameValidationTestCases := []validationTestCase{
|
||||||
{
|
{
|
||||||
description: "Reference name contains only characters",
|
description: "Reference name contains only characters",
|
||||||
data: TestForm{
|
data: TestForm{
|
||||||
@ -252,10 +254,7 @@ var gitRefNameValidationTestCases = []validationTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_GitRefNameValidation(t *testing.T) {
|
|
||||||
AddBindingRules()
|
|
||||||
|
|
||||||
for _, testCase := range gitRefNameValidationTestCases {
|
for _, testCase := range gitRefNameValidationTestCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
@ -17,7 +17,10 @@ func getRegexPatternErrorString(pattern string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var regexValidationTestCases = []validationTestCase{
|
func Test_RegexPatternValidation(t *testing.T) {
|
||||||
|
AddBindingRules()
|
||||||
|
|
||||||
|
regexValidationTestCases := []validationTestCase{
|
||||||
{
|
{
|
||||||
description: "Empty regex pattern",
|
description: "Empty regex pattern",
|
||||||
data: TestForm{
|
data: TestForm{
|
||||||
@ -46,10 +49,7 @@ var regexValidationTestCases = []validationTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_RegexPatternValidation(t *testing.T) {
|
|
||||||
AddBindingRules()
|
|
||||||
|
|
||||||
for _, testCase := range regexValidationTestCases {
|
for _, testCase := range regexValidationTestCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
@ -9,7 +9,10 @@ import (
|
|||||||
"gitea.com/go-chi/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
var urlValidationTestCases = []validationTestCase{
|
func Test_ValidURLValidation(t *testing.T) {
|
||||||
|
AddBindingRules()
|
||||||
|
|
||||||
|
urlValidationTestCases := []validationTestCase{
|
||||||
{
|
{
|
||||||
description: "Empty URL",
|
description: "Empty URL",
|
||||||
data: TestForm{
|
data: TestForm{
|
||||||
@ -97,10 +100,7 @@ var urlValidationTestCases = []validationTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_ValidURLValidation(t *testing.T) {
|
|
||||||
AddBindingRules()
|
|
||||||
|
|
||||||
for _, testCase := range urlValidationTestCases {
|
for _, testCase := range urlValidationTestCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
@ -9,9 +9,12 @@ import (
|
|||||||
"gitea.com/go-chi/binding"
|
"gitea.com/go-chi/binding"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This is a copy of all the URL tests cases, plus additional ones to
|
func Test_ValidURLListValidation(t *testing.T) {
|
||||||
// account for multiple URLs
|
AddBindingRules()
|
||||||
var urlListValidationTestCases = []validationTestCase{
|
|
||||||
|
// This is a copy of all the URL tests cases, plus additional ones to
|
||||||
|
// account for multiple URLs
|
||||||
|
urlListValidationTestCases := []validationTestCase{
|
||||||
{
|
{
|
||||||
description: "Empty URL",
|
description: "Empty URL",
|
||||||
data: TestForm{
|
data: TestForm{
|
||||||
@ -144,10 +147,7 @@ var urlListValidationTestCases = []validationTestCase{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_ValidURLListValidation(t *testing.T) {
|
|
||||||
AddBindingRules()
|
|
||||||
|
|
||||||
for _, testCase := range urlListValidationTestCases {
|
for _, testCase := range urlListValidationTestCases {
|
||||||
t.Run(testCase.description, func(t *testing.T) {
|
t.Run(testCase.description, func(t *testing.T) {
|
||||||
|
@ -6,6 +6,7 @@ package middleware
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/reqctx"
|
"code.gitea.io/gitea/modules/reqctx"
|
||||||
@ -65,3 +66,27 @@ func (f *Flash) Success(msg any, current ...bool) {
|
|||||||
f.SuccessMsg = flashMsgStringOrHTML(msg)
|
f.SuccessMsg = flashMsgStringOrHTML(msg)
|
||||||
f.set("success", f.SuccessMsg, current...)
|
f.set("success", f.SuccessMsg, current...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ParseCookieFlashMessage(val string) *Flash {
|
||||||
|
if vals, _ := url.ParseQuery(val); len(vals) > 0 {
|
||||||
|
return &Flash{
|
||||||
|
Values: vals,
|
||||||
|
ErrorMsg: vals.Get("error"),
|
||||||
|
SuccessMsg: vals.Get("success"),
|
||||||
|
InfoMsg: vals.Get("info"),
|
||||||
|
WarningMsg: vals.Get("warning"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetSiteCookieFlashMessage(dataStore reqctx.RequestDataStore, req *http.Request, cookieName string) (string, *Flash) {
|
||||||
|
// Get the last flash message from cookie
|
||||||
|
lastFlashCookie := GetSiteCookie(req, cookieName)
|
||||||
|
lastFlashMsg := ParseCookieFlashMessage(lastFlashCookie)
|
||||||
|
if lastFlashMsg != nil {
|
||||||
|
lastFlashMsg.DataStore = dataStore
|
||||||
|
return lastFlashCookie, lastFlashMsg
|
||||||
|
}
|
||||||
|
return lastFlashCookie, nil
|
||||||
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
|
|
||||||
package middleware
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IsAPIPath returns true if the specified URL is an API path
|
|
||||||
func IsAPIPath(req *http.Request) bool {
|
|
||||||
return strings.HasPrefix(req.URL.Path, "/api/")
|
|
||||||
}
|
|
@ -258,18 +258,18 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetFeedType return if it is a feed request and altered name and feed type.
|
// GetFeedType return if it is a feed request and altered name and feed type.
|
||||||
func GetFeedType(name string, req *http.Request) (bool, string, string) {
|
func GetFeedType(name string, req *http.Request) (showFeed bool, feedType string) {
|
||||||
if strings.HasSuffix(name, ".rss") ||
|
if strings.HasSuffix(name, ".rss") ||
|
||||||
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
|
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
|
||||||
return true, strings.TrimSuffix(name, ".rss"), "rss"
|
return true, "rss"
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasSuffix(name, ".atom") ||
|
if strings.HasSuffix(name, ".atom") ||
|
||||||
strings.Contains(req.Header.Get("Accept"), "application/atom+xml") {
|
strings.Contains(req.Header.Get("Accept"), "application/atom+xml") {
|
||||||
return true, strings.TrimSuffix(name, ".atom"), "atom"
|
return true, "atom"
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, name, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
|
// feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
// RenderBranchFeed render format for branch or file
|
// RenderBranchFeed render format for branch or file
|
||||||
func RenderBranchFeed(ctx *context.Context) {
|
func RenderBranchFeed(ctx *context.Context) {
|
||||||
_, _, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
|
_, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
|
||||||
if ctx.Repo.TreePath == "" {
|
if ctx.Repo.TreePath == "" {
|
||||||
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||||
} else {
|
} else {
|
||||||
|
@ -311,21 +311,21 @@ func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleRepoHomeFeed(ctx *context.Context) bool {
|
func handleRepoHomeFeed(ctx *context.Context) bool {
|
||||||
if setting.Other.EnableFeed {
|
if !setting.Other.EnableFeed {
|
||||||
isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
|
return false
|
||||||
if isFeed {
|
}
|
||||||
switch {
|
isFeed, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
|
||||||
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
|
if !isFeed {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
|
||||||
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||||
case ctx.Repo.TreePath == "":
|
} else if ctx.Repo.TreePath == "" {
|
||||||
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||||
case ctx.Repo.TreePath != "":
|
} else {
|
||||||
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Home render repository home page
|
// Home render repository home page
|
||||||
|
@ -1152,7 +1152,7 @@ func registerRoutes(m *web.Router) {
|
|||||||
)
|
)
|
||||||
// end "/{username}/{reponame}/settings"
|
// end "/{username}/{reponame}/settings"
|
||||||
|
|
||||||
// user/org home, including rss feeds
|
// user/org home, including rss feeds like "/{username}/{reponame}.rss"
|
||||||
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home)
|
m.Get("/{username}/{reponame}", optSignIn, context.RepoAssignment, context.RepoRefByType(git.RefTypeBranch), repo.SetEditorconfigIfExists, repo.Home)
|
||||||
|
|
||||||
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
|
m.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/webauthn"
|
"code.gitea.io/gitea/modules/auth/webauthn"
|
||||||
@ -21,44 +22,74 @@ import (
|
|||||||
user_service "code.gitea.io/gitea/services/user"
|
user_service "code.gitea.io/gitea/services/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type globalVarsStruct struct {
|
||||||
|
gitRawOrAttachPathRe *regexp.Regexp
|
||||||
|
lfsPathRe *regexp.Regexp
|
||||||
|
archivePathRe *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalVars = sync.OnceValue(func() *globalVarsStruct {
|
||||||
|
return &globalVarsStruct{
|
||||||
|
gitRawOrAttachPathRe: regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`),
|
||||||
|
lfsPathRe: regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`),
|
||||||
|
archivePathRe: regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Init should be called exactly once when the application starts to allow plugins
|
// Init should be called exactly once when the application starts to allow plugins
|
||||||
// to allocate necessary resources
|
// to allocate necessary resources
|
||||||
func Init() {
|
func Init() {
|
||||||
webauthn.Init()
|
webauthn.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type authPathDetector struct {
|
||||||
|
req *http.Request
|
||||||
|
vars *globalVarsStruct
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthPathDetector(req *http.Request) *authPathDetector {
|
||||||
|
return &authPathDetector{req: req, vars: globalVars()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isAPIPath returns true if the specified URL is an API path
|
||||||
|
func (a *authPathDetector) isAPIPath() bool {
|
||||||
|
return strings.HasPrefix(a.req.URL.Path, "/api/")
|
||||||
|
}
|
||||||
|
|
||||||
// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
|
// isAttachmentDownload check if request is a file download (GET) with URL to an attachment
|
||||||
func isAttachmentDownload(req *http.Request) bool {
|
func (a *authPathDetector) isAttachmentDownload() bool {
|
||||||
return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"
|
return strings.HasPrefix(a.req.URL.Path, "/attachments/") && a.req.Method == "GET"
|
||||||
}
|
}
|
||||||
|
|
||||||
// isContainerPath checks if the request targets the container endpoint
|
// isContainerPath checks if the request targets the container endpoint
|
||||||
func isContainerPath(req *http.Request) bool {
|
func (a *authPathDetector) isContainerPath() bool {
|
||||||
return strings.HasPrefix(req.URL.Path, "/v2/")
|
return strings.HasPrefix(a.req.URL.Path, "/v2/")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
func (a *authPathDetector) isGitRawOrAttachPath() bool {
|
||||||
gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`)
|
return a.vars.gitRawOrAttachPathRe.MatchString(a.req.URL.Path)
|
||||||
lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`)
|
|
||||||
archivePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`)
|
|
||||||
)
|
|
||||||
|
|
||||||
func isGitRawOrAttachPath(req *http.Request) bool {
|
|
||||||
return gitRawOrAttachPathRe.MatchString(req.URL.Path)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isGitRawOrAttachOrLFSPath(req *http.Request) bool {
|
func (a *authPathDetector) isGitRawOrAttachOrLFSPath() bool {
|
||||||
if isGitRawOrAttachPath(req) {
|
if a.isGitRawOrAttachPath() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if setting.LFS.StartServer {
|
if setting.LFS.StartServer {
|
||||||
return lfsPathRe.MatchString(req.URL.Path)
|
return a.vars.lfsPathRe.MatchString(a.req.URL.Path)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isArchivePath(req *http.Request) bool {
|
func (a *authPathDetector) isArchivePath() bool {
|
||||||
return archivePathRe.MatchString(req.URL.Path)
|
return a.vars.archivePathRe.MatchString(a.req.URL.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *authPathDetector) isAuthenticatedTokenRequest() bool {
|
||||||
|
switch a.req.URL.Path {
|
||||||
|
case "/login/oauth/userinfo", "/login/oauth/introspect":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSignIn clears existing session variables and stores new ones for the specified user object
|
// handleSignIn clears existing session variables and stores new ones for the specified user object
|
||||||
|
@ -110,21 +110,21 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
|
|||||||
t.Run(tt.path, func(t *testing.T) {
|
t.Run(tt.path, func(t *testing.T) {
|
||||||
req, _ := http.NewRequest("POST", "http://localhost"+tt.path, nil)
|
req, _ := http.NewRequest("POST", "http://localhost"+tt.path, nil)
|
||||||
setting.LFS.StartServer = false
|
setting.LFS.StartServer = false
|
||||||
assert.Equal(t, tt.want, isGitRawOrAttachOrLFSPath(req))
|
assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
|
||||||
|
|
||||||
setting.LFS.StartServer = true
|
setting.LFS.StartServer = true
|
||||||
assert.Equal(t, tt.want, isGitRawOrAttachOrLFSPath(req))
|
assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for _, tt := range lfsTests {
|
for _, tt := range lfsTests {
|
||||||
t.Run(tt, func(t *testing.T) {
|
t.Run(tt, func(t *testing.T) {
|
||||||
req, _ := http.NewRequest("POST", tt, nil)
|
req, _ := http.NewRequest("POST", tt, nil)
|
||||||
setting.LFS.StartServer = false
|
setting.LFS.StartServer = false
|
||||||
got := isGitRawOrAttachOrLFSPath(req)
|
got := newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
|
||||||
assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, gitRawOrAttachPathRe.MatchString(tt))
|
assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, globalVars().gitRawOrAttachPathRe.MatchString(tt))
|
||||||
|
|
||||||
setting.LFS.StartServer = true
|
setting.LFS.StartServer = true
|
||||||
got = isGitRawOrAttachOrLFSPath(req)
|
got = newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
|
||||||
assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v", tt, got, setting.LFS.StartServer)
|
assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v", tt, got, setting.LFS.StartServer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Ensure the struct implements the interface.
|
// Ensure the struct implements the interface.
|
||||||
@ -49,7 +48,8 @@ func (b *Basic) Name() string {
|
|||||||
// Returns nil if header is empty or validation fails.
|
// Returns nil if header is empty or validation fails.
|
||||||
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
// Basic authentication should only fire on API, Download or on Git or LFSPaths
|
// Basic authentication should only fire on API, Download or on Git or LFSPaths
|
||||||
if !middleware.IsAPIPath(req) && !isContainerPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) {
|
detector := newAuthPathDetector(req)
|
||||||
|
if !detector.isAPIPath() && !detector.isContainerPath() && !detector.isAttachmentDownload() && !detector.isGitRawOrAttachOrLFSPath() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
|
||||||
"code.gitea.io/gitea/services/actions"
|
"code.gitea.io/gitea/services/actions"
|
||||||
"code.gitea.io/gitea/services/oauth2_provider"
|
"code.gitea.io/gitea/services/oauth2_provider"
|
||||||
)
|
)
|
||||||
@ -162,8 +161,9 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat
|
|||||||
// Returns nil if verification fails.
|
// Returns nil if verification fails.
|
||||||
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||||
// These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
|
// These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs
|
||||||
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
|
detector := newAuthPathDetector(req)
|
||||||
!isGitRawOrAttachPath(req) && !isArchivePath(req) {
|
if !detector.isAPIPath() && !detector.isAttachmentDownload() && !detector.isAuthenticatedTokenRequest() &&
|
||||||
|
!detector.isGitRawOrAttachPath() && !detector.isArchivePath() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,13 +190,3 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor
|
|||||||
log.Trace("OAuth2 Authorization: Logged in user %-v", user)
|
log.Trace("OAuth2 Authorization: Logged in user %-v", user)
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAuthenticatedTokenRequest(req *http.Request) bool {
|
|
||||||
switch req.URL.Path {
|
|
||||||
case "/login/oauth/userinfo":
|
|
||||||
fallthrough
|
|
||||||
case "/login/oauth/introspect":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
|
||||||
|
|
||||||
gouuid "github.com/google/uuid"
|
gouuid "github.com/google/uuid"
|
||||||
)
|
)
|
||||||
@ -117,7 +116,8 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
|
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
|
||||||
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawOrAttachOrLFSPath(req) {
|
detector := newAuthPathDetector(req)
|
||||||
|
if !detector.isAPIPath() && !detector.isAttachmentDownload() && !detector.isGitRawOrAttachOrLFSPath() {
|
||||||
if sess != nil && (sess.Get("uid") == nil || sess.Get("uid").(int64) != user.ID) {
|
if sess != nil && (sess.Get("uid") == nil || sess.Get("uid").(int64) != user.ID) {
|
||||||
handleSignIn(w, req, sess, user)
|
handleSignIn(w, req, sess, user)
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
|
||||||
"code.gitea.io/gitea/services/auth/source/sspi"
|
"code.gitea.io/gitea/services/auth/source/sspi"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
gitea_context "code.gitea.io/gitea/services/context"
|
||||||
|
|
||||||
@ -120,7 +119,8 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure requests to API paths and PWA resources do not create a new session
|
// Make sure requests to API paths and PWA resources do not create a new session
|
||||||
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) {
|
detector := newAuthPathDetector(req)
|
||||||
|
if !detector.isAPIPath() && !detector.isAttachmentDownload() {
|
||||||
handleSignIn(w, req, sess, user)
|
handleSignIn(w, req, sess, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,8 +155,9 @@ func (s *SSPI) shouldAuthenticate(req *http.Request) (shouldAuth bool) {
|
|||||||
} else if req.FormValue("auth_with_sspi") == "1" {
|
} else if req.FormValue("auth_with_sspi") == "1" {
|
||||||
shouldAuth = true
|
shouldAuth = true
|
||||||
}
|
}
|
||||||
} else if middleware.IsAPIPath(req) || isAttachmentDownload(req) {
|
} else {
|
||||||
shouldAuth = true
|
detector := newAuthPathDetector(req)
|
||||||
|
shouldAuth = detector.isAPIPath() || detector.isAttachmentDownload()
|
||||||
}
|
}
|
||||||
return shouldAuth
|
return shouldAuth
|
||||||
}
|
}
|
||||||
|
@ -165,18 +165,10 @@ func Contexter() func(next http.Handler) http.Handler {
|
|||||||
ctx.Base.SetContextValue(WebContextKey, ctx)
|
ctx.Base.SetContextValue(WebContextKey, ctx)
|
||||||
ctx.Csrf = NewCSRFProtector(csrfOpts)
|
ctx.Csrf = NewCSRFProtector(csrfOpts)
|
||||||
|
|
||||||
// Get the last flash message from cookie
|
// get the last flash message from cookie
|
||||||
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
|
lastFlashCookie, lastFlashMsg := middleware.GetSiteCookieFlashMessage(ctx, ctx.Req, CookieNameFlash)
|
||||||
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
|
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
|
||||||
// store last Flash message into the template data, to render it
|
ctx.Data["Flash"] = lastFlashMsg // store last Flash message into the template data, to render it
|
||||||
ctx.Data["Flash"] = &middleware.Flash{
|
|
||||||
DataStore: ctx,
|
|
||||||
Values: vals,
|
|
||||||
ErrorMsg: vals.Get("error"),
|
|
||||||
SuccessMsg: vals.Get("success"),
|
|
||||||
InfoMsg: vals.Get("info"),
|
|
||||||
WarningMsg: vals.Get("warning"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are new messages in the ctx.Flash, write them into cookie
|
// if there are new messages in the ctx.Flash, write them into cookie
|
||||||
|
@ -12,7 +12,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@ -58,9 +57,8 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg := session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.EqualValues(t, `Branch protection for rule "master" has been updated.`, flashMsg.SuccessMsg)
|
||||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
|
||||||
|
|
||||||
// Request editor page
|
// Request editor page
|
||||||
req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
|
req = NewRequest(t, "GET", "/user2/repo1/_new/master/")
|
||||||
@ -98,9 +96,8 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
|||||||
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
|
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
|
||||||
|
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg = session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.EqualValues(t, `Removing branch protection rule "1" failed.`, flashMsg.ErrorMsg)
|
||||||
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -464,9 +463,8 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhit
|
|||||||
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg := ctx.Session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.EqualValues(t, `Branch protection for rule "`+branch+`" has been updated.`, flashMsg.SuccessMsg)
|
||||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
"code.gitea.io/gitea/modules/web/middleware"
|
||||||
"code.gitea.io/gitea/routers"
|
"code.gitea.io/gitea/routers"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
gitea_context "code.gitea.io/gitea/services/context"
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
@ -118,12 +119,11 @@ type TestSession struct {
|
|||||||
jar http.CookieJar
|
jar http.CookieJar
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestSession) GetCookie(name string) *http.Cookie {
|
func (s *TestSession) GetRawCookie(name string) *http.Cookie {
|
||||||
baseURL, err := url.Parse(setting.AppURL)
|
baseURL, err := url.Parse(setting.AppURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range s.jar.Cookies(baseURL) {
|
for _, c := range s.jar.Cookies(baseURL) {
|
||||||
if c.Name == name {
|
if c.Name == name {
|
||||||
return c
|
return c
|
||||||
@ -132,6 +132,20 @@ func (s *TestSession) GetCookie(name string) *http.Cookie {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *TestSession) GetSiteCookie(name string) string {
|
||||||
|
c := s.GetRawCookie(name)
|
||||||
|
if c != nil {
|
||||||
|
v, _ := url.QueryUnescape(c.Value)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *TestSession) GetCookieFlashMessage() *middleware.Flash {
|
||||||
|
cookie := s.GetSiteCookie(gitea_context.CookieNameFlash)
|
||||||
|
return middleware.ParseCookieFlashMessage(cookie)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *TestSession) MakeRequest(t testing.TB, rw *RequestWrapper, expectedStatus int) *httptest.ResponseRecorder {
|
func (s *TestSession) MakeRequest(t testing.TB, rw *RequestWrapper, expectedStatus int) *httptest.ResponseRecorder {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
req := rw.Request
|
req := rw.Request
|
||||||
@ -458,9 +472,9 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
|
|||||||
// GetUserCSRFToken returns CSRF token for current user
|
// GetUserCSRFToken returns CSRF token for current user
|
||||||
func GetUserCSRFToken(t testing.TB, session *TestSession) string {
|
func GetUserCSRFToken(t testing.TB, session *TestSession) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
cookie := session.GetCookie("_csrf")
|
cookie := session.GetSiteCookie("_csrf")
|
||||||
require.NotEmpty(t, cookie)
|
require.NotEmpty(t, cookie)
|
||||||
return cookie.Value
|
return cookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserCSRFToken returns CSRF token for anonymous user (not logged in)
|
// GetUserCSRFToken returns CSRF token for anonymous user (not logged in)
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -20,7 +19,6 @@ import (
|
|||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/gitrepo"
|
"code.gitea.io/gitea/modules/gitrepo"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
|
||||||
"code.gitea.io/gitea/services/migrations"
|
"code.gitea.io/gitea/services/migrations"
|
||||||
mirror_service "code.gitea.io/gitea/services/mirror"
|
mirror_service "code.gitea.io/gitea/services/mirror"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
@ -92,9 +90,8 @@ func testCreatePushMirror(t *testing.T, session *TestSession, owner, repo, addre
|
|||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg := session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotEmpty(t, flashMsg.SuccessMsg)
|
||||||
assert.Contains(t, flashCookie.Value, "success")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func doRemovePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64) bool {
|
func doRemovePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64) bool {
|
||||||
@ -104,8 +101,8 @@ func doRemovePushMirror(t *testing.T, session *TestSession, owner, repo string,
|
|||||||
"push_mirror_id": strconv.FormatInt(pushMirrorID, 10),
|
"push_mirror_id": strconv.FormatInt(pushMirrorID, 10),
|
||||||
})
|
})
|
||||||
resp := session.MakeRequest(t, req, NoExpectedStatus)
|
resp := session.MakeRequest(t, req, NoExpectedStatus)
|
||||||
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg := session.GetCookieFlashMessage()
|
||||||
return resp.Code == http.StatusSeeOther && flashCookie != nil && strings.Contains(flashCookie.Value, "success")
|
return resp.Code == http.StatusSeeOther && assert.NotEmpty(t, flashMsg.SuccessMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func doUpdatePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64, interval string) bool {
|
func doUpdatePushMirror(t *testing.T, session *TestSession, owner, repo string, pushMirrorID int64, interval string) bool {
|
||||||
|
@ -11,7 +11,6 @@ import (
|
|||||||
git_model "code.gitea.io/gitea/models/git"
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
gitea_context "code.gitea.io/gitea/services/context"
|
|
||||||
"code.gitea.io/gitea/tests"
|
"code.gitea.io/gitea/tests"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@ -82,9 +81,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
|
|||||||
"to": "branch1",
|
"to": "branch1",
|
||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg := session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotEmpty(t, flashMsg.ErrorMsg)
|
||||||
assert.Contains(t, flashCookie.Value, "error")
|
|
||||||
|
|
||||||
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||||
assert.Equal(t, "branch2", branch2.Name)
|
assert.Equal(t, "branch2", branch2.Name)
|
||||||
@ -110,9 +108,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
|
|||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
|
flashMsg = session.GetCookieFlashMessage()
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotEmpty(t, flashMsg.SuccessMsg)
|
||||||
assert.Contains(t, flashCookie.Value, "success")
|
|
||||||
|
|
||||||
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
|
||||||
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"})
|
||||||
|
@ -79,7 +79,7 @@ func TestSigninWithRememberMe(t *testing.T) {
|
|||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
c := session.GetCookie(setting.CookieRememberName)
|
c := session.GetRawCookie(setting.CookieRememberName)
|
||||||
assert.NotNil(t, c)
|
assert.NotNil(t, c)
|
||||||
|
|
||||||
session = emptyTestSession(t)
|
session = emptyTestSession(t)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user