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
"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 | |||||
} |
// 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 | |||||
} |
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 | |||||
} |
// 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 | |||||
} |
// 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) | |||||
} |
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) { |
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) | |||||
} |
// 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) | |||||
} |
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 { |
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) | ||||
}) | }) | ||||
} | } |
"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) | ||||
}) | }) | ||||
} | } |
} | } | ||||
// 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 { |
// 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 | ||||
} | } |
) | ) | ||||
// 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 |
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) { |
} | } | ||||
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) | |||||
} |
"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 |
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 |
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{}, |
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 { |
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 |
"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])) |
} | } | ||||
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 |
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) { |
"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{ |
// "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 |
// "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 |
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 | ||||
} | } |
return | return | ||||
} | } | ||||
if ctx.HasError() { | |||||
if ctx.HasAPIError() { | |||||
ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) | ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg()) | ||||
return | return | ||||
} | } |
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{ |
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) |
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) | ||||
} | } |
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 | ||||
} | } |
) | ) | ||||
// 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" { |
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))) | |||||
}) | }) | ||||
}) | }) | ||||
) | ) | ||||
// 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) | ||||
} | } |
// 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 |
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 | ||||
} | } |
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) | |||||
} | } |
} | } | ||||
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 |
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) | ||||
} | } | ||||
} | } |
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 |
// 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 |
} | } | ||||
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 |
"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 { |
// 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 | |||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
} | } | ||||
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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
// 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) | ||||
} | } |
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)) | ||||