1
0
Fork 0
forked from forgejo/forgejo

Refactor cookie (#24107)

Close #24062

At the beginning, I just wanted to fix the warning mentioned by #24062

But, the cookie code really doesn't look good to me, so clean up them.

Complete the TODO on `SetCookie`: 

> TODO: Copied from gitea.com/macaron/macaron and should be improved
after macaron removed.
This commit is contained in:
wxiaoguang 2023-04-14 03:45:33 +08:00 committed by GitHub
parent b7221bec34
commit 5b9557aef5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 141 additions and 328 deletions

View file

@ -45,6 +45,8 @@ import (
"golang.org/x/crypto/pbkdf2"
)
const CookieNameFlash = "gitea_flash"
// Render represents a template render
type Render interface {
TemplateLookup(tmpl string) (*template.Template, error)
@ -60,7 +62,7 @@ type Context struct {
Render Render
translation.Locale
Cache cache.Cache
csrf CSRFProtector
Csrf CSRFProtector
Flash *middleware.Flash
Session session.Store
@ -478,38 +480,26 @@ func (ctx *Context) Redirect(location string, status ...int) {
http.Redirect(ctx.Resp, ctx.Req, location, code)
}
// SetCookie convenience function to set most cookies consistently
// SetSiteCookie convenience function to set most cookies consistently
// CSRF and a few others are the exception here
func (ctx *Context) SetCookie(name, value string, expiry int) {
middleware.SetCookie(ctx.Resp, name, value,
expiry,
setting.AppSubURL,
setting.SessionConfig.Domain,
setting.SessionConfig.Secure,
true,
middleware.SameSite(setting.SessionConfig.SameSite))
func (ctx *Context) SetSiteCookie(name, value string, maxAge int) {
middleware.SetSiteCookie(ctx.Resp, name, value, maxAge)
}
// DeleteCookie convenience function to delete most cookies consistently
// DeleteSiteCookie convenience function to delete most cookies consistently
// CSRF and a few others are the exception here
func (ctx *Context) DeleteCookie(name string) {
middleware.SetCookie(ctx.Resp, name, "",
-1,
setting.AppSubURL,
setting.SessionConfig.Domain,
setting.SessionConfig.Secure,
true,
middleware.SameSite(setting.SessionConfig.SameSite))
func (ctx *Context) DeleteSiteCookie(name string) {
middleware.SetSiteCookie(ctx.Resp, name, "", -1)
}
// GetCookie returns given cookie value from request header.
func (ctx *Context) GetCookie(name string) string {
return middleware.GetCookie(ctx.Req, name)
// GetSiteCookie returns given cookie value from request header.
func (ctx *Context) GetSiteCookie(name string) string {
return middleware.GetSiteCookie(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)
val := ctx.GetSiteCookie(name)
return ctx.CookieDecrypt(secret, val)
}
@ -530,10 +520,9 @@ func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
}
// SetSuperSecureCookie sets given cookie value to response header with secret string.
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, expiry int) {
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, maxAge int) {
text := ctx.CookieEncrypt(secret, value)
ctx.SetCookie(name, text, expiry)
ctx.SetSiteCookie(name, text, maxAge)
}
// CookieEncrypt encrypts a given value using the provided secret
@ -549,19 +538,19 @@ func (ctx *Context) CookieEncrypt(secret, value string) string {
// GetCookieInt returns cookie result in int type.
func (ctx *Context) GetCookieInt(name string) int {
r, _ := strconv.Atoi(ctx.GetCookie(name))
r, _ := strconv.Atoi(ctx.GetSiteCookie(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)
r, _ := strconv.ParseInt(ctx.GetSiteCookie(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)
v, _ := strconv.ParseFloat(ctx.GetSiteCookie(name), 64)
return v
}
@ -659,7 +648,10 @@ func WithContext(req *http.Request, ctx *Context) *http.Request {
// GetContext retrieves install context from request
func GetContext(req *http.Request) *Context {
return req.Context().Value(contextKey).(*Context)
if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
return ctx
}
return nil
}
// GetContextUser returns context user
@ -726,13 +718,13 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
ctx.Data["Context"] = &ctx
ctx.Req = WithContext(req, &ctx)
ctx.csrf = PrepareCSRFProtector(csrfOpts, &ctx)
ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
// Get flash.
flashCookie := ctx.GetCookie("macaron_flash")
vals, _ := url.ParseQuery(flashCookie)
if len(vals) > 0 {
f := &middleware.Flash{
// Get the last flash message from cookie
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
// store last Flash message into the template data, to render it
ctx.Data["Flash"] = &middleware.Flash{
DataStore: &ctx,
Values: vals,
ErrorMsg: vals.Get("error"),
@ -740,40 +732,18 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
InfoMsg: vals.Get("info"),
WarningMsg: vals.Get("warning"),
}
ctx.Data["Flash"] = f
}
f := &middleware.Flash{
DataStore: &ctx,
Values: url.Values{},
ErrorMsg: "",
WarningMsg: "",
InfoMsg: "",
SuccessMsg: "",
}
// prepare an empty Flash message for current request
ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
ctx.Resp.Before(func(resp ResponseWriter) {
if flash := f.Encode(); len(flash) > 0 {
middleware.SetCookie(resp, "macaron_flash", flash, 0,
setting.SessionConfig.CookiePath,
middleware.Domain(setting.SessionConfig.Domain),
middleware.HTTPOnly(true),
middleware.Secure(setting.SessionConfig.Secure),
middleware.SameSite(setting.SessionConfig.SameSite),
)
return
if val := ctx.Flash.Encode(); val != "" {
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
} else if lastFlashCookie != "" {
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
}
middleware.SetCookie(ctx.Resp, "macaron_flash", "", -1,
setting.SessionConfig.CookiePath,
middleware.Domain(setting.SessionConfig.Domain),
middleware.HTTPOnly(true),
middleware.Secure(setting.SessionConfig.Secure),
middleware.SameSite(setting.SessionConfig.SameSite),
)
})
ctx.Flash = f
// 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
@ -785,7 +755,7 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
ctx.Data["CsrfToken"] = ctx.csrf.GetToken()
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these