Browse Source

Decouple the different contexts from each other (#24786)

Replace #16455

Close #21803

Mixing different Gitea contexts together causes some problems:

1. Unable to respond proper content when error occurs, eg: Web should
respond HTML while API should respond JSON
2. Unclear dependency, eg: it's unclear when Context is used in
APIContext, which fields should be initialized, which methods are
necessary.


To make things clear, this PR introduces a Base context, it only
provides basic Req/Resp/Data features.

This PR mainly moves code. There are still many legacy problems and
TODOs in code, leave unrelated changes to future PRs.
tags/v1.20.0-rc0
wxiaoguang 1 year ago
parent
commit
6b33152b7d
No account linked to committer's email address
57 changed files with 882 additions and 778 deletions
  1. 83
    32
      modules/context/api.go
  2. 300
    0
      modules/context/base.go
  3. 60
    77
      modules/context/context.go
  4. 0
    43
      modules/context/context_data.go
  5. 0
    72
      modules/context/context_form.go
  6. 0
    27
      modules/context/context_request.go
  7. 2
    100
      modules/context/context_response.go
  8. 0
    23
      modules/context/context_serve.go
  9. 1
    1
      modules/context/org.go
  10. 42
    35
      modules/context/package.go
  11. 10
    19
      modules/context/private.go
  12. 21
    20
      modules/context/repo.go
  13. 3
    10
      modules/context/response.go
  14. 2
    2
      modules/context/utils.go
  15. 77
    50
      modules/test/context_tests.go
  16. 11
    3
      modules/translation/translation.go
  17. 13
    2
      modules/web/handler.go
  18. 55
    44
      routers/api/actions/artifacts.go
  19. 2
    2
      routers/api/packages/api.go
  20. 8
    8
      routers/api/v1/api.go
  21. 2
    2
      routers/api/v1/misc/markup.go
  22. 9
    24
      routers/api/v1/misc/markup_test.go
  23. 1
    1
      routers/api/v1/notify/notifications.go
  24. 6
    6
      routers/api/v1/repo/file.go
  25. 2
    3
      routers/api/v1/repo/hook_test.go
  26. 2
    2
      routers/api/v1/repo/issue.go
  27. 3
    3
      routers/api/v1/repo/issue_comment.go
  28. 3
    3
      routers/api/v1/repo/issue_tracked_time.go
  29. 1
    1
      routers/api/v1/repo/migrate.go
  30. 6
    9
      routers/api/v1/repo/repo_test.go
  31. 1
    1
      routers/api/v1/repo/status.go
  32. 1
    1
      routers/api/v1/user/helper.go
  33. 6
    5
      routers/api/v1/utils/git.go
  34. 4
    4
      routers/common/markup.go
  35. 1
    1
      routers/common/middleware.go
  36. 4
    4
      routers/common/serve.go
  37. 1
    1
      routers/init.go
  38. 5
    14
      routers/install/install.go
  39. 1
    9
      routers/web/misc/markup.go
  40. 1
    1
      routers/web/repo/attachment.go
  41. 5
    5
      routers/web/repo/download.go
  42. 1
    1
      routers/web/repo/http.go
  43. 2
    2
      routers/web/repo/issue.go
  44. 1
    1
      routers/web/repo/wiki.go
  45. 34
    18
      services/auth/middleware.go
  46. 9
    7
      services/context/user.go
  47. 3
    3
      services/forms/admin.go
  48. 1
    1
      services/forms/auth_form.go
  49. 3
    3
      services/forms/org.go
  50. 1
    1
      services/forms/package_form.go
  51. 2
    2
      services/forms/repo_branch_form.go
  52. 36
    36
      services/forms/repo_form.go
  53. 1
    1
      services/forms/repo_tag_form.go
  54. 1
    1
      services/forms/runner.go
  55. 24
    24
      services/forms/user_form.go
  56. 3
    3
      services/forms/user_form_auth_openid.go
  57. 5
    4
      services/markup/processorhelper_test.go

+ 83
- 32
modules/context/api.go View File



"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
mc "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/httpcache"
"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/web/middleware"

"gitea.com/go-chi/cache"
) )


// APIContext is a specific context for API service // APIContext is a specific context for API service
type APIContext struct { type APIContext struct {
*Context
Org *APIOrganization
*Base

Cache cache.Cache

Doer *user_model.User // current signed-in user
IsSigned bool
IsBasicAuth bool

ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer

Repo *Repository
Org *APIOrganization
Package *Package
} }


// Currently, we have the following common fields in error response: // Currently, we have the following common fields in error response:


var apiContextKey = apiContextKeyType{} var apiContextKey = apiContextKeyType{}


// WithAPIContext set up api context in request
func WithAPIContext(req *http.Request, ctx *APIContext) *http.Request {
return req.WithContext(context.WithValue(req.Context(), apiContextKey, ctx))
}

// GetAPIContext returns a context for API routes // GetAPIContext returns a context for API routes
func GetAPIContext(req *http.Request) *APIContext { func GetAPIContext(req *http.Request) *APIContext {
return req.Context().Value(apiContextKey).(*APIContext) return req.Context().Value(apiContextKey).(*APIContext)
} }


otpHeader := ctx.Req.Header.Get("X-Gitea-OTP") otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
twofa, err := auth.GetTwoFactorByUID(ctx.Context.Doer.ID)
twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID)
if err != nil { if err != nil {
if auth.IsErrTwoFactorNotEnrolled(err) { if auth.IsErrTwoFactorNotEnrolled(err) {
return // No 2FA enrollment for this user return // No 2FA enrollment for this user
} }
ctx.Context.Error(http.StatusInternalServerError)
ctx.Error(http.StatusInternalServerError, "GetTwoFactorByUID", err)
return return
} }
ok, err := twofa.ValidateTOTP(otpHeader) ok, err := twofa.ValidateTOTP(otpHeader)
if err != nil { if err != nil {
ctx.Context.Error(http.StatusInternalServerError)
ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err)
return return
} }
if !ok { if !ok {
ctx.Context.Error(http.StatusUnauthorized)
ctx.Error(http.StatusUnauthorized, "", nil)
return return
} }
} }
func APIContexter() func(http.Handler) http.Handler { func APIContexter() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
locale := middleware.Locale(w, req)
ctx := APIContext{
Context: &Context{
Resp: NewResponse(w),
Data: middleware.GetContextData(req.Context()),
Locale: locale,
Cache: cache.GetCache(),
Repo: &Repository{
PullRequest: &PullRequest{},
},
Org: &Organization{},
},
Org: &APIOrganization{},
base, baseCleanUp := NewBaseContext(w, req)
ctx := &APIContext{
Base: base,
Cache: mc.GetCache(),
Repo: &Repository{PullRequest: &PullRequest{}},
Org: &APIOrganization{},
} }
defer ctx.Close()
defer baseCleanUp()


ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
ctx.Base.AppendContextValue(apiContextKey, ctx)
ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })


// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") { if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform") httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions) ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)


ctx.Data["Context"] = &ctx

next.ServeHTTP(ctx.Resp, ctx.Req) next.ServeHTTP(ctx.Resp, ctx.Req)
}) })
} }
return func() { return func() {
// If it's been set to nil then assume someone else has closed it. // If it's been set to nil then assume someone else has closed it.
if ctx.Repo.GitRepo != nil { if ctx.Repo.GitRepo != nil {
ctx.Repo.GitRepo.Close()
_ = ctx.Repo.GitRepo.Close()
} }
} }
} }
} }


var err error var err error
refName := getRefName(ctx.Context, RepoRefAny)
refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)


if ctx.Repo.GitRepo.IsBranchExist(refName) { if ctx.Repo.GitRepo.IsBranchExist(refName) {
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
}) })
} }

// HasAPIError returns true if error occurs in form validation.
func (ctx *APIContext) HasAPIError() bool {
hasErr, ok := ctx.Data["HasError"]
if !ok {
return false
}
return hasErr.(bool)
}

// GetErrMsg returns error message in form validation.
func (ctx *APIContext) GetErrMsg() string {
msg, _ := ctx.Data["ErrorMsg"].(string)
if msg == "" {
msg = "invalid form data"
}
return msg
}

// NotFoundOrServerError use error check function to determine if the error
// is about not found. It responds with 404 status code for not found error,
// or error context description for logging purpose of 500 server error.
func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
if errCheck(logErr) {
ctx.JSON(http.StatusNotFound, nil)
return
}
ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
}

// IsUserSiteAdmin returns true if current user is a site admin
func (ctx *APIContext) IsUserSiteAdmin() bool {
return ctx.IsSigned && ctx.Doer.IsAdmin
}

// IsUserRepoAdmin returns true if current user is admin in current repo
func (ctx *APIContext) IsUserRepoAdmin() bool {
return ctx.Repo.IsAdmin()
}

// IsUserRepoWriter returns true if current user has write privilege in current repo
func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
for _, unitType := range unitTypes {
if ctx.Repo.CanWrite(unitType) {
return true
}
}

return false
}

+ 300
- 0
modules/context/base.go View File

// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package context

import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"strconv"
"strings"
"time"

"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web/middleware"

"github.com/go-chi/chi/v5"
)

type contextValuePair struct {
key any
valueFn func() any
}

type Base struct {
originCtx context.Context
contextValues []contextValuePair

Resp ResponseWriter
Req *http.Request

// Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
// Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
Data middleware.ContextData

// Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
Locale translation.Locale
}

func (b *Base) Deadline() (deadline time.Time, ok bool) {
return b.originCtx.Deadline()
}

func (b *Base) Done() <-chan struct{} {
return b.originCtx.Done()
}

func (b *Base) Err() error {
return b.originCtx.Err()
}

func (b *Base) Value(key any) any {
for _, pair := range b.contextValues {
if pair.key == key {
return pair.valueFn()
}
}
return b.originCtx.Value(key)
}

func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
return b
}

func (b *Base) AppendContextValue(key, value any) any {
b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
return b
}

func (b *Base) GetData() middleware.ContextData {
return b.Data
}

// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
val := b.RespHeader().Get("Access-Control-Expose-Headers")
if len(val) != 0 {
b.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
} else {
b.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
}
}

// SetTotalCountHeader set "X-Total-Count" header
func (b *Base) SetTotalCountHeader(total int64) {
b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
b.AppendAccessControlExposeHeaders("X-Total-Count")
}

// Written returns true if there are something sent to web browser
func (b *Base) Written() bool {
return b.Resp.Status() > 0
}

// Status writes status code
func (b *Base) Status(status int) {
b.Resp.WriteHeader(status)
}

// Write writes data to web browser
func (b *Base) Write(bs []byte) (int, error) {
return b.Resp.Write(bs)
}

// RespHeader returns the response header
func (b *Base) RespHeader() http.Header {
return b.Resp.Header()
}

// Error returned an error to web browser
func (b *Base) Error(status int, contents ...string) {
v := http.StatusText(status)
if len(contents) > 0 {
v = contents[0]
}
http.Error(b.Resp, v, status)
}

// JSON render content as JSON
func (b *Base) JSON(status int, content interface{}) {
b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
b.Resp.WriteHeader(status)
if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
log.Error("Render JSON failed: %v", err)
}
}

