Store OAuth2 session data in database (#3660)
* Store OAuth2 session data in database * Rename table to `oauth2_session` and do not skip xormstorage initialization error
This commit is contained in:
		
							parent
							
								
									8d5f58d834
								
							
						
					
					
						commit
						5a62eb30df
					
				
					 11 changed files with 603 additions and 12 deletions
				
			
		|  | @ -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() { | func InitOAuth2() error { | ||||||
| 	oauth2.Init() | 	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() { | func Init(x *xorm.Engine) error { | ||||||
| 	sessionDir := filepath.Join(setting.AppDataPath, "sessions", "oauth2") | 	store, err := xormstore.NewOptions(x, xormstore.Options{ | ||||||
| 	if err := os.MkdirAll(sessionDir, 0700); err != nil { | 		TableName: "oauth2_session", | ||||||
| 		log.Fatal(4, "Fail to create dir %s: %v", sessionDir, err) | 	}, []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() | ||||||
|  |  | ||||||
							
								
								
									
										75
									
								
								vendor/github.com/lafriks/xormstore/Gopkg.lock
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								vendor/github.com/lafriks/xormstore/Gopkg.lock
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
							
								
								
									
										50
									
								
								vendor/github.com/lafriks/xormstore/Gopkg.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/lafriks/xormstore/Gopkg.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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" | ||||||
							
								
								
									
										19
									
								
								vendor/github.com/lafriks/xormstore/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/lafriks/xormstore/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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. | ||||||
							
								
								
									
										48
									
								
								vendor/github.com/lafriks/xormstore/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/lafriks/xormstore/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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. | ||||||
							
								
								
									
										70
									
								
								vendor/github.com/lafriks/xormstore/test
									
									
									
										generated
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										70
									
								
								vendor/github.com/lafriks/xormstore/test
									
									
									
										generated
									
									
										vendored
									
									
										Executable file
									
								
							|  | @ -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 | ||||||
							
								
								
									
										60
									
								
								vendor/github.com/lafriks/xormstore/util/time_stamp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								vendor/github.com/lafriks/xormstore/util/time_stamp.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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() | ||||||
|  | } | ||||||
							
								
								
									
										251
									
								
								vendor/github.com/lafriks/xormstore/xormstore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										251
									
								
								vendor/github.com/lafriks/xormstore/xormstore.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							|  | @ -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", | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue