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 @@
|
||||||
|
[![GoDoc](https://godoc.org/github.com/lafriks/xormstore?status.svg)](https://godoc.org/github.com/lafriks/xormstore)
|
||||||
|
[![Build Status](https://travis-ci.org/lafriks/xormstore.svg?branch=master)](https://travis-ci.org/lafriks/xormstore)
|
||||||
|
[![codecov](https://codecov.io/gh/lafriks/xormstore/branch/master/graph/badge.svg)](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",
|
||||||
|
|
Reference in a new issue