// RemoteAddr returns the client machine ip address
func (b *Base) RemoteAddr() string {
return b.Req.RemoteAddr
}

// Params returns the param on route
func (b *Base) Params(p string) string {
s, _ := url.PathUnescape(chi.URLParam(b.Req, strings.TrimPrefix(p, ":")))
return s
}

// ParamsInt64 returns the param on route as int64
func (b *Base) ParamsInt64(p string) int64 {
v, _ := strconv.ParseInt(b.Params(p), 10, 64)
return v
}

// SetParams set params into routes
func (b *Base) SetParams(k, v string) {
chiCtx := chi.RouteContext(b)
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
}

// FormString returns the first value matching the provided key in the form as a string
func (b *Base) FormString(key string) string {
return b.Req.FormValue(key)
}

// FormStrings returns a string slice for the provided key from the form
func (b *Base) FormStrings(key string) []string {
if b.Req.Form == nil {
if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
return nil
}
}
if v, ok := b.Req.Form[key]; ok {
return v
}
return nil
}

// FormTrim returns the first value for the provided key in the form as a space trimmed string
func (b *Base) FormTrim(key string) string {
return strings.TrimSpace(b.Req.FormValue(key))
}

// FormInt returns the first value for the provided key in the form as an int
func (b *Base) FormInt(key string) int {
v, _ := strconv.Atoi(b.Req.FormValue(key))
return v
}

// FormInt64 returns the first value for the provided key in the form as an int64
func (b *Base) FormInt64(key string) int64 {
v, _ := strconv.ParseInt(b.Req.FormValue(key), 10, 64)
return v
}

// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
func (b *Base) FormBool(key string) bool {
s := b.Req.FormValue(key)
v, _ := strconv.ParseBool(s)
v = v || strings.EqualFold(s, "on")
return v
}

// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
// for the provided key exists in the form else it returns OptionalBoolNone
func (b *Base) FormOptionalBool(key string) util.OptionalBool {
value := b.Req.FormValue(key)
if len(value) == 0 {
return util.OptionalBoolNone
}
s := b.Req.FormValue(key)
v, _ := strconv.ParseBool(s)
v = v || strings.EqualFold(s, "on")
return util.OptionalBoolOf(v)
}

func (b *Base) SetFormString(key, value string) {
_ = b.Req.FormValue(key) // force parse form
b.Req.Form.Set(key, value)
}

// PlainTextBytes renders bytes as plain text
func (b *Base) plainTextInternal(skip, status int, bs []byte) {
statusPrefix := status / 100
if statusPrefix == 4 || statusPrefix == 5 {
log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
}
b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
b.Resp.WriteHeader(status)
if _, err := b.Resp.Write(bs); err != nil {
log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
}
}

// PlainTextBytes renders bytes as plain text
func (b *Base) PlainTextBytes(status int, bs []byte) {
b.plainTextInternal(2, status, bs)
}

// PlainText renders content as plain text
func (b *Base) PlainText(status int, text string) {
b.plainTextInternal(2, status, []byte(text))
}

// Redirect redirects the request
func (b *Base) Redirect(location string, status ...int) {
code := http.StatusSeeOther
if len(status) == 1 {
code = status[0]
}

if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
// 1. the first request to "/my-path" contains cookie
// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
// 4. then the browser accepts the empty session, then the user is logged out
// So in this case, we should remove the session cookie from the response header
removeSessionCookieHeader(b.Resp)
}
http.Redirect(b.Resp, b.Req, location, code)
}

type ServeHeaderOptions httplib.ServeHeaderOptions

func (b *Base) SetServeHeaders(opt *ServeHeaderOptions) {
httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opt))
}

// ServeContent serves content to http request
func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opts))
http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
}

// Close frees all resources hold by Context
func (b *Base) cleanUp() {
if b.Req != nil && b.Req.MultipartForm != nil {
_ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
}
}

func (b *Base) Tr(msg string, args ...any) string {
return b.Locale.Tr(msg, args...)
}

func (b *Base) TrN(cnt any, key1, keyN string, args ...any) string {
return b.Locale.TrN(cnt, key1, keyN, args...)
}

func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
b = &Base{
originCtx: req.Context(),
Req: req,
Resp: WrapResponseWriter(resp),
Locale: middleware.Locale(resp, req),
Data: middleware.GetContextData(req.Context()),
}
b.AppendContextValue(translation.ContextKey, b.Locale)
b.Req = b.Req.WithContext(b)
return b, b.cleanUp
}

+ 60
- 77
modules/context/context.go View File

package context package context


