Skip to content

Commit 54170e9

Browse files
committed
refactor
1 parent 2b749af commit 54170e9

File tree

18 files changed

+141
-327
lines changed

18 files changed

+141
-327
lines changed

modules/context/auth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
6767
}
6868

6969
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
70-
ctx.csrf.Validate(ctx)
70+
ctx.Csrf.Validate(ctx)
7171
if ctx.Written() {
7272
return
7373
}
@@ -89,7 +89,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
8989

9090
// Redirect to log in page if auto-signin info is provided and has not signed in.
9191
if !options.SignOutRequired && !ctx.IsSigned &&
92-
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
92+
len(ctx.GetSiteCookie(setting.CookieUserName)) > 0 {
9393
if ctx.Req.URL.Path != "/user/events" {
9494
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
9595
}

modules/context/context.go

Lines changed: 35 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ type Context struct {
6060
Render Render
6161
translation.Locale
6262
Cache cache.Cache
63-
csrf CSRFProtector
63+
Csrf CSRFProtector
6464
Flash *middleware.Flash
6565
Session session.Store
6666

@@ -478,38 +478,26 @@ func (ctx *Context) Redirect(location string, status ...int) {
478478
http.Redirect(ctx.Resp, ctx.Req, location, code)
479479
}
480480

481-
// SetCookie convenience function to set most cookies consistently
481+
// SetSiteCookie convenience function to set most cookies consistently
482482
// CSRF and a few others are the exception here
483-
func (ctx *Context) SetCookie(name, value string, expiry int) {
484-
middleware.SetCookie(ctx.Resp, name, value,
485-
expiry,
486-
setting.AppSubURL,
487-
setting.SessionConfig.Domain,
488-
setting.SessionConfig.Secure,
489-
true,
490-
middleware.SameSite(setting.SessionConfig.SameSite))
483+
func (ctx *Context) SetSiteCookie(name, value string, maxAge int) {
484+
middleware.SetSiteCookie(ctx.Resp, name, value, maxAge)
491485
}
492486

493-
// DeleteCookie convenience function to delete most cookies consistently
487+
// DeleteSiteCookie convenience function to delete most cookies consistently
494488
// CSRF and a few others are the exception here
495-
func (ctx *Context) DeleteCookie(name string) {
496-
middleware.SetCookie(ctx.Resp, name, "",
497-
-1,
498-
setting.AppSubURL,
499-
setting.SessionConfig.Domain,
500-
setting.SessionConfig.Secure,
501-
true,
502-
middleware.SameSite(setting.SessionConfig.SameSite))
489+
func (ctx *Context) DeleteSiteCookie(name string) {
490+
middleware.SetSiteCookie(ctx.Resp, name, "", -1)
503491
}
504492

505-
// GetCookie returns given cookie value from request header.
506-
func (ctx *Context) GetCookie(name string) string {
507-
return middleware.GetCookie(ctx.Req, name)
493+
// GetSiteCookie returns given cookie value from request header.
494+
func (ctx *Context) GetSiteCookie(name string) string {
495+
return middleware.GetSiteCookie(ctx.Req, name)
508496
}
509497

510498
// GetSuperSecureCookie returns given cookie value from request header with secret string.
511499
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
512-
val := ctx.GetCookie(name)
500+
val := ctx.GetSiteCookie(name)
513501
return ctx.CookieDecrypt(secret, val)
514502
}
515503

@@ -530,10 +518,9 @@ func (ctx *Context) CookieDecrypt(secret, val string) (string, bool) {
530518
}
531519

532520
// SetSuperSecureCookie sets given cookie value to response header with secret string.
533-
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, expiry int) {
521+
func (ctx *Context) SetSuperSecureCookie(secret, name, value string, maxAge int) {
534522
text := ctx.CookieEncrypt(secret, value)
535-
536-
ctx.SetCookie(name, text, expiry)
523+
ctx.SetSiteCookie(name, text, maxAge)
537524
}
538525

539526
// CookieEncrypt encrypts a given value using the provided secret
@@ -549,19 +536,19 @@ func (ctx *Context) CookieEncrypt(secret, value string) string {
549536

550537
// GetCookieInt returns cookie result in int type.
551538
func (ctx *Context) GetCookieInt(name string) int {
552-
r, _ := strconv.Atoi(ctx.GetCookie(name))
539+
r, _ := strconv.Atoi(ctx.GetSiteCookie(name))
553540
return r
554541
}
555542

556543
// GetCookieInt64 returns cookie result in int64 type.
557544
func (ctx *Context) GetCookieInt64(name string) int64 {
558-
r, _ := strconv.ParseInt(ctx.GetCookie(name), 10, 64)
545+
r, _ := strconv.ParseInt(ctx.GetSiteCookie(name), 10, 64)
559546
return r
560547
}
561548

562549
// GetCookieFloat64 returns cookie result in float64 type.
563550
func (ctx *Context) GetCookieFloat64(name string) float64 {
564-
v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64)
551+
v, _ := strconv.ParseFloat(ctx.GetSiteCookie(name), 64)
565552
return v
566553
}
567554

@@ -659,7 +646,10 @@ func WithContext(req *http.Request, ctx *Context) *http.Request {
659646

660647
// GetContext retrieves install context from request
661648
func GetContext(req *http.Request) *Context {
662-
return req.Context().Value(contextKey).(*Context)
649+
if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
650+
return ctx
651+
}
652+
return nil
663653
}
664654

665655
// GetContextUser returns context user
@@ -687,6 +677,8 @@ func getCsrfOpts() CsrfOptions {
687677
}
688678
}
689679

680+
const CookieNameFlash = "gitea_flash"
681+
690682
// Contexter initializes a classic context for a request.
691683
func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
692684
_, rnd := templates.HTMLRenderer(ctx)
@@ -726,54 +718,32 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
726718
ctx.Data["Context"] = &ctx
727719

728720
ctx.Req = WithContext(req, &ctx)
729-
ctx.csrf = PrepareCSRFProtector(csrfOpts, &ctx)
721+
ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
730722

731-
// Get flash.
732-
flashCookie := ctx.GetCookie("macaron_flash")
733-
vals, _ := url.ParseQuery(flashCookie)
734-
if len(vals) > 0 {
735-
f := &middleware.Flash{
723+
// Get the last flash message from cookie
724+
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
725+
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
726+
// store last Flash message into the template data, to render it
727+
ctx.Data["Flash"] = &middleware.Flash{
736728
DataStore: &ctx,
737729
Values: vals,
738730
ErrorMsg: vals.Get("error"),
739731
SuccessMsg: vals.Get("success"),
740732
InfoMsg: vals.Get("info"),
741733
WarningMsg: vals.Get("warning"),
742734
}
743-
ctx.Data["Flash"] = f
744735
}
745736

746-
f := &middleware.Flash{
747-
DataStore: &ctx,
748-
Values: url.Values{},
749-
ErrorMsg: "",
750-
WarningMsg: "",
751-
InfoMsg: "",
752-
SuccessMsg: "",
753-
}
737+
// prepare an empty Flash message for current request
738+
ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
754739
ctx.Resp.Before(func(resp ResponseWriter) {
755-
if flash := f.Encode(); len(flash) > 0 {
756-
middleware.SetCookie(resp, "macaron_flash", flash, 0,
757-
setting.SessionConfig.CookiePath,
758-
middleware.Domain(setting.SessionConfig.Domain),
759-
middleware.HTTPOnly(true),
760-
middleware.Secure(setting.SessionConfig.Secure),
761-
middleware.SameSite(setting.SessionConfig.SameSite),
762-
)
763-
return
740+
if val := ctx.Flash.Encode(); val != "" {
741+
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
742+
} else if lastFlashCookie != "" {
743+
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
764744
}
765-
766-
middleware.SetCookie(ctx.Resp, "macaron_flash", "", -1,
767-
setting.SessionConfig.CookiePath,
768-
middleware.Domain(setting.SessionConfig.Domain),
769-
middleware.HTTPOnly(true),
770-
middleware.Secure(setting.SessionConfig.Secure),
771-
middleware.SameSite(setting.SessionConfig.SameSite),
772-
)
773745
})
774746

775-
ctx.Flash = f
776-
777747
// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
778748
if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
779749
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 {
785755
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
786756
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
787757

788-
ctx.Data["CsrfToken"] = ctx.csrf.GetToken()
758+
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
789759
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
790760

791761
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these

modules/context/csrf.go

Lines changed: 37 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -42,37 +42,26 @@ type CSRFProtector interface {
4242
GetToken() string
4343
// Validate validates the token in http context.
4444
Validate(ctx *Context)
45+
// DeleteCookie deletes the cookie
46+
DeleteCookie(ctx *Context)
4547
}
4648

4749
type csrfProtector struct {
48-
// Header name value for setting and getting csrf token.
49-
Header string
50-
// Form name value for setting and getting csrf token.
51-
Form string
52-
// Cookie name value for setting and getting csrf token.
53-
Cookie string
54-
// Cookie domain
55-
CookieDomain string
56-
// Cookie path
57-
CookiePath string
58-
// Cookie HttpOnly flag value used for the csrf token.
59-
CookieHTTPOnly bool
50+
opt CsrfOptions
6051
// Token generated to pass via header, cookie, or hidden form value.
6152
Token string
6253
// This value must be unique per user.
6354
ID string
64-
// Secret used along with the unique id above to generate the Token.
65-
Secret string
6655
}
6756

6857
// GetHeaderName returns the name of the HTTP header for csrf token.
6958
func (c *csrfProtector) GetHeaderName() string {
70-
return c.Header
59+
return c.opt.Header
7160
}
7261

7362
// GetFormName returns the name of the form value for csrf token.
7463
func (c *csrfProtector) GetFormName() string {
75-
return c.Form
64+
return c.opt.Form
7665
}
7766

7867
// GetToken returns the current token. This is typically used
@@ -138,23 +127,32 @@ func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
138127
if opt.SessionKey == "" {
139128
opt.SessionKey = "uid"
140129
}
130+
if opt.CookieLifeTime == 0 {
131+
opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
132+
}
133+
141134
opt.oldSessionKey = "_old_" + opt.SessionKey
142135
return opt
143136
}
144137

138+
func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
139+
return &http.Cookie{
140+
Name: c.opt.Cookie,
141+
Value: value,
142+
Path: c.opt.CookiePath,
143+
Domain: c.opt.CookieDomain,
144+
MaxAge: c.opt.CookieLifeTime,
145+
Secure: c.opt.Secure,
146+
HttpOnly: c.opt.CookieHTTPOnly,
147+
SameSite: c.opt.SameSite,
148+
}
149+
}
150+
145151
// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
146152
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
147153
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
148154
opt = prepareDefaultCsrfOptions(opt)
149-
x := &csrfProtector{
150-
Secret: opt.Secret,
151-
Header: opt.Header,
152-
Form: opt.Form,
153-
Cookie: opt.Cookie,
154-
CookieDomain: opt.CookieDomain,
155-
CookiePath: opt.CookiePath,
156-
CookieHTTPOnly: opt.CookieHTTPOnly,
157-
}
155+
x := &csrfProtector{opt: opt}
158156

159157
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
160158
return x
@@ -175,7 +173,7 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
175173

176174
oldUID := ctx.Session.Get(opt.oldSessionKey)
177175
uidChanged := oldUID == nil || oldUID.(string) != x.ID
178-
cookieToken := ctx.GetCookie(opt.Cookie)
176+
cookieToken := ctx.GetSiteCookie(opt.Cookie)
179177

180178
needsNew := true
181179
if uidChanged {
@@ -193,21 +191,10 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
193191

194192
if needsNew {
195193
// FIXME: actionId.
196-
x.Token = GenerateCsrfToken(x.Secret, x.ID, "POST", time.Now())
194+
x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
197195
if opt.SetCookie {
198-
var expires interface{}
199-
if opt.CookieLifeTime == 0 {
200-
expires = time.Now().Add(CsrfTokenTimeout)
201-
}
202-
middleware.SetCookie(ctx.Resp, opt.Cookie, x.Token,
203-
opt.CookieLifeTime,
204-
opt.CookiePath,
205-
opt.CookieDomain,
206-
opt.Secure,
207-
opt.CookieHTTPOnly,
208-
expires,
209-
middleware.SameSite(opt.SameSite),
210-
)
196+
cookie := newCsrfCookie(x, x.Token)
197+
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
211198
}
212199
}
213200

@@ -218,8 +205,8 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
218205
}
219206

220207
func (c *csrfProtector) validateToken(ctx *Context, token string) {
221-
if !ValidCsrfToken(token, c.Secret, c.ID, "POST", time.Now()) {
222-
middleware.DeleteCSRFCookie(ctx.Resp)
208+
if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
209+
c.DeleteCookie(ctx)
223210
if middleware.IsAPIPath(ctx.Req) {
224211
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
225212
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
@@ -245,3 +232,11 @@ func (c *csrfProtector) Validate(ctx *Context) {
245232
}
246233
c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
247234
}
235+
236+
func (c *csrfProtector) DeleteCookie(ctx *Context) {
237+
if c.opt.SetCookie {
238+
cookie := newCsrfCookie(c, "")
239+
cookie.MaxAge = -1
240+
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
241+
}
242+
}

modules/setting/session.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var SessionConfig = struct {
2121
ProviderConfig string
2222
// Cookie name to save session ID. Default is "MacaronSession".
2323
CookieName string
24-
// Cookie path to store. Default is "/".
24+
// Cookie path to store. Default is "/". HINT: there was a bug, the old value doesn't have trailing slash, and could be empty "".
2525
CookiePath string
2626
// GC interval time in seconds. Default is 3600.
2727
Gclifetime int64
@@ -49,7 +49,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
4949
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
5050
}
5151
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
52-
SessionConfig.CookiePath = AppSubURL
52+
SessionConfig.CookiePath = AppSubURL + "/" // there was a bug, old code only set CookePath=AppSubURL, no trailing slash
5353
SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(false)
5454
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
5555
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)

0 commit comments

Comments
 (0)