* Store OAuth2 session data in database * Rename table to `oauth2_session` and do not skip xormstorage initialization errortags/v1.5.0-dev
| @@ -97,14 +97,17 @@ func GetActiveOAuth2Providers() ([]string, map[string]OAuth2Provider, error) { | |||||
| } | } | ||||
| // InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library | // InitOAuth2 initialize the OAuth2 lib and register all active OAuth2 providers in the library | ||||
| func InitOAuth2() { | |||||
| oauth2.Init() | |||||
| func InitOAuth2() error { | |||||
| if err := oauth2.Init(x); err != nil { | |||||
| return err | |||||
| } | |||||
| loginSources, _ := GetActiveOAuth2ProviderLoginSources() | loginSources, _ := GetActiveOAuth2ProviderLoginSources() | ||||
| for _, source := range loginSources { | for _, source := range loginSources { | ||||
| oAuth2Config := source.OAuth2() | oAuth2Config := source.OAuth2() | ||||
| oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping) | oauth2.RegisterProvider(source.Name, oAuth2Config.Provider, oAuth2Config.ClientID, oAuth2Config.ClientSecret, oAuth2Config.OpenIDConnectAutoDiscoveryURL, oAuth2Config.CustomURLMapping) | ||||
| } | } | ||||
| return nil | |||||
| } | } | ||||
| // wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2 | // wrapOpenIDConnectInitializeError is used to wrap the error but this cannot be done in modules/auth/oauth2 | ||||
| @@ -7,13 +7,12 @@ package oauth2 | |||||
| import ( | import ( | ||||
| "math" | "math" | ||||
| "net/http" | "net/http" | ||||
| "os" | |||||
| "path/filepath" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "github.com/gorilla/sessions" | |||||
| "github.com/go-xorm/xorm" | |||||
| "github.com/lafriks/xormstore" | |||||
| "github.com/markbates/goth" | "github.com/markbates/goth" | ||||
| "github.com/markbates/goth/gothic" | "github.com/markbates/goth/gothic" | ||||
| "github.com/markbates/goth/providers/bitbucket" | "github.com/markbates/goth/providers/bitbucket" | ||||
| @@ -41,13 +40,14 @@ type CustomURLMapping struct { | |||||
| } | } | ||||
| // Init initialize the setup of the OAuth2 library | // Init initialize the setup of the OAuth2 library | ||||
| func Init() { | |||||
| sessionDir := filepath.Join(setting.AppDataPath, "sessions", "oauth2") | |||||
| if err := os.MkdirAll(sessionDir, 0700); err != nil { | |||||
| log.Fatal(4, "Fail to create dir %s: %v", sessionDir, err) | |||||
| } | |||||
| func Init(x *xorm.Engine) error { | |||||
| store, err := xormstore.NewOptions(x, xormstore.Options{ | |||||
| TableName: "oauth2_session", | |||||
| }, []byte(sessionUsersStoreKey)) | |||||
| store := sessions.NewFilesystemStore(sessionDir, []byte(sessionUsersStoreKey)) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| // according to the Goth lib: | // according to the Goth lib: | ||||
| // set the maxLength of the cookies stored on the disk to a larger number to prevent issues with: | // set the maxLength of the cookies stored on the disk to a larger number to prevent issues with: | ||||
| // securecookie: the value is too long | // securecookie: the value is too long | ||||
| @@ -65,6 +65,7 @@ func Init() { | |||||
| return req.Header.Get(providerHeaderKey), nil | return req.Header.Get(providerHeaderKey), nil | ||||
| } | } | ||||
| return nil | |||||
| } | } | ||||
| // Auth OAuth2 auth service | // Auth OAuth2 auth service | ||||
| @@ -60,7 +60,9 @@ func GlobalInit() { | |||||
| log.Fatal(4, "Failed to initialize ORM engine: %v", err) | log.Fatal(4, "Failed to initialize ORM engine: %v", err) | ||||
| } | } | ||||
| models.HasEngine = true | models.HasEngine = true | ||||
| models.InitOAuth2() | |||||
| if err := models.InitOAuth2(); err != nil { | |||||
| log.Fatal(4, "Failed to initialize OAuth2 support: %v", err) | |||||
| } | |||||
| models.LoadRepoConfig() | models.LoadRepoConfig() | ||||
| models.NewRepoContext() | models.NewRepoContext() | ||||
| @@ -0,0 +1,75 @@ | |||||
| # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. | |||||
| [[projects]] | |||||
| branch = "master" | |||||
| name = "github.com/denisenkom/go-mssqldb" | |||||
| packages = ["."] | |||||
| revision = "ee492709d4324cdcb051d2ac266b77ddc380f5c5" | |||||
| [[projects]] | |||||
| name = "github.com/go-sql-driver/mysql" | |||||
| packages = ["."] | |||||
| revision = "a0583e0143b1624142adab07e0e97fe106d99561" | |||||
| version = "v1.3" | |||||
| [[projects]] | |||||
| branch = "master" | |||||
| name = "github.com/go-xorm/builder" | |||||
| packages = ["."] | |||||
| revision = "488224409dd8aa2ce7a5baf8d10d55764a913738" | |||||
| [[projects]] | |||||
| name = "github.com/go-xorm/core" | |||||
| packages = ["."] | |||||
| revision = "da1adaf7a28ca792961721a34e6e04945200c890" | |||||
| version = "v0.5.7" | |||||
| [[projects]] | |||||
| name = "github.com/go-xorm/xorm" | |||||
| packages = ["."] | |||||
| revision = "1933dd69e294c0a26c0266637067f24dbb25770c" | |||||
| version = "v0.6.4" | |||||
| [[projects]] | |||||
| name = "github.com/gorilla/context" | |||||
| packages = ["."] | |||||
| revision = "1ea25387ff6f684839d82767c1733ff4d4d15d0a" | |||||
| version = "v1.1" | |||||
| [[projects]] | |||||
| name = "github.com/gorilla/securecookie" | |||||
| packages = ["."] | |||||
| revision = "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983" | |||||
| version = "v1.1.1" | |||||
| [[projects]] | |||||
| name = "github.com/gorilla/sessions" | |||||
| packages = ["."] | |||||
| revision = "ca9ada44574153444b00d3fd9c8559e4cc95f896" | |||||
| version = "v1.1" | |||||
| [[projects]] | |||||
| branch = "master" | |||||
| name = "github.com/lib/pq" | |||||
| packages = [".","oid"] | |||||
| revision = "88edab0803230a3898347e77b474f8c1820a1f20" | |||||
| [[projects]] | |||||
| name = "github.com/mattn/go-sqlite3" | |||||
| packages = ["."] | |||||
| revision = "6c771bb9887719704b210e87e934f08be014bdb1" | |||||
| version = "v1.6.0" | |||||
| [[projects]] | |||||
| branch = "master" | |||||
| name = "golang.org/x/crypto" | |||||
| packages = ["md4"] | |||||
| revision = "c7dcf104e3a7a1417abc0230cb0d5240d764159d" | |||||
| [solve-meta] | |||||
| analyzer-name = "dep" | |||||
| analyzer-version = 1 | |||||
| inputs-digest = "bba98a94e8c6668ae9556b4978bbffdfc5d4d535d522c8865465335bfaa2fc70" | |||||
| solver-name = "gps-cdcl" | |||||
| solver-version = 1 | |||||
| @@ -0,0 +1,50 @@ | |||||
| # Gopkg.toml example | |||||
| # | |||||
| # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md | |||||
| # for detailed Gopkg.toml documentation. | |||||
| # | |||||
| # required = ["github.com/user/thing/cmd/thing"] | |||||
| # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] | |||||
| # | |||||
| # [[constraint]] | |||||
| # name = "github.com/user/project" | |||||
| # version = "1.0.0" | |||||
| # | |||||
| # [[constraint]] | |||||
| # name = "github.com/user/project2" | |||||
| # branch = "dev" | |||||
| # source = "github.com/myfork/project2" | |||||
| # | |||||
| # [[override]] | |||||
| # name = "github.com/x/y" | |||||
| # version = "2.4.0" | |||||
| [[constraint]] | |||||
| name = "github.com/go-sql-driver/mysql" | |||||
| version = "1.3.0" | |||||
| [[constraint]] | |||||
| name = "github.com/go-xorm/xorm" | |||||
| version = "0.6.4" | |||||
| [[constraint]] | |||||
| name = "github.com/gorilla/context" | |||||
| version = "1.1.0" | |||||
| [[constraint]] | |||||
| name = "github.com/gorilla/securecookie" | |||||
| version = "1.1.1" | |||||
| [[constraint]] | |||||
| name = "github.com/gorilla/sessions" | |||||
| version = "1.1.0" | |||||
| [[constraint]] | |||||
| branch = "master" | |||||
| name = "github.com/lib/pq" | |||||
| [[constraint]] | |||||
| name = "github.com/mattn/go-sqlite3" | |||||
| version = "1.6.0" | |||||
| @@ -0,0 +1,19 @@ | |||||
| Copyright (c) 2018 Lauris Bukšis-Haberkorns, Mattias Wadman | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy of | |||||
| this software and associated documentation files (the "Software"), to deal in | |||||
| the Software without restriction, including without limitation the rights to | |||||
| use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||||
| of the Software, and to permit persons to whom the Software is furnished to do | |||||
| so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| @@ -0,0 +1,48 @@ | |||||
| [](https://godoc.org/github.com/lafriks/xormstore) | |||||
| [](https://travis-ci.org/lafriks/xormstore) | |||||
| [](https://codecov.io/gh/lafriks/xormstore) | |||||
| #### XORM backend for gorilla sessions | |||||
| go get github.com/lafriks/xormstore | |||||
| #### Example | |||||
| ```go | |||||
| // initialize and setup cleanup | |||||
| store := xormstore.New(engine, []byte("secret")) | |||||
| // db cleanup every hour | |||||
| // close quit channel to stop cleanup | |||||
| quit := make(chan struct{}) | |||||
| go store.PeriodicCleanup(1*time.Hour, quit) | |||||
| ``` | |||||
| ```go | |||||
| // in HTTP handler | |||||
| func handlerFunc(w http.ResponseWriter, r *http.Request) { | |||||
| session, err := store.Get(r, "session") | |||||
| session.Values["user_id"] = 123 | |||||
| store.Save(r, w, session) | |||||
| http.Error(w, "", http.StatusOK) | |||||
| } | |||||
| ``` | |||||
| For more details see [xormstore godoc documentation](https://godoc.org/github.com/lafriks/xormstore). | |||||
| #### Testing | |||||
| Just sqlite3 tests: | |||||
| go test | |||||
| All databases using docker: | |||||
| ./test | |||||
| If docker is not local (docker-machine etc): | |||||
| DOCKER_IP=$(docker-machine ip dev) ./test | |||||
| #### License | |||||
| xormstore is licensed under the MIT license. See [LICENSE](LICENSE) for the full license text. | |||||
| @@ -0,0 +1,70 @@ | |||||
| #!/bin/bash | |||||
| DOCKER_IP=${DOCKER_IP:-127.0.0.1} | |||||
| sqlite3() { | |||||
| DATABASE_URI="sqlite3://file:dummy?mode=memory&cache=shared" go test -v -race -cover -coverprofile=coverage.txt -covermode=atomic | |||||
| return $? | |||||
| } | |||||
| postgres10() { | |||||
| ID=$(docker run -p 5432 -d postgres:10-alpine) | |||||
| PORT=$(docker port "$ID" 5432 | cut -d : -f 2) | |||||
| DATABASE_URI="postgres://user=postgres password=postgres dbname=postgres host=$DOCKER_IP port=$PORT sslmode=disable" go test -v -race -cover | |||||
| S=$? | |||||
| docker rm -vf "$ID" > /dev/null | |||||
| return $S | |||||
| } | |||||
| postgres96() { | |||||
| ID=$(docker run -p 5432 -d postgres:9.6-alpine) | |||||
| PORT=$(docker port "$ID" 5432 | cut -d : -f 2) | |||||
| DATABASE_URI="postgres://user=postgres password=postgres dbname=postgres host=$DOCKER_IP port=$PORT sslmode=disable" go test -v -race -cover | |||||
| S=$? | |||||
| docker rm -vf "$ID" > /dev/null | |||||
| return $S | |||||
| } | |||||
| postgres94() { | |||||
| ID=$(docker run -p 5432 -d postgres:9.4-alpine) | |||||
| PORT=$(docker port "$ID" 5432 | cut -d : -f 2) | |||||
| DATABASE_URI="postgres://user=postgres password=postgres dbname=postgres host=$DOCKER_IP port=$PORT sslmode=disable" go test -v -race -cover | |||||
| S=$? | |||||
| docker rm -vf "$ID" > /dev/null | |||||
| return $S | |||||
| } | |||||
| mysql57() { | |||||
| ID=$(docker run \ | |||||
| -e MYSQL_ROOT_PASSWORD=root \ | |||||
| -e MYSQL_USER=mysql \ | |||||
| -e MYSQL_PASSWORD=mysql \ | |||||
| -e MYSQL_DATABASE=mysql \ | |||||
| -p 3306 -d mysql:5.7) | |||||
| PORT=$(docker port "$ID" 3306 | cut -d : -f 2) | |||||
| DATABASE_URI="mysql://mysql:mysql@tcp($DOCKER_IP:$PORT)/mysql?charset=utf8&parseTime=True" go test -v -race -cover | |||||
| S=$? | |||||
| docker rm -vf "$ID" > /dev/null | |||||
| return $S | |||||
| } | |||||
| mariadb10() { | |||||
| ID=$(docker run \ | |||||
| -e MYSQL_ROOT_PASSWORD=root \ | |||||
| -e MYSQL_USER=mysql \ | |||||
| -e MYSQL_PASSWORD=mysql \ | |||||
| -e MYSQL_DATABASE=mysql \ | |||||
| -p 3306 -d mariadb:10) | |||||
| PORT=$(docker port "$ID" 3306 | cut -d : -f 2) | |||||
| DATABASE_URI="mysql://mysql:mysql@tcp($DOCKER_IP:$PORT)/mysql?charset=utf8&parseTime=True" go test -v -race -cover | |||||
| S=$? | |||||
| docker rm -vf "$ID" > /dev/null | |||||
| return $S | |||||
| } | |||||
| sqlite3 || exit 1 | |||||
| postgres94 || exit 1 | |||||
| postgres96 || exit 1 | |||||
| postgres10 || exit 1 | |||||
| mysql57 || exit 1 | |||||
| mariadb10 || exit 1 | |||||
| @@ -0,0 +1,60 @@ | |||||
| package util | |||||
| import ( | |||||
| "time" | |||||
| ) | |||||
| // TimeStamp defines a timestamp | |||||
| type TimeStamp int64 | |||||
| // TimeStampNow returns now int64 | |||||
| func TimeStampNow() TimeStamp { | |||||
| return TimeStamp(time.Now().Unix()) | |||||
| } | |||||
| // Add adds seconds and return sum | |||||
| func (ts TimeStamp) Add(seconds int64) TimeStamp { | |||||
| return ts + TimeStamp(seconds) | |||||
| } | |||||
| // AddDuration adds time.Duration and return sum | |||||
| func (ts TimeStamp) AddDuration(interval time.Duration) TimeStamp { | |||||
| return ts + TimeStamp(interval/time.Second) | |||||
| } | |||||
| // Year returns the time's year | |||||
| func (ts TimeStamp) Year() int { | |||||
| return ts.AsTime().Year() | |||||
| } | |||||
| // AsTime convert timestamp as time.Time in Local locale | |||||
| func (ts TimeStamp) AsTime() (tm time.Time) { | |||||
| tm = time.Unix(int64(ts), 0).Local() | |||||
| return | |||||
| } | |||||
| // AsTimePtr convert timestamp as *time.Time in Local locale | |||||
| func (ts TimeStamp) AsTimePtr() *time.Time { | |||||
| tm := time.Unix(int64(ts), 0).Local() | |||||
| return &tm | |||||
| } | |||||
| // Format formats timestamp as | |||||
| func (ts TimeStamp) Format(f string) string { | |||||
| return ts.AsTime().Format(f) | |||||
| } | |||||
| // FormatLong formats as RFC1123Z | |||||
| func (ts TimeStamp) FormatLong() string { | |||||
| return ts.Format(time.RFC1123Z) | |||||
| } | |||||
| // FormatShort formats as short | |||||
| func (ts TimeStamp) FormatShort() string { | |||||
| return ts.Format("Jan 02, 2006") | |||||
| } | |||||
| // IsZero is zero time | |||||
| func (ts TimeStamp) IsZero() bool { | |||||
| return ts.AsTime().IsZero() | |||||
| } | |||||
| @@ -0,0 +1,251 @@ | |||||
| /* | |||||
| Package xormstore is a XORM backend for gorilla sessions | |||||
| Simplest form: | |||||
| store, err := xormstore.New(engine, []byte("secret-hash-key")) | |||||
| All options: | |||||
| store, err := xormstore.NewOptions( | |||||
| engine, // *xorm.Engine | |||||
| xormstore.Options{ | |||||
| TableName: "sessions", // "sessions" is default | |||||
| SkipCreateTable: false, // false is default | |||||
| }, | |||||
| []byte("secret-hash-key"), // 32 or 64 bytes recommended, required | |||||
| []byte("secret-encyption-key")) // nil, 16, 24 or 32 bytes, optional | |||||
| if err != nil { | |||||
| // xormstore can not be initialized | |||||
| } | |||||
| // some more settings, see sessions.Options | |||||
| store.SessionOpts.Secure = true | |||||
| store.SessionOpts.HttpOnly = true | |||||
| store.SessionOpts.MaxAge = 60 * 60 * 24 * 60 | |||||
| If you want periodic cleanup of expired sessions: | |||||
| quit := make(chan struct{}) | |||||
| go store.PeriodicCleanup(1*time.Hour, quit) | |||||
| For more information about the keys see https://github.com/gorilla/securecookie | |||||
| For API to use in HTTP handlers see https://github.com/gorilla/sessions | |||||
| */ | |||||
| package xormstore | |||||
| import ( | |||||
| "encoding/base32" | |||||
| "net/http" | |||||
| "strings" | |||||
| "time" | |||||
| "github.com/lafriks/xormstore/util" | |||||
| "github.com/go-xorm/xorm" | |||||
| "github.com/gorilla/context" | |||||
| "github.com/gorilla/securecookie" | |||||
| "github.com/gorilla/sessions" | |||||
| ) | |||||
| const sessionIDLen = 32 | |||||
| const defaultTableName = "sessions" | |||||
| const defaultMaxAge = 60 * 60 * 24 * 30 // 30 days | |||||
| const defaultPath = "/" | |||||
| // Options for xormstore | |||||
| type Options struct { | |||||
| TableName string | |||||
| SkipCreateTable bool | |||||
| } | |||||
| // Store represent a xormstore | |||||
| type Store struct { | |||||
| e *xorm.Engine | |||||
| opts Options | |||||
| Codecs []securecookie.Codec | |||||
| SessionOpts *sessions.Options | |||||
| } | |||||
| type xormSession struct { | |||||
| ID string `xorm:"VARCHAR(400) PK NAME 'id'"` | |||||
| Data string `xorm:"TEXT"` | |||||
| CreatedUnix util.TimeStamp `xorm:"created"` | |||||
| UpdatedUnix util.TimeStamp `xorm:"updated"` | |||||
| ExpiresUnix util.TimeStamp `xorm:"INDEX"` | |||||
| tableName string `xorm:"-"` // just to store table name for easier access | |||||
| } | |||||
| // Define a type for context keys so that they can't clash with anything else stored in context | |||||
| type contextKey string | |||||
| func (xs *xormSession) TableName() string { | |||||
| return xs.tableName | |||||
| } | |||||
| // New creates a new xormstore session | |||||
| func New(e *xorm.Engine, keyPairs ...[]byte) (*Store, error) { | |||||
| return NewOptions(e, Options{}, keyPairs...) | |||||
| } | |||||
| // NewOptions creates a new xormstore session with options | |||||
| func NewOptions(e *xorm.Engine, opts Options, keyPairs ...[]byte) (*Store, error) { | |||||
| st := &Store{ | |||||
| e: e, | |||||
| opts: opts, | |||||
| Codecs: securecookie.CodecsFromPairs(keyPairs...), | |||||
| SessionOpts: &sessions.Options{ | |||||
| Path: defaultPath, | |||||
| MaxAge: defaultMaxAge, | |||||
| }, | |||||
| } | |||||
| if st.opts.TableName == "" { | |||||
| st.opts.TableName = defaultTableName | |||||
| } | |||||
| if !st.opts.SkipCreateTable { | |||||
| if err := st.e.Sync2(&xormSession{tableName: st.opts.TableName}); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| return st, nil | |||||
| } | |||||
| // Get returns a session for the given name after adding it to the registry. | |||||
| func (st *Store) Get(r *http.Request, name string) (*sessions.Session, error) { | |||||
| return sessions.GetRegistry(r).Get(st, name) | |||||
| } | |||||
| // New creates a session with name without adding it to the registry. | |||||
| func (st *Store) New(r *http.Request, name string) (*sessions.Session, error) { | |||||
| session := sessions.NewSession(st, name) | |||||
| opts := *st.SessionOpts | |||||
| session.Options = &opts | |||||
| st.MaxAge(st.SessionOpts.MaxAge) | |||||
| // try fetch from db if there is a cookie | |||||
| if cookie, err := r.Cookie(name); err == nil { | |||||
| if err := securecookie.DecodeMulti(name, cookie.Value, &session.ID, st.Codecs...); err != nil { | |||||
| return session, nil | |||||
| } | |||||
| s := &xormSession{tableName: st.opts.TableName} | |||||
| if has, err := st.e.Where("id = ? AND expires_unix >= ?", session.ID, util.TimeStampNow()).Get(s); !has || err != nil { | |||||
| return session, nil | |||||
| } | |||||
| if err := securecookie.DecodeMulti(session.Name(), s.Data, &session.Values, st.Codecs...); err != nil { | |||||
| return session, nil | |||||
| } | |||||
| context.Set(r, contextKey(name), s) | |||||
| } | |||||
| return session, nil | |||||
| } | |||||
| // Save session and set cookie header | |||||
| func (st *Store) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error { | |||||
| s, _ := context.Get(r, contextKey(session.Name())).(*xormSession) | |||||
| // delete if max age is < 0 | |||||
| if session.Options.MaxAge < 0 { | |||||
| if s != nil { | |||||
| if _, err := st.e.Delete(&xormSession{ | |||||
| ID: session.ID, | |||||
| tableName: st.opts.TableName, | |||||
| }); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options)) | |||||
| return nil | |||||
| } | |||||
| data, err := securecookie.EncodeMulti(session.Name(), session.Values, st.Codecs...) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| now := util.TimeStampNow() | |||||
| expire := now.AddDuration(time.Second * time.Duration(session.Options.MaxAge)) | |||||
| if s == nil { | |||||
| // generate random session ID key suitable for storage in the db | |||||
| session.ID = strings.TrimRight( | |||||
| base32.StdEncoding.EncodeToString( | |||||
| securecookie.GenerateRandomKey(sessionIDLen)), "=") | |||||
| s = &xormSession{ | |||||
| ID: session.ID, | |||||
| Data: data, | |||||
| CreatedUnix: now, | |||||
| UpdatedUnix: now, | |||||
| ExpiresUnix: expire, | |||||
| tableName: st.opts.TableName, | |||||
| } | |||||
| if _, err := st.e.Insert(s); err != nil { | |||||
| return err | |||||
| } | |||||
| context.Set(r, contextKey(session.Name()), s) | |||||
| } else { | |||||
| s.Data = data | |||||
| s.UpdatedUnix = now | |||||
| s.ExpiresUnix = expire | |||||
| if _, err := st.e.ID(s.ID).Cols("data", "updated_unix", "expires_unix").Update(s); err != nil { | |||||
| return err | |||||
| } | |||||
| } | |||||
| // set session id cookie | |||||
| id, err := securecookie.EncodeMulti(session.Name(), session.ID, st.Codecs...) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| http.SetCookie(w, sessions.NewCookie(session.Name(), id, session.Options)) | |||||
| return nil | |||||
| } | |||||
| // MaxAge sets the maximum age for the store and the underlying cookie | |||||
| // implementation. Individual sessions can be deleted by setting | |||||
| // Options.MaxAge = -1 for that session. | |||||
| func (st *Store) MaxAge(age int) { | |||||
| st.SessionOpts.MaxAge = age | |||||
| for _, codec := range st.Codecs { | |||||
| if sc, ok := codec.(*securecookie.SecureCookie); ok { | |||||
| sc.MaxAge(age) | |||||
| } | |||||
| } | |||||
| } | |||||
| // MaxLength restricts the maximum length of new sessions to l. | |||||
| // If l is 0 there is no limit to the size of a session, use with caution. | |||||
| // The default is 4096 (default for securecookie) | |||||
| func (st *Store) MaxLength(l int) { | |||||
| for _, c := range st.Codecs { | |||||
| if codec, ok := c.(*securecookie.SecureCookie); ok { | |||||
| codec.MaxLength(l) | |||||
| } | |||||
| } | |||||
| } | |||||
| // Cleanup deletes expired sessions | |||||
| func (st *Store) Cleanup() { | |||||
| st.e.Where("expires_unix < ?", util.TimeStampNow()).Delete(&xormSession{tableName: st.opts.TableName}) | |||||
| } | |||||
| // PeriodicCleanup runs Cleanup every interval. Close quit channel to stop. | |||||
| func (st *Store) PeriodicCleanup(interval time.Duration, quit <-chan struct{}) { | |||||
| t := time.NewTicker(interval) | |||||
| defer t.Stop() | |||||
| for { | |||||
| select { | |||||
| case <-t.C: | |||||
| st.Cleanup() | |||||
| case <-quit: | |||||
| return | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -647,6 +647,18 @@ | |||||
| "revision": "cb6bfca970f6908083f26f39a79009d608efd5cd", | "revision": "cb6bfca970f6908083f26f39a79009d608efd5cd", | ||||
| "revisionTime": "2016-10-16T15:41:25Z" | "revisionTime": "2016-10-16T15:41:25Z" | ||||
| }, | }, | ||||
| { | |||||
| "checksumSHA1": "/X7eCdN7MX8zgCjA9s0ktzgTPlA=", | |||||
| "path": "github.com/lafriks/xormstore", | |||||
| "revision": "3a80a383a04b29ec2e1bf61279dd948aa809335b", | |||||
| "revisionTime": "2018-04-09T10:45:24Z" | |||||
| }, | |||||
| { | |||||
| "checksumSHA1": "Vxvfs8mukr9GOLSuGIPU4ODyOZc=", | |||||
| "path": "github.com/lafriks/xormstore/util", | |||||
| "revision": "c0e2f3dc1ecab3536617967e4b47ee5b9e2ca229", | |||||
| "revisionTime": "2018-03-11T19:16:53Z" | |||||
| }, | |||||
| { | { | ||||
| "checksumSHA1": "QV4HZTfaXvhD+5PcGM2p+7aCYYI=", | "checksumSHA1": "QV4HZTfaXvhD+5PcGM2p+7aCYYI=", | ||||
| "path": "github.com/lib/pq", | "path": "github.com/lib/pq", | ||||