summaryrefslogtreecommitdiffstats
path: root/modules/context/context.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2021-01-26 23:36:53 +0800
committerGitHub <noreply@github.com>2021-01-26 16:36:53 +0100
commit6433ba0ec3dfde67f45267aa12bd713c4a44c740 (patch)
tree8813388f7e58ff23ad24af9ccbdb5f0350cb3a09 /modules/context/context.go
parent3adbbb4255c42cde04d59b6ebf5ead7e3edda3e7 (diff)
downloadgitea-6433ba0ec3dfde67f45267aa12bd713c4a44c740.tar.gz
gitea-6433ba0ec3dfde67f45267aa12bd713c4a44c740.zip
Move macaron to chi (#14293)
Use [chi](https://github.com/go-chi/chi) instead of the forked [macaron](https://gitea.com/macaron/macaron). Since macaron and chi have conflicts with session share, this big PR becomes a have-to thing. According my previous idea, we can replace macaron step by step but I'm wrong. :( Below is a list of big changes on this PR. - [x] Define `context.ResponseWriter` interface with an implementation `context.Response`. - [x] Use chi instead of macaron, and also a customize `Route` to wrap chi so that the router usage is similar as before. - [x] Create different routers for `web`, `api`, `internal` and `install` so that the codes will be more clear and no magic . - [x] Use https://github.com/unrolled/render instead of macaron's internal render - [x] Use https://github.com/NYTimes/gziphandler instead of https://gitea.com/macaron/gzip - [x] Use https://gitea.com/go-chi/session which is a modified version of https://gitea.com/macaron/session and removed `nodb` support since it will not be maintained. **BREAK** - [x] Use https://gitea.com/go-chi/captcha which is a modified version of https://gitea.com/macaron/captcha - [x] Use https://gitea.com/go-chi/cache which is a modified version of https://gitea.com/macaron/cache - [x] Use https://gitea.com/go-chi/binding which is a modified version of https://gitea.com/macaron/binding - [x] Use https://github.com/go-chi/cors instead of https://gitea.com/macaron/cors - [x] Dropped https://gitea.com/macaron/i18n and make a new one in `code.gitea.io/gitea/modules/translation` - [x] Move validation form structs from `code.gitea.io/gitea/modules/auth` to `code.gitea.io/gitea/modules/forms` to avoid dependency cycle. - [x] Removed macaron log service because it's not need any more. **BREAK** - [x] All form structs have to be get by `web.GetForm(ctx)` in the route function but not as a function parameter on routes definition. - [x] Move Git HTTP protocol implementation to use routers directly. - [x] Fix the problem that chi routes don't support trailing slash but macaron did. - [x] `/api/v1/swagger` now will be redirect to `/api/swagger` but not render directly so that `APIContext` will not create a html render. Notices: - Chi router don't support request with trailing slash - Integration test `TestUserHeatmap` maybe mysql version related. It's failed on my macOS(mysql 5.7.29 installed via brew) but succeed on CI. Co-authored-by: 6543 <6543@obermui.de>
Diffstat (limited to 'modules/context/context.go')
-rw-r--r--modules/context/context.go559
1 files changed, 447 insertions, 112 deletions
diff --git a/modules/context/context.go b/modules/context/context.go
index e4121649ae..630129b8c1 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -6,37 +6,55 @@
package context
import (
+ "context"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
"html"
"html/template"
"io"
"net/http"
"net/url"
"path"
+ "strconv"
"strings"
"time"
"code.gitea.io/gitea/models"
- "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/auth/sso"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/middlewares"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
+ "code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
- "gitea.com/macaron/cache"
- "gitea.com/macaron/csrf"
- "gitea.com/macaron/i18n"
- "gitea.com/macaron/macaron"
- "gitea.com/macaron/session"
+ "gitea.com/go-chi/cache"
+ "gitea.com/go-chi/session"
+ "github.com/go-chi/chi"
"github.com/unknwon/com"
+ "github.com/unknwon/i18n"
+ "github.com/unrolled/render"
+ "golang.org/x/crypto/pbkdf2"
)
+// Render represents a template render
+type Render interface {
+ TemplateLookup(tmpl string) *template.Template
+ HTML(w io.Writer, status int, name string, binding interface{}, htmlOpt ...render.HTMLOptions) error
+}
+
// Context represents context of a request.
type Context struct {
- *macaron.Context
+ Resp ResponseWriter
+ Req *http.Request
+ Data map[string]interface{}
+ Render Render
+ translation.Locale
Cache cache.Cache
- csrf csrf.CSRF
- Flash *session.Flash
+ csrf CSRF
+ Flash *middlewares.Flash
Session session.Store
Link string // current request URL
@@ -163,13 +181,22 @@ func (ctx *Context) RedirectToFirst(location ...string) {
// HTML calls Context.HTML and converts template name to string.
func (ctx *Context) HTML(status int, name base.TplName) {
log.Debug("Template: %s", name)
- ctx.Context.HTML(status, string(name))
+ if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil {
+ ctx.ServerError("Render failed", err)
+ }
+}
+
+// HTMLString render content to a string but not http.ResponseWriter
+func (ctx *Context) HTMLString(name string, data interface{}) (string, error) {
+ var buf strings.Builder
+ err := ctx.Render.HTML(&buf, 200, string(name), data)
+ return buf.String(), err
}
// RenderWithErr used for page has form validation but need to prompt error to users.
func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) {
if form != nil {
- auth.AssignForm(form, ctx.Data)
+ middlewares.AssignForm(form, ctx.Data)
}
ctx.Flash.ErrorMsg = msg
ctx.Data["Flash"] = ctx.Flash
@@ -184,7 +211,7 @@ func (ctx *Context) NotFound(title string, err error) {
func (ctx *Context) notFoundInternal(title string, err error) {
if err != nil {
log.ErrorWithSkip(2, "%s: %v", title, err)
- if macaron.Env != macaron.PROD {
+ if !setting.IsProd() {
ctx.Data["ErrorMsg"] = err
}
}
@@ -203,7 +230,7 @@ func (ctx *Context) ServerError(title string, err error) {
func (ctx *Context) serverErrorInternal(title string, err error) {
if err != nil {
log.ErrorWithSkip(2, "%s: %v", title, err)
- if macaron.Env != macaron.PROD {
+ if !setting.IsProd() {
ctx.Data["ErrorMsg"] = err
}
}
@@ -224,6 +251,44 @@ func (ctx *Context) NotFoundOrServerError(title string, errck func(error) bool,
ctx.serverErrorInternal(title, err)
}
+// Header returns a header
+func (ctx *Context) Header() http.Header {
+ return ctx.Resp.Header()
+}
+
+// FIXME: We should differ Query and Form, currently we just use form as query
+// Currently to be compatible with macaron, we keep it.
+
+// Query returns request form as string with default
+func (ctx *Context) Query(key string, defaults ...string) string {
+ return (*Forms)(ctx.Req).MustString(key, defaults...)
+}
+
+// QueryTrim returns request form as string with default and trimmed spaces
+func (ctx *Context) QueryTrim(key string, defaults ...string) string {
+ return (*Forms)(ctx.Req).MustTrimmed(key, defaults...)
+}
+
+// QueryStrings returns request form as strings with default
+func (ctx *Context) QueryStrings(key string, defaults ...[]string) []string {
+ return (*Forms)(ctx.Req).MustStrings(key, defaults...)
+}
+
+// QueryInt returns request form as int with default
+func (ctx *Context) QueryInt(key string, defaults ...int) int {
+ return (*Forms)(ctx.Req).MustInt(key, defaults...)
+}
+
+// QueryInt64 returns request form as int64 with default
+func (ctx *Context) QueryInt64(key string, defaults ...int64) int64 {
+ return (*Forms)(ctx.Req).MustInt64(key, defaults...)
+}
+
+// QueryBool returns request form as bool with default
+func (ctx *Context) QueryBool(key string, defaults ...bool) bool {
+ return (*Forms)(ctx.Req).MustBool(key, defaults...)
+}
+
// HandleText handles HTTP status code
func (ctx *Context) HandleText(status int, title string) {
if (status/100 == 4) || (status/100 == 5) {
@@ -249,66 +314,324 @@ func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interfa
ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
ctx.Resp.Header().Set("Pragma", "public")
ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
- http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r)
+ http.ServeContent(ctx.Resp, ctx.Req, name, modtime, r)
+}
+
+// PlainText render content as plain text
+func (ctx *Context) PlainText(status int, bs []byte) {
+ ctx.Resp.WriteHeader(status)
+ ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf8")
+ if _, err := ctx.Resp.Write(bs); err != nil {
+ ctx.ServerError("Render JSON failed", err)
+ }
+}
+
+// ServeFile serves given file to response.
+func (ctx *Context) ServeFile(file string, names ...string) {
+ var name string
+ if len(names) > 0 {
+ name = names[0]
+ } else {
+ name = path.Base(file)
+ }
+ ctx.Resp.Header().Set("Content-Description", "File Transfer")
+ ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
+ ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name)
+ ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary")
+ ctx.Resp.Header().Set("Expires", "0")
+ ctx.Resp.Header().Set("Cache-Control", "must-revalidate")
+ ctx.Resp.Header().Set("Pragma", "public")
+ http.ServeFile(ctx.Resp, ctx.Req, file)
+}
+
+// Error returned an error to web browser
+func (ctx *Context) Error(status int, contents ...string) {
+ var 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.WriteHeader(status)
+ ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf8")
+ if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
+ ctx.ServerError("Render JSON failed", err)
+ }
+}
+
+// Redirect redirect the request
+func (ctx *Context) Redirect(location string, status ...int) {
+ code := http.StatusFound
+ if len(status) == 1 {
+ code = status[0]
+ }
+
+ http.Redirect(ctx.Resp, ctx.Req, location, code)
+}
+
+// SetCookie set cookies to web browser
+func (ctx *Context) SetCookie(name string, value string, others ...interface{}) {
+ middlewares.SetCookie(ctx.Resp, name, value, others...)
+}
+
+// GetCookie returns given cookie value from request header.
+func (ctx *Context) GetCookie(name string) string {
+ return middlewares.GetCookie(ctx.Req, name)
+}
+
+// GetSuperSecureCookie returns given cookie value from request header with secret string.
+func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
+ val := ctx.GetCookie(name)
+ if val == "" {
+ return "", false
+ }
+
+ text, err := hex.DecodeString(val)
+ if err != nil {
+ return "", false
+ }
+
+ key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
+ text, err = com.AESGCMDecrypt(key, text)
+ return string(text), err == nil
+}
+
+// SetSuperSecureCookie sets given cookie value to response header with secret string.
+func (ctx *Context) SetSuperSecureCookie(secret, name, value string, others ...interface{}) {
+ key := pbkdf2.Key([]byte(secret), []byte(secret), 1000, 16, sha256.New)
+ text, err := com.AESGCMEncrypt(key, []byte(value))
+ if err != nil {
+ panic("error encrypting cookie: " + err.Error())
+ }
+
+ ctx.SetCookie(name, hex.EncodeToString(text), others...)
+}
+
+// GetCookieInt returns cookie result in int type.
+func (ctx *Context) GetCookieInt(name string) int {
+ r, _ := strconv.Atoi(ctx.GetCookie(name))
+ return r
+}
+
+// GetCookieInt64 returns cookie result in int64 type.
+func (ctx *Context) GetCookieInt64(name string) int64 {
+ r, _ := strconv.ParseInt(ctx.GetCookie(name), 10, 64)
+ return r
+}
+
+// GetCookieFloat64 returns cookie result in float64 type.
+func (ctx *Context) GetCookieFloat64(name string) float64 {
+ v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64)
+ return v
+}
+
+// RemoteAddr returns the client machie 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.Req.Context())
+ chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
+}
+
+// Write writes data to webbrowser
+func (ctx *Context) Write(bs []byte) (int, error) {
+ return ctx.Resp.Write(bs)
+}
+
+// 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)
+}
+
+// Handler represents a custom handler
+type Handler func(*Context)
+
+// enumerate all content
+var (
+ contextKey interface{} = "default_context"
+)
+
+// 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))
+}
+
+// GetContext retrieves install context from request
+func GetContext(req *http.Request) *Context {
+ return req.Context().Value(contextKey).(*Context)
+}
+
+func getCsrfOpts() CsrfOptions {
+ return CsrfOptions{
+ Secret: setting.SecretKey,
+ Cookie: setting.CSRFCookieName,
+ SetCookie: true,
+ Secure: setting.SessionConfig.Secure,
+ CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
+ Header: "X-Csrf-Token",
+ CookieDomain: setting.SessionConfig.Domain,
+ CookiePath: setting.SessionConfig.CookiePath,
+ }
}
// Contexter initializes a classic context for a request.
-func Contexter() macaron.Handler {
- return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) {
- ctx := &Context{
- Context: c,
- Cache: cache,
- csrf: x,
- Flash: f,
- Session: sess,
- Link: setting.AppSubURL + strings.TrimSuffix(c.Req.URL.EscapedPath(), "/"),
- Repo: &Repository{
- PullRequest: &PullRequest{},
- },
- Org: &Organization{},
+func Contexter() func(next http.Handler) http.Handler {
+ rnd := templates.HTMLRenderer()
+
+ var c cache.Cache
+ var err error
+ if setting.CacheService.Enabled {
+ c, err = cache.NewCacher(cache.Options{
+ Adapter: setting.CacheService.Adapter,
+ AdapterConfig: setting.CacheService.Conn,
+ Interval: setting.CacheService.Interval,
+ })
+ if err != nil {
+ panic(err)
}
- ctx.Data["Language"] = ctx.Locale.Language()
- c.Data["Link"] = ctx.Link
- ctx.Data["CurrentURL"] = setting.AppSubURL + c.Req.URL.RequestURI()
- ctx.Data["PageStartTime"] = time.Now()
- // Quick responses appropriate go-get meta with status 200
- // regardless of if user have access to the repository,
- // or the repository does not exist at all.
- // This is particular a workaround for "go get" command which does not respect
- // .netrc file.
- if ctx.Query("go-get") == "1" {
- ownerName := c.Params(":username")
- repoName := c.Params(":reponame")
- trimmedRepoName := strings.TrimSuffix(repoName, ".git")
-
- if ownerName == "" || trimmedRepoName == "" {
- _, _ = c.Write([]byte(`<!doctype html>
+ }
+
+ var csrfOpts = getCsrfOpts()
+ //var flashEncryptionKey, _ = NewSecret()
+
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+ var locale = middlewares.Locale(resp, req)
+ var startTime = time.Now()
+ var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
+ var ctx = Context{
+ Resp: NewResponse(resp),
+ Cache: c,
+ Locale: locale,
+ Link: link,
+ Render: rnd,
+ Session: session.GetSession(req),
+ Repo: &Repository{
+ PullRequest: &PullRequest{},
+ },
+ Org: &Organization{},
+ Data: map[string]interface{}{
+ "CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
+ "PageStartTime": startTime,
+ "TmplLoadTimes": func() string {
+ return time.Since(startTime).String()
+ },
+ "Link": link,
+ },
+ }
+
+ ctx.Req = WithContext(req, &ctx)
+ ctx.csrf = Csrfer(csrfOpts, &ctx)
+
+ // Get flash.
+ flashCookie := ctx.GetCookie("macaron_flash")
+ vals, _ := url.ParseQuery(flashCookie)
+ if len(vals) > 0 {
+ f := &middlewares.Flash{
+ DataStore: &ctx,
+ Values: vals,
+ ErrorMsg: vals.Get("error"),
+ SuccessMsg: vals.Get("success"),
+ InfoMsg: vals.Get("info"),
+ WarningMsg: vals.Get("warning"),
+ }
+ ctx.Data["Flash"] = f
+ }
+
+ f := &middlewares.Flash{
+ DataStore: &ctx,
+ Values: url.Values{},
+ ErrorMsg: "",
+ WarningMsg: "",
+ InfoMsg: "",
+ SuccessMsg: "",
+ }
+ ctx.Resp.Before(func(resp ResponseWriter) {
+ if flash := f.Encode(); len(flash) > 0 {
+ if err == nil {
+ middlewares.SetCookie(resp, "macaron_flash", flash, 0,
+ setting.SessionConfig.CookiePath,
+ middlewares.Domain(setting.SessionConfig.Domain),
+ middlewares.HTTPOnly(true),
+ middlewares.Secure(setting.SessionConfig.Secure),
+ //middlewares.SameSite(opt.SameSite), FIXME: we need a samesite config
+ )
+ return
+ }
+ }
+
+ ctx.SetCookie("macaron_flash", "", -1,
+ setting.SessionConfig.CookiePath,
+ middlewares.Domain(setting.SessionConfig.Domain),
+ middlewares.HTTPOnly(true),
+ middlewares.Secure(setting.SessionConfig.Secure),
+ //middlewares.SameSite(), FIXME: we need a samesite config
+ )
+ })
+
+ ctx.Flash = f
+
+ // Quick responses appropriate go-get meta with status 200
+ // regardless of if user have access to the repository,
+ // or the repository does not exist at all.
+ // This is particular a workaround for "go get" command which does not respect
+ // .netrc file.
+ if ctx.Query("go-get") == "1" {
+ ownerName := ctx.Params(":username")
+ repoName := ctx.Params(":reponame")
+ trimmedRepoName := strings.TrimSuffix(repoName, ".git")
+
+ if ownerName == "" || trimmedRepoName == "" {
+ _, _ = ctx.Write([]byte(`<!doctype html>
<html>
<body>
invalid import path
</body>
</html>
`))
- c.WriteHeader(400)
- return
- }
- branchName := "master"
-
- repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
- if err == nil && len(repo.DefaultBranch) > 0 {
- branchName = repo.DefaultBranch
- }
- prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
-
- appURL, _ := url.Parse(setting.AppURL)
-
- insecure := ""
- if appURL.Scheme == string(setting.HTTP) {
- insecure = "--insecure "
- }
- c.Header().Set("Content-Type", "text/html")
- c.WriteHeader(http.StatusOK)
- _, _ = c.Write([]byte(com.Expand(`<!doctype html>
+ ctx.Status(400)
+ return
+ }
+ branchName := "master"
+
+ repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
+ if err == nil && len(repo.DefaultBranch) > 0 {
+ branchName = repo.DefaultBranch
+ }
+ prefix := setting.AppURL + path.Join(url.PathEscape(ownerName), url.PathEscape(repoName), "src", "branch", util.PathEscapeSegments(branchName))
+
+ appURL, _ := url.Parse(setting.AppURL)
+
+ insecure := ""
+ if appURL.Scheme == string(setting.HTTP) {
+ insecure = "--insecure "
+ }
+ ctx.Header().Set("Content-Type", "text/html")
+ ctx.Status(http.StatusOK)
+ _, _ = ctx.Write([]byte(com.Expand(`<!doctype html>
<html>
<head>
<meta name="go-import" content="{GoGetImport} git {CloneLink}">
@@ -319,60 +642,72 @@ func Contexter() macaron.Handler {
</body>
</html>
`, map[string]string{
- "GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName),
- "CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName),
- "GoDocDirectory": prefix + "{/dir}",
- "GoDocFile": prefix + "{/dir}/{file}#L{line}",
- "Insecure": insecure,
- })))
- return
- }
-
- // Get user from session if logged in.
- ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req.Request, c.Resp, ctx, ctx.Session)
-
- if ctx.User != nil {
- ctx.IsSigned = true
- ctx.Data["IsSigned"] = ctx.IsSigned
- ctx.Data["SignedUser"] = ctx.User
- ctx.Data["SignedUserID"] = ctx.User.ID
- ctx.Data["SignedUserName"] = ctx.User.Name
- ctx.Data["IsAdmin"] = ctx.User.IsAdmin
- } else {
- ctx.Data["SignedUserID"] = int64(0)
- ctx.Data["SignedUserName"] = ""
- }
-
- // 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 err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
- ctx.ServerError("ParseMultipartForm", err)
+ "GoGetImport": ComposeGoGetImport(ownerName, trimmedRepoName),
+ "CloneLink": models.ComposeHTTPSCloneURL(ownerName, repoName),
+ "GoDocDirectory": prefix + "{/dir}",
+ "GoDocFile": prefix + "{/dir}/{file}#L{line}",
+ "Insecure": insecure,
+ })))
return
}
- }
-
- ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
-
- ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken())
- ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
- log.Debug("Session ID: %s", sess.ID())
- log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
-
- ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
- ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
- ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
- ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
- ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage
- ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
- ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
+ // 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 err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
+ ctx.ServerError("ParseMultipartForm", err)
+ return
+ }
+ }
- ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
- ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
- ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
+ // Get user from session if logged in.
+ ctx.User, ctx.IsBasicAuth = sso.SignedInUser(ctx.Req, ctx.Resp, &ctx, ctx.Session)
+
+ if ctx.User != nil {
+ ctx.IsSigned = true
+ ctx.Data["IsSigned"] = ctx.IsSigned
+ ctx.Data["SignedUser"] = ctx.User
+ ctx.Data["SignedUserID"] = ctx.User.ID
+ ctx.Data["SignedUserName"] = ctx.User.Name
+ ctx.Data["IsAdmin"] = ctx.User.IsAdmin
+ } else {
+ ctx.Data["SignedUserID"] = int64(0)
+ ctx.Data["SignedUserName"] = ""
+ }
- ctx.Data["ManifestData"] = setting.ManifestData
+ ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
+
+ ctx.Data["CsrfToken"] = html.EscapeString(ctx.csrf.GetToken())
+ ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
+ log.Debug("Session ID: %s", ctx.Session.ID())
+ log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
+
+ ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
+ ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
+ ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
+
+ ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton
+ ctx.Data["ShowMilestonesDashboardPage"] = setting.Service.ShowMilestonesDashboardPage
+ ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding
+ ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion
+
+ ctx.Data["EnableSwagger"] = setting.API.EnableSwagger
+ ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
+ ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
+
+ ctx.Data["ManifestData"] = setting.ManifestData
+
+ ctx.Data["i18n"] = locale
+ ctx.Data["Tr"] = i18n.Tr
+ ctx.Data["Lang"] = locale.Language()
+ ctx.Data["AllLangs"] = translation.AllLangs()
+ for _, lang := range translation.AllLangs() {
+ if lang.Lang == locale.Language() {
+ ctx.Data["LangName"] = lang.Name
+ break
+ }
+ }
- c.Map(ctx)
+ next.ServeHTTP(ctx.Resp, ctx.Req)
+ })
}
}