summaryrefslogtreecommitdiffstats
path: root/modules/middlewares
diff options
context:
space:
mode:
Diffstat (limited to 'modules/middlewares')
-rw-r--r--modules/middlewares/binding.go145
-rw-r--r--modules/middlewares/cookie.go61
-rw-r--r--modules/middlewares/data.go10
-rw-r--r--modules/middlewares/flash.go65
-rw-r--r--modules/middlewares/locale.go8
-rw-r--r--modules/middlewares/redis.go217
-rw-r--r--modules/middlewares/virtual.go196
7 files changed, 286 insertions, 416 deletions
diff --git a/modules/middlewares/binding.go b/modules/middlewares/binding.go
new file mode 100644
index 0000000000..1dabdbb62e
--- /dev/null
+++ b/modules/middlewares/binding.go
@@ -0,0 +1,145 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package middlewares
+
+import (
+ "reflect"
+ "strings"
+
+ "code.gitea.io/gitea/modules/translation"
+ "code.gitea.io/gitea/modules/validation"
+
+ "gitea.com/go-chi/binding"
+ "github.com/unknwon/com"
+)
+
+// Form form binding interface
+type Form interface {
+ binding.Validator
+}
+
+func init() {
+ binding.SetNameMapper(com.ToSnakeCase)
+}
+
+// AssignForm assign form values back to the template data.
+func AssignForm(form interface{}, data map[string]interface{}) {
+ typ := reflect.TypeOf(form)
+ val := reflect.ValueOf(form)
+
+ for typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ val = val.Elem()
+ }
+
+ for i := 0; i < typ.NumField(); i++ {
+ field := typ.Field(i)
+
+ fieldName := field.Tag.Get("form")
+ // Allow ignored fields in the struct
+ if fieldName == "-" {
+ continue
+ } else if len(fieldName) == 0 {
+ fieldName = com.ToSnakeCase(field.Name)
+ }
+
+ data[fieldName] = val.Field(i).Interface()
+ }
+}
+
+func getRuleBody(field reflect.StructField, prefix string) string {
+ for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
+ if strings.HasPrefix(rule, prefix) {
+ return rule[len(prefix) : len(rule)-1]
+ }
+ }
+ return ""
+}
+
+// GetSize get size int form tag
+func GetSize(field reflect.StructField) string {
+ return getRuleBody(field, "Size(")
+}
+
+// GetMinSize get minimal size in form tag
+func GetMinSize(field reflect.StructField) string {
+ return getRuleBody(field, "MinSize(")
+}
+
+// GetMaxSize get max size in form tag
+func GetMaxSize(field reflect.StructField) string {
+ return getRuleBody(field, "MaxSize(")
+}
+
+// GetInclude get include in form tag
+func GetInclude(field reflect.StructField) string {
+ return getRuleBody(field, "Include(")
+}
+
+// Validate validate TODO:
+func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors {
+ if errs.Len() == 0 {
+ return errs
+ }
+
+ data["HasError"] = true
+ // If the field with name errs[0].FieldNames[0] is not found in form
+ // somehow, some code later on will panic on Data["ErrorMsg"].(string).
+ // So initialize it to some default.
+ data["ErrorMsg"] = l.Tr("form.unknown_error")
+ AssignForm(f, data)
+
+ typ := reflect.TypeOf(f)
+ val := reflect.ValueOf(f)
+
+ if typ.Kind() == reflect.Ptr {
+ typ = typ.Elem()
+ val = val.Elem()
+ }
+
+ if field, ok := typ.FieldByName(errs[0].FieldNames[0]); ok {
+ fieldName := field.Tag.Get("form")
+ if fieldName != "-" {
+ data["Err_"+field.Name] = true
+
+ trName := field.Tag.Get("locale")
+ if len(trName) == 0 {
+ trName = l.Tr("form." + field.Name)
+ } else {
+ trName = l.Tr(trName)
+ }
+
+ switch errs[0].Classification {
+ case binding.ERR_REQUIRED:
+ data["ErrorMsg"] = trName + l.Tr("form.require_error")
+ case binding.ERR_ALPHA_DASH:
+ data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_error")
+ case binding.ERR_ALPHA_DASH_DOT:
+ data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_error")
+ case validation.ErrGitRefName:
+ data["ErrorMsg"] = trName + l.Tr("form.git_ref_name_error")
+ case binding.ERR_SIZE:
+ data["ErrorMsg"] = trName + l.Tr("form.size_error", GetSize(field))
+ case binding.ERR_MIN_SIZE:
+ data["ErrorMsg"] = trName + l.Tr("form.min_size_error", GetMinSize(field))
+ case binding.ERR_MAX_SIZE:
+ data["ErrorMsg"] = trName + l.Tr("form.max_size_error", GetMaxSize(field))
+ case binding.ERR_EMAIL:
+ data["ErrorMsg"] = trName + l.Tr("form.email_error")
+ case binding.ERR_URL:
+ data["ErrorMsg"] = trName + l.Tr("form.url_error")
+ case binding.ERR_INCLUDE:
+ data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field))
+ case validation.ErrGlobPattern:
+ data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message)
+ default:
+ data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
+ }
+ return errs
+ }
+ }
+ return errs
+}
diff --git a/modules/middlewares/cookie.go b/modules/middlewares/cookie.go
index 80d0e3b453..d18541833f 100644
--- a/modules/middlewares/cookie.go
+++ b/modules/middlewares/cookie.go
@@ -1,3 +1,4 @@
+// Copyright 2020 The Macaron Authors
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -12,6 +13,56 @@ import (
"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{
@@ -102,3 +153,13 @@ func SetCookie(resp http.ResponseWriter, name string, value string, others ...in
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)
+ if err != nil {
+ return ""
+ }
+ val, _ := url.QueryUnescape(cookie.Value)
+ return val
+}
diff --git a/modules/middlewares/data.go b/modules/middlewares/data.go
new file mode 100644
index 0000000000..2690289362
--- /dev/null
+++ b/modules/middlewares/data.go
@@ -0,0 +1,10 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package middlewares
+
+// DataStore represents a data store
+type DataStore interface {
+ GetData() map[string]interface{}
+}
diff --git a/modules/middlewares/flash.go b/modules/middlewares/flash.go
new file mode 100644
index 0000000000..38217288e8
--- /dev/null
+++ b/modules/middlewares/flash.go
@@ -0,0 +1,65 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package middlewares
+
+import "net/url"
+
+// flashes enumerates all the flash types
+const (
+ SuccessFlash = "SuccessMsg"
+ ErrorFlash = "ErrorMsg"
+ WarnFlash = "WarningMsg"
+ InfoFlash = "InfoMsg"
+)
+
+var (
+ // FlashNow FIXME:
+ FlashNow bool
+)
+
+// Flash represents a one time data transfer between two requests.
+type Flash struct {
+ DataStore
+ url.Values
+ ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
+}
+
+func (f *Flash) set(name, msg string, current ...bool) {
+ isShow := false
+ if (len(current) == 0 && FlashNow) ||
+ (len(current) > 0 && current[0]) {
+ isShow = true
+ }
+
+ if isShow {
+ f.GetData()["Flash"] = f
+ } else {
+ f.Set(name, msg)
+ }
+}
+
+// Error sets error message
+func (f *Flash) Error(msg string, current ...bool) {
+ f.ErrorMsg = msg
+ f.set("error", msg, current...)
+}
+
+// Warning sets warning message
+func (f *Flash) Warning(msg string, current ...bool) {
+ f.WarningMsg = msg
+ f.set("warning", msg, current...)
+}
+
+// Info sets info message
+func (f *Flash) Info(msg string, current ...bool) {
+ f.InfoMsg = msg
+ f.set("info", msg, current...)
+}
+
+// Success sets success message
+func (f *Flash) Success(msg string, current ...bool) {
+ f.SuccessMsg = msg
+ f.set("success", msg, current...)
+}
diff --git a/modules/middlewares/locale.go b/modules/middlewares/locale.go
index 98af890cfd..7cfba81bda 100644
--- a/modules/middlewares/locale.go
+++ b/modules/middlewares/locale.go
@@ -23,12 +23,14 @@ func Locale(resp http.ResponseWriter, req *http.Request) translation.Locale {
// 2. Get language information from cookies.
if len(lang) == 0 {
ck, _ := req.Cookie("lang")
- lang = ck.Value
- hasCookie = true
+ if ck != nil {
+ lang = ck.Value
+ hasCookie = true
+ }
}
// Check again in case someone modify by purpose.
- if !i18n.IsExist(lang) {
+ if lang != "" && !i18n.IsExist(lang) {
lang = ""
hasCookie = false
}
diff --git a/modules/middlewares/redis.go b/modules/middlewares/redis.go
deleted file mode 100644
index ced1c1ee81..0000000000
--- a/modules/middlewares/redis.go
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2013 Beego Authors
-// Copyright 2014 The Macaron Authors
-// Copyright 2020 The Gitea Authors. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package middlewares
-
-import (
- "fmt"
- "sync"
- "time"
-
- "code.gitea.io/gitea/modules/nosql"
-
- "gitea.com/go-chi/session"
- "github.com/go-redis/redis/v7"
-)
-
-// RedisStore represents a redis session store implementation.
-// TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
-type RedisStore struct {
- c redis.UniversalClient
- prefix, sid string
- duration time.Duration
- lock sync.RWMutex
- data map[interface{}]interface{}
-}
-
-// NewRedisStore creates and returns a redis session store.
-func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore {
- return &RedisStore{
- c: c,
- prefix: prefix,
- sid: sid,
- duration: dur,
- data: kv,
- }
-}
-
-// Set sets value to given key in session.
-func (s *RedisStore) Set(key, val interface{}) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.data[key] = val
- return nil
-}
-
-// Get gets value by given key in session.
-func (s *RedisStore) Get(key interface{}) interface{} {
- s.lock.RLock()
- defer s.lock.RUnlock()
-
- return s.data[key]
-}
-
-// Delete delete a key from session.
-func (s *RedisStore) Delete(key interface{}) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- delete(s.data, key)
- return nil
-}
-
-// ID returns current session ID.
-func (s *RedisStore) ID() string {
- return s.sid
-}
-
-// Release releases resource and save data to provider.
-func (s *RedisStore) Release() error {
- // Skip encoding if the data is empty
- if len(s.data) == 0 {
- return nil
- }
-
- data, err := session.EncodeGob(s.data)
- if err != nil {
- return err
- }
-
- return s.c.Set(s.prefix+s.sid, string(data), s.duration).Err()
-}
-
-// Flush deletes all session data.
-func (s *RedisStore) Flush() error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.data = make(map[interface{}]interface{})
- return nil
-}
-
-// RedisProvider represents a redis session provider implementation.
-type RedisProvider struct {
- c redis.UniversalClient
- duration time.Duration
- prefix string
-}
-
-// Init initializes redis session provider.
-// configs: network=tcp,addr=:6379,password=macaron,db=0,pool_size=100,idle_timeout=180,prefix=session;
-func (p *RedisProvider) Init(maxlifetime int64, configs string) (err error) {
- p.duration, err = time.ParseDuration(fmt.Sprintf("%ds", maxlifetime))
- if err != nil {
- return err
- }
-
- uri := nosql.ToRedisURI(configs)
-
- for k, v := range uri.Query() {
- switch k {
- case "prefix":
- p.prefix = v[0]
- }
- }
-
- p.c = nosql.GetManager().GetRedisClient(uri.String())
- return p.c.Ping().Err()
-}
-
-// Read returns raw session store by session ID.
-func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
- psid := p.prefix + sid
- if !p.Exist(sid) {
- if err := p.c.Set(psid, "", p.duration).Err(); err != nil {
- return nil, err
- }
- }
-
- var kv map[interface{}]interface{}
- kvs, err := p.c.Get(psid).Result()
- if err != nil {
- return nil, err
- }
- if len(kvs) == 0 {
- kv = make(map[interface{}]interface{})
- } else {
- kv, err = session.DecodeGob([]byte(kvs))
- if err != nil {
- return nil, err
- }
- }
-
- return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil
-}
-
-// Exist returns true if session with given ID exists.
-func (p *RedisProvider) Exist(sid string) bool {
- v, err := p.c.Exists(p.prefix + sid).Result()
- return err == nil && v == 1
-}
-
-// Destroy deletes a session by session ID.
-func (p *RedisProvider) Destroy(sid string) error {
- return p.c.Del(p.prefix + sid).Err()
-}
-
-// Regenerate regenerates a session store from old session ID to new one.
-func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err error) {
- poldsid := p.prefix + oldsid
- psid := p.prefix + sid
-
- if p.Exist(sid) {
- return nil, fmt.Errorf("new sid '%s' already exists", sid)
- } else if !p.Exist(oldsid) {
- // Make a fake old session.
- if err = p.c.Set(poldsid, "", p.duration).Err(); err != nil {
- return nil, err
- }
- }
-
- if err = p.c.Rename(poldsid, psid).Err(); err != nil {
- return nil, err
- }
-
- var kv map[interface{}]interface{}
- kvs, err := p.c.Get(psid).Result()
- if err != nil {
- return nil, err
- }
-
- if len(kvs) == 0 {
- kv = make(map[interface{}]interface{})
- } else {
- kv, err = session.DecodeGob([]byte(kvs))
- if err != nil {
- return nil, err
- }
- }
-
- return NewRedisStore(p.c, p.prefix, sid, p.duration, kv), nil
-}
-
-// Count counts and returns number of sessions.
-func (p *RedisProvider) Count() int {
- return int(p.c.DBSize().Val())
-}
-
-// GC calls GC to clean expired sessions.
-func (*RedisProvider) GC() {}
-
-func init() {
- session.Register("redis", &RedisProvider{})
-}
diff --git a/modules/middlewares/virtual.go b/modules/middlewares/virtual.go
deleted file mode 100644
index 70d780d65d..0000000000
--- a/modules/middlewares/virtual.go
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2019 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package middlewares
-
-import (
- "encoding/json"
- "fmt"
- "sync"
-
- "gitea.com/go-chi/session"
- couchbase "gitea.com/go-chi/session/couchbase"
- memcache "gitea.com/go-chi/session/memcache"
- mysql "gitea.com/go-chi/session/mysql"
- postgres "gitea.com/go-chi/session/postgres"
-)
-
-// VirtualSessionProvider represents a shadowed session provider implementation.
-// TODO: copied from modules/session/redis.go and should remove that one until macaron removed.
-type VirtualSessionProvider struct {
- lock sync.RWMutex
- provider session.Provider
-}
-
-// Init initializes the cookie session provider with given root path.
-func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
- var opts session.Options
- if err := json.Unmarshal([]byte(config), &opts); err != nil {
- return err
- }
- // Note that these options are unprepared so we can't just use NewManager here.
- // Nor can we access the provider map in session.
- // So we will just have to do this by hand.
- // This is only slightly more wrong than modules/setting/session.go:23
- switch opts.Provider {
- case "memory":
- o.provider = &session.MemProvider{}
- case "file":
- o.provider = &session.FileProvider{}
- case "redis":
- o.provider = &RedisProvider{}
- case "mysql":
- o.provider = &mysql.MysqlProvider{}
- case "postgres":
- o.provider = &postgres.PostgresProvider{}
- case "couchbase":
- o.provider = &couchbase.CouchbaseProvider{}
- case "memcache":
- o.provider = &memcache.MemcacheProvider{}
- default:
- return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
- }
- return o.provider.Init(gclifetime, opts.ProviderConfig)
-}
-
-// Read returns raw session store by session ID.
-func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
- o.lock.RLock()
- defer o.lock.RUnlock()
- if o.provider.Exist(sid) {
- return o.provider.Read(sid)
- }
- kv := make(map[interface{}]interface{})
- kv["_old_uid"] = "0"
- return NewVirtualStore(o, sid, kv), nil
-}
-
-// Exist returns true if session with given ID exists.
-func (o *VirtualSessionProvider) Exist(sid string) bool {
- return true
-}
-
-// Destroy deletes a session by session ID.
-func (o *VirtualSessionProvider) Destroy(sid string) error {
- o.lock.Lock()
- defer o.lock.Unlock()
- return o.provider.Destroy(sid)
-}
-
-// Regenerate regenerates a session store from old session ID to new one.
-func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
- o.lock.Lock()
- defer o.lock.Unlock()
- return o.provider.Regenerate(oldsid, sid)
-}
-
-// Count counts and returns number of sessions.
-func (o *VirtualSessionProvider) Count() int {
- o.lock.RLock()
- defer o.lock.RUnlock()
- return o.provider.Count()
-}
-
-// GC calls GC to clean expired sessions.
-func (o *VirtualSessionProvider) GC() {
- o.provider.GC()
-}
-
-func init() {
- session.Register("VirtualSession", &VirtualSessionProvider{})
-}
-
-// VirtualStore represents a virtual session store implementation.
-type VirtualStore struct {
- p *VirtualSessionProvider
- sid string
- lock sync.RWMutex
- data map[interface{}]interface{}
- released bool
-}
-
-// NewVirtualStore creates and returns a virtual session store.
-func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
- return &VirtualStore{
- p: p,
- sid: sid,
- data: kv,
- }
-}
-
-// Set sets value to given key in session.
-func (s *VirtualStore) Set(key, val interface{}) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.data[key] = val
- return nil
-}
-
-// Get gets value by given key in session.
-func (s *VirtualStore) Get(key interface{}) interface{} {
- s.lock.RLock()
- defer s.lock.RUnlock()
-
- return s.data[key]
-}
-
-// Delete delete a key from session.
-func (s *VirtualStore) Delete(key interface{}) error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- delete(s.data, key)
- return nil
-}
-
-// ID returns current session ID.
-func (s *VirtualStore) ID() string {
- return s.sid
-}
-
-// Release releases resource and save data to provider.
-func (s *VirtualStore) Release() error {
- s.lock.Lock()
- defer s.lock.Unlock()
- // Now need to lock the provider
- s.p.lock.Lock()
- defer s.p.lock.Unlock()
- if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) {
- // Now ensure that we don't exist!
- realProvider := s.p.provider
-
- if !s.released && realProvider.Exist(s.sid) {
- // This is an error!
- return fmt.Errorf("new sid '%s' already exists", s.sid)
- }
- realStore, err := realProvider.Read(s.sid)
- if err != nil {
- return err
- }
- if err := realStore.Flush(); err != nil {
- return err
- }
- for key, value := range s.data {
- if err := realStore.Set(key, value); err != nil {
- return err
- }
- }
- err = realStore.Release()
- if err == nil {
- s.released = true
- }
- return err
- }
- return nil
-}
-
-// Flush deletes all session data.
-func (s *VirtualStore) Flush() error {
- s.lock.Lock()
- defer s.lock.Unlock()
-
- s.data = make(map[interface{}]interface{})
- return nil
-}