import ( import (
"context"
"html" "html"
"html/template" "html/template"
"io" "io"


// Context represents context of a request. // Context represents context of a request.
type Context struct { type Context struct {
Resp ResponseWriter
Req *http.Request
Render Render
*Base


Data middleware.ContextData // data used by MVC templates
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
Render Render
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`


Locale translation.Locale
Cache cache.Cache Cache cache.Cache
Csrf CSRFProtector Csrf CSRFProtector
Flash *middleware.Flash Flash *middleware.Flash
Session session.Store Session session.Store


Link string // current request URL (without query string)
Doer *user_model.User
Link string // current request URL (without query string)

Doer *user_model.User // current signed-in user
IsSigned bool IsSigned bool
IsBasicAuth bool IsBasicAuth bool


ContextUser *user_model.User
Repo *Repository
Org *Organization
Package *Package
}
ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer


// Close frees all resources hold by Context
func (ctx *Context) Close() error {
var err error
if ctx.Req != nil && ctx.Req.MultipartForm != nil {
err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
}
// TODO: close opened repo, and more
return err
Repo *Repository
Org *Organization
Package *Package
} }


// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString. // TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
return ctx.Locale.Tr(msg, trArgs...) return ctx.Locale.Tr(msg, trArgs...)
} }


func (ctx *Context) Tr(msg string, args ...any) string {
return ctx.Locale.Tr(msg, args...)
}

func (ctx *Context) TrN(cnt any, key1, keyN string, args ...any) string {
return ctx.Locale.TrN(cnt, key1, keyN, args...)
}

// Deadline is part of the interface for context.Context and we pass this to the request context
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
return ctx.Req.Context().Deadline()
}

// Done is part of the interface for context.Context and we pass this to the request context
func (ctx *Context) Done() <-chan struct{} {
return ctx.Req.Context().Done()
}

// Err is part of the interface for context.Context and we pass this to the request context
func (ctx *Context) Err() error {
return ctx.Req.Context().Err()
}

// Value is part of the interface for context.Context and we pass this to the request context
func (ctx *Context) Value(key interface{}) interface{} {
if key == git.RepositoryContextKey && ctx.Repo != nil {
return ctx.Repo.GitRepo
}
if key == translation.ContextKey && ctx.Locale != nil {
return ctx.Locale
}
return ctx.Req.Context().Value(key)
}

type contextKeyType struct{} type contextKeyType struct{}


var contextKey interface{} = contextKeyType{} var contextKey interface{} = contextKeyType{}


// WithContext set up install context in request
func WithContext(req *http.Request, ctx *Context) *http.Request {
return req.WithContext(context.WithValue(req.Context(), contextKey, ctx))
func GetContext(req *http.Request) *Context {
ctx, _ := req.Context().Value(contextKey).(*Context)
return ctx
} }


// GetContext retrieves install context from request
func GetContext(req *http.Request) *Context {
if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
return ctx
// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
type ValidateContext struct {
*Base
}

// GetValidateContext gets a context for middleware form validation
func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
if ctxAPI, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
ctx = &ValidateContext{Base: ctxAPI.Base}
} else if ctxWeb, ok := req.Context().Value(contextKey).(*Context); ok {
ctx = &ValidateContext{Base: ctxWeb.Base}
} else {
panic("invalid context, expect either APIContext or Context")
} }
return nil
return ctx
} }


// Contexter initializes a classic context for a request. // Contexter initializes a classic context for a request.
} }
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := Context{
Resp: NewResponse(resp),
base, baseCleanUp := NewBaseContext(resp, req)
ctx := &Context{
Base: base,
Cache: mc.GetCache(), Cache: mc.GetCache(),
Locale: middleware.Locale(resp, req),
Link: setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"), Link: setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"),
Render: rnd, Render: rnd,
Session: session.GetSession(req), Session: session.GetSession(req),
Repo: &Repository{
PullRequest: &PullRequest{},
},
Org: &Organization{},
Data: middleware.GetContextData(req.Context()),
Repo: &Repository{PullRequest: &PullRequest{}},
Org: &Organization{},
} }
defer ctx.Close()
defer baseCleanUp()


ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
ctx.Data["Context"] = &ctx ctx.Data["Context"] = &ctx
ctx.PageData = map[string]any{} ctx.PageData = map[string]any{}
ctx.Data["PageData"] = ctx.PageData ctx.Data["PageData"] = ctx.PageData


ctx.Req = WithContext(req, &ctx)
ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
ctx.Base.AppendContextValue(contextKey, ctx)
ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })

ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)


// Get the last flash message from cookie // Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash) lastFlashCookie := middleware.GetSiteCookie(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 // store last Flash message into the template data, to render it
ctx.Data["Flash"] = &middleware.Flash{ ctx.Data["Flash"] = &middleware.Flash{
DataStore: &ctx,
DataStore: ctx,
Values: vals, Values: vals,
ErrorMsg: vals.Get("error"), ErrorMsg: vals.Get("error"),
SuccessMsg: vals.Get("success"), SuccessMsg: vals.Get("success"),
} }


// prepare an empty Flash message for current request // prepare an empty Flash message for current request
ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
ctx.Resp.Before(func(resp ResponseWriter) { ctx.Resp.Before(func(resp ResponseWriter) {
if val := ctx.Flash.Encode(); val != "" { if val := ctx.Flash.Encode(); val != "" {
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0) middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
}) })
} }
} }

// HasError returns true if error occurs in form validation.
// Attention: this function changes ctx.Data and ctx.Flash
func (ctx *Context) HasError() bool {
hasErr, ok := ctx.Data["HasError"]
if !ok {
return false
}
ctx.Flash.ErrorMsg = ctx.GetErrMsg()
ctx.Data["Flash"] = ctx.Flash
return hasErr.(bool)
}

// GetErrMsg returns error message in form validation.
func (ctx *Context) GetErrMsg() string {
msg, _ := ctx.Data["ErrorMsg"].(string)
if msg == "" {
msg = "invalid form data"
}
return msg
}

+ 0
- 43
modules/context/context_data.go View File

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package context

import "code.gitea.io/gitea/modules/web/middleware"

// GetData returns the data
func (ctx *Context) GetData() middleware.ContextData {
return ctx.Data
}

// HasAPIError returns true if error occurs in form validation.
func (ctx *Context) HasAPIError() bool {
hasErr, ok := ctx.Data["HasError"]
if !ok {
return false
}
return hasErr.(bool)
}

// GetErrMsg returns error message
func (ctx *Context) GetErrMsg() string {
return ctx.Data["ErrorMsg"].(string)
}

// HasError returns true if error occurs in form validation.
// Attention: this function changes ctx.Data and ctx.Flash
func (ctx *Context) HasError() bool {
hasErr, ok := ctx.Data["HasError"]
if !ok {
return false
}
ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
ctx.Data["Flash"] = ctx.Flash
return hasErr.(bool)
}

// HasValue returns true if value of given name exists.
func (ctx *Context) HasValue(name string) bool {
_, ok := ctx.Data[name]
return ok
}

+ 0
- 72
modules/context/context_form.go View File

// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package context

import (
"strconv"
"strings"

"code.gitea.io/gitea/modules/util"
)

// FormString returns the first value matching the provided key in the form as a string
func (ctx *Context) FormString(key string) string {
return ctx.Req.FormValue(key)
}

// FormStrings returns a string slice for the provided key from the form
func (ctx *Context) FormStrings(key string) []string {
if ctx.Req.Form == nil {
if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil {
return nil
}
}
if v, ok := ctx.Req.Form[key]; ok {
return v
}
return nil
}

// FormTrim returns the first value for the provided key in the form as a space trimmed string
func (ctx *Context) FormTrim(key string) string {
return strings.TrimSpace(ctx.Req.FormValue(key))
}

// FormInt returns the first value for the provided key in the form as an int
func (ctx *Context) FormInt(key string) int {
v, _ := strconv.Atoi(ctx.Req.FormValue(key))
return v
}

// FormInt64 returns the first value for the provided key in the form as an int64
func (ctx *Context) FormInt64(key string) int64 {
v, _ := strconv.ParseInt(ctx.Req.FormValue(key), 10, 64)
return v
}

// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
func (ctx *Context) FormBool(key string) bool {
s := ctx.Req.FormValue(key)
v, _ := strconv.ParseBool(s)
v = v || strings.EqualFold(s, "on")
return v
}

// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
// for the provided key exists in the form else it returns OptionalBoolNone
func (ctx *Context) FormOptionalBool(key string) util.OptionalBool {
value := ctx.Req.FormValue(key)
if len(value) == 0 {
return util.OptionalBoolNone
}
s := ctx.Req.FormValue(key)
v, _ := strconv.ParseBool(s)
v = v || strings.EqualFold(s, "on")
return util.OptionalBoolOf(v)
}

func (ctx *Context) SetFormString(key, value string) {
_ = ctx.Req.FormValue(key) // force parse form
ctx.Req.Form.Set(key, value)
}

+ 0
- 27
modules/context/context_request.go View File

import ( import (
"io" "io"
"net/http" "net/http"
"net/url"
"strconv"
"strings" "strings"

"github.com/go-chi/chi/v5"
) )


// RemoteAddr returns the client machine ip address
func (ctx *Context) RemoteAddr() string {
return ctx.Req.RemoteAddr
}

// Params returns the param on route
func (ctx *Context) Params(p string) string {
s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
return s
}

// ParamsInt64 returns the param on route as int64
func (ctx *Context) ParamsInt64(p string) int64 {
v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
return v
}

// SetParams set params into routes
func (ctx *Context) SetParams(k, v string) {
chiCtx := chi.RouteContext(ctx)
chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
}

// UploadStream returns the request body or the first form file // UploadStream returns the request body or the first form file
// Only form files need to get closed. // Only form files need to get closed.
func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) { func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {

+ 2
- 100
modules/context/context_response.go View File



user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/json"
"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/templates" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/modules/web/middleware"
) )


// SetTotalCountHeader set "X-Total-Count" header
func (ctx *Context) SetTotalCountHeader(total int64) {
ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
ctx.AppendAccessControlExposeHeaders("X-Total-Count")
}

// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
func (ctx *Context) AppendAccessControlExposeHeaders(names ...string) {
val := ctx.RespHeader().Get("Access-Control-Expose-Headers")
if len(val) != 0 {
ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
} else {
ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
}
}

// Written returns true if there are something sent to web browser
func (ctx *Context) Written() bool {
return ctx.Resp.Status() > 0
}

// Status writes status code
func (ctx *Context) Status(status int) {
ctx.Resp.WriteHeader(status)
}

// Write writes data to web browser
func (ctx *Context) Write(bs []byte) (int, error) {
return ctx.Resp.Write(bs)
}

// RedirectToUser redirect to a differently-named user // RedirectToUser redirect to a differently-named user
func RedirectToUser(ctx *Context, userName string, redirectUserID int64) {
func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
user, err := user_model.GetUserByID(ctx, redirectUserID) user, err := user_model.GetUserByID(ctx, redirectUserID)
if err != nil { if err != nil {
ctx.ServerError("GetUserByID", err)
ctx.Error(http.StatusInternalServerError, "unable to get user")
return return
} }


} }
ctx.serverErrorInternal(logMsg, logErr) ctx.serverErrorInternal(logMsg, logErr)
} }

// PlainTextBytes renders bytes as plain text
func (ctx *Context) plainTextInternal(skip, status int, bs []byte) {
statusPrefix := status / 100
if statusPrefix == 4 || statusPrefix == 5 {
log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
}
ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
ctx.Resp.WriteHeader(status)
if _, err := ctx.Resp.Write(bs); err != nil {
log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
}
}

// PlainTextBytes renders bytes as plain text
func (ctx *Context) PlainTextBytes(status int, bs []byte) {
ctx.plainTextInternal(2, status, bs)
}

// PlainText renders content as plain text
func (ctx *Context) PlainText(status int, text string) {
ctx.plainTextInternal(2, status, []byte(text))
}

// RespHeader returns the response header
func (ctx *Context) RespHeader() http.Header {
return ctx.Resp.Header()
}

// Error returned an error to web browser
func (ctx *Context) Error(status int, contents ...string) {
v := http.StatusText(status)
if len(contents) > 0 {
v = contents[0]
}
http.Error(ctx.Resp, v, status)
}

// JSON render content as JSON
func (ctx *Context) JSON(status int, content interface{}) {
ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
ctx.Resp.WriteHeader(status)
if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
ctx.ServerError("Render JSON failed", err)
}
}

// Redirect redirects the request
func (ctx *Context) Redirect(location string, status ...int) {
code := http.StatusSeeOther
if len(status) == 1 {
code = status[0]
}

if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
// 1. the first request to "/my-path" contains cookie
// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
// 4. then the browser accepts the empty session, then the user is logged out
// So in this case, we should remove the session cookie from the response header
removeSessionCookieHeader(ctx.Resp)
}
http.Redirect(ctx.Resp, ctx.Req, location, code)
}

+ 0
- 23
modules/context/context_serve.go View File

// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package context

import (
"io"
"net/http"

"code.gitea.io/gitea/modules/httplib"
)

type ServeHeaderOptions httplib.ServeHeaderOptions

func (ctx *Context) SetServeHeaders(opt *ServeHeaderOptions) {
httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opt))
}

// ServeContent serves content to http request
func (ctx *Context) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opts))
http.ServeContent(ctx.Resp, ctx.Req, opts.Filename, opts.LastModified, r)
}

+ 1
- 1
modules/context/org.go View File

if organization.IsErrOrgNotExist(err) { if organization.IsErrOrgNotExist(err) {
redirectUserID, err := user_model.LookupUserRedirect(orgName) redirectUserID, err := user_model.LookupUserRedirect(orgName)
if err == nil { if err == nil {
RedirectToUser(ctx, orgName, redirectUserID)
RedirectToUser(ctx.Base, orgName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) { } else if user_model.IsErrUserRedirectNotExist(err) {
ctx.NotFound("GetUserByName", err) ctx.NotFound("GetUserByName", err)
} else { } else {

+ 42
- 35
modules/context/package.go View File

package context package context


import ( import (
gocontext "context"
"fmt" "fmt"
"net/http" "net/http"


"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/web/middleware"
) )


// Package contains owner, access mode and optional the package descriptor // Package contains owner, access mode and optional the package descriptor
Descriptor *packages_model.PackageDescriptor Descriptor *packages_model.PackageDescriptor
} }


type packageAssignmentCtx struct {
*Base
Doer *user_model.User
ContextUser *user_model.User
}

// PackageAssignment returns a middleware to handle Context.Package assignment // PackageAssignment returns a middleware to handle Context.Package assignment
func PackageAssignment() func(ctx *Context) { func PackageAssignment() func(ctx *Context) {
return func(ctx *Context) { return func(ctx *Context) {
packageAssignment(ctx, func(status int, title string, obj interface{}) {
errorFn := func(status int, title string, obj interface{}) {
err, ok := obj.(error) err, ok := obj.(error)
if !ok { if !ok {
err = fmt.Errorf("%s", obj) err = fmt.Errorf("%s", obj)
} else { } else {
ctx.ServerError(title, err) ctx.ServerError(title, err)
} }
})
}
paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
ctx.Package = packageAssignment(paCtx, errorFn)
} }
} }


// PackageAssignmentAPI returns a middleware to handle Context.Package assignment // PackageAssignmentAPI returns a middleware to handle Context.Package assignment
func PackageAssignmentAPI() func(ctx *APIContext) { func PackageAssignmentAPI() func(ctx *APIContext) {
return func(ctx *APIContext) { return func(ctx *APIContext) {
packageAssignment(ctx.Context, ctx.Error)
paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
ctx.Package = packageAssignment(paCtx, ctx.Error)
} }
} }


func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
ctx.Package = &Package{
func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, interface{})) *Package {
pkg := &Package{
Owner: ctx.ContextUser, Owner: ctx.ContextUser,
} }

var err error var err error
ctx.Package.AccessMode, err = determineAccessMode(ctx)
pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
if err != nil { if err != nil {
errCb(http.StatusInternalServerError, "determineAccessMode", err) errCb(http.StatusInternalServerError, "determineAccessMode", err)
return
return pkg
} }


packageType := ctx.Params("type") packageType := ctx.Params("type")
name := ctx.Params("name") name := ctx.Params("name")
version := ctx.Params("version") version := ctx.Params("version")
if packageType != "" && name != "" && version != "" { if packageType != "" && name != "" && version != "" {
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.Type(packageType), name, version)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
if err != nil { if err != nil {
if err == packages_model.ErrPackageNotExist { if err == packages_model.ErrPackageNotExist {
errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err) errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
} else { } else {
errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err) errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
} }
return
return pkg
} }


ctx.Package.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
if err != nil { if err != nil {
errCb(http.StatusInternalServerError, "GetPackageDescriptor", err) errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
return
return pkg
} }
} }

return pkg
} }


func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
if setting.Service.RequireSignInView && ctx.Doer == nil {
func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
if setting.Service.RequireSignInView && doer == nil {
return perm.AccessModeNone, nil return perm.AccessModeNone, nil
} }


if ctx.Doer != nil && !ctx.Doer.IsGhost() && (!ctx.Doer.IsActive || ctx.Doer.ProhibitLogin) {
if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
return perm.AccessModeNone, nil return perm.AccessModeNone, nil
} }


// TODO: ActionUser permission check // TODO: ActionUser permission check
accessMode := perm.AccessModeNone accessMode := perm.AccessModeNone
if ctx.Package.Owner.IsOrganization() {
org := organization.OrgFromUser(ctx.Package.Owner)
if pkg.Owner.IsOrganization() {
org := organization.OrgFromUser(pkg.Owner)


if ctx.Doer != nil && !ctx.Doer.IsGhost() {
if doer != nil && !doer.IsGhost() {
// 1. If user is logged in, check all team packages permissions // 1. If user is logged in, check all team packages permissions
teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
if err != nil { if err != nil {
return accessMode, err return accessMode, err
} }
accessMode = perm accessMode = perm
} }
} }
} else if organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
} else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
// 2. If user is non-login, check if org is visible to non-login user // 2. If user is non-login, check if org is visible to non-login user
accessMode = perm.AccessModeRead accessMode = perm.AccessModeRead
} }
} else { } else {
if ctx.Doer != nil && !ctx.Doer.IsGhost() {
if doer != nil && !doer.IsGhost() {
// 1. Check if user is package owner // 1. Check if user is package owner
if ctx.Doer.ID == ctx.Package.Owner.ID {
if doer.ID == pkg.Owner.ID {
accessMode = perm.AccessModeOwner accessMode = perm.AccessModeOwner
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
} else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
accessMode = perm.AccessModeRead accessMode = perm.AccessModeRead
} }
} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
} else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
accessMode = perm.AccessModeRead accessMode = perm.AccessModeRead
} }
} }
} }


// PackageContexter initializes a package context for a request. // PackageContexter initializes a package context for a request.
func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
rnd := templates.HTMLRenderer()
func PackageContexter() func(next http.Handler) http.Handler {
renderer := templates.HTMLRenderer()
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := Context{
Resp: NewResponse(resp),
Data: middleware.GetContextData(req.Context()),
Render: rnd,
base, baseCleanUp := NewBaseContext(resp, req)
ctx := &Context{
Base: base,
Render: renderer, // it is still needed when rendering 500 page in a package handler
} }
defer ctx.Close()

ctx.Req = WithContext(req, &ctx)
defer baseCleanUp()


ctx.Base.AppendContextValue(contextKey, ctx)
next.ServeHTTP(ctx.Resp, ctx.Req) next.ServeHTTP(ctx.Resp, ctx.Req)
}) })
} }

+ 10
- 19
modules/context/private.go View File



"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/web/middleware"
) )


// PrivateContext represents a context for private routes // PrivateContext represents a context for private routes
type PrivateContext struct { type PrivateContext struct {
*Context
*Base
Override context.Context Override context.Context

Repo *Repository
} }


// Deadline is part of the interface for context.Context and we pass this to the request context // Deadline is part of the interface for context.Context and we pass this to the request context
if ctx.Override != nil { if ctx.Override != nil {
return ctx.Override.Deadline() return ctx.Override.Deadline()
} }
return ctx.Req.Context().Deadline()
return ctx.Base.Deadline()
} }


// Done is part of the interface for context.Context and we pass this to the request context // Done is part of the interface for context.Context and we pass this to the request context
if ctx.Override != nil { if ctx.Override != nil {
return ctx.Override.Done() return ctx.Override.Done()
} }
return ctx.Req.Context().Done()
return ctx.Base.Done()
} }


// Err is part of the interface for context.Context and we pass this to the request context // Err is part of the interface for context.Context and we pass this to the request context
if ctx.Override != nil { if ctx.Override != nil {
return ctx.Override.Err() return ctx.Override.Err()
} }
return ctx.Req.Context().Err()
return ctx.Base.Err()
} }


var privateContextKey interface{} = "default_private_context" var privateContextKey interface{} = "default_private_context"


// WithPrivateContext set up private context in request
func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request {
return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx))
}

// GetPrivateContext returns a context for Private routes // GetPrivateContext returns a context for Private routes
func GetPrivateContext(req *http.Request) *PrivateContext { func GetPrivateContext(req *http.Request) *PrivateContext {
return req.Context().Value(privateContextKey).(*PrivateContext) return req.Context().Value(privateContextKey).(*PrivateContext)
func PrivateContexter() func(http.Handler) http.Handler { func PrivateContexter() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := &PrivateContext{
Context: &Context{
Resp: NewResponse(w),
Data: middleware.GetContextData(req.Context()),
},
}
defer ctx.Close()
base, baseCleanUp := NewBaseContext(w, req)
ctx := &PrivateContext{Base: base}
defer baseCleanUp()
ctx.Base.AppendContextValue(privateContextKey, ctx)


ctx.Req = WithPrivateContext(req, ctx)
ctx.Data["Context"] = ctx
next.ServeHTTP(ctx.Resp, ctx.Req) next.ServeHTTP(ctx.Resp, ctx.Req)
}) })
} }

+ 21
- 20
modules/context/repo.go View File

} }


// RedirectToRepo redirect to a differently-named repository // RedirectToRepo redirect to a differently-named repository
func RedirectToRepo(ctx *Context, redirectRepoID int64) {
func RedirectToRepo(ctx *Base, redirectRepoID int64) {
ownerName := ctx.Params(":username") ownerName := ctx.Params(":username")
previousRepoName := ctx.Params(":reponame") previousRepoName := ctx.Params(":reponame")


repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID) repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
if err != nil { if err != nil {
ctx.ServerError("GetRepositoryByID", err)
log.Error("GetRepositoryByID: %v", err)
ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
return return
} }


} }


if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil { if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
RedirectToUser(ctx, userName, redirectUserID)
RedirectToUser(ctx.Base, userName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) { } else if user_model.IsErrUserRedirectNotExist(err) {
ctx.NotFound("GetUserByName", nil) ctx.NotFound("GetUserByName", nil)
} else { } else {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName) redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
if err == nil { if err == nil {
RedirectToRepo(ctx, redirectRepoID)
RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) { } else if repo_model.IsErrRedirectNotExist(err) {
if ctx.FormString("go-get") == "1" { if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx) EarlyResponseForGoGetMeta(ctx)
return false return false
} }


func getRefNameFromPath(ctx *Context, path string, isExist func(string) bool) string {
func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
refName := "" refName := ""
parts := strings.Split(path, "/") parts := strings.Split(path, "/")
for i, part := range parts { for i, part := range parts {
refName = strings.TrimPrefix(refName+"/"+part, "/") refName = strings.TrimPrefix(refName+"/"+part, "/")
if isExist(refName) { if isExist(refName) {
ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
repo.TreePath = strings.Join(parts[i+1:], "/")
return refName return refName
} }
} }
return "" return ""
} }


func getRefName(ctx *Context, pathType RepoRefType) string {
func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
path := ctx.Params("*") path := ctx.Params("*")
switch pathType { switch pathType {
case RepoRefLegacy, RepoRefAny: case RepoRefLegacy, RepoRefAny:
if refName := getRefName(ctx, RepoRefBranch); len(refName) > 0 {
if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
return refName return refName
} }
if refName := getRefName(ctx, RepoRefTag); len(refName) > 0 {
if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
return refName return refName
} }
// For legacy and API support only full commit sha // For legacy and API support only full commit sha
parts := strings.Split(path, "/") parts := strings.Split(path, "/")
if len(parts) > 0 && len(parts[0]) == git.SHAFullLength { if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0] return parts[0]
} }
if refName := getRefName(ctx, RepoRefBlob); len(refName) > 0 {
if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
return refName return refName
} }
ctx.Repo.TreePath = path
return ctx.Repo.Repository.DefaultBranch
repo.TreePath = path
return repo.Repository.DefaultBranch
case RepoRefBranch: case RepoRefBranch:
ref := getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
if len(ref) == 0 { if len(ref) == 0 {
// maybe it's a renamed branch // maybe it's a renamed branch
return getRefNameFromPath(ctx, path, func(s string) bool {
b, exist, err := git_model.FindRenamedBranch(ctx, ctx.Repo.Repository.ID, s)
return getRefNameFromPath(ctx, repo, path, func(s string) bool {
b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
if err != nil { if err != nil {
log.Error("FindRenamedBranch", err) log.Error("FindRenamedBranch", err)
return false return false


return ref return ref
case RepoRefTag: case RepoRefTag:
return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
case RepoRefCommit: case RepoRefCommit:
parts := strings.Split(path, "/") parts := strings.Split(path, "/")
if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength { if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
ctx.Repo.TreePath = strings.Join(parts[1:], "/")
repo.TreePath = strings.Join(parts[1:], "/")
return parts[0] return parts[0]
} }
case RepoRefBlob: case RepoRefBlob:
_, err := ctx.Repo.GitRepo.GetBlob(path)
_, err := repo.GitRepo.GetBlob(path)
if err != nil { if err != nil {
return "" return ""
} }
} }
ctx.Repo.IsViewBranch = true ctx.Repo.IsViewBranch = true
} else { } else {
refName = getRefName(ctx, refType)
refName = getRefName(ctx.Base, ctx.Repo, refType)
ctx.Repo.RefName = refName ctx.Repo.RefName = refName
isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool) isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
if isRenamedBranch && has { if isRenamedBranch && has {

+ 3
- 10
modules/context/response.go View File

// ResponseWriter represents a response writer for HTTP // ResponseWriter represents a response writer for HTTP
type ResponseWriter interface { type ResponseWriter interface {
http.ResponseWriter http.ResponseWriter
Flush()
http.Flusher
Status() int Status() int
Before(func(ResponseWriter)) Before(func(ResponseWriter))
Size() int
} }


var _ ResponseWriter = &Response{} var _ ResponseWriter = &Response{}
beforeExecuted bool beforeExecuted bool
} }


// Size return written size
func (r *Response) Size() int {
return r.written
}

// Write writes bytes to HTTP endpoint // Write writes bytes to HTTP endpoint
func (r *Response) Write(bs []byte) (int, error) { func (r *Response) Write(bs []byte) (int, error) {
if !r.beforeExecuted { if !r.beforeExecuted {
} }
} }


// Flush flush cached data
// Flush flushes cached data
func (r *Response) Flush() { func (r *Response) Flush() {
if f, ok := r.ResponseWriter.(http.Flusher); ok { if f, ok := r.ResponseWriter.(http.Flusher); ok {
f.Flush() f.Flush()
r.befores = append(r.befores, f) r.befores = append(r.befores, f)
} }


// NewResponse creates a response
func NewResponse(resp http.ResponseWriter) *Response {
func WrapResponseWriter(resp http.ResponseWriter) *Response {
if v, ok := resp.(*Response); ok { if v, ok := resp.(*Response); ok {
return v return v
} }

+ 2
- 2
modules/context/utils.go View File

) )


// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since // GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
func GetQueryBeforeSince(ctx *Context) (before, since int64, err error) {
func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
qCreatedBefore, err := prepareQueryArg(ctx, "before") qCreatedBefore, err := prepareQueryArg(ctx, "before")
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }


// prepareQueryArg unescape and trim a query arg // prepareQueryArg unescape and trim a query arg
func prepareQueryArg(ctx *Context, name string) (value string, err error) {
func prepareQueryArg(ctx *Base, name string) (value string, err error) {
value, err = url.PathUnescape(ctx.FormString(name)) value, err = url.PathUnescape(ctx.FormString(name))
value = strings.TrimSpace(value) value = strings.TrimSpace(value)
return value, err return value, err

+ 77
- 50
modules/test/context_tests.go View File

package test package test


import ( import (
scontext "context"
gocontext "context"
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
// MockContext mock context for unit tests // MockContext mock context for unit tests
// TODO: move this function to other packages, because it depends on "models" package // TODO: move this function to other packages, because it depends on "models" package
func MockContext(t *testing.T, path string) *context.Context { func MockContext(t *testing.T, path string) *context.Context {
resp := &mockResponseWriter{}
ctx := context.Context{
resp := httptest.NewRecorder()
requestURL, err := url.Parse(path)
assert.NoError(t, err)
req := &http.Request{
URL: requestURL,
Form: url.Values{},
}

base, baseCleanUp := context.NewBaseContext(resp, req)
base.Data = middleware.ContextData{}
base.Locale = &translation.MockLocale{}
ctx := &context.Context{
Base: base,
Render: &mockRender{}, Render: &mockRender{},
Data: make(middleware.ContextData),
Flash: &middleware.Flash{
Values: make(url.Values),
},
Resp: context.NewResponse(resp),
Locale: &translation.MockLocale{},
Flash: &middleware.Flash{Values: url.Values{}},
} }
defer ctx.Close()
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later


chiCtx := chi.NewRouteContext()
ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
return ctx
}

// MockAPIContext mock context for unit tests
// TODO: move this function to other packages, because it depends on "models" package
func MockAPIContext(t *testing.T, path string) *context.APIContext {
resp := httptest.NewRecorder()
requestURL, err := url.Parse(path) requestURL, err := url.Parse(path)
assert.NoError(t, err) assert.NoError(t, err)
req := &http.Request{ req := &http.Request{
Form: url.Values{}, Form: url.Values{},
} }


base, baseCleanUp := context.NewBaseContext(resp, req)
base.Data = middleware.ContextData{}
base.Locale = &translation.MockLocale{}
ctx := &context.APIContext{Base: base}
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later

chiCtx := chi.NewRouteContext() chiCtx := chi.NewRouteContext()
req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
ctx.Req = context.WithContext(req, &ctx)
return &ctx
ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
return ctx
} }


// LoadRepo load a repo into a test context. // LoadRepo load a repo into a test context.
func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
ctx.Repo = &context.Repository{}
ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
var doer *user_model.User
repo := &context.Repository{}
switch ctx := ctx.(type) {
case *context.Context:
ctx.Repo = repo
doer = ctx.Doer
case *context.APIContext:
ctx.Repo = repo
doer = ctx.Doer
default:
assert.Fail(t, "context is not *context.Context or *context.APIContext")
return
}

repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
var err error var err error
ctx.Repo.Owner, err = user_model.GetUserByID(ctx, ctx.Repo.Repository.OwnerID)
repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
assert.NoError(t, err) assert.NoError(t, err)
ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, ctx.Doer)
repo.RepoLink = repo.Repository.Link()
repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
assert.NoError(t, err) assert.NoError(t, err)
} }


// LoadRepoCommit loads a repo's commit into a test context. // LoadRepoCommit loads a repo's commit into a test context.
func LoadRepoCommit(t *testing.T, ctx *context.Context) {
gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
var repo *context.Repository
switch ctx := ctx.(type) {
case *context.Context:
repo = ctx.Repo
case *context.APIContext:
repo = ctx.Repo
default:
assert.Fail(t, "context is not *context.Context or *context.APIContext")
return
}

gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())
assert.NoError(t, err) assert.NoError(t, err)
defer gitRepo.Close() defer gitRepo.Close()
branch, err := gitRepo.GetHEADBranch() branch, err := gitRepo.GetHEADBranch()
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, branch) assert.NotNil(t, branch)
if branch != nil { if branch != nil {
ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
assert.NoError(t, err) assert.NoError(t, err)
} }
} }


// LoadUser load a user into a test context. // LoadUser load a user into a test context.
func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
switch ctx := ctx.(type) {
case *context.Context:
ctx.Doer = doer
case *context.APIContext:
ctx.Doer = doer
default:
assert.Fail(t, "context is not *context.Context or *context.APIContext")
return
}
} }


// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has // LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
assert.NoError(t, err) assert.NoError(t, err)
} }


type mockResponseWriter struct {
httptest.ResponseRecorder
size int
}

func (rw *mockResponseWriter) Write(b []byte) (int, error) {
rw.size += len(b)
return rw.ResponseRecorder.Write(b)
}

func (rw *mockResponseWriter) Status() int {
return rw.ResponseRecorder.Code
}

func (rw *mockResponseWriter) Written() bool {
return rw.ResponseRecorder.Code > 0
}

func (rw *mockResponseWriter) Size() int {
return rw.size
}

func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
return nil
}

type mockRender struct{} type mockRender struct{}


func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) { func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {

+ 11
- 3
modules/translation/translation.go View File

} }


var ( var (
lock *sync.RWMutex
lock *sync.RWMutex

allLangs []*LangType
allLangMap map[string]*LangType

matcher language.Matcher matcher language.Matcher
allLangs []*LangType
allLangMap map[string]*LangType
supportedTags []language.Tag supportedTags []language.Tag
) )


} }
return l.msgPrinter.Sprintf("%v", number.Decimal(v)) return l.msgPrinter.Sprintf("%v", number.Decimal(v))
} }

func init() {
// prepare a default matcher, especially for tests
supportedTags = []language.Tag{language.English}
matcher = language.NewMatcher(supportedTags)
}

+ 13
- 2
modules/web/handler.go View File

"reflect" "reflect"


"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/web/routing" "code.gitea.io/gitea/modules/web/routing"
) )


reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) }, reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) },
} }


func RegisterHandleTypeProvider[T any](fn func(req *http.Request) ResponseStatusProvider) {
argTypeProvider[reflect.TypeOf((*T)(nil)).Elem()] = fn
}

// responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written // responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
type responseWriter struct { type responseWriter struct {
respWriter http.ResponseWriter respWriter http.ResponseWriter
} }
} }


func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value) []reflect.Value {
func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value, fnInfo *routing.FuncInfo) []reflect.Value {
defer func() {
if err := recover(); err != nil {
log.Error("unable to prepare handler arguments for %s: %v", fnInfo.String(), err)
panic(err)
}
}()
isPreCheck := req == nil isPreCheck := req == nil


argsIn := make([]reflect.Value, fn.Type().NumIn()) argsIn := make([]reflect.Value, fn.Type().NumIn())
} }


// prepare the arguments for the handler and do pre-check // prepare the arguments for the handler and do pre-check
argsIn := prepareHandleArgsIn(resp, req, fn)
argsIn := prepareHandleArgsIn(resp, req, fn, funcInfo)
if req == nil { if req == nil {
preCheckHandler(fn, argsIn) preCheckHandler(fn, argsIn)
return // it's doing pre-check, just return return // it's doing pre-check, just return

+ 55
- 44
routers/api/actions/artifacts.go View File



package actions package actions


// Github Actions Artifacts API Simple Description
// GitHub Actions Artifacts API Simple Description
// //
// 1. Upload artifact // 1. Upload artifact
// 1.1. Post upload url // 1.1. Post upload url


import ( import (
"compress/gzip" "compress/gzip"
gocontext "context"
"crypto/md5" "crypto/md5"
"encoding/base64" "encoding/base64"
"errors" "errors"


const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts" const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"


func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
type artifactContextKeyType struct{}

var artifactContextKey = artifactContextKeyType{}

type ArtifactContext struct {
*context.Base

ActionTask *actions.ActionTask
}

func init() {
web.RegisterHandleTypeProvider[*ArtifactContext](func(req *http.Request) web.ResponseStatusProvider {
return req.Context().Value(artifactContextKey).(*ArtifactContext)
})
}

func ArtifactsRoutes(prefix string) *web.Route {
m := web.NewRoute() m := web.NewRoute()
m.Use(withContexter(goctx))
m.Use(ArtifactContexter())


r := artifactRoutes{ r := artifactRoutes{
prefix: prefix, prefix: prefix,
return m return m
} }


// withContexter initializes a package context for a request.
func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler {
func ArtifactContexter() func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := context.Context{
Resp: context.NewResponse(resp),
Data: map[string]interface{}{},
}
defer ctx.Close()
base, baseCleanUp := context.NewBaseContext(resp, req)
defer baseCleanUp()
ctx := &ArtifactContext{Base: base}
ctx.AppendContextValue(artifactContextKey, ctx)


// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN // action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
// we should verify the ACTIONS_RUNTIME_TOKEN // we should verify the ACTIONS_RUNTIME_TOKEN
ctx.Error(http.StatusUnauthorized, "Bad authorization header") ctx.Error(http.StatusUnauthorized, "Bad authorization header")
return return
} }

authToken := strings.TrimPrefix(authHeader, "Bearer ") authToken := strings.TrimPrefix(authHeader, "Bearer ")
task, err := actions.GetRunningTaskByToken(req.Context(), authToken) task, err := actions.GetRunningTaskByToken(req.Context(), authToken)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "Error runner api getting task") ctx.Error(http.StatusInternalServerError, "Error runner api getting task")
return return
} }
ctx.Data["task"] = task


if err := task.LoadJob(goctx); err != nil {
if err := task.LoadJob(req.Context()); err != nil {
log.Error("Error runner api getting job: %v", err) log.Error("Error runner api getting job: %v", err)
ctx.Error(http.StatusInternalServerError, "Error runner api getting job") ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
return return
} }


ctx.Req = context.WithContext(req, &ctx)

ctx.ActionTask = task
next.ServeHTTP(ctx.Resp, ctx.Req) next.ServeHTTP(ctx.Resp, ctx.Req)
}) })
} }
FileContainerResourceURL string `json:"fileContainerResourceUrl"` FileContainerResourceURL string `json:"fileContainerResourceUrl"`
} }


func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTask, int64, bool) {
task, ok := ctx.Data["task"].(*actions.ActionTask)
if !ok {
log.Error("Error getting task in context")
ctx.Error(http.StatusInternalServerError, "Error getting task in context")
return nil, 0, false
}
func (ar artifactRoutes) validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
task := ctx.ActionTask
runID := ctx.ParamsInt64("run_id") runID := ctx.ParamsInt64("run_id")
if task.Job.RunID != runID { if task.Job.RunID != runID {
log.Error("Error runID not match") log.Error("Error runID not match")
} }


// getUploadArtifactURL generates a URL for uploading an artifact // getUploadArtifactURL generates a URL for uploading an artifact
func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
func (ar artifactRoutes) getUploadArtifactURL(ctx *ArtifactContext) {
task, runID, ok := ar.validateRunID(ctx) task, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return


// getUploadFileSize returns the size of the file to be uploaded. // getUploadFileSize returns the size of the file to be uploaded.
// The raw size is the size of the file as reported by the header X-TFS-FileLength. // The raw size is the size of the file as reported by the header X-TFS-FileLength.
func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64, error) {
func (ar artifactRoutes) getUploadFileSize(ctx *ArtifactContext) (int64, int64, error) {
contentLength := ctx.Req.ContentLength contentLength := ctx.Req.ContentLength
xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64) xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
if xTfsLength > 0 { if xTfsLength > 0 {
return contentLength, contentLength, nil return contentLength, contentLength, nil
} }


func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
func (ar artifactRoutes) saveUploadChunk(ctx *ArtifactContext,
artifact *actions.ActionArtifact, artifact *actions.ActionArtifact,
contentSize, runID int64, contentSize, runID int64,
) (int64, error) { ) (int64, error) {
// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32 // The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "") var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")


func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
_, runID, ok := ar.validateRunID(ctx) _, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return


// comfirmUploadArtifact comfirm upload artifact. // comfirmUploadArtifact comfirm upload artifact.
// if all chunks are uploaded, merge them to one file. // if all chunks are uploaded, merge them to one file.
func (ar artifactRoutes) comfirmUploadArtifact(ctx *context.Context) {
func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
_, runID, ok := ar.validateRunID(ctx) _, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return
Path string Path string
} }


func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64) error {
func (ar artifactRoutes) mergeArtifactChunks(ctx *ArtifactContext, runID int64) error {
storageDir := fmt.Sprintf("tmp%d", runID) storageDir := fmt.Sprintf("tmp%d", runID)
var chunks []*chunkItem var chunks []*chunkItem
if err := ar.fs.IterateObjects(storageDir, func(path string, obj storage.Object) error { if err := ar.fs.IterateObjects(storageDir, func(path string, obj storage.Object) error {


// use multiReader // use multiReader
readers := make([]io.Reader, 0, len(allChunks)) readers := make([]io.Reader, 0, len(allChunks))
readerClosers := make([]io.Closer, 0, len(allChunks))
closeReaders := func() {
for _, r := range readers {
_ = r.(io.Closer).Close() // it guarantees to be io.Closer by the following loop's Open function
}
readers = nil
}
defer closeReaders()

for _, c := range allChunks { for _, c := range allChunks {
reader, err := ar.fs.Open(c.Path)
if err != nil {
var readCloser io.ReadCloser
if readCloser, err = ar.fs.Open(c.Path); err != nil {
return fmt.Errorf("open chunk error: %v, %s", err, c.Path) return fmt.Errorf("open chunk error: %v, %s", err, c.Path)
} }
readers = append(readers, reader)
readerClosers = append(readerClosers, reader)
readers = append(readers, readCloser)
} }
mergedReader := io.MultiReader(readers...) mergedReader := io.MultiReader(readers...)


return fmt.Errorf("merged file size is not equal to chunk length") return fmt.Errorf("merged file size is not equal to chunk length")
} }


// close readers
for _, r := range readerClosers {
r.Close()
}

// save storage path to artifact // save storage path to artifact
log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath) log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
artifact.StoragePath = storagePath artifact.StoragePath = storagePath
return fmt.Errorf("update artifact error: %v", err) return fmt.Errorf("update artifact error: %v", err)
} }


closeReaders() // close before delete

// drop chunks // drop chunks
for _, c := range cs { for _, c := range cs {
if err := ar.fs.Delete(c.Path); err != nil { if err := ar.fs.Delete(c.Path); err != nil {
} }
) )


func (ar artifactRoutes) listArtifacts(ctx *context.Context) {
func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
_, runID, ok := ar.validateRunID(ctx) _, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return
} }


artficats, err := actions.ListArtifactsByRunID(ctx, runID)
artifacts, err := actions.ListArtifactsByRunID(ctx, runID)
if err != nil { if err != nil {
log.Error("Error getting artifacts: %v", err) log.Error("Error getting artifacts: %v", err)
ctx.Error(http.StatusInternalServerError, err.Error()) ctx.Error(http.StatusInternalServerError, err.Error())
return return
} }


artficatsData := make([]listArtifactsResponseItem, 0, len(artficats))
for _, a := range artficats {
artficatsData := make([]listArtifactsResponseItem, 0, len(artifacts))
for _, a := range artifacts {
artficatsData = append(artficatsData, listArtifactsResponseItem{ artficatsData = append(artficatsData, listArtifactsResponseItem{
Name: a.ArtifactName, Name: a.ArtifactName,
FileContainerResourceURL: ar.buildArtifactURL(runID, a.ID, "path"), FileContainerResourceURL: ar.buildArtifactURL(runID, a.ID, "path"),
} }
) )


func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
_, runID, ok := ar.validateRunID(ctx) _, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return
ctx.JSON(http.StatusOK, respData) ctx.JSON(http.StatusOK, respData)
} }


func (ar artifactRoutes) downloadArtifact(ctx *context.Context) {
func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
_, runID, ok := ar.validateRunID(ctx) _, runID, ok := ar.validateRunID(ctx)
if !ok { if !ok {
return return

+ 2
- 2
routers/api/packages/api.go View File

func CommonRoutes(ctx gocontext.Context) *web.Route { func CommonRoutes(ctx gocontext.Context) *web.Route {
r := web.NewRoute() r := web.NewRoute()


r.Use(context.PackageContexter(ctx))
r.Use(context.PackageContexter())


verifyAuth(r, []auth.Method{ verifyAuth(r, []auth.Method{
&auth.OAuth2{}, &auth.OAuth2{},
func ContainerRoutes(ctx gocontext.Context) *web.Route { func ContainerRoutes(ctx gocontext.Context) *web.Route {
r := web.NewRoute() r := web.NewRoute()


r.Use(context.PackageContexter(ctx))
r.Use(context.PackageContexter())


verifyAuth(r, []auth.Method{ verifyAuth(r, []auth.Method{
&auth.Basic{}, &auth.Basic{},

+ 8
- 8
routers/api/v1/api.go View File

if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil { if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
context.RedirectToUser(ctx.Context, userName, redirectUserID)
context.RedirectToUser(ctx.Base, userName, redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) { } else if user_model.IsErrUserRedirectNotExist(err) {
ctx.NotFound("GetUserByName", err) ctx.NotFound("GetUserByName", err)
} else { } else {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName) redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
if err == nil { if err == nil {
context.RedirectToRepo(ctx.Context, redirectRepoID)
context.RedirectToRepo(ctx.Base, redirectRepoID)
} else if repo_model.IsErrRedirectNotExist(err) { } else if repo_model.IsErrRedirectNotExist(err) {
ctx.NotFound() ctx.NotFound()
} else { } else {
ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope) ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope)
return return
} }
if ctx.Context.IsBasicAuth {
if ctx.IsBasicAuth {
ctx.CheckForOTP() ctx.CheckForOTP()
return return
} }


func reqBasicAuth() func(ctx *context.APIContext) { func reqBasicAuth() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if !ctx.Context.IsBasicAuth {
if !ctx.IsBasicAuth {
ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required") ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
return return
} }
// reqOrgOwnership user should be an organization owner, or a site admin // reqOrgOwnership user should be an organization owner, or a site admin
func reqOrgOwnership() func(ctx *context.APIContext) { func reqOrgOwnership() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if ctx.Context.IsUserSiteAdmin() {
if ctx.IsUserSiteAdmin() {
return return
} }


// reqTeamMembership user should be an team member, or a site admin // reqTeamMembership user should be an team member, or a site admin
func reqTeamMembership() func(ctx *context.APIContext) { func reqTeamMembership() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if ctx.Context.IsUserSiteAdmin() {
if ctx.IsUserSiteAdmin() {
return return
} }
if ctx.Org.Team == nil { if ctx.Org.Team == nil {
// reqOrgMembership user should be an organization member, or a site admin // reqOrgMembership user should be an organization member, or a site admin
func reqOrgMembership() func(ctx *context.APIContext) { func reqOrgMembership() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if ctx.Context.IsUserSiteAdmin() {
if ctx.IsUserSiteAdmin() {
return return
} }


if organization.IsErrOrgNotExist(err) { if organization.IsErrOrgNotExist(err) {
redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org")) redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org"))
if err == nil { if err == nil {
context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID)
context.RedirectToUser(ctx.Base, ctx.Params(":org"), redirectUserID)
} else if user_model.IsErrUserRedirectNotExist(err) { } else if user_model.IsErrUserRedirectNotExist(err) {
ctx.NotFound("GetOrgByName", err) ctx.NotFound("GetOrgByName", err)
} else { } else {

+ 2
- 2
routers/api/v1/misc/markup.go View File

return return
} }


common.RenderMarkup(ctx.Context, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
} }


// Markdown render markdown document to HTML // Markdown render markdown document to HTML
mode = form.Mode mode = form.Mode
} }


common.RenderMarkup(ctx.Context, mode, form.Text, form.Context, "", form.Wiki)
common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki)
} }


// MarkdownRaw render raw markdown HTML // MarkdownRaw render raw markdown HTML

+ 9
- 24
routers/api/v1/misc/markup_test.go View File

"code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup"
"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"
"code.gitea.io/gitea/modules/templates"
"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/modules/web/middleware"
AppSubURL = AppURL + Repo + "/" AppSubURL = AppURL + Repo + "/"
) )


func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) {
rnd := templates.HTMLRenderer()
func createAPIContext(req *http.Request) (*context.APIContext, *httptest.ResponseRecorder) {
resp := httptest.NewRecorder() resp := httptest.NewRecorder()
c := &context.Context{
Req: req,
Resp: context.NewResponse(resp),
Render: rnd,
Data: make(middleware.ContextData),
}
defer c.Close()
base, baseCleanUp := context.NewBaseContext(resp, req)
base.Data = middleware.ContextData{}
c := &context.APIContext{Base: base}
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later


return c, resp return c, resp
} }


func wrap(ctx *context.Context) *context.APIContext {
return &context.APIContext{
Context: ctx,
}
}

func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) { func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
setting.AppURL = AppURL setting.AppURL = AppURL


Method: "POST", Method: "POST",
URL: requrl, URL: requrl,
} }
m, resp := createContext(req)
ctx := wrap(m)
ctx, resp := createAPIContext(req)


options.Text = text options.Text = text
web.SetForm(ctx, &options) web.SetForm(ctx, &options)
Method: "POST", Method: "POST",
URL: requrl, URL: requrl,
} }
m, resp := createContext(req)
ctx := wrap(m)
ctx, resp := createAPIContext(req)


options.Text = text options.Text = text
web.SetForm(ctx, &options) web.SetForm(ctx, &options)
Method: "POST", Method: "POST",
URL: requrl, URL: requrl,
} }
m, resp := createContext(req)
ctx := wrap(m)
ctx, resp := createAPIContext(req)


for i := 0; i < len(simpleCases); i += 2 { for i := 0; i < len(simpleCases); i += 2 {
options.Text = simpleCases[i] options.Text = simpleCases[i]
Method: "POST", Method: "POST",
URL: requrl, URL: requrl,
} }
m, resp := createContext(req)
ctx := wrap(m)
ctx, resp := createAPIContext(req)


for i := 0; i < len(simpleCases); i += 2 { for i := 0; i < len(simpleCases); i += 2 {
ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i])) ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i]))

+ 1
- 1
routers/api/v1/notify/notifications.go View File

} }


func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions { func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return nil return nil

+ 6
- 6
routers/api/v1/repo/file.go View File



ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry))) ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))


if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
ctx.Error(http.StatusInternalServerError, "ServeBlob", err) ctx.Error(http.StatusInternalServerError, "ServeBlob", err)
} }
} }
} }


// OK not cached - serve! // OK not cached - serve!
if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
ctx.ServerError("ServeBlob", err) ctx.ServerError("ServeBlob", err)
} }
return return
} }


if err := dataRc.Close(); err != nil { if err := dataRc.Close(); err != nil {
log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Context.Repo.Repository, err)
log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Repo.Repository, err)
} }


// Check if the blob represents a pointer // Check if the blob represents a pointer
} }


// OK not cached - serve! // OK not cached - serve!
common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
return return
} }


return return
} }


common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
return return
} else if err != nil { } else if err != nil {
ctx.ServerError("GetLFSMetaObjectByOid", err) ctx.ServerError("GetLFSMetaObjectByOid", err)
} }
defer lfsDataRc.Close() defer lfsDataRc.Close()


common.ServeContentByReadSeeker(ctx.Context, ctx.Repo.TreePath, lastModified, lfsDataRc)
common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
} }


func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) { func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) {

+ 2
- 3
routers/api/v1/repo/hook_test.go View File



"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"


"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
func TestTestHook(t *testing.T) { func TestTestHook(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)


ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
ctx := test.MockAPIContext(t, "user2/repo1/wiki/_pages")
ctx.SetParams(":id", "1") ctx.SetParams(":id", "1")
test.LoadRepo(t, ctx, 1) test.LoadRepo(t, ctx, 1)
test.LoadRepoCommit(t, ctx) test.LoadRepoCommit(t, ctx)
test.LoadUser(t, ctx, 2) test.LoadUser(t, ctx, 2)
TestHook(&context.APIContext{Context: ctx, Org: nil})
TestHook(ctx)
assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status()) assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())


unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{ unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{

+ 2
- 2
routers/api/v1/repo/issue.go View File

// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"


before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"
before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return

+ 3
- 3
routers/api/v1/repo/issue_comment.go View File

// "200": // "200":
// "$ref": "#/responses/CommentList" // "$ref": "#/responses/CommentList"


before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
// "200": // "200":
// "$ref": "#/responses/TimelineList" // "$ref": "#/responses/TimelineList"


before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
// "200": // "200":
// "$ref": "#/responses/CommentList" // "$ref": "#/responses/CommentList"


before, since, err := context.GetQueryBeforeSince(ctx.Context)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return

+ 3
- 3
routers/api/v1/repo/issue_tracked_time.go View File

opts.UserID = user.ID opts.UserID = user.ID
} }


if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
} }
} }


var err error var err error
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
} }
} }


var err error var err error
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err) ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return return
} }

+ 1
- 1
routers/api/v1/repo/migrate.go View File

return return
} }


if ctx.HasError() {
if ctx.HasAPIError() {
ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
return return
} }

+ 6
- 9
routers/api/v1/repo/repo_test.go View File



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"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
func TestRepoEdit(t *testing.T) { func TestRepoEdit(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)


ctx := test.MockContext(t, "user2/repo1")
ctx := test.MockAPIContext(t, "user2/repo1")
test.LoadRepo(t, ctx, 1) test.LoadRepo(t, ctx, 1)
test.LoadUser(t, ctx, 2) test.LoadUser(t, ctx, 2)
ctx.Repo.Owner = ctx.Doer ctx.Repo.Owner = ctx.Doer
Archived: &archived, Archived: &archived,
} }


apiCtx := &context.APIContext{Context: ctx, Org: nil}
web.SetForm(apiCtx, &opts)
Edit(apiCtx)
web.SetForm(ctx, &opts)
Edit(ctx)


assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
func TestRepoEditNameChange(t *testing.T) { func TestRepoEditNameChange(t *testing.T) {
unittest.PrepareTestEnv(t) unittest.PrepareTestEnv(t)


ctx := test.MockContext(t, "user2/repo1")
ctx := test.MockAPIContext(t, "user2/repo1")
test.LoadRepo(t, ctx, 1) test.LoadRepo(t, ctx, 1)
test.LoadUser(t, ctx, 2) test.LoadUser(t, ctx, 2)
ctx.Repo.Owner = ctx.Doer ctx.Repo.Owner = ctx.Doer
Name: &name, Name: &name,
} }


apiCtx := &context.APIContext{Context: ctx, Org: nil}
web.SetForm(apiCtx, &opts)
Edit(apiCtx)
web.SetForm(ctx, &opts)
Edit(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())


unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{

+ 1
- 1
routers/api/v1/repo/status.go View File

ctx.Error(http.StatusBadRequest, "ref/sha not given", nil) ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
return return
} }
sha = utils.MustConvertToSHA1(ctx.Context, sha)
sha = utils.MustConvertToSHA1(ctx.Base, ctx.Repo, sha)
repo := ctx.Repo.Repository repo := ctx.Repo.Repository


listOptions := utils.GetListOptions(ctx) listOptions := utils.GetListOptions(ctx)

+ 1
- 1
routers/api/v1/user/helper.go View File

if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil { if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil {
context.RedirectToUser(ctx.Context, username, redirectUserID)
context.RedirectToUser(ctx.Base, username, redirectUserID)
} else { } else {
ctx.NotFound("GetUserByName", err) ctx.NotFound("GetUserByName", err)
} }

+ 6
- 5
routers/api/v1/utils/git.go View File

package utils package utils


import ( import (
gocontext "context"
"fmt" "fmt"
"net/http" "net/http"


} }
} }


sha = MustConvertToSHA1(ctx.Context, sha)
sha = MustConvertToSHA1(ctx, ctx.Repo, sha)


if ctx.Repo.GitRepo != nil { if ctx.Repo.GitRepo != nil {
err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha) err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
} }


// ConvertToSHA1 returns a full-length SHA1 from a potential ID string // ConvertToSHA1 returns a full-length SHA1 from a potential ID string
func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
func ConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) (git.SHA1, error) {
if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) { if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
sha1, err := git.NewIDFromString(commitID) sha1, err := git.NewIDFromString(commitID)
if err == nil { if err == nil {
} }
} }


gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, ctx.Repo.Repository.RepoPath())
gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
if err != nil { if err != nil {
return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err) return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
} }
} }


// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1 // MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
func MustConvertToSHA1(ctx *context.Context, commitID string) string {
sha, err := ConvertToSHA1(ctx, commitID)
func MustConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) string {
sha, err := ConvertToSHA1(ctx, repo, commitID)
if err != nil { if err != nil {
return commitID return commitID
} }

+ 4
- 4
routers/common/markup.go View File

) )


// RenderMarkup renders markup text for the /markup and /markdown endpoints // RenderMarkup renders markup text for the /markup and /markdown endpoints
func RenderMarkup(ctx *context.Context, mode, text, urlPrefix, filePath string, wiki bool) {
func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) {
var markupType string var markupType string
relativePath := "" relativePath := ""


} }


meta := map[string]string{} meta := map[string]string{}
if ctx.Repo != nil && ctx.Repo.Repository != nil {
if repo != nil && repo.Repository != nil {
if mode == "comment" { if mode == "comment" {
meta = ctx.Repo.Repository.ComposeMetas()
meta = repo.Repository.ComposeMetas()
} else { } else {
meta = ctx.Repo.Repository.ComposeDocumentMetas()
meta = repo.Repository.ComposeDocumentMetas()
} }
} }
if mode != "comment" { if mode != "comment" {

+ 1
- 1
routers/common/middleware.go View File

return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true) ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
defer finished() defer finished()
next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx)))
next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
}) })
}) })



+ 4
- 4
routers/common/serve.go View File

) )


// ServeBlob download a git.Blob // ServeBlob download a git.Blob
func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) error {
func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified time.Time) error {
if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
return nil return nil
} }
} }
}() }()


httplib.ServeContentByReader(ctx.Req, ctx.Resp, ctx.Repo.TreePath, blob.Size(), dataRc)
httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, blob.Size(), dataRc)
return nil return nil
} }


func ServeContentByReader(ctx *context.Context, filePath string, size int64, reader io.Reader) {
func ServeContentByReader(ctx *context.Base, filePath string, size int64, reader io.Reader) {
httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader) httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader)
} }


func ServeContentByReadSeeker(ctx *context.Context, filePath string, modTime time.Time, reader io.ReadSeeker) {
func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime time.Time, reader io.ReadSeeker) {
httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader) httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader)
} }

+ 1
- 1
routers/init.go View File

// In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/ // In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/
// TODO: this prefix should be generated with a token string with runner ? // TODO: this prefix should be generated with a token string with runner ?
prefix = "/api/actions_pipeline" prefix = "/api/actions_pipeline"
r.Mount(prefix, actions_router.ArtifactsRoutes(ctx, prefix))
r.Mount(prefix, actions_router.ArtifactsRoutes(prefix))
} }


return r return r

+ 5
- 14
routers/install/install.go View File

dbTypeNames := getSupportedDbTypeNames() dbTypeNames := getSupportedDbTypeNames()
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
base, baseCleanUp := context.NewBaseContext(resp, req)
ctx := context.Context{ ctx := context.Context{
Resp: context.NewResponse(resp),
Base: base,
Flash: &middleware.Flash{}, Flash: &middleware.Flash{},
Locale: middleware.Locale(resp, req),
Render: rnd, Render: rnd,
Data: middleware.GetContextData(req.Context()),
Session: session.GetSession(req), Session: session.GetSession(req),
} }
defer ctx.Close()
defer baseCleanUp()


ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
ctx.Data.MergeFrom(middleware.ContextData{ ctx.Data.MergeFrom(middleware.ContextData{


"PasswordHashAlgorithms": hash.RecommendedHashAlgorithms, "PasswordHashAlgorithms": hash.RecommendedHashAlgorithms,
}) })
ctx.Req = context.WithContext(req, &ctx)
next.ServeHTTP(resp, ctx.Req) next.ServeHTTP(resp, ctx.Req)
}) })
} }
ctx.Data["CurDbType"] = form.DbType ctx.Data["CurDbType"] = form.DbType


if ctx.HasError() { if ctx.HasError() {
if ctx.HasValue("Err_SMTPUser") {
ctx.Data["Err_SMTP"] = true
}
if ctx.HasValue("Err_AdminName") ||
ctx.HasValue("Err_AdminPasswd") ||
ctx.HasValue("Err_AdminEmail") {
ctx.Data["Err_Admin"] = true
}

ctx.Data["Err_SMTP"] = ctx.Data["Err_SMTPUser"] != nil
ctx.Data["Err_Admin"] = ctx.Data["Err_AdminName"] != nil || ctx.Data["Err_AdminPasswd"] != nil || ctx.Data["Err_AdminEmail"] != nil
ctx.HTML(http.StatusOK, tplInstall) ctx.HTML(http.StatusOK, tplInstall)
return return
} }

+ 1
- 9
routers/web/misc/markup.go View File

package misc package misc


import ( import (
"net/http"

"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
// Markup render markup document to HTML // Markup render markup document to HTML
func Markup(ctx *context.Context) { func Markup(ctx *context.Context) {
form := web.GetForm(ctx).(*api.MarkupOption) form := web.GetForm(ctx).(*api.MarkupOption)

if ctx.HasAPIError() {
ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
return
}

common.RenderMarkup(ctx, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
} }

+ 1
- 1
routers/web/repo/attachment.go View File

} }
defer fr.Close() defer fr.Close()


common.ServeContentByReadSeeker(ctx, attach.Name, attach.CreatedUnix.AsTime(), fr)
common.ServeContentByReadSeeker(ctx.Base, attach.Name, attach.CreatedUnix.AsTime(), fr)
} }


// GetAttachment serve attachments // GetAttachment serve attachments

+ 5
- 5
routers/web/repo/download.go View File

log.Error("ServeBlobOrLFS: Close: %v", err) log.Error("ServeBlobOrLFS: Close: %v", err)
} }
closed = true closed = true
return common.ServeBlob(ctx, blob, lastModified)
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
} }
if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) { if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
return nil return nil
log.Error("ServeBlobOrLFS: Close: %v", err) log.Error("ServeBlobOrLFS: Close: %v", err)
} }
}() }()
common.ServeContentByReadSeeker(ctx, ctx.Repo.TreePath, lastModified, lfsDataRc)
common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
return nil return nil
} }
if err = dataRc.Close(); err != nil { if err = dataRc.Close(); err != nil {
} }
closed = true closed = true


return common.ServeBlob(ctx, blob, lastModified)
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
} }


func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) { func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) {
return return
} }


if err := common.ServeBlob(ctx, blob, lastModified); err != nil {
if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
ctx.ServerError("ServeBlob", err) ctx.ServerError("ServeBlob", err)
} }
} }
} }
return return
} }
if err = common.ServeBlob(ctx, blob, time.Time{}); err != nil {
if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, time.Time{}); err != nil {
ctx.ServerError("ServeBlob", err) ctx.ServerError("ServeBlob", err)
} }
} }

+ 1
- 1
routers/web/repo/http.go View File

if err != nil { if err != nil {
if repo_model.IsErrRepoNotExist(err) { if repo_model.IsErrRepoNotExist(err) {
if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil { if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil {
context.RedirectToRepo(ctx, redirectRepoID)
context.RedirectToRepo(ctx.Base, redirectRepoID)
return return
} }
repoExist = false repoExist = false

+ 2
- 2
routers/web/repo/issue.go View File



// SearchIssues searches for issues across the repositories that the user has access to // SearchIssues searches for issues across the repositories that the user has access to
func SearchIssues(ctx *context.Context) { func SearchIssues(ctx *context.Context) {
before, since, err := context.GetQueryBeforeSince(ctx)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, err.Error()) ctx.Error(http.StatusUnprocessableEntity, err.Error())
return return


// ListIssues list the issues of a repository // ListIssues list the issues of a repository
func ListIssues(ctx *context.Context) { func ListIssues(ctx *context.Context) {
before, since, err := context.GetQueryBeforeSince(ctx)
before, since, err := context.GetQueryBeforeSince(ctx.Base)
if err != nil { if err != nil {
ctx.Error(http.StatusUnprocessableEntity, err.Error()) ctx.Error(http.StatusUnprocessableEntity, err.Error())
return return

+ 1
- 1
routers/web/repo/wiki.go View File

} }


if entry != nil { if entry != nil {
if err = common.ServeBlob(ctx, entry.Blob(), time.Time{}); err != nil {
if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), time.Time{}); err != nil {
ctx.ServerError("ServeBlob", err) ctx.ServerError("ServeBlob", err)
} }
return return

+ 34
- 18
services/auth/middleware.go View File

"strings" "strings"


"code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
// Auth is a middleware to authenticate a web user // Auth is a middleware to authenticate a web user
func Auth(authMethod Method) func(*context.Context) { func Auth(authMethod Method) func(*context.Context) {
return func(ctx *context.Context) { return func(ctx *context.Context) {
if err := authShared(ctx, authMethod); err != nil {
ar, err := authShared(ctx.Base, ctx.Session, authMethod)
if err != nil {
log.Error("Failed to verify user: %v", err) log.Error("Failed to verify user: %v", err)
ctx.Error(http.StatusUnauthorized, "Verify") ctx.Error(http.StatusUnauthorized, "Verify")
return return
} }
ctx.Doer = ar.Doer
ctx.IsSigned = ar.Doer != nil
ctx.IsBasicAuth = ar.IsBasicAuth
if ctx.Doer == nil { if ctx.Doer == nil {
// ensure the session uid is deleted // ensure the session uid is deleted
_ = ctx.Session.Delete("uid") _ = ctx.Session.Delete("uid")
// APIAuth is a middleware to authenticate an api user // APIAuth is a middleware to authenticate an api user
func APIAuth(authMethod Method) func(*context.APIContext) { func APIAuth(authMethod Method) func(*context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
if err := authShared(ctx.Context, authMethod); err != nil {
ar, err := authShared(ctx.Base, nil, authMethod)
if err != nil {
ctx.Error(http.StatusUnauthorized, "APIAuth", err) ctx.Error(http.StatusUnauthorized, "APIAuth", err)
return
} }
ctx.Doer = ar.Doer
ctx.IsSigned = ar.Doer != nil
ctx.IsBasicAuth = ar.IsBasicAuth
} }
} }


func authShared(ctx *context.Context, authMethod Method) error {
var err error
ctx.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
type authResult struct {
Doer *user_model.User
IsBasicAuth bool
}

func authShared(ctx *context.Base, sessionStore SessionStore, authMethod Method) (ar authResult, err error) {
ar.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, sessionStore)
if err != nil { if err != nil {
return err
return ar, err
} }
if ctx.Doer != nil {
if ctx.Locale.Language() != ctx.Doer.Language {
if ar.Doer != nil {
if ctx.Locale.Language() != ar.Doer.Language {
ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req) ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
} }
ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
ctx.IsSigned = true
ctx.Data["IsSigned"] = ctx.IsSigned
ctx.Data[middleware.ContextDataKeySignedUser] = ctx.Doer
ctx.Data["SignedUserID"] = ctx.Doer.ID
ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
ar.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
ctx.Data["IsSigned"] = true
ctx.Data[middleware.ContextDataKeySignedUser] = ar.Doer
ctx.Data["SignedUserID"] = ar.Doer.ID
ctx.Data["IsAdmin"] = ar.Doer.IsAdmin
} else { } else {
ctx.Data["SignedUserID"] = int64(0) ctx.Data["SignedUserID"] = int64(0)
} }
return nil
return ar, nil
} }


// VerifyOptions contains required or check options // VerifyOptions contains required or check options
DisableCSRF bool DisableCSRF bool
} }


// Checks authentication according to options
// VerifyAuthWithOptions checks authentication according to options
func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) { func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
return func(ctx *context.Context) { return func(ctx *context.Context) {
// Check prohibit login users. // Check prohibit login users.
} }
} }


// Checks authentication according to options
// VerifyAuthWithOptionsAPI checks authentication according to options
func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) { func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
// Check prohibit login users. // Check prohibit login users.
return return
} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm { } else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
ctx.Data["Title"] = ctx.Tr("auth.active_your_account") ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
ctx.HTML(http.StatusOK, "user/auth/activate")
ctx.JSON(http.StatusForbidden, map[string]string{
"message": "This account is not activated.",
})
return return
} }
if ctx.IsSigned && ctx.IsBasicAuth { if ctx.IsSigned && ctx.IsBasicAuth {

+ 9
- 7
services/context/user.go View File

// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes // UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
func UserAssignmentWeb() func(ctx *context.Context) { func UserAssignmentWeb() func(ctx *context.Context) {
return func(ctx *context.Context) { return func(ctx *context.Context) {
userAssignment(ctx, func(status int, title string, obj interface{}) {
errorFn := func(status int, title string, obj interface{}) {
err, ok := obj.(error) err, ok := obj.(error)
if !ok { if !ok {
err = fmt.Errorf("%s", obj) err = fmt.Errorf("%s", obj)
} else { } else {
ctx.ServerError(title, err) ctx.ServerError(title, err)
} }
})
}
ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, errorFn)
} }
} }


// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes // UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
func UserAssignmentAPI() func(ctx *context.APIContext) { func UserAssignmentAPI() func(ctx *context.APIContext) {
return func(ctx *context.APIContext) { return func(ctx *context.APIContext) {
userAssignment(ctx.Context, ctx.Error)
ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error)
} }
} }


func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) {
func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, interface{})) (contextUser *user_model.User) {
username := ctx.Params(":username") username := ctx.Params(":username")


if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(username) {
ctx.ContextUser = ctx.Doer
if doer != nil && doer.LowerName == strings.ToLower(username) {
contextUser = doer
} else { } else {
var err error var err error
ctx.ContextUser, err = user_model.GetUserByName(ctx, username)
contextUser, err = user_model.GetUserByName(ctx, username)
if err != nil { if err != nil {
if user_model.IsErrUserNotExist(err) { if user_model.IsErrUserNotExist(err) {
if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil { if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil {
} }
} }
} }
return contextUser
} }

+ 3
- 3
services/forms/admin.go View File



// Validate validates form fields // Validate validates form fields
func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates form fields // Validate validates form fields
func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates form fields // Validate validates form fields
func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 1
- 1
services/forms/auth_form.go View File



// Validate validates fields // Validate validates fields
func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 3
- 3
services/forms/org.go View File



// Validate validates the fields // Validate validates the fields
func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 1
- 1
services/forms/package_form.go View File

} }


func (f *PackageCleanupRuleForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *PackageCleanupRuleForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 2
- 2
services/forms/repo_branch_form.go View File



// Validate validates the fields // Validate validates the fields
func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 36
- 36
services/forms/repo_form.go View File



// Validate validates the fields // Validate validates the fields
func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) { if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
errs = append(errs, binding.Error{ errs = append(errs, binding.Error{
FieldNames: []string{"Channel"}, FieldNames: []string{"Channel"},


// Validate validates the fields // Validate validates the fields
func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, i, ctx.Locale) return middleware.Validate(errs, ctx.Data, i, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }


// Validate validates the fields // Validate validates the fields
// FIXME: use code generation to generate this method. // FIXME: use code generation to generate this method.
func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 1
- 1
services/forms/repo_tag_form.go View File



// Validate validates the fields // Validate validates the fields
func (f *ProtectTagForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *ProtectTagForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 1
- 1
services/forms/runner.go View File



// Validate validates form fields // Validate validates form fields
func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 24
- 24
services/forms/user_form.go View File



// Validate validates the fields // Validate validates the fields
func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *UpdateLanguageForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *UpdateLanguageForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the field // Validate validates the field
func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *WebauthnRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *WebauthnRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *PackageSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *PackageSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 3
- 3
services/forms/user_form_auth_openid.go View File



// Validate validates the fields // Validate validates the fields
func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }




// Validate validates the fields // Validate validates the fields
func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
ctx := context.GetValidateContext(req)
return middleware.Validate(errs, ctx.Data, f, ctx.Locale) return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
} }

+ 5
- 4
services/markup/processorhelper_test.go View File

import ( import (
"context" "context"
"net/http" "net/http"
"net/http/httptest"
"testing" "testing"


"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch)) assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))


// when using web context, use user.IsUserVisibleToViewer to check // when using web context, use user.IsUserVisibleToViewer to check
var err error
giteaCtx := &gitea_context.Context{}
giteaCtx.Req, err = http.NewRequest("GET", "/", nil)
req, err := http.NewRequest("GET", "/", nil)
assert.NoError(t, err) assert.NoError(t, err)
base, baseCleanUp := gitea_context.NewBaseContext(httptest.NewRecorder(), req)
defer baseCleanUp()
giteaCtx := &gitea_context.Context{Base: base}


giteaCtx.Doer = nil
assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic)) assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate)) assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))



Loading…
Cancel
Save