forked from NYANDEV/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:
parent
b7221bec34
commit
5b9557aef5
18 changed files with 141 additions and 328 deletions
|
@ -67,7 +67,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
|
if !options.SignOutRequired && !options.DisableCSRF && ctx.Req.Method == "POST" {
|
||||||
ctx.csrf.Validate(ctx)
|
ctx.Csrf.Validate(ctx)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ func Toggle(options *ToggleOptions) func(ctx *Context) {
|
||||||
|
|
||||||
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
// Redirect to log in page if auto-signin info is provided and has not signed in.
|
||||||
if !options.SignOutRequired && !ctx.IsSigned &&
|
if !options.SignOutRequired && !ctx.IsSigned &&
|
||||||
len(ctx.GetCookie(setting.CookieUserName)) > 0 {
|
len(ctx.GetSiteCookie(setting.CookieUserName)) > 0 {
|
||||||
if ctx.Req.URL.Path != "/user/events" {
|
if ctx.Req.URL.Path != "/user/events" {
|
||||||
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
middleware.SetRedirectToCookie(ctx.Resp, setting.AppSubURL+ctx.Req.URL.RequestURI())
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ import (
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const CookieNameFlash = "gitea_flash"
|
||||||
|
|
||||||
// Render represents a template render
|
// Render represents a template render
|
||||||
type Render interface {
|
type Render interface {
|
||||||
TemplateLookup(tmpl string) (*template.Template, error)
|
TemplateLookup(tmpl string) (*template.Template, error)
|
||||||
|
@ -60,7 +62,7 @@ type Context struct {
|
||||||
Render Render
|
Render Render
|
||||||
translation.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
|
||||||
|
|
||||||
|
@ -478,38 +480,26 @@ func (ctx *Context) Redirect(location string, status ...int) {
|
||||||
http.Redirect(ctx.Resp, ctx.Req, location, code)
|
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
|
// CSRF and a few others are the exception here
|
||||||
func (ctx *Context) SetCookie(name, value string, expiry int) {
|
func (ctx *Context) SetSiteCookie(name, value string, maxAge int) {
|
||||||
middleware.SetCookie(ctx.Resp, name, value,
|
middleware.SetSiteCookie(ctx.Resp, name, value, maxAge)
|
||||||
expiry,
|
|
||||||
setting.AppSubURL,
|
|
||||||
setting.SessionConfig.Domain,
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
middleware.SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
// CSRF and a few others are the exception here
|
||||||
func (ctx *Context) DeleteCookie(name string) {
|
func (ctx *Context) DeleteSiteCookie(name string) {
|
||||||
middleware.SetCookie(ctx.Resp, name, "",
|
middleware.SetSiteCookie(ctx.Resp, name, "", -1)
|
||||||
-1,
|
|
||||||
setting.AppSubURL,
|
|
||||||
setting.SessionConfig.Domain,
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
middleware.SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCookie returns given cookie value from request header.
|
// GetSiteCookie returns given cookie value from request header.
|
||||||
func (ctx *Context) GetCookie(name string) string {
|
func (ctx *Context) GetSiteCookie(name string) string {
|
||||||
return middleware.GetCookie(ctx.Req, name)
|
return middleware.GetSiteCookie(ctx.Req, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSuperSecureCookie returns given cookie value from request header with secret string.
|
// GetSuperSecureCookie returns given cookie value from request header with secret string.
|
||||||
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
func (ctx *Context) GetSuperSecureCookie(secret, name string) (string, bool) {
|
||||||
val := ctx.GetCookie(name)
|
val := ctx.GetSiteCookie(name)
|
||||||
return ctx.CookieDecrypt(secret, val)
|
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.
|
// 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)
|
text := ctx.CookieEncrypt(secret, value)
|
||||||
|
ctx.SetSiteCookie(name, text, maxAge)
|
||||||
ctx.SetCookie(name, text, expiry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CookieEncrypt encrypts a given value using the provided secret
|
// 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.
|
// GetCookieInt returns cookie result in int type.
|
||||||
func (ctx *Context) GetCookieInt(name string) int {
|
func (ctx *Context) GetCookieInt(name string) int {
|
||||||
r, _ := strconv.Atoi(ctx.GetCookie(name))
|
r, _ := strconv.Atoi(ctx.GetSiteCookie(name))
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCookieInt64 returns cookie result in int64 type.
|
// GetCookieInt64 returns cookie result in int64 type.
|
||||||
func (ctx *Context) GetCookieInt64(name string) int64 {
|
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
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCookieFloat64 returns cookie result in float64 type.
|
// GetCookieFloat64 returns cookie result in float64 type.
|
||||||
func (ctx *Context) GetCookieFloat64(name string) float64 {
|
func (ctx *Context) GetCookieFloat64(name string) float64 {
|
||||||
v, _ := strconv.ParseFloat(ctx.GetCookie(name), 64)
|
v, _ := strconv.ParseFloat(ctx.GetSiteCookie(name), 64)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -659,7 +648,10 @@ func WithContext(req *http.Request, ctx *Context) *http.Request {
|
||||||
|
|
||||||
// GetContext retrieves install context from request
|
// GetContext retrieves install context from request
|
||||||
func GetContext(req *http.Request) *Context {
|
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
|
// GetContextUser returns context user
|
||||||
|
@ -726,13 +718,13 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
|
||||||
ctx.Data["Context"] = &ctx
|
ctx.Data["Context"] = &ctx
|
||||||
|
|
||||||
ctx.Req = WithContext(req, &ctx)
|
ctx.Req = WithContext(req, &ctx)
|
||||||
ctx.csrf = PrepareCSRFProtector(csrfOpts, &ctx)
|
ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
|
||||||
|
|
||||||
// Get flash.
|
// Get the last flash message from cookie
|
||||||
flashCookie := ctx.GetCookie("macaron_flash")
|
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
|
||||||
vals, _ := url.ParseQuery(flashCookie)
|
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
|
||||||
if len(vals) > 0 {
|
// store last Flash message into the template data, to render it
|
||||||
f := &middleware.Flash{
|
ctx.Data["Flash"] = &middleware.Flash{
|
||||||
DataStore: &ctx,
|
DataStore: &ctx,
|
||||||
Values: vals,
|
Values: vals,
|
||||||
ErrorMsg: vals.Get("error"),
|
ErrorMsg: vals.Get("error"),
|
||||||
|
@ -740,40 +732,18 @@ func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
|
||||||
InfoMsg: vals.Get("info"),
|
InfoMsg: vals.Get("info"),
|
||||||
WarningMsg: vals.Get("warning"),
|
WarningMsg: vals.Get("warning"),
|
||||||
}
|
}
|
||||||
ctx.Data["Flash"] = f
|
|
||||||
}
|
}
|
||||||
|
|
||||||
f := &middleware.Flash{
|
// prepare an empty Flash message for current request
|
||||||
DataStore: &ctx,
|
ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
|
||||||
Values: url.Values{},
|
|
||||||
ErrorMsg: "",
|
|
||||||
WarningMsg: "",
|
|
||||||
InfoMsg: "",
|
|
||||||
SuccessMsg: "",
|
|
||||||
}
|
|
||||||
ctx.Resp.Before(func(resp ResponseWriter) {
|
ctx.Resp.Before(func(resp ResponseWriter) {
|
||||||
if flash := f.Encode(); len(flash) > 0 {
|
if val := ctx.Flash.Encode(); val != "" {
|
||||||
middleware.SetCookie(resp, "macaron_flash", flash, 0,
|
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
|
||||||
setting.SessionConfig.CookiePath,
|
} else if lastFlashCookie != "" {
|
||||||
middleware.Domain(setting.SessionConfig.Domain),
|
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
|
||||||
middleware.HTTPOnly(true),
|
|
||||||
middleware.Secure(setting.SessionConfig.Secure),
|
|
||||||
middleware.SameSite(setting.SessionConfig.SameSite),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 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") {
|
||||||
if err := ctx.Req.ParseMultipartForm(setting.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
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")
|
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["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) + `">`)
|
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
|
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
|
||||||
|
|
|
@ -42,37 +42,26 @@ type CSRFProtector interface {
|
||||||
GetToken() string
|
GetToken() string
|
||||||
// Validate validates the token in http context.
|
// Validate validates the token in http context.
|
||||||
Validate(ctx *Context)
|
Validate(ctx *Context)
|
||||||
|
// DeleteCookie deletes the cookie
|
||||||
|
DeleteCookie(ctx *Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type csrfProtector struct {
|
type csrfProtector struct {
|
||||||
// Header name value for setting and getting csrf token.
|
opt CsrfOptions
|
||||||
Header string
|
|
||||||
// Form name value for setting and getting csrf token.
|
|
||||||
Form string
|
|
||||||
// Cookie name value for setting and getting csrf token.
|
|
||||||
Cookie string
|
|
||||||
// Cookie domain
|
|
||||||
CookieDomain string
|
|
||||||
// Cookie path
|
|
||||||
CookiePath string
|
|
||||||
// Cookie HttpOnly flag value used for the csrf token.
|
|
||||||
CookieHTTPOnly bool
|
|
||||||
// Token generated to pass via header, cookie, or hidden form value.
|
// Token generated to pass via header, cookie, or hidden form value.
|
||||||
Token string
|
Token string
|
||||||
// This value must be unique per user.
|
// This value must be unique per user.
|
||||||
ID string
|
ID string
|
||||||
// Secret used along with the unique id above to generate the Token.
|
|
||||||
Secret string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeaderName returns the name of the HTTP header for csrf token.
|
// GetHeaderName returns the name of the HTTP header for csrf token.
|
||||||
func (c *csrfProtector) GetHeaderName() string {
|
func (c *csrfProtector) GetHeaderName() string {
|
||||||
return c.Header
|
return c.opt.Header
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFormName returns the name of the form value for csrf token.
|
// GetFormName returns the name of the form value for csrf token.
|
||||||
func (c *csrfProtector) GetFormName() string {
|
func (c *csrfProtector) GetFormName() string {
|
||||||
return c.Form
|
return c.opt.Form
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken returns the current token. This is typically used
|
// GetToken returns the current token. This is typically used
|
||||||
|
@ -138,23 +127,32 @@ func prepareDefaultCsrfOptions(opt CsrfOptions) CsrfOptions {
|
||||||
if opt.SessionKey == "" {
|
if opt.SessionKey == "" {
|
||||||
opt.SessionKey = "uid"
|
opt.SessionKey = "uid"
|
||||||
}
|
}
|
||||||
|
if opt.CookieLifeTime == 0 {
|
||||||
|
opt.CookieLifeTime = int(CsrfTokenTimeout.Seconds())
|
||||||
|
}
|
||||||
|
|
||||||
opt.oldSessionKey = "_old_" + opt.SessionKey
|
opt.oldSessionKey = "_old_" + opt.SessionKey
|
||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newCsrfCookie(c *csrfProtector, value string) *http.Cookie {
|
||||||
|
return &http.Cookie{
|
||||||
|
Name: c.opt.Cookie,
|
||||||
|
Value: value,
|
||||||
|
Path: c.opt.CookiePath,
|
||||||
|
Domain: c.opt.CookieDomain,
|
||||||
|
MaxAge: c.opt.CookieLifeTime,
|
||||||
|
Secure: c.opt.Secure,
|
||||||
|
HttpOnly: c.opt.CookieHTTPOnly,
|
||||||
|
SameSite: c.opt.SameSite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
|
// PrepareCSRFProtector returns a CSRFProtector to be used for every request.
|
||||||
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
|
// Additionally, depending on options set, generated tokens will be sent via Header and/or Cookie.
|
||||||
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
|
func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
|
||||||
opt = prepareDefaultCsrfOptions(opt)
|
opt = prepareDefaultCsrfOptions(opt)
|
||||||
x := &csrfProtector{
|
x := &csrfProtector{opt: opt}
|
||||||
Secret: opt.Secret,
|
|
||||||
Header: opt.Header,
|
|
||||||
Form: opt.Form,
|
|
||||||
Cookie: opt.Cookie,
|
|
||||||
CookieDomain: opt.CookieDomain,
|
|
||||||
CookiePath: opt.CookiePath,
|
|
||||||
CookieHTTPOnly: opt.CookieHTTPOnly,
|
|
||||||
}
|
|
||||||
|
|
||||||
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
|
if opt.Origin && len(ctx.Req.Header.Get("Origin")) > 0 {
|
||||||
return x
|
return x
|
||||||
|
@ -175,7 +173,7 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
|
||||||
|
|
||||||
oldUID := ctx.Session.Get(opt.oldSessionKey)
|
oldUID := ctx.Session.Get(opt.oldSessionKey)
|
||||||
uidChanged := oldUID == nil || oldUID.(string) != x.ID
|
uidChanged := oldUID == nil || oldUID.(string) != x.ID
|
||||||
cookieToken := ctx.GetCookie(opt.Cookie)
|
cookieToken := ctx.GetSiteCookie(opt.Cookie)
|
||||||
|
|
||||||
needsNew := true
|
needsNew := true
|
||||||
if uidChanged {
|
if uidChanged {
|
||||||
|
@ -193,21 +191,10 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
|
||||||
|
|
||||||
if needsNew {
|
if needsNew {
|
||||||
// FIXME: actionId.
|
// FIXME: actionId.
|
||||||
x.Token = GenerateCsrfToken(x.Secret, x.ID, "POST", time.Now())
|
x.Token = GenerateCsrfToken(x.opt.Secret, x.ID, "POST", time.Now())
|
||||||
if opt.SetCookie {
|
if opt.SetCookie {
|
||||||
var expires interface{}
|
cookie := newCsrfCookie(x, x.Token)
|
||||||
if opt.CookieLifeTime == 0 {
|
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
expires = time.Now().Add(CsrfTokenTimeout)
|
|
||||||
}
|
|
||||||
middleware.SetCookie(ctx.Resp, opt.Cookie, x.Token,
|
|
||||||
opt.CookieLifeTime,
|
|
||||||
opt.CookiePath,
|
|
||||||
opt.CookieDomain,
|
|
||||||
opt.Secure,
|
|
||||||
opt.CookieHTTPOnly,
|
|
||||||
expires,
|
|
||||||
middleware.SameSite(opt.SameSite),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,8 +205,8 @@ func PrepareCSRFProtector(opt CsrfOptions, ctx *Context) CSRFProtector {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *csrfProtector) validateToken(ctx *Context, token string) {
|
func (c *csrfProtector) validateToken(ctx *Context, token string) {
|
||||||
if !ValidCsrfToken(token, c.Secret, c.ID, "POST", time.Now()) {
|
if !ValidCsrfToken(token, c.opt.Secret, c.ID, "POST", time.Now()) {
|
||||||
middleware.DeleteCSRFCookie(ctx.Resp)
|
c.DeleteCookie(ctx)
|
||||||
if middleware.IsAPIPath(ctx.Req) {
|
if middleware.IsAPIPath(ctx.Req) {
|
||||||
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
|
// currently, there should be no access to the APIPath with CSRF token. because templates shouldn't use the `/api/` endpoints.
|
||||||
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
|
http.Error(ctx.Resp, "Invalid CSRF token.", http.StatusBadRequest)
|
||||||
|
@ -245,3 +232,11 @@ func (c *csrfProtector) Validate(ctx *Context) {
|
||||||
}
|
}
|
||||||
c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
|
c.validateToken(ctx, "") // no csrf token, use an empty token to respond error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *csrfProtector) DeleteCookie(ctx *Context) {
|
||||||
|
if c.opt.SetCookie {
|
||||||
|
cookie := newCsrfCookie(c, "")
|
||||||
|
cookie.MaxAge = -1
|
||||||
|
ctx.Resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ var SessionConfig = struct {
|
||||||
ProviderConfig string
|
ProviderConfig string
|
||||||
// Cookie name to save session ID. Default is "MacaronSession".
|
// Cookie name to save session ID. Default is "MacaronSession".
|
||||||
CookieName string
|
CookieName string
|
||||||
// Cookie path to store. Default is "/".
|
// Cookie path to store. Default is "/". HINT: there was a bug, the old value doesn't have trailing slash, and could be empty "".
|
||||||
CookiePath string
|
CookiePath string
|
||||||
// GC interval time in seconds. Default is 3600.
|
// GC interval time in seconds. Default is 3600.
|
||||||
Gclifetime int64
|
Gclifetime int64
|
||||||
|
@ -49,7 +49,7 @@ func loadSessionFrom(rootCfg ConfigProvider) {
|
||||||
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
SessionConfig.ProviderConfig = path.Join(AppWorkPath, SessionConfig.ProviderConfig)
|
||||||
}
|
}
|
||||||
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
|
SessionConfig.CookieName = sec.Key("COOKIE_NAME").MustString("i_like_gitea")
|
||||||
SessionConfig.CookiePath = AppSubURL
|
SessionConfig.CookiePath = AppSubURL + "/" // there was a bug, old code only set CookePath=AppSubURL, no trailing slash
|
||||||
SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(false)
|
SessionConfig.Secure = sec.Key("COOKIE_SECURE").MustBool(false)
|
||||||
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
|
SessionConfig.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(86400)
|
||||||
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)
|
SessionConfig.Maxlifetime = sec.Key("SESSION_LIFE_TIME").MustInt64(86400)
|
||||||
|
|
|
@ -7,184 +7,23 @@ package middleware
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"strings"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxAge sets the maximum age for a provided cookie
|
|
||||||
func MaxAge(maxAge int) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.MaxAge = maxAge
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Path sets the path for a provided cookie
|
|
||||||
func Path(path string) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.Path = path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Domain sets the domain for a provided cookie
|
|
||||||
func Domain(domain string) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.Domain = domain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secure sets the secure setting for a provided cookie
|
|
||||||
func Secure(secure bool) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.Secure = secure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPOnly sets the HttpOnly setting for a provided cookie
|
|
||||||
func HTTPOnly(httpOnly bool) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.HttpOnly = httpOnly
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expires sets the expires and rawexpires for a provided cookie
|
|
||||||
func Expires(expires time.Time) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.Expires = expires
|
|
||||||
c.RawExpires = expires.Format(time.UnixDate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SameSite sets the SameSite for a provided cookie
|
|
||||||
func SameSite(sameSite http.SameSite) func(*http.Cookie) {
|
|
||||||
return func(c *http.Cookie) {
|
|
||||||
c.SameSite = sameSite
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewCookie creates a cookie
|
|
||||||
func NewCookie(name, value string, maxAge int) *http.Cookie {
|
|
||||||
return &http.Cookie{
|
|
||||||
Name: name,
|
|
||||||
Value: value,
|
|
||||||
HttpOnly: true,
|
|
||||||
Path: setting.SessionConfig.CookiePath,
|
|
||||||
Domain: setting.SessionConfig.Domain,
|
|
||||||
MaxAge: maxAge,
|
|
||||||
Secure: setting.SessionConfig.Secure,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRedirectToCookie convenience function to set the RedirectTo cookie consistently
|
// SetRedirectToCookie convenience function to set the RedirectTo cookie consistently
|
||||||
func SetRedirectToCookie(resp http.ResponseWriter, value string) {
|
func SetRedirectToCookie(resp http.ResponseWriter, value string) {
|
||||||
SetCookie(resp, "redirect_to", value,
|
SetSiteCookie(resp, "redirect_to", value, 0)
|
||||||
0,
|
|
||||||
setting.AppSubURL,
|
|
||||||
"",
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRedirectToCookie convenience function to delete most cookies consistently
|
// DeleteRedirectToCookie convenience function to delete most cookies consistently
|
||||||
func DeleteRedirectToCookie(resp http.ResponseWriter) {
|
func DeleteRedirectToCookie(resp http.ResponseWriter) {
|
||||||
SetCookie(resp, "redirect_to", "",
|
SetSiteCookie(resp, "redirect_to", "", -1)
|
||||||
-1,
|
|
||||||
setting.AppSubURL,
|
|
||||||
"",
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCSRFCookie convenience function to delete SessionConfigPath cookies consistently
|
// GetSiteCookie returns given cookie value from request header.
|
||||||
func DeleteCSRFCookie(resp http.ResponseWriter) {
|
func GetSiteCookie(req *http.Request, name string) string {
|
||||||
SetCookie(resp, setting.CSRFCookieName, "",
|
|
||||||
-1,
|
|
||||||
setting.SessionConfig.CookiePath,
|
|
||||||
setting.SessionConfig.Domain) // FIXME: Do we need to set the Secure, httpOnly and SameSite values too?
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCookie set the cookies. (name, value, lifetime, path, domain, secure, httponly, expires, {sameSite, ...})
|
|
||||||
// TODO: Copied from gitea.com/macaron/macaron and should be improved after macaron removed.
|
|
||||||
func SetCookie(resp http.ResponseWriter, name, value string, others ...interface{}) {
|
|
||||||
cookie := http.Cookie{}
|
|
||||||
cookie.Name = name
|
|
||||||
cookie.Value = url.QueryEscape(value)
|
|
||||||
|
|
||||||
if len(others) > 0 {
|
|
||||||
switch v := others[0].(type) {
|
|
||||||
case int:
|
|
||||||
cookie.MaxAge = v
|
|
||||||
case int64:
|
|
||||||
cookie.MaxAge = int(v)
|
|
||||||
case int32:
|
|
||||||
cookie.MaxAge = int(v)
|
|
||||||
case func(*http.Cookie):
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cookie.Path = "/"
|
|
||||||
if len(others) > 1 {
|
|
||||||
if v, ok := others[1].(string); ok && len(v) > 0 {
|
|
||||||
cookie.Path = v
|
|
||||||
} else if v, ok := others[1].(func(*http.Cookie)); ok {
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(others) > 2 {
|
|
||||||
if v, ok := others[2].(string); ok && len(v) > 0 {
|
|
||||||
cookie.Domain = v
|
|
||||||
} else if v, ok := others[2].(func(*http.Cookie)); ok {
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(others) > 3 {
|
|
||||||
switch v := others[3].(type) {
|
|
||||||
case bool:
|
|
||||||
cookie.Secure = v
|
|
||||||
case func(*http.Cookie):
|
|
||||||
v(&cookie)
|
|
||||||
default:
|
|
||||||
if others[3] != nil {
|
|
||||||
cookie.Secure = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(others) > 4 {
|
|
||||||
if v, ok := others[4].(bool); ok && v {
|
|
||||||
cookie.HttpOnly = true
|
|
||||||
} else if v, ok := others[4].(func(*http.Cookie)); ok {
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(others) > 5 {
|
|
||||||
if v, ok := others[5].(time.Time); ok {
|
|
||||||
cookie.Expires = v
|
|
||||||
cookie.RawExpires = v.Format(time.UnixDate)
|
|
||||||
} else if v, ok := others[5].(func(*http.Cookie)); ok {
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(others) > 6 {
|
|
||||||
for _, other := range others[6:] {
|
|
||||||
if v, ok := other.(func(*http.Cookie)); ok {
|
|
||||||
v(&cookie)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resp.Header().Add("Set-Cookie", cookie.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCookie returns given cookie value from request header.
|
|
||||||
func GetCookie(req *http.Request, name string) string {
|
|
||||||
cookie, err := req.Cookie(name)
|
cookie, err := req.Cookie(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
|
@ -192,3 +31,24 @@ func GetCookie(req *http.Request, name string) string {
|
||||||
val, _ := url.QueryUnescape(cookie.Value)
|
val, _ := url.QueryUnescape(cookie.Value)
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSiteCookie returns given cookie value from request header.
|
||||||
|
func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) {
|
||||||
|
cookie := &http.Cookie{
|
||||||
|
Name: name,
|
||||||
|
Value: url.QueryEscape(value),
|
||||||
|
MaxAge: maxAge,
|
||||||
|
Path: setting.SessionConfig.CookiePath,
|
||||||
|
Domain: setting.SessionConfig.Domain,
|
||||||
|
Secure: setting.SessionConfig.Secure,
|
||||||
|
HttpOnly: true,
|
||||||
|
SameSite: setting.SessionConfig.SameSite,
|
||||||
|
}
|
||||||
|
resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
if maxAge < 0 {
|
||||||
|
// There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "".
|
||||||
|
// So we have to delete the cookie on path="" again, because some old code leaves cookies on path="".
|
||||||
|
cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/")
|
||||||
|
resp.Header().Add("Set-Cookie", cookie.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ package middleware
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
|
||||||
"code.gitea.io/gitea/modules/translation"
|
"code.gitea.io/gitea/modules/translation"
|
||||||
"code.gitea.io/gitea/modules/translation/i18n"
|
"code.gitea.io/gitea/modules/translation/i18n"
|
||||||
|
|
||||||
|
@ -49,23 +48,12 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLocaleCookie convenience function to set the locale cookie consistently
|
// SetLocaleCookie convenience function to set the locale cookie consistently
|
||||||
func SetLocaleCookie(resp http.ResponseWriter, lang string, expiry int) {
|
func SetLocaleCookie(resp http.ResponseWriter, lang string, maxAge int) {
|
||||||
SetCookie(resp, "lang", lang, expiry,
|
SetSiteCookie(resp, "lang", lang, maxAge)
|
||||||
setting.AppSubURL,
|
|
||||||
setting.SessionConfig.Domain,
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteLocaleCookie convenience function to delete the locale cookie consistently
|
// DeleteLocaleCookie convenience function to delete the locale cookie consistently
|
||||||
// Setting the lang cookie will trigger the middleware to reset the language to previous state.
|
// Setting the lang cookie will trigger the middleware to reset the language to previous state.
|
||||||
func DeleteLocaleCookie(resp http.ResponseWriter) {
|
func DeleteLocaleCookie(resp http.ResponseWriter) {
|
||||||
SetCookie(resp, "lang", "",
|
SetSiteCookie(resp, "lang", "", -1)
|
||||||
-1,
|
|
||||||
setting.AppSubURL,
|
|
||||||
setting.SessionConfig.Domain,
|
|
||||||
setting.SessionConfig.Secure,
|
|
||||||
true,
|
|
||||||
SameSite(setting.SessionConfig.SameSite))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -559,7 +559,7 @@ func SubmitInstall(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
days := 86400 * setting.LogInRememberDays
|
days := 86400 * setting.LogInRememberDays
|
||||||
ctx.SetCookie(setting.CookieUserName, u.Name, days)
|
ctx.SetSiteCookie(setting.CookieUserName, u.Name, days)
|
||||||
|
|
||||||
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
|
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
|
||||||
setting.CookieRememberName, u.Name, days)
|
setting.CookieRememberName, u.Name, days)
|
||||||
|
|
|
@ -49,7 +49,7 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
uname := ctx.GetCookie(setting.CookieUserName)
|
uname := ctx.GetSiteCookie(setting.CookieUserName)
|
||||||
if len(uname) == 0 {
|
if len(uname) == 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -58,8 +58,8 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if !isSucceed {
|
if !isSucceed {
|
||||||
log.Trace("auto-login cookie cleared: %s", uname)
|
log.Trace("auto-login cookie cleared: %s", uname)
|
||||||
ctx.DeleteCookie(setting.CookieUserName)
|
ctx.DeleteSiteCookie(setting.CookieUserName)
|
||||||
ctx.DeleteCookie(setting.CookieRememberName)
|
ctx.DeleteSiteCookie(setting.CookieRememberName)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ func AutoSignIn(ctx *context.Context) (bool, error) {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
middleware.DeleteCSRFCookie(ctx.Resp)
|
ctx.Csrf.DeleteCookie(ctx)
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ func checkAutoLogin(ctx *context.Context) bool {
|
||||||
if len(redirectTo) > 0 {
|
if len(redirectTo) > 0 {
|
||||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||||
} else {
|
} else {
|
||||||
redirectTo = ctx.GetCookie("redirect_to")
|
redirectTo = ctx.GetSiteCookie("redirect_to")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSucceed {
|
if isSucceed {
|
||||||
|
@ -291,7 +291,7 @@ func handleSignIn(ctx *context.Context, u *user_model.User, remember bool) {
|
||||||
func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string {
|
func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRedirect bool) string {
|
||||||
if remember {
|
if remember {
|
||||||
days := 86400 * setting.LogInRememberDays
|
days := 86400 * setting.LogInRememberDays
|
||||||
ctx.SetCookie(setting.CookieUserName, u.Name, days)
|
ctx.SetSiteCookie(setting.CookieUserName, u.Name, days)
|
||||||
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
|
ctx.SetSuperSecureCookie(base.EncodeMD5(u.Rands+u.Passwd),
|
||||||
setting.CookieRememberName, u.Name, days)
|
setting.CookieRememberName, u.Name, days)
|
||||||
}
|
}
|
||||||
|
@ -330,7 +330,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear whatever CSRF cookie has right now, force to generate a new one
|
// Clear whatever CSRF cookie has right now, force to generate a new one
|
||||||
middleware.DeleteCSRFCookie(ctx.Resp)
|
ctx.Csrf.DeleteCookie(ctx)
|
||||||
|
|
||||||
// Register last login
|
// Register last login
|
||||||
u.SetLastLogin()
|
u.SetLastLogin()
|
||||||
|
@ -339,7 +339,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe
|
||||||
return setting.AppSubURL + "/"
|
return setting.AppSubURL + "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
|
if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
|
||||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||||
if obeyRedirect {
|
if obeyRedirect {
|
||||||
ctx.RedirectToFirst(redirectTo)
|
ctx.RedirectToFirst(redirectTo)
|
||||||
|
@ -368,10 +368,9 @@ func getUserName(gothUser *goth.User) string {
|
||||||
func HandleSignOut(ctx *context.Context) {
|
func HandleSignOut(ctx *context.Context) {
|
||||||
_ = ctx.Session.Flush()
|
_ = ctx.Session.Flush()
|
||||||
_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
|
_ = ctx.Session.Destroy(ctx.Resp, ctx.Req)
|
||||||
ctx.DeleteCookie(setting.CookieUserName)
|
ctx.DeleteSiteCookie(setting.CookieUserName)
|
||||||
ctx.DeleteCookie(setting.CookieRememberName)
|
ctx.DeleteSiteCookie(setting.CookieRememberName)
|
||||||
middleware.DeleteCSRFCookie(ctx.Resp)
|
ctx.Csrf.DeleteCookie(ctx)
|
||||||
middleware.DeleteLocaleCookie(ctx.Resp)
|
|
||||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1112,7 +1112,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear whatever CSRF cookie has right now, force to generate a new one
|
// Clear whatever CSRF cookie has right now, force to generate a new one
|
||||||
middleware.DeleteCSRFCookie(ctx.Resp)
|
ctx.Csrf.DeleteCookie(ctx)
|
||||||
|
|
||||||
// Register last login
|
// Register last login
|
||||||
u.SetLastLogin()
|
u.SetLastLogin()
|
||||||
|
@ -1148,7 +1148,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
|
if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 {
|
||||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||||
ctx.RedirectToFirst(redirectTo)
|
ctx.RedirectToFirst(redirectTo)
|
||||||
return
|
return
|
||||||
|
|
|
@ -47,7 +47,7 @@ func SignInOpenID(ctx *context.Context) {
|
||||||
if len(redirectTo) > 0 {
|
if len(redirectTo) > 0 {
|
||||||
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
middleware.SetRedirectToCookie(ctx.Resp, redirectTo)
|
||||||
} else {
|
} else {
|
||||||
redirectTo = ctx.GetCookie("redirect_to")
|
redirectTo = ctx.GetSiteCookie("redirect_to")
|
||||||
}
|
}
|
||||||
|
|
||||||
if isSucceed {
|
if isSucceed {
|
||||||
|
|
|
@ -336,7 +336,7 @@ func MustChangePasswordPost(ctx *context.Context) {
|
||||||
|
|
||||||
log.Trace("User updated password: %s", u.Name)
|
log.Trace("User updated password: %s", u.Name)
|
||||||
|
|
||||||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
|
if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) {
|
||||||
middleware.DeleteRedirectToCookie(ctx.Resp)
|
middleware.DeleteRedirectToCookie(ctx.Resp)
|
||||||
ctx.RedirectToFirst(redirectTo)
|
ctx.RedirectToFirst(redirectTo)
|
||||||
return
|
return
|
||||||
|
|
|
@ -54,7 +54,7 @@ func Home(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check auto-login.
|
// Check auto-login.
|
||||||
uname := ctx.GetCookie(setting.CookieUserName)
|
uname := ctx.GetSiteCookie(setting.CookieUserName)
|
||||||
if len(uname) != 0 {
|
if len(uname) != 0 {
|
||||||
ctx.Redirect(setting.AppSubURL + "/user/login")
|
ctx.Redirect(setting.AppSubURL + "/user/login")
|
||||||
return
|
return
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
"code.gitea.io/gitea/modules/auth/webauthn"
|
"code.gitea.io/gitea/modules/auth/webauthn"
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/session"
|
"code.gitea.io/gitea/modules/session"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -91,5 +92,7 @@ func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore
|
||||||
middleware.SetLocaleCookie(resp, user.Language, 0)
|
middleware.SetLocaleCookie(resp, user.Language, 0)
|
||||||
|
|
||||||
// Clear whatever CSRF has right now, force to generate a new one
|
// Clear whatever CSRF has right now, force to generate a new one
|
||||||
middleware.DeleteCSRFCookie(resp)
|
if ctx := gitea_context.GetContext(req); ctx != nil {
|
||||||
|
ctx.Csrf.DeleteCookie(ctx)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,9 +13,9 @@ import (
|
||||||
"code.gitea.io/gitea/models/avatars"
|
"code.gitea.io/gitea/models/avatars"
|
||||||
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"
|
||||||
|
gitea_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"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
"code.gitea.io/gitea/modules/web/middleware"
|
"code.gitea.io/gitea/modules/web/middleware"
|
||||||
"code.gitea.io/gitea/services/auth/source/sspi"
|
"code.gitea.io/gitea/services/auth/source/sspi"
|
||||||
|
@ -46,9 +46,7 @@ var (
|
||||||
// via the built-in SSPI module in Windows for SPNEGO authentication.
|
// via the built-in SSPI module in Windows for SPNEGO authentication.
|
||||||
// On successful authentication returns a valid user object.
|
// On successful authentication returns a valid user object.
|
||||||
// Returns nil if authentication fails.
|
// Returns nil if authentication fails.
|
||||||
type SSPI struct {
|
type SSPI struct{}
|
||||||
rnd *templates.HTMLRender
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init creates a new global websspi.Authenticator object
|
// Init creates a new global websspi.Authenticator object
|
||||||
func (s *SSPI) Init(ctx context.Context) error {
|
func (s *SSPI) Init(ctx context.Context) error {
|
||||||
|
@ -58,7 +56,6 @@ func (s *SSPI) Init(ctx context.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, s.rnd = templates.HTMLRenderer(ctx)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,12 +98,9 @@ func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore,
|
||||||
}
|
}
|
||||||
store.GetData()["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
|
store.GetData()["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn
|
||||||
store.GetData()["EnableSSPI"] = true
|
store.GetData()["EnableSSPI"] = true
|
||||||
|
// in this case, the store is Gitea's web Context
|
||||||
err := s.rnd.HTML(w, http.StatusUnauthorized, string(tplSignIn), templates.BaseVars().Merge(store.GetData()))
|
// FIXME: it doesn't look good to render the page here, why not redirect?
|
||||||
if err != nil {
|
store.(*gitea_context.Context).HTML(http.StatusUnauthorized, tplSignIn)
|
||||||
log.Error("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if outToken != "" {
|
if outToken != "" {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -52,7 +53,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
})
|
})
|
||||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie := session.GetCookie("macaron_flash")
|
flashCookie := session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527master%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
||||||
|
|
||||||
|
@ -92,7 +93,7 @@ func TestCreateFileOnProtectedBranch(t *testing.T) {
|
||||||
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
|
assert.EqualValues(t, "/user2/repo1/settings/branches", res["redirect"])
|
||||||
|
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie = session.GetCookie("macaron_flash")
|
flashCookie = session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25271%2527%2Bfailed.", flashCookie.Value)
|
assert.EqualValues(t, "error%3DRemoving%2Bbranch%2Bprotection%2Brule%2B%25271%2527%2Bfailed.", flashCookie.Value)
|
||||||
})
|
})
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
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"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/lfs"
|
"code.gitea.io/gitea/modules/lfs"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -436,7 +437,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil
|
||||||
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
// Check if master branch has been locked successfully
|
// Check if master branch has been locked successfully
|
||||||
flashCookie := ctx.Session.GetCookie("macaron_flash")
|
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
assert.EqualValues(t, "success%3DBranch%2Bprotection%2Bfor%2Brule%2B%2527"+url.QueryEscape(branch)+"%2527%2Bhas%2Bbeen%2Bupdated.", flashCookie.Value)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"code.gitea.io/gitea/models/auth"
|
"code.gitea.io/gitea/models/auth"
|
||||||
"code.gitea.io/gitea/models/unittest"
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/graceful"
|
"code.gitea.io/gitea/modules/graceful"
|
||||||
"code.gitea.io/gitea/modules/json"
|
"code.gitea.io/gitea/modules/json"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -290,7 +291,7 @@ func getTokenForLoggedInUser(t testing.TB, session *TestSession, scopes ...auth.
|
||||||
// Log the flash values on failure
|
// Log the flash values on failure
|
||||||
if !assert.Equal(t, resp.Result().Header["Location"], []string{"/user/settings/applications"}) {
|
if !assert.Equal(t, resp.Result().Header["Location"], []string{"/user/settings/applications"}) {
|
||||||
for _, cookie := range resp.Result().Cookies() {
|
for _, cookie := range resp.Result().Cookies() {
|
||||||
if cookie.Name != "macaron_flash" {
|
if cookie.Name != gitea_context.CookieNameFlash {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
flash, _ := url.ParseQuery(cookie.Value)
|
flash, _ := url.ParseQuery(cookie.Value)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
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"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
gitea_context "code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/repository"
|
"code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
@ -91,7 +92,7 @@ func doCreatePushMirror(ctx APITestContext, address, username, password string)
|
||||||
})
|
})
|
||||||
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
flashCookie := ctx.Session.GetCookie("macaron_flash")
|
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.Contains(t, flashCookie.Value, "success")
|
assert.Contains(t, flashCookie.Value, "success")
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ func doRemovePushMirror(ctx APITestContext, address, username, password string,
|
||||||
})
|
})
|
||||||
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
||||||
|
|
||||||
flashCookie := ctx.Session.GetCookie("macaron_flash")
|
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
||||||
assert.NotNil(t, flashCookie)
|
assert.NotNil(t, flashCookie)
|
||||||
assert.Contains(t, flashCookie.Value, "success")
|
assert.Contains(t, flashCookie.Value, "success")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue