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`
pull/33392/head^2
wxiaoguang 6 days ago committed by GitHub
parent 6a516a0d14
commit 2c1ff8701a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      models/db/name.go
  2. 27
      models/repo/repo.go
  3. 1
      models/repo/repo_test.go
  4. 8
      models/user/user.go
  5. 15
      models/user/user_test.go
  6. 10
      modules/validation/glob_pattern_test.go
  7. 27
      modules/validation/helpers.go
  8. 9
      modules/validation/refname_test.go
  9. 10
      modules/validation/regex_pattern_test.go
  10. 10
      modules/validation/validurl_test.go
  11. 14
      modules/validation/validurllist_test.go
  12. 25
      modules/web/middleware/flash.go
  13. 14
      modules/web/middleware/request.go
  14. 8
      routers/web/feed/convert.go
  15. 2
      routers/web/feed/render.go
  16. 20
      routers/web/repo/view_home.go
  17. 2
      routers/web/web.go
  18. 65
      services/auth/auth.go
  19. 10
      services/auth/auth_test.go
  20. 4
      services/auth/basic.go
  21. 16
      services/auth/oauth2.go
  22. 4
      services/auth/reverseproxy.go
  23. 9
      services/auth/sspi.go
  24. 14
      services/context/context.go
  25. 11
      tests/integration/editor_test.go
  26. 6
      tests/integration/git_general_test.go
  27. 22
      tests/integration/integration_test.go
  28. 11
      tests/integration/mirror_push_test.go
  29. 11
      tests/integration/rename_branch_test.go
  30. 2
      tests/integration/signin_test.go

@ -11,8 +11,6 @@ import (
"code.gitea.io/gitea/modules/util"
)
var ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
// ErrNameReserved represents a "reserved name" error.
type ErrNameReserved struct {
Name string
@ -79,7 +77,7 @@ func (err ErrNameCharsNotAllowed) Unwrap() error {
func IsUsableName(reservedNames, reservedPatterns []string, name string) error {
name = strings.TrimSpace(strings.ToLower(name))
if utf8.RuneCountInString(name) == 0 {
return ErrNameEmpty
return util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
}
for i := range reservedNames {

@ -14,6 +14,7 @@ import (
"regexp"
"strconv"
"strings"
"sync"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
@ -61,20 +62,30 @@ func (err ErrRepoIsArchived) Error() string {
return fmt.Sprintf("%s is archived", err.Repo.LogString())
}
var (
validRepoNamePattern = regexp.MustCompile(`[-.\w]+`)
invalidRepoNamePattern = regexp.MustCompile(`[.]{2,}`)
reservedRepoNames = []string{".", "..", "-"}
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
)
type globalVarsStruct struct {
validRepoNamePattern *regexp.Regexp
invalidRepoNamePattern *regexp.Regexp
reservedRepoNames []string
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
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
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

@ -219,4 +219,5 @@ func TestIsUsableRepoName(t *testing.T) {
assert.Error(t, IsUsableRepoName("the..repo"))
assert.Error(t, IsUsableRepoName("foo.wiki"))
assert.Error(t, IsUsableRepoName("foo.git"))
assert.Error(t, IsUsableRepoName("foo.RSS"))
}

@ -502,10 +502,10 @@ func (u *User) IsMailable() bool {
return u.IsActive
}
// IsUserExist checks if given user name exist,
// the user name should be noncased unique.
// IsUserExist checks if given username exist,
// the username should be non-cased unique.
// 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) {
if len(name) == 0 {
return false, nil
@ -515,7 +515,7 @@ func IsUserExist(ctx context.Context, uid int64, name string) (bool, error) {
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.
// (16 bytes = 16 * 8 = 128 bits)
const SaltByteLength = 16

@ -25,6 +25,21 @@ import (
"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) {
assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})

@ -19,7 +19,10 @@ func getGlobPatternErrorString(pattern string) string {
return ""
}
var globValidationTestCases = []validationTestCase{
func Test_GlobPatternValidation(t *testing.T) {
AddBindingRules()
globValidationTestCases := []validationTestCase{
{
description: "Empty glob pattern",
data: TestForm{
@ -48,10 +51,7 @@ var globValidationTestCases = []validationTestCase{
},
},
},
}
func Test_GlobPatternValidation(t *testing.T) {
AddBindingRules()
}
for _, testCase := range globValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {

@ -8,13 +8,26 @@ import (
"net/url"
"regexp"
"strings"
"sync"
"code.gitea.io/gitea/modules/setting"
"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 {
return net.ParseIP(ip).IsLoopback()
@ -105,9 +118,9 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
if !IsValidExternalURL(uri) {
return false
}
vars := globalVars()
// 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] != "}") {
return false
}
@ -116,14 +129,10 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
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
func IsValidUsername(name string) bool {
// It is difficult to find a single pattern that is both readable and effective,
// 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"
)
var gitRefNameValidationTestCases = []validationTestCase{
func Test_GitRefNameValidation(t *testing.T) {
AddBindingRules()
gitRefNameValidationTestCases := []validationTestCase{
{
description: "Reference name contains only characters",
data: TestForm{
@ -252,10 +254,7 @@ var gitRefNameValidationTestCases = []validationTestCase{
},
},
},
}
func Test_GitRefNameValidation(t *testing.T) {
AddBindingRules()
}
for _, testCase := range gitRefNameValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {

@ -17,7 +17,10 @@ func getRegexPatternErrorString(pattern string) string {
return ""
}
var regexValidationTestCases = []validationTestCase{
func Test_RegexPatternValidation(t *testing.T) {
AddBindingRules()
regexValidationTestCases := []validationTestCase{
{
description: "Empty regex pattern",
data: TestForm{
@ -46,10 +49,7 @@ var regexValidationTestCases = []validationTestCase{
},
},
},
}
func Test_RegexPatternValidation(t *testing.T) {
AddBindingRules()
}
for _, testCase := range regexValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {

@ -9,7 +9,10 @@ import (
"gitea.com/go-chi/binding"
)
var urlValidationTestCases = []validationTestCase{
func Test_ValidURLValidation(t *testing.T) {
AddBindingRules()
urlValidationTestCases := []validationTestCase{
{
description: "Empty URL",
data: TestForm{
@ -97,10 +100,7 @@ var urlValidationTestCases = []validationTestCase{
},
},
},
}
func Test_ValidURLValidation(t *testing.T) {
AddBindingRules()
}
for _, testCase := range urlValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {

@ -9,9 +9,12 @@ import (
"gitea.com/go-chi/binding"
)
// This is a copy of all the URL tests cases, plus additional ones to
// account for multiple URLs
var urlListValidationTestCases = []validationTestCase{
func Test_ValidURLListValidation(t *testing.T) {
AddBindingRules()
// This is a copy of all the URL tests cases, plus additional ones to
// account for multiple URLs
urlListValidationTestCases := []validationTestCase{
{
description: "Empty URL",
data: TestForm{
@ -144,10 +147,7 @@ var urlListValidationTestCases = []validationTestCase{
},
},
},
}
func Test_ValidURLListValidation(t *testing.T) {
AddBindingRules()
}
for _, testCase := range urlListValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {

@ -6,6 +6,7 @@ package middleware
import (
"fmt"
"html/template"
"net/http"
"net/url"
"code.gitea.io/gitea/modules/reqctx"
@ -65,3 +66,27 @@ func (f *Flash) Success(msg any, current ...bool) {
f.SuccessMsg = flashMsgStringOrHTML(msg)
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.
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") ||
strings.Contains(req.Header.Get("Accept"), "application/rss+xml") {
return true, strings.TrimSuffix(name, ".rss"), "rss"
return true, "rss"
}
if strings.HasSuffix(name, ".atom") ||
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

@ -9,7 +9,7 @@ import (
// RenderBranchFeed render format for branch or file
func RenderBranchFeed(ctx *context.Context) {
_, _, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
_, showFeedType := GetFeedType(ctx.PathParam("reponame"), ctx.Req)
if ctx.Repo.TreePath == "" {
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
} else {

@ -311,21 +311,21 @@ func prepareToRenderDirOrFile(entry *git.TreeEntry) func(ctx *context.Context) {
}
func handleRepoHomeFeed(ctx *context.Context) bool {
if setting.Other.EnableFeed {
isFeed, _, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
if isFeed {
switch {
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
if !setting.Other.EnableFeed {
return false
}
isFeed, showFeedType := feed.GetFeedType(ctx.PathParam("reponame"), ctx.Req)
if !isFeed {
return false
}
if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath == "":
} else if ctx.Repo.TreePath == "" {
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath != "":
} else {
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
}
return true
}
}
return false
}
// Home render repository home page

@ -1152,7 +1152,7 @@ func registerRoutes(m *web.Router) {
)
// 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.Post("/{username}/{reponame}/markup", optSignIn, context.RepoAssignment, reqUnitsWithMarkdown, web.Bind(structs.MarkupOption{}), misc.Markup)

@ -9,6 +9,7 @@ import (
"net/http"
"regexp"
"strings"
"sync"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/webauthn"
@ -21,44 +22,74 @@ import (
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
// to allocate necessary resources
func 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
func isAttachmentDownload(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/attachments/") && req.Method == "GET"
func (a *authPathDetector) isAttachmentDownload() bool {
return strings.HasPrefix(a.req.URL.Path, "/attachments/") && a.req.Method == "GET"
}
// isContainerPath checks if the request targets the container endpoint
func isContainerPath(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/v2/")
func (a *authPathDetector) isContainerPath() bool {
return strings.HasPrefix(a.req.URL.Path, "/v2/")
}
var (
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/`)
)
func isGitRawOrAttachPath(req *http.Request) bool {
return gitRawOrAttachPathRe.MatchString(req.URL.Path)
func (a *authPathDetector) isGitRawOrAttachPath() bool {
return a.vars.gitRawOrAttachPathRe.MatchString(a.req.URL.Path)
}
func isGitRawOrAttachOrLFSPath(req *http.Request) bool {
if isGitRawOrAttachPath(req) {
func (a *authPathDetector) isGitRawOrAttachOrLFSPath() bool {
if a.isGitRawOrAttachPath() {
return true
}
if setting.LFS.StartServer {
return lfsPathRe.MatchString(req.URL.Path)
return a.vars.lfsPathRe.MatchString(a.req.URL.Path)
}
return false
}
func isArchivePath(req *http.Request) bool {
return archivePathRe.MatchString(req.URL.Path)
func (a *authPathDetector) isArchivePath() bool {
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

@ -110,21 +110,21 @@ func Test_isGitRawOrLFSPath(t *testing.T) {
t.Run(tt.path, func(t *testing.T) {
req, _ := http.NewRequest("POST", "http://localhost"+tt.path, nil)
setting.LFS.StartServer = false
assert.Equal(t, tt.want, isGitRawOrAttachOrLFSPath(req))
assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
setting.LFS.StartServer = true
assert.Equal(t, tt.want, isGitRawOrAttachOrLFSPath(req))
assert.Equal(t, tt.want, newAuthPathDetector(req).isGitRawOrAttachOrLFSPath())
})
}
for _, tt := range lfsTests {
t.Run(tt, func(t *testing.T) {
req, _ := http.NewRequest("POST", tt, nil)
setting.LFS.StartServer = false
got := isGitRawOrAttachOrLFSPath(req)
assert.Equalf(t, setting.LFS.StartServer, got, "isGitOrLFSPath(%q) = %v, want %v, %v", tt, got, setting.LFS.StartServer, gitRawOrAttachPathRe.MatchString(tt))
got := newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
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
got = isGitRawOrAttachOrLFSPath(req)
got = newAuthPathDetector(req).isGitRawOrAttachOrLFSPath()
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/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"
)
// Ensure the struct implements the interface.
@ -49,7 +48,8 @@ func (b *Basic) Name() string {
// 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) {
// 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
}

@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/actions"
"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.
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
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) &&
!isGitRawOrAttachPath(req) && !isArchivePath(req) {
detector := newAuthPathDetector(req)
if !detector.isAPIPath() && !detector.isAttachmentDownload() && !detector.isAuthenticatedTokenRequest() &&
!detector.isGitRawOrAttachPath() && !detector.isArchivePath() {
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)
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/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/web/middleware"
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
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) {
handleSignIn(w, req, sess, user)
}

@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/optional"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/auth/source/sspi"
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
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) {
detector := newAuthPathDetector(req)
if !detector.isAPIPath() && !detector.isAttachmentDownload() {
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" {
shouldAuth = true
}
} else if middleware.IsAPIPath(req) || isAttachmentDownload(req) {
shouldAuth = true
} else {
detector := newAuthPathDetector(req)
shouldAuth = detector.isAPIPath() || detector.isAttachmentDownload()
}
return shouldAuth
}

@ -165,18 +165,10 @@ func Contexter() func(next http.Handler) http.Handler {
ctx.Base.SetContextValue(WebContextKey, ctx)
ctx.Csrf = NewCSRFProtector(csrfOpts)
// Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
// get the last flash message from cookie
lastFlashCookie, lastFlashMsg := middleware.GetSiteCookieFlashMessage(ctx, ctx.Req, CookieNameFlash)
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
// 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"),
}
ctx.Data["Flash"] = lastFlashMsg // store last Flash message into the template data, to render it
}
// if there are new messages in the ctx.Flash, write them into cookie

@ -12,7 +12,6 @@ import (
"testing"
"code.gitea.io/gitea/modules/json"
gitea_context "code.gitea.io/gitea/services/context"
"github.com/stretchr/testify/assert"
)
@ -58,9 +57,8 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
})
session.MakeRequest(t, req, http.StatusSeeOther)
// Check if master branch has been locked successfully
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522master%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
flashMsg := session.GetCookieFlashMessage()
assert.EqualValues(t, `Branch protection for rule "master" has been updated.`, flashMsg.SuccessMsg)
// Request editor page
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"])
// Check if master branch has been locked successfully
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25221%2522%2Bfailed.", flashCookie.Value)
flashMsg = session.GetCookieFlashMessage()
assert.EqualValues(t, `Removing branch protection rule "1" failed.`, flashMsg.ErrorMsg)
})
}

@ -28,7 +28,6 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -464,9 +463,8 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelistPush, userToWhit
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
// Check if master branch has been locked successfully
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2522"+url.QueryEscape(branch)+"%2522%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
flashMsg := ctx.Session.GetCookieFlashMessage()
assert.EqualValues(t, `Branch protection for rule "`+branch+`" has been updated.`, flashMsg.SuccessMsg)
}
}

@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/routers"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
@ -118,12 +119,11 @@ type TestSession struct {
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)
if err != nil {
return nil
}
for _, c := range s.jar.Cookies(baseURL) {
if c.Name == name {
return c
@ -132,6 +132,20 @@ func (s *TestSession) GetCookie(name string) *http.Cookie {
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 {
t.Helper()
req := rw.Request
@ -458,9 +472,9 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile
// GetUserCSRFToken returns CSRF token for current user
func GetUserCSRFToken(t testing.TB, session *TestSession) string {
t.Helper()
cookie := session.GetCookie("_csrf")
cookie := session.GetSiteCookie("_csrf")
require.NotEmpty(t, cookie)
return cookie.Value
return cookie
}
// GetUserCSRFToken returns CSRF token for anonymous user (not logged in)

@ -9,7 +9,6 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"testing"
"time"
@ -20,7 +19,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
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)
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.Contains(t, flashCookie.Value, "success")
flashMsg := session.GetCookieFlashMessage()
assert.NotEmpty(t, flashMsg.SuccessMsg)
}
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),
})
resp := session.MakeRequest(t, req, NoExpectedStatus)
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
return resp.Code == http.StatusSeeOther && flashCookie != nil && strings.Contains(flashCookie.Value, "success")
flashMsg := session.GetCookieFlashMessage()
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 {

@ -11,7 +11,6 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
gitea_context "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert"
@ -82,9 +81,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
"to": "branch1",
})
session.MakeRequest(t, req, http.StatusSeeOther)
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.Contains(t, flashCookie.Value, "error")
flashMsg := session.GetCookieFlashMessage()
assert.NotEmpty(t, flashMsg.ErrorMsg)
branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
assert.Equal(t, "branch2", branch2.Name)
@ -110,9 +108,8 @@ func testRenameBranch(t *testing.T, u *url.URL) {
})
session.MakeRequest(t, req, http.StatusSeeOther)
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
assert.NotNil(t, flashCookie)
assert.Contains(t, flashCookie.Value, "success")
flashMsg = session.GetCookieFlashMessage()
assert.NotEmpty(t, flashMsg.SuccessMsg)
unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"})
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)
c := session.GetCookie(setting.CookieRememberName)
c := session.GetRawCookie(setting.CookieRememberName)
assert.NotNil(t, c)
session = emptyTestSession(t)

Loading…
Cancel
Save