From 8ef9965eef3250a5578739258c9f12315ead1771 Mon Sep 17 00:00:00 2001 From: Ming Deng Date: Thu, 3 Sep 2020 23:36:09 +0800 Subject: [PATCH] Adapter: session module --- .../session/couchbase/sess_couchbase.go | 118 ++++++ pkg/adapter/session/ledis/ledis_session.go | 86 +++++ pkg/adapter/session/memcache/sess_memcache.go | 118 ++++++ pkg/adapter/session/mysql/sess_mysql.go | 135 +++++++ .../session/postgres/sess_postgresql.go | 139 ++++++++ pkg/adapter/session/provider_adapter.go | 104 ++++++ pkg/adapter/session/redis/sess_redis.go | 121 +++++++ .../session/redis_cluster/redis_cluster.go | 120 +++++++ .../redis_sentinel/sess_redis_sentinel.go | 121 +++++++ .../sess_redis_sentinel_test.go | 90 +++++ pkg/adapter/session/sess_cookie.go | 114 ++++++ pkg/adapter/session/sess_cookie_test.go | 105 ++++++ pkg/adapter/session/sess_file.go | 106 ++++++ pkg/adapter/session/sess_file_test.go | 336 ++++++++++++++++++ pkg/adapter/session/sess_mem.go | 106 ++++++ pkg/adapter/session/sess_mem_test.go | 58 +++ pkg/adapter/session/sess_test.go | 51 +++ pkg/adapter/session/sess_utils.go | 29 ++ pkg/adapter/session/session.go | 166 +++++++++ pkg/adapter/session/ssdb/sess_ssdb.go | 84 +++++ pkg/adapter/session/store_adapter.go | 84 +++++ pkg/infrastructure/session/sess_cookie.go | 2 +- pkg/infrastructure/session/sess_mem.go | 6 +- 23 files changed, 2395 insertions(+), 4 deletions(-) create mode 100644 pkg/adapter/session/couchbase/sess_couchbase.go create mode 100644 pkg/adapter/session/ledis/ledis_session.go create mode 100644 pkg/adapter/session/memcache/sess_memcache.go create mode 100644 pkg/adapter/session/mysql/sess_mysql.go create mode 100644 pkg/adapter/session/postgres/sess_postgresql.go create mode 100644 pkg/adapter/session/provider_adapter.go create mode 100644 pkg/adapter/session/redis/sess_redis.go create mode 100644 pkg/adapter/session/redis_cluster/redis_cluster.go create mode 100644 pkg/adapter/session/redis_sentinel/sess_redis_sentinel.go create mode 100644 pkg/adapter/session/redis_sentinel/sess_redis_sentinel_test.go create mode 100644 pkg/adapter/session/sess_cookie.go create mode 100644 pkg/adapter/session/sess_cookie_test.go create mode 100644 pkg/adapter/session/sess_file.go create mode 100644 pkg/adapter/session/sess_file_test.go create mode 100644 pkg/adapter/session/sess_mem.go create mode 100644 pkg/adapter/session/sess_mem_test.go create mode 100644 pkg/adapter/session/sess_test.go create mode 100644 pkg/adapter/session/sess_utils.go create mode 100644 pkg/adapter/session/session.go create mode 100644 pkg/adapter/session/ssdb/sess_ssdb.go create mode 100644 pkg/adapter/session/store_adapter.go diff --git a/pkg/adapter/session/couchbase/sess_couchbase.go b/pkg/adapter/session/couchbase/sess_couchbase.go new file mode 100644 index 00000000..bce09641 --- /dev/null +++ b/pkg/adapter/session/couchbase/sess_couchbase.go @@ -0,0 +1,118 @@ +// Copyright 2014 beego Author. 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 couchbase for session provider +// +// depend on github.com/couchbaselabs/go-couchbasee +// +// go install github.com/couchbaselabs/go-couchbase +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/couchbase" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("couchbase", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"http://host:port/, Pool, Bucket"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package couchbase + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + beecb "github.com/astaxie/beego/pkg/infrastructure/session/couchbase" +) + +// SessionStore store each session +type SessionStore beecb.SessionStore + +// Provider couchabse provided +type Provider beecb.Provider + +// Set value to couchabse session +func (cs *SessionStore) Set(key, value interface{}) error { + return (*beecb.SessionStore)(cs).Set(context.Background(), key, value) +} + +// Get value from couchabse session +func (cs *SessionStore) Get(key interface{}) interface{} { + return (*beecb.SessionStore)(cs).Get(context.Background(), key) +} + +// Delete value in couchbase session by given key +func (cs *SessionStore) Delete(key interface{}) error { + return (*beecb.SessionStore)(cs).Delete(context.Background(), key) +} + +// Flush Clean all values in couchbase session +func (cs *SessionStore) Flush() error { + return (*beecb.SessionStore)(cs).Flush(context.Background()) +} + +// SessionID Get couchbase session store id +func (cs *SessionStore) SessionID() string { + return (*beecb.SessionStore)(cs).SessionID(context.Background()) +} + +// SessionRelease Write couchbase session with Gob string +func (cs *SessionStore) SessionRelease(w http.ResponseWriter) { + (*beecb.SessionStore)(cs).SessionRelease(context.Background(), w) +} + +// SessionInit init couchbase session +// savepath like couchbase server REST/JSON URL +// e.g. http://host:port/, Pool, Bucket +func (cp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*beecb.Provider)(cp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read couchbase session by sid +func (cp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*beecb.Provider)(cp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist Check couchbase session exist. +// it checkes sid exist or not. +func (cp *Provider) SessionExist(sid string) bool { + res, _ := (*beecb.Provider)(cp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate remove oldsid and use sid to generate new session +func (cp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*beecb.Provider)(cp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy Remove bucket in this couchbase +func (cp *Provider) SessionDestroy(sid string) error { + return (*beecb.Provider)(cp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Recycle +func (cp *Provider) SessionGC() { + (*beecb.Provider)(cp).SessionGC(context.Background()) +} + +// SessionAll return all active session +func (cp *Provider) SessionAll() int { + return (*beecb.Provider)(cp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/ledis/ledis_session.go b/pkg/adapter/session/ledis/ledis_session.go new file mode 100644 index 00000000..96198837 --- /dev/null +++ b/pkg/adapter/session/ledis/ledis_session.go @@ -0,0 +1,86 @@ +// Package ledis provide session Provider +package ledis + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + beeLedis "github.com/astaxie/beego/pkg/infrastructure/session/ledis" +) + +// SessionStore ledis session store +type SessionStore beeLedis.SessionStore + +// Set value in ledis session +func (ls *SessionStore) Set(key, value interface{}) error { + return (*beeLedis.SessionStore)(ls).Set(context.Background(), key, value) +} + +// Get value in ledis session +func (ls *SessionStore) Get(key interface{}) interface{} { + return (*beeLedis.SessionStore)(ls).Get(context.Background(), key) +} + +// Delete value in ledis session +func (ls *SessionStore) Delete(key interface{}) error { + return (*beeLedis.SessionStore)(ls).Delete(context.Background(), key) +} + +// Flush clear all values in ledis session +func (ls *SessionStore) Flush() error { + return (*beeLedis.SessionStore)(ls).Flush(context.Background()) +} + +// SessionID get ledis session id +func (ls *SessionStore) SessionID() string { + return (*beeLedis.SessionStore)(ls).SessionID(context.Background()) +} + +// SessionRelease save session values to ledis +func (ls *SessionStore) SessionRelease(w http.ResponseWriter) { + (*beeLedis.SessionStore)(ls).SessionRelease(context.Background(), w) +} + +// Provider ledis session provider +type Provider beeLedis.Provider + +// SessionInit init ledis session +// savepath like ledis server saveDataPath,pool size +// e.g. 127.0.0.1:6379,100,astaxie +func (lp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*beeLedis.Provider)(lp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read ledis session by sid +func (lp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*beeLedis.Provider)(lp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check ledis session exist by sid +func (lp *Provider) SessionExist(sid string) bool { + res, _ := (*beeLedis.Provider)(lp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for ledis session +func (lp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*beeLedis.Provider)(lp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete ledis session by id +func (lp *Provider) SessionDestroy(sid string) error { + return (*beeLedis.Provider)(lp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Impelment method, no used. +func (lp *Provider) SessionGC() { + (*beeLedis.Provider)(lp).SessionGC(context.Background()) +} + +// SessionAll return all active session +func (lp *Provider) SessionAll() int { + return (*beeLedis.Provider)(lp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/memcache/sess_memcache.go b/pkg/adapter/session/memcache/sess_memcache.go new file mode 100644 index 00000000..8afa79aa --- /dev/null +++ b/pkg/adapter/session/memcache/sess_memcache.go @@ -0,0 +1,118 @@ +// Copyright 2014 beego Author. 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 memcache for session provider +// +// depend on github.com/bradfitz/gomemcache/memcache +// +// go install github.com/bradfitz/gomemcache/memcache +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/memcache" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("memcache", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:11211"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package memcache + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + + beemem "github.com/astaxie/beego/pkg/infrastructure/session/memcache" +) + +// SessionStore memcache session store +type SessionStore beemem.SessionStore + +// Set value in memcache session +func (rs *SessionStore) Set(key, value interface{}) error { + return (*beemem.SessionStore)(rs).Set(context.Background(), key, value) +} + +// Get value in memcache session +func (rs *SessionStore) Get(key interface{}) interface{} { + return (*beemem.SessionStore)(rs).Get(context.Background(), key) +} + +// Delete value in memcache session +func (rs *SessionStore) Delete(key interface{}) error { + return (*beemem.SessionStore)(rs).Delete(context.Background(), key) +} + +// Flush clear all values in memcache session +func (rs *SessionStore) Flush() error { + return (*beemem.SessionStore)(rs).Flush(context.Background()) +} + +// SessionID get memcache session id +func (rs *SessionStore) SessionID() string { + return (*beemem.SessionStore)(rs).SessionID(context.Background()) +} + +// SessionRelease save session values to memcache +func (rs *SessionStore) SessionRelease(w http.ResponseWriter) { + (*beemem.SessionStore)(rs).SessionRelease(context.Background(), w) +} + +// MemProvider memcache session provider +type MemProvider beemem.MemProvider + +// SessionInit init memcache session +// savepath like +// e.g. 127.0.0.1:9090 +func (rp *MemProvider) SessionInit(maxlifetime int64, savePath string) error { + return (*beemem.MemProvider)(rp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read memcache session by sid +func (rp *MemProvider) SessionRead(sid string) (session.Store, error) { + s, err := (*beemem.MemProvider)(rp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check memcache session exist by sid +func (rp *MemProvider) SessionExist(sid string) bool { + res, _ := (*beemem.MemProvider)(rp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for memcache session +func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*beemem.MemProvider)(rp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete memcache session by id +func (rp *MemProvider) SessionDestroy(sid string) error { + return (*beemem.MemProvider)(rp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Impelment method, no used. +func (rp *MemProvider) SessionGC() { + (*beemem.MemProvider)(rp).SessionGC(context.Background()) +} + +// SessionAll return all activeSession +func (rp *MemProvider) SessionAll() int { + return (*beemem.MemProvider)(rp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/mysql/sess_mysql.go b/pkg/adapter/session/mysql/sess_mysql.go new file mode 100644 index 00000000..1850a380 --- /dev/null +++ b/pkg/adapter/session/mysql/sess_mysql.go @@ -0,0 +1,135 @@ +// Copyright 2014 beego Author. 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 mysql for session provider +// +// depends on github.com/go-sql-driver/mysql: +// +// go install github.com/go-sql-driver/mysql +// +// mysql session support need create table as sql: +// CREATE TABLE `session` ( +// `session_key` char(64) NOT NULL, +// `session_data` blob, +// `session_expiry` int(11) unsigned NOT NULL, +// PRIMARY KEY (`session_key`) +// ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/mysql" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("mysql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package mysql + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + "github.com/astaxie/beego/pkg/infrastructure/session/mysql" + + // import mysql driver + _ "github.com/go-sql-driver/mysql" +) + +var ( + // TableName store the session in MySQL + TableName = mysql.TableName + mysqlpder = &Provider{} +) + +// SessionStore mysql session store +type SessionStore mysql.SessionStore + +// Set value in mysql session. +// it is temp value in map. +func (st *SessionStore) Set(key, value interface{}) error { + return (*mysql.SessionStore)(st).Set(context.Background(), key, value) +} + +// Get value from mysql session +func (st *SessionStore) Get(key interface{}) interface{} { + return (*mysql.SessionStore)(st).Get(context.Background(), key) +} + +// Delete value in mysql session +func (st *SessionStore) Delete(key interface{}) error { + return (*mysql.SessionStore)(st).Delete(context.Background(), key) +} + +// Flush clear all values in mysql session +func (st *SessionStore) Flush() error { + return (*mysql.SessionStore)(st).Flush(context.Background()) +} + +// SessionID get session id of this mysql session store +func (st *SessionStore) SessionID() string { + return (*mysql.SessionStore)(st).SessionID(context.Background()) +} + +// SessionRelease save mysql session values to database. +// must call this method to save values to database. +func (st *SessionStore) SessionRelease(w http.ResponseWriter) { + (*mysql.SessionStore)(st).SessionRelease(context.Background(), w) +} + +// Provider mysql session provider +type Provider mysql.Provider + +// SessionInit init mysql session. +// savepath is the connection string of mysql. +func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*mysql.Provider)(mp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead get mysql session by sid +func (mp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*mysql.Provider)(mp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check mysql session exist +func (mp *Provider) SessionExist(sid string) bool { + res, _ := (*mysql.Provider)(mp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for mysql session +func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*mysql.Provider)(mp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete mysql session by sid +func (mp *Provider) SessionDestroy(sid string) error { + return (*mysql.Provider)(mp).SessionDestroy(context.Background(), sid) +} + +// SessionGC delete expired values in mysql session +func (mp *Provider) SessionGC() { + (*mysql.Provider)(mp).SessionGC(context.Background()) +} + +// SessionAll count values in mysql session +func (mp *Provider) SessionAll() int { + return (*mysql.Provider)(mp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/postgres/sess_postgresql.go b/pkg/adapter/session/postgres/sess_postgresql.go new file mode 100644 index 00000000..de1adbc4 --- /dev/null +++ b/pkg/adapter/session/postgres/sess_postgresql.go @@ -0,0 +1,139 @@ +// Copyright 2014 beego Author. 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 postgres for session provider +// +// depends on github.com/lib/pq: +// +// go install github.com/lib/pq +// +// +// needs this table in your database: +// +// CREATE TABLE session ( +// session_key char(64) NOT NULL, +// session_data bytea, +// session_expiry timestamp NOT NULL, +// CONSTRAINT session_key PRIMARY KEY(session_key) +// ); +// +// will be activated with these settings in app.conf: +// +// SessionOn = true +// SessionProvider = postgresql +// SessionSavePath = "user=a password=b dbname=c sslmode=disable" +// SessionName = session +// +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/postgresql" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("postgresql", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"user=pqgotest dbname=pqgotest sslmode=verify-full"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package postgres + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + // import postgresql Driver + _ "github.com/lib/pq" + + "github.com/astaxie/beego/pkg/infrastructure/session/postgres" +) + +// SessionStore postgresql session store +type SessionStore postgres.SessionStore + +// Set value in postgresql session. +// it is temp value in map. +func (st *SessionStore) Set(key, value interface{}) error { + return (*postgres.SessionStore)(st).Set(context.Background(), key, value) +} + +// Get value from postgresql session +func (st *SessionStore) Get(key interface{}) interface{} { + return (*postgres.SessionStore)(st).Get(context.Background(), key) +} + +// Delete value in postgresql session +func (st *SessionStore) Delete(key interface{}) error { + return (*postgres.SessionStore)(st).Delete(context.Background(), key) +} + +// Flush clear all values in postgresql session +func (st *SessionStore) Flush() error { + return (*postgres.SessionStore)(st).Flush(context.Background()) +} + +// SessionID get session id of this postgresql session store +func (st *SessionStore) SessionID() string { + return (*postgres.SessionStore)(st).SessionID(context.Background()) +} + +// SessionRelease save postgresql session values to database. +// must call this method to save values to database. +func (st *SessionStore) SessionRelease(w http.ResponseWriter) { + (*postgres.SessionStore)(st).SessionRelease(context.Background(), w) +} + +// Provider postgresql session provider +type Provider postgres.Provider + +// SessionInit init postgresql session. +// savepath is the connection string of postgresql. +func (mp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*postgres.Provider)(mp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead get postgresql session by sid +func (mp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*postgres.Provider)(mp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check postgresql session exist +func (mp *Provider) SessionExist(sid string) bool { + res, _ := (*postgres.Provider)(mp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for postgresql session +func (mp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*postgres.Provider)(mp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete postgresql session by sid +func (mp *Provider) SessionDestroy(sid string) error { + return (*postgres.Provider)(mp).SessionDestroy(context.Background(), sid) +} + +// SessionGC delete expired values in postgresql session +func (mp *Provider) SessionGC() { + (*postgres.Provider)(mp).SessionGC(context.Background()) +} + +// SessionAll count values in postgresql session +func (mp *Provider) SessionAll() int { + return (*postgres.Provider)(mp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/provider_adapter.go b/pkg/adapter/session/provider_adapter.go new file mode 100644 index 00000000..11177a4d --- /dev/null +++ b/pkg/adapter/session/provider_adapter.go @@ -0,0 +1,104 @@ +// Copyright 2020 +// +// 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 session + +import ( + "context" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +type oldToNewProviderAdapter struct { + delegate Provider +} + +func (o *oldToNewProviderAdapter) SessionInit(ctx context.Context, gclifetime int64, config string) error { + return o.delegate.SessionInit(gclifetime, config) +} + +func (o *oldToNewProviderAdapter) SessionRead(ctx context.Context, sid string) (session.Store, error) { + store, err := o.delegate.SessionRead(sid) + return &oldToNewStoreAdapter{ + delegate: store, + }, err +} + +func (o *oldToNewProviderAdapter) SessionExist(ctx context.Context, sid string) (bool, error) { + return o.delegate.SessionExist(sid), nil +} + +func (o *oldToNewProviderAdapter) SessionRegenerate(ctx context.Context, oldsid, sid string) (session.Store, error) { + s, err := o.delegate.SessionRegenerate(oldsid, sid) + return &oldToNewStoreAdapter{ + delegate: s, + }, err +} + +func (o *oldToNewProviderAdapter) SessionDestroy(ctx context.Context, sid string) error { + return o.delegate.SessionDestroy(sid) +} + +func (o *oldToNewProviderAdapter) SessionAll(ctx context.Context) int { + return o.delegate.SessionAll() +} + +func (o *oldToNewProviderAdapter) SessionGC(ctx context.Context) { + o.delegate.SessionGC() +} + +type newToOldProviderAdapter struct { + delegate session.Provider +} + +func (n *newToOldProviderAdapter) SessionInit(gclifetime int64, config string) error { + return n.delegate.SessionInit(context.Background(), gclifetime, config) +} + +func (n *newToOldProviderAdapter) SessionRead(sid string) (Store, error) { + s, err := n.delegate.SessionRead(context.Background(), sid) + if adt, ok := s.(*oldToNewStoreAdapter); err == nil && ok { + return adt.delegate, err + } + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +func (n *newToOldProviderAdapter) SessionExist(sid string) bool { + res, _ := n.delegate.SessionExist(context.Background(), sid) + return res +} + +func (n *newToOldProviderAdapter) SessionRegenerate(oldsid, sid string) (Store, error) { + s, err := n.delegate.SessionRegenerate(context.Background(), oldsid, sid) + if adt, ok := s.(*oldToNewStoreAdapter); err == nil && ok { + return adt.delegate, err + } + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +func (n *newToOldProviderAdapter) SessionDestroy(sid string) error { + return n.delegate.SessionDestroy(context.Background(), sid) +} + +func (n *newToOldProviderAdapter) SessionAll() int { + return n.delegate.SessionAll(context.Background()) +} + +func (n *newToOldProviderAdapter) SessionGC() { + n.delegate.SessionGC(context.Background()) +} diff --git a/pkg/adapter/session/redis/sess_redis.go b/pkg/adapter/session/redis/sess_redis.go new file mode 100644 index 00000000..6c521e50 --- /dev/null +++ b/pkg/adapter/session/redis/sess_redis.go @@ -0,0 +1,121 @@ +// Copyright 2014 beego Author. 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 redis for session provider +// +// depend on github.com/gomodule/redigo/redis +// +// go install github.com/gomodule/redigo/redis +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/redis" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("redis", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package redis + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + + beeRedis "github.com/astaxie/beego/pkg/infrastructure/session/redis" +) + +// MaxPoolSize redis max pool size +var MaxPoolSize = beeRedis.MaxPoolSize + +// SessionStore redis session store +type SessionStore beeRedis.SessionStore + +// Set value in redis session +func (rs *SessionStore) Set(key, value interface{}) error { + return (*beeRedis.SessionStore)(rs).Set(context.Background(), key, value) +} + +// Get value in redis session +func (rs *SessionStore) Get(key interface{}) interface{} { + return (*beeRedis.SessionStore)(rs).Get(context.Background(), key) +} + +// Delete value in redis session +func (rs *SessionStore) Delete(key interface{}) error { + return (*beeRedis.SessionStore)(rs).Delete(context.Background(), key) +} + +// Flush clear all values in redis session +func (rs *SessionStore) Flush() error { + return (*beeRedis.SessionStore)(rs).Flush(context.Background()) +} + +// SessionID get redis session id +func (rs *SessionStore) SessionID() string { + return (*beeRedis.SessionStore)(rs).SessionID(context.Background()) +} + +// SessionRelease save session values to redis +func (rs *SessionStore) SessionRelease(w http.ResponseWriter) { + (*beeRedis.SessionStore)(rs).SessionRelease(context.Background(), w) +} + +// Provider redis session provider +type Provider beeRedis.Provider + +// SessionInit init redis session +// savepath like redis server addr,pool size,password,dbnum,IdleTimeout second +// e.g. 127.0.0.1:6379,100,astaxie,0,30 +func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*beeRedis.Provider)(rp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read redis session by sid +func (rp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*beeRedis.Provider)(rp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check redis session exist by sid +func (rp *Provider) SessionExist(sid string) bool { + res, _ := (*beeRedis.Provider)(rp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for redis session +func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*beeRedis.Provider)(rp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete redis session by id +func (rp *Provider) SessionDestroy(sid string) error { + return (*beeRedis.Provider)(rp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Impelment method, no used. +func (rp *Provider) SessionGC() { + (*beeRedis.Provider)(rp).SessionGC(context.Background()) +} + +// SessionAll return all activeSession +func (rp *Provider) SessionAll() int { + return (*beeRedis.Provider)(rp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/redis_cluster/redis_cluster.go b/pkg/adapter/session/redis_cluster/redis_cluster.go new file mode 100644 index 00000000..03a805e4 --- /dev/null +++ b/pkg/adapter/session/redis_cluster/redis_cluster.go @@ -0,0 +1,120 @@ +// Copyright 2014 beego Author. 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 redis for session provider +// +// depend on github.com/go-redis/redis +// +// go install github.com/go-redis/redis +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/redis_cluster" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("redis_cluster", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:7070;127.0.0.1:7071"}``) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package redis_cluster + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + cluster "github.com/astaxie/beego/pkg/infrastructure/session/redis_cluster" +) + +// MaxPoolSize redis_cluster max pool size +var MaxPoolSize = cluster.MaxPoolSize + +// SessionStore redis_cluster session store +type SessionStore cluster.SessionStore + +// Set value in redis_cluster session +func (rs *SessionStore) Set(key, value interface{}) error { + return (*cluster.SessionStore)(rs).Set(context.Background(), key, value) +} + +// Get value in redis_cluster session +func (rs *SessionStore) Get(key interface{}) interface{} { + return (*cluster.SessionStore)(rs).Get(context.Background(), key) +} + +// Delete value in redis_cluster session +func (rs *SessionStore) Delete(key interface{}) error { + return (*cluster.SessionStore)(rs).Delete(context.Background(), key) +} + +// Flush clear all values in redis_cluster session +func (rs *SessionStore) Flush() error { + return (*cluster.SessionStore)(rs).Flush(context.Background()) +} + +// SessionID get redis_cluster session id +func (rs *SessionStore) SessionID() string { + return (*cluster.SessionStore)(rs).SessionID(context.Background()) +} + +// SessionRelease save session values to redis_cluster +func (rs *SessionStore) SessionRelease(w http.ResponseWriter) { + (*cluster.SessionStore)(rs).SessionRelease(context.Background(), w) +} + +// Provider redis_cluster session provider +type Provider cluster.Provider + +// SessionInit init redis_cluster session +// savepath like redis server addr,pool size,password,dbnum +// e.g. 127.0.0.1:6379;127.0.0.1:6380,100,test,0 +func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*cluster.Provider)(rp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read redis_cluster session by sid +func (rp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*cluster.Provider)(rp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check redis_cluster session exist by sid +func (rp *Provider) SessionExist(sid string) bool { + res, _ := (*cluster.Provider)(rp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for redis_cluster session +func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*cluster.Provider)(rp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete redis session by id +func (rp *Provider) SessionDestroy(sid string) error { + return (*cluster.Provider)(rp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Impelment method, no used. +func (rp *Provider) SessionGC() { + (*cluster.Provider)(rp).SessionGC(context.Background()) +} + +// SessionAll return all activeSession +func (rp *Provider) SessionAll() int { + return (*cluster.Provider)(rp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/redis_sentinel/sess_redis_sentinel.go b/pkg/adapter/session/redis_sentinel/sess_redis_sentinel.go new file mode 100644 index 00000000..f5eb8a4f --- /dev/null +++ b/pkg/adapter/session/redis_sentinel/sess_redis_sentinel.go @@ -0,0 +1,121 @@ +// Copyright 2014 beego Author. 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 redis for session provider +// +// depend on github.com/go-redis/redis +// +// go install github.com/go-redis/redis +// +// Usage: +// import( +// _ "github.com/astaxie/beego/session/redis_sentinel" +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("redis_sentinel", ``{"cookieName":"gosessionid","gclifetime":3600,"ProviderConfig":"127.0.0.1:26379;127.0.0.2:26379"}``) +// go globalSessions.GC() +// } +// +// more detail about params: please check the notes on the function SessionInit in this package +package redis_sentinel + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + + sentinel "github.com/astaxie/beego/pkg/infrastructure/session/redis_sentinel" +) + +// DefaultPoolSize redis_sentinel default pool size +var DefaultPoolSize = sentinel.DefaultPoolSize + +// SessionStore redis_sentinel session store +type SessionStore sentinel.SessionStore + +// Set value in redis_sentinel session +func (rs *SessionStore) Set(key, value interface{}) error { + return (*sentinel.SessionStore)(rs).Set(context.Background(), key, value) +} + +// Get value in redis_sentinel session +func (rs *SessionStore) Get(key interface{}) interface{} { + return (*sentinel.SessionStore)(rs).Get(context.Background(), key) +} + +// Delete value in redis_sentinel session +func (rs *SessionStore) Delete(key interface{}) error { + return (*sentinel.SessionStore)(rs).Delete(context.Background(), key) +} + +// Flush clear all values in redis_sentinel session +func (rs *SessionStore) Flush() error { + return (*sentinel.SessionStore)(rs).Flush(context.Background()) +} + +// SessionID get redis_sentinel session id +func (rs *SessionStore) SessionID() string { + return (*sentinel.SessionStore)(rs).SessionID(context.Background()) +} + +// SessionRelease save session values to redis_sentinel +func (rs *SessionStore) SessionRelease(w http.ResponseWriter) { + (*sentinel.SessionStore)(rs).SessionRelease(context.Background(), w) +} + +// Provider redis_sentinel session provider +type Provider sentinel.Provider + +// SessionInit init redis_sentinel session +// savepath like redis sentinel addr,pool size,password,dbnum,masterName +// e.g. 127.0.0.1:26379;127.0.0.2:26379,100,1qaz2wsx,0,mymaster +func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { + return (*sentinel.Provider)(rp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead read redis_sentinel session by sid +func (rp *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*sentinel.Provider)(rp).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist check redis_sentinel session exist by sid +func (rp *Provider) SessionExist(sid string) bool { + res, _ := (*sentinel.Provider)(rp).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for redis_sentinel session +func (rp *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*sentinel.Provider)(rp).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy delete redis session by id +func (rp *Provider) SessionDestroy(sid string) error { + return (*sentinel.Provider)(rp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Impelment method, no used. +func (rp *Provider) SessionGC() { + (*sentinel.Provider)(rp).SessionGC(context.Background()) +} + +// SessionAll return all activeSession +func (rp *Provider) SessionAll() int { + return (*sentinel.Provider)(rp).SessionAll(context.Background()) +} diff --git a/pkg/adapter/session/redis_sentinel/sess_redis_sentinel_test.go b/pkg/adapter/session/redis_sentinel/sess_redis_sentinel_test.go new file mode 100644 index 00000000..7c33985f --- /dev/null +++ b/pkg/adapter/session/redis_sentinel/sess_redis_sentinel_test.go @@ -0,0 +1,90 @@ +package redis_sentinel + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/astaxie/beego/pkg/adapter/session" +) + +func TestRedisSentinel(t *testing.T) { + sessionConfig := &session.ManagerConfig{ + CookieName: "gosessionid", + EnableSetCookie: true, + Gclifetime: 3600, + Maxlifetime: 3600, + Secure: false, + CookieLifeTime: 3600, + ProviderConfig: "127.0.0.1:6379,100,,0,master", + } + globalSessions, e := session.NewManager("redis_sentinel", sessionConfig) + if e != nil { + t.Log(e) + return + } + // todo test if e==nil + go globalSessions.GC() + + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("session start failed:", err) + } + defer sess.SessionRelease(w) + + // SET AND GET + err = sess.Set("username", "astaxie") + if err != nil { + t.Fatal("set username failed:", err) + } + username := sess.Get("username") + if username != "astaxie" { + t.Fatal("get username failed") + } + + // DELETE + err = sess.Delete("username") + if err != nil { + t.Fatal("delete username failed:", err) + } + username = sess.Get("username") + if username != nil { + t.Fatal("delete username failed") + } + + // FLUSH + err = sess.Set("username", "astaxie") + if err != nil { + t.Fatal("set failed:", err) + } + err = sess.Set("password", "1qaz2wsx") + if err != nil { + t.Fatal("set failed:", err) + } + username = sess.Get("username") + if username != "astaxie" { + t.Fatal("get username failed") + } + password := sess.Get("password") + if password != "1qaz2wsx" { + t.Fatal("get password failed") + } + err = sess.Flush() + if err != nil { + t.Fatal("flush failed:", err) + } + username = sess.Get("username") + if username != nil { + t.Fatal("flush failed") + } + password = sess.Get("password") + if password != nil { + t.Fatal("flush failed") + } + + sess.SessionRelease(w) + +} diff --git a/pkg/adapter/session/sess_cookie.go b/pkg/adapter/session/sess_cookie.go new file mode 100644 index 00000000..32216040 --- /dev/null +++ b/pkg/adapter/session/sess_cookie.go @@ -0,0 +1,114 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +// CookieSessionStore Cookie SessionStore +type CookieSessionStore session.CookieSessionStore + +// Set value to cookie session. +// the value are encoded as gob with hash block string. +func (st *CookieSessionStore) Set(key, value interface{}) error { + return (*session.CookieSessionStore)(st).Set(context.Background(), key, value) +} + +// Get value from cookie session +func (st *CookieSessionStore) Get(key interface{}) interface{} { + return (*session.CookieSessionStore)(st).Get(context.Background(), key) +} + +// Delete value in cookie session +func (st *CookieSessionStore) Delete(key interface{}) error { + return (*session.CookieSessionStore)(st).Delete(context.Background(), key) +} + +// Flush Clean all values in cookie session +func (st *CookieSessionStore) Flush() error { + return (*session.CookieSessionStore)(st).Flush(context.Background()) +} + +// SessionID Return id of this cookie session +func (st *CookieSessionStore) SessionID() string { + return (*session.CookieSessionStore)(st).SessionID(context.Background()) +} + +// SessionRelease Write cookie session to http response cookie +func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { + (*session.CookieSessionStore)(st).SessionRelease(context.Background(), w) +} + +// CookieProvider Cookie session provider +type CookieProvider session.CookieProvider + +// SessionInit Init cookie session provider with max lifetime and config json. +// maxlifetime is ignored. +// json config: +// securityKey - hash string +// blockKey - gob encode hash string. it's saved as aes crypto. +// securityName - recognized name in encoded cookie string +// cookieName - cookie name +// maxage - cookie max life time. +func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error { + return (*session.CookieProvider)(pder).SessionInit(context.Background(), maxlifetime, config) +} + +// SessionRead Get SessionStore in cooke. +// decode cooke string to map and put into SessionStore with sid. +func (pder *CookieProvider) SessionRead(sid string) (Store, error) { + s, err := (*session.CookieProvider)(pder).SessionRead(context.Background(), sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionExist Cookie session is always existed +func (pder *CookieProvider) SessionExist(sid string) bool { + res, _ := (*session.CookieProvider)(pder).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate Implement method, no used. +func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (Store, error) { + s, err := (*session.CookieProvider)(pder).SessionRegenerate(context.Background(), oldsid, sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionDestroy Implement method, no used. +func (pder *CookieProvider) SessionDestroy(sid string) error { + return (*session.CookieProvider)(pder).SessionDestroy(context.Background(), sid) +} + +// SessionGC Implement method, no used. +func (pder *CookieProvider) SessionGC() { + (*session.CookieProvider)(pder).SessionGC(context.Background()) +} + +// SessionAll Implement method, return 0. +func (pder *CookieProvider) SessionAll() int { + return (*session.CookieProvider)(pder).SessionAll(context.Background()) +} + +// SessionUpdate Implement method, no used. +func (pder *CookieProvider) SessionUpdate(sid string) error { + return (*session.CookieProvider)(pder).SessionUpdate(context.Background(), sid) +} diff --git a/pkg/adapter/session/sess_cookie_test.go b/pkg/adapter/session/sess_cookie_test.go new file mode 100644 index 00000000..b6726005 --- /dev/null +++ b/pkg/adapter/session/sess_cookie_test.go @@ -0,0 +1,105 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestCookie(t *testing.T) { + config := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}` + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, err := NewManager("cookie", conf) + if err != nil { + t.Fatal("init cookie session err", err) + } + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } + err = sess.Set("username", "astaxie") + if err != nil { + t.Fatal("set error,", err) + } + if username := sess.Get("username"); username != "astaxie" { + t.Fatal("get username error") + } + sess.SessionRelease(w) + if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" { + t.Fatal("setcookie error") + } else { + parts := strings.Split(strings.TrimSpace(cookiestr), ";") + for k, v := range parts { + nameval := strings.Split(v, "=") + if k == 0 && nameval[0] != "gosessionid" { + t.Fatal("error") + } + } + } +} + +func TestDestorySessionCookie(t *testing.T) { + config := `{"cookieName":"gosessionid","enableSetCookie":true,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}` + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, err := NewManager("cookie", conf) + if err != nil { + t.Fatal("init cookie session err", err) + } + + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + session, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("session start err,", err) + } + + // request again ,will get same sesssion id . + r1, _ := http.NewRequest("GET", "/", nil) + r1.Header.Set("Cookie", w.Header().Get("Set-Cookie")) + w = httptest.NewRecorder() + newSession, err := globalSessions.SessionStart(w, r1) + if err != nil { + t.Fatal("session start err,", err) + } + if newSession.SessionID() != session.SessionID() { + t.Fatal("get cookie session id is not the same again.") + } + + // After destroy session , will get a new session id . + globalSessions.SessionDestroy(w, r1) + r2, _ := http.NewRequest("GET", "/", nil) + r2.Header.Set("Cookie", w.Header().Get("Set-Cookie")) + + w = httptest.NewRecorder() + newSession, err = globalSessions.SessionStart(w, r2) + if err != nil { + t.Fatal("session start error") + } + if newSession.SessionID() == session.SessionID() { + t.Fatal("after destroy session and reqeust again ,get cookie session id is same.") + } +} diff --git a/pkg/adapter/session/sess_file.go b/pkg/adapter/session/sess_file.go new file mode 100644 index 00000000..b9648998 --- /dev/null +++ b/pkg/adapter/session/sess_file.go @@ -0,0 +1,106 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +// FileSessionStore File session store +type FileSessionStore session.FileSessionStore + +// Set value to file session +func (fs *FileSessionStore) Set(key, value interface{}) error { + return (*session.FileSessionStore)(fs).Set(context.Background(), key, value) +} + +// Get value from file session +func (fs *FileSessionStore) Get(key interface{}) interface{} { + return (*session.FileSessionStore)(fs).Get(context.Background(), key) +} + +// Delete value in file session by given key +func (fs *FileSessionStore) Delete(key interface{}) error { + return (*session.FileSessionStore)(fs).Delete(context.Background(), key) +} + +// Flush Clean all values in file session +func (fs *FileSessionStore) Flush() error { + return (*session.FileSessionStore)(fs).Flush(context.Background()) +} + +// SessionID Get file session store id +func (fs *FileSessionStore) SessionID() string { + return (*session.FileSessionStore)(fs).SessionID(context.Background()) +} + +// SessionRelease Write file session to local file with Gob string +func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { + (*session.FileSessionStore)(fs).SessionRelease(context.Background(), w) +} + +// FileProvider File session provider +type FileProvider session.FileProvider + +// SessionInit Init file session provider. +// savePath sets the session files path. +func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { + return (*session.FileProvider)(fp).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead Read file session by sid. +// if file is not exist, create it. +// the file path is generated from sid string. +func (fp *FileProvider) SessionRead(sid string) (Store, error) { + s, err := (*session.FileProvider)(fp).SessionRead(context.Background(), sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionExist Check file session exist. +// it checks the file named from sid exist or not. +func (fp *FileProvider) SessionExist(sid string) bool { + res, _ := (*session.FileProvider)(fp).SessionExist(context.Background(), sid) + return res +} + +// SessionDestroy Remove all files in this save path +func (fp *FileProvider) SessionDestroy(sid string) error { + return (*session.FileProvider)(fp).SessionDestroy(context.Background(), sid) +} + +// SessionGC Recycle files in save path +func (fp *FileProvider) SessionGC() { + (*session.FileProvider)(fp).SessionGC(context.Background()) +} + +// SessionAll Get active file session number. +// it walks save path to count files. +func (fp *FileProvider) SessionAll() int { + return (*session.FileProvider)(fp).SessionAll(context.Background()) +} + +// SessionRegenerate Generate new sid for file session. +// it delete old file and create new file named from new sid. +func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (Store, error) { + s, err := (*session.FileProvider)(fp).SessionRegenerate(context.Background(), oldsid, sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} diff --git a/pkg/adapter/session/sess_file_test.go b/pkg/adapter/session/sess_file_test.go new file mode 100644 index 00000000..4c90a3ac --- /dev/null +++ b/pkg/adapter/session/sess_file_test.go @@ -0,0 +1,336 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "fmt" + "os" + "sync" + "testing" + "time" +) + +const sid = "Session_id" +const sidNew = "Session_id_new" +const sessionPath = "./_session_runtime" + +var ( + mutex sync.Mutex +) + +func TestFileProvider_SessionExist(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + if fp.SessionExist(sid) { + t.Error() + } + + _, err := fp.SessionRead(sid) + if err != nil { + t.Error(err) + } + + if !fp.SessionExist(sid) { + t.Error() + } +} + +func TestFileProvider_SessionExist2(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + if fp.SessionExist(sid) { + t.Error() + } + + if fp.SessionExist("") { + t.Error() + } + + if fp.SessionExist("1") { + t.Error() + } +} + +func TestFileProvider_SessionRead(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + s, err := fp.SessionRead(sid) + if err != nil { + t.Error(err) + } + + _ = s.Set("sessionValue", 18975) + v := s.Get("sessionValue") + + if v.(int) != 18975 { + t.Error() + } +} + +func TestFileProvider_SessionRead1(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + _, err := fp.SessionRead("") + if err == nil { + t.Error(err) + } + + _, err = fp.SessionRead("1") + if err == nil { + t.Error(err) + } +} + +func TestFileProvider_SessionAll(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + sessionCount := 546 + + for i := 1; i <= sessionCount; i++ { + _, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i)) + if err != nil { + t.Error(err) + } + } + + if fp.SessionAll() != sessionCount { + t.Error() + } +} + +func TestFileProvider_SessionRegenerate(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + _, err := fp.SessionRead(sid) + if err != nil { + t.Error(err) + } + + if !fp.SessionExist(sid) { + t.Error() + } + + _, err = fp.SessionRegenerate(sid, sidNew) + if err != nil { + t.Error(err) + } + + if fp.SessionExist(sid) { + t.Error() + } + + if !fp.SessionExist(sidNew) { + t.Error() + } +} + +func TestFileProvider_SessionDestroy(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + _, err := fp.SessionRead(sid) + if err != nil { + t.Error(err) + } + + if !fp.SessionExist(sid) { + t.Error() + } + + err = fp.SessionDestroy(sid) + if err != nil { + t.Error(err) + } + + if fp.SessionExist(sid) { + t.Error() + } +} + +func TestFileProvider_SessionGC(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(1, sessionPath) + + sessionCount := 412 + + for i := 1; i <= sessionCount; i++ { + _, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i)) + if err != nil { + t.Error(err) + } + } + + time.Sleep(2 * time.Second) + + fp.SessionGC() + if fp.SessionAll() != 0 { + t.Error() + } +} + +func TestFileSessionStore_Set(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + sessionCount := 100 + s, _ := fp.SessionRead(sid) + for i := 1; i <= sessionCount; i++ { + err := s.Set(i, i) + if err != nil { + t.Error(err) + } + } +} + +func TestFileSessionStore_Get(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + sessionCount := 100 + s, _ := fp.SessionRead(sid) + for i := 1; i <= sessionCount; i++ { + _ = s.Set(i, i) + + v := s.Get(i) + if v.(int) != i { + t.Error() + } + } +} + +func TestFileSessionStore_Delete(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + s, _ := fp.SessionRead(sid) + s.Set("1", 1) + + if s.Get("1") == nil { + t.Error() + } + + s.Delete("1") + + if s.Get("1") != nil { + t.Error() + } +} + +func TestFileSessionStore_Flush(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + sessionCount := 100 + s, _ := fp.SessionRead(sid) + for i := 1; i <= sessionCount; i++ { + _ = s.Set(i, i) + } + + _ = s.Flush() + + for i := 1; i <= sessionCount; i++ { + if s.Get(i) != nil { + t.Error() + } + } +} + +func TestFileSessionStore_SessionID(t *testing.T) { + mutex.Lock() + defer mutex.Unlock() + os.RemoveAll(sessionPath) + defer os.RemoveAll(sessionPath) + fp := &FileProvider{} + + _ = fp.SessionInit(180, sessionPath) + + sessionCount := 85 + + for i := 1; i <= sessionCount; i++ { + s, err := fp.SessionRead(fmt.Sprintf("%s_%d", sid, i)) + if err != nil { + t.Error(err) + } + if s.SessionID() != fmt.Sprintf("%s_%d", sid, i) { + t.Error(err) + } + } +} diff --git a/pkg/adapter/session/sess_mem.go b/pkg/adapter/session/sess_mem.go new file mode 100644 index 00000000..818c8329 --- /dev/null +++ b/pkg/adapter/session/sess_mem.go @@ -0,0 +1,106 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +// MemSessionStore memory session store. +// it saved sessions in a map in memory. +type MemSessionStore session.MemSessionStore + +// Set value to memory session +func (st *MemSessionStore) Set(key, value interface{}) error { + return (*session.MemSessionStore)(st).Set(context.Background(), key, value) +} + +// Get value from memory session by key +func (st *MemSessionStore) Get(key interface{}) interface{} { + return (*session.MemSessionStore)(st).Get(context.Background(), key) +} + +// Delete in memory session by key +func (st *MemSessionStore) Delete(key interface{}) error { + return (*session.MemSessionStore)(st).Delete(context.Background(), key) +} + +// Flush clear all values in memory session +func (st *MemSessionStore) Flush() error { + return (*session.MemSessionStore)(st).Flush(context.Background()) +} + +// SessionID get this id of memory session store +func (st *MemSessionStore) SessionID() string { + return (*session.MemSessionStore)(st).SessionID(context.Background()) +} + +// SessionRelease Implement method, no used. +func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { + (*session.MemSessionStore)(st).SessionRelease(context.Background(), w) +} + +// MemProvider Implement the provider interface +type MemProvider session.MemProvider + +// SessionInit init memory session +func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { + return (*session.MemProvider)(pder).SessionInit(context.Background(), maxlifetime, savePath) +} + +// SessionRead get memory session store by sid +func (pder *MemProvider) SessionRead(sid string) (Store, error) { + s, err := (*session.MemProvider)(pder).SessionRead(context.Background(), sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionExist check session store exist in memory session by sid +func (pder *MemProvider) SessionExist(sid string) bool { + res, _ := (*session.MemProvider)(pder).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate generate new sid for session store in memory session +func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (Store, error) { + s, err := (*session.MemProvider)(pder).SessionRegenerate(context.Background(), oldsid, sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionDestroy delete session store in memory session by id +func (pder *MemProvider) SessionDestroy(sid string) error { + return (*session.MemProvider)(pder).SessionDestroy(context.Background(), sid) +} + +// SessionGC clean expired session stores in memory session +func (pder *MemProvider) SessionGC() { + (*session.MemProvider)(pder).SessionGC(context.Background()) +} + +// SessionAll get count number of memory session +func (pder *MemProvider) SessionAll() int { + return (*session.MemProvider)(pder).SessionAll(context.Background()) +} + +// SessionUpdate expand time of session store by id in memory session +func (pder *MemProvider) SessionUpdate(sid string) error { + return (*session.MemProvider)(pder).SessionUpdate(context.Background(), sid) +} diff --git a/pkg/adapter/session/sess_mem_test.go b/pkg/adapter/session/sess_mem_test.go new file mode 100644 index 00000000..2e8934b8 --- /dev/null +++ b/pkg/adapter/session/sess_mem_test.go @@ -0,0 +1,58 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +func TestMem(t *testing.T) { + config := `{"cookieName":"gosessionid","gclifetime":10, "enableSetCookie":true}` + conf := new(ManagerConfig) + if err := json.Unmarshal([]byte(config), conf); err != nil { + t.Fatal("json decode error", err) + } + globalSessions, _ := NewManager("memory", conf) + go globalSessions.GC() + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + sess, err := globalSessions.SessionStart(w, r) + if err != nil { + t.Fatal("set error,", err) + } + defer sess.SessionRelease(w) + err = sess.Set("username", "astaxie") + if err != nil { + t.Fatal("set error,", err) + } + if username := sess.Get("username"); username != "astaxie" { + t.Fatal("get username error") + } + if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" { + t.Fatal("setcookie error") + } else { + parts := strings.Split(strings.TrimSpace(cookiestr), ";") + for k, v := range parts { + nameval := strings.Split(v, "=") + if k == 0 && nameval[0] != "gosessionid" { + t.Fatal("error") + } + } + } +} diff --git a/pkg/adapter/session/sess_test.go b/pkg/adapter/session/sess_test.go new file mode 100644 index 00000000..aba702ca --- /dev/null +++ b/pkg/adapter/session/sess_test.go @@ -0,0 +1,51 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "testing" +) + +func Test_gob(t *testing.T) { + a := make(map[interface{}]interface{}) + a["username"] = "astaxie" + a[12] = 234 + a["user"] = User{"asta", "xie"} + b, err := EncodeGob(a) + if err != nil { + t.Error(err) + } + c, err := DecodeGob(b) + if err != nil { + t.Error(err) + } + if len(c) == 0 { + t.Error("decodeGob empty") + } + if c["username"] != "astaxie" { + t.Error("decode string error") + } + if c[12] != 234 { + t.Error("decode int error") + } + if c["user"].(User).Username != "asta" { + t.Error("decode struct error") + } +} + +type User struct { + Username string + NickName string +} diff --git a/pkg/adapter/session/sess_utils.go b/pkg/adapter/session/sess_utils.go new file mode 100644 index 00000000..3d107198 --- /dev/null +++ b/pkg/adapter/session/sess_utils.go @@ -0,0 +1,29 @@ +// Copyright 2014 beego Author. 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 session + +import ( + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +// EncodeGob encode the obj to gob +func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { + return session.EncodeGob(obj) +} + +// DecodeGob decode data to map +func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) { + return session.DecodeGob(encoded) +} diff --git a/pkg/adapter/session/session.go b/pkg/adapter/session/session.go new file mode 100644 index 00000000..eea2f90e --- /dev/null +++ b/pkg/adapter/session/session.go @@ -0,0 +1,166 @@ +// Copyright 2014 beego Author. 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 session provider +// +// Usage: +// import( +// "github.com/astaxie/beego/session" +// ) +// +// func init() { +// globalSessions, _ = session.NewManager("memory", `{"cookieName":"gosessionid", "enableSetCookie,omitempty": true, "gclifetime":3600, "maxLifetime": 3600, "secure": false, "cookieLifeTime": 3600, "providerConfig": ""}`) +// go globalSessions.GC() +// } +// +// more docs: http://beego.me/docs/module/session.md +package session + +import ( + "io" + "net/http" + "os" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +// Store contains all data for one session process with specific id. +type Store interface { + Set(key, value interface{}) error // set session value + Get(key interface{}) interface{} // get session value + Delete(key interface{}) error // delete session value + SessionID() string // back current sessionID + SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data + Flush() error // delete all data +} + +// Provider contains global session methods and saved SessionStores. +// it can operate a SessionStore by its id. +type Provider interface { + SessionInit(gclifetime int64, config string) error + SessionRead(sid string) (Store, error) + SessionExist(sid string) bool + SessionRegenerate(oldsid, sid string) (Store, error) + SessionDestroy(sid string) error + SessionAll() int // get all active session + SessionGC() +} + +// SLogger a helpful variable to log information about session +var SLogger = NewSessionLog(os.Stderr) + +// Register makes a session provide available by the provided name. +// If Register is called twice with the same name or if driver is nil, +// it panics. +func Register(name string, provide Provider) { + session.Register(name, &oldToNewProviderAdapter{ + delegate: provide, + }) +} + +// GetProvider +func GetProvider(name string) (Provider, error) { + res, err := session.GetProvider(name) + if adt, ok := res.(*oldToNewProviderAdapter); err == nil && ok { + return adt.delegate, err + } + + return &newToOldProviderAdapter{ + delegate: res, + }, err +} + +// ManagerConfig define the session config +type ManagerConfig session.ManagerConfig + +// Manager contains Provider and its configuration. +type Manager session.Manager + +// NewManager Create new Manager with provider name and json config string. +// provider name: +// 1. cookie +// 2. file +// 3. memory +// 4. redis +// 5. mysql +// json config: +// 1. is https default false +// 2. hashfunc default sha1 +// 3. hashkey default beegosessionkey +// 4. maxage default is none +func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) { + m, err := session.NewManager(provideName, (*session.ManagerConfig)(cf)) + return (*Manager)(m), err +} + +// GetProvider return current manager's provider +func (manager *Manager) GetProvider() Provider { + return &newToOldProviderAdapter{ + delegate: (*session.Manager)(manager).GetProvider(), + } +} + +// SessionStart generate or read the session id from http request. +// if session id exists, return SessionStore with this id. +func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (Store, error) { + s, err := (*session.Manager)(manager).SessionStart(w, r) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// SessionDestroy Destroy session by its id in http request cookie. +func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { + (*session.Manager)(manager).SessionDestroy(w, r) +} + +// GetSessionStore Get SessionStore by its id. +func (manager *Manager) GetSessionStore(sid string) (Store, error) { + s, err := (*session.Manager)(manager).GetSessionStore(sid) + return &NewToOldStoreAdapter{ + delegate: s, + }, err +} + +// GC Start session gc process. +// it can do gc in times after gc lifetime. +func (manager *Manager) GC() { + (*session.Manager)(manager).GC() +} + +// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request. +func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) Store { + s := (*session.Manager)(manager).SessionRegenerateID(w, r) + return &NewToOldStoreAdapter{ + delegate: s, + } +} + +// GetActiveSession Get all active sessions count number. +func (manager *Manager) GetActiveSession() int { + return (*session.Manager)(manager).GetActiveSession() +} + +// SetSecure Set cookie with https. +func (manager *Manager) SetSecure(secure bool) { + (*session.Manager)(manager).SetSecure(secure) +} + +// Log implement the log.Logger +type Log session.Log + +// NewSessionLog set io.Writer to create a Logger for session. +func NewSessionLog(out io.Writer) *Log { + return (*Log)(session.NewSessionLog(out)) +} diff --git a/pkg/adapter/session/ssdb/sess_ssdb.go b/pkg/adapter/session/ssdb/sess_ssdb.go new file mode 100644 index 00000000..aee3a364 --- /dev/null +++ b/pkg/adapter/session/ssdb/sess_ssdb.go @@ -0,0 +1,84 @@ +package ssdb + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/adapter/session" + + beeSsdb "github.com/astaxie/beego/pkg/infrastructure/session/ssdb" +) + +// Provider holds ssdb client and configs +type Provider beeSsdb.Provider + +// SessionInit init the ssdb with the config +func (p *Provider) SessionInit(maxLifetime int64, savePath string) error { + return (*beeSsdb.Provider)(p).SessionInit(context.Background(), maxLifetime, savePath) +} + +// SessionRead return a ssdb client session Store +func (p *Provider) SessionRead(sid string) (session.Store, error) { + s, err := (*beeSsdb.Provider)(p).SessionRead(context.Background(), sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionExist judged whether sid is exist in session +func (p *Provider) SessionExist(sid string) bool { + res, _ := (*beeSsdb.Provider)(p).SessionExist(context.Background(), sid) + return res +} + +// SessionRegenerate regenerate session with new sid and delete oldsid +func (p *Provider) SessionRegenerate(oldsid, sid string) (session.Store, error) { + s, err := (*beeSsdb.Provider)(p).SessionRegenerate(context.Background(), oldsid, sid) + return session.CreateNewToOldStoreAdapter(s), err +} + +// SessionDestroy destroy the sid +func (p *Provider) SessionDestroy(sid string) error { + return (*beeSsdb.Provider)(p).SessionDestroy(context.Background(), sid) +} + +// SessionGC not implemented +func (p *Provider) SessionGC() { + (*beeSsdb.Provider)(p).SessionGC(context.Background()) +} + +// SessionAll not implemented +func (p *Provider) SessionAll() int { + return (*beeSsdb.Provider)(p).SessionAll(context.Background()) +} + +// SessionStore holds the session information which stored in ssdb +type SessionStore beeSsdb.SessionStore + +// Set the key and value +func (s *SessionStore) Set(key, value interface{}) error { + return (*beeSsdb.SessionStore)(s).Set(context.Background(), key, value) +} + +// Get return the value by the key +func (s *SessionStore) Get(key interface{}) interface{} { + return (*beeSsdb.SessionStore)(s).Get(context.Background(), key) +} + +// Delete the key in session store +func (s *SessionStore) Delete(key interface{}) error { + return (*beeSsdb.SessionStore)(s).Delete(context.Background(), key) +} + +// Flush delete all keys and values +func (s *SessionStore) Flush() error { + return (*beeSsdb.SessionStore)(s).Flush(context.Background()) +} + +// SessionID return the sessionID +func (s *SessionStore) SessionID() string { + return (*beeSsdb.SessionStore)(s).SessionID(context.Background()) +} + +// SessionRelease Store the keyvalues into ssdb +func (s *SessionStore) SessionRelease(w http.ResponseWriter) { + (*beeSsdb.SessionStore)(s).SessionRelease(context.Background(), w) +} diff --git a/pkg/adapter/session/store_adapter.go b/pkg/adapter/session/store_adapter.go new file mode 100644 index 00000000..c1a03c38 --- /dev/null +++ b/pkg/adapter/session/store_adapter.go @@ -0,0 +1,84 @@ +// Copyright 2020 +// +// 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 session + +import ( + "context" + "net/http" + + "github.com/astaxie/beego/pkg/infrastructure/session" +) + +type NewToOldStoreAdapter struct { + delegate session.Store +} + +func CreateNewToOldStoreAdapter(s session.Store) Store { + return &NewToOldStoreAdapter{ + delegate: s, + } +} + +func (n *NewToOldStoreAdapter) Set(key, value interface{}) error { + return n.delegate.Set(context.Background(), key, value) +} + +func (n *NewToOldStoreAdapter) Get(key interface{}) interface{} { + return n.delegate.Get(context.Background(), key) +} + +func (n *NewToOldStoreAdapter) Delete(key interface{}) error { + return n.delegate.Delete(context.Background(), key) +} + +func (n *NewToOldStoreAdapter) SessionID() string { + return n.delegate.SessionID(context.Background()) +} + +func (n *NewToOldStoreAdapter) SessionRelease(w http.ResponseWriter) { + n.delegate.SessionRelease(context.Background(), w) +} + +func (n *NewToOldStoreAdapter) Flush() error { + return n.delegate.Flush(context.Background()) +} + +type oldToNewStoreAdapter struct { + delegate Store +} + +func (o *oldToNewStoreAdapter) Set(ctx context.Context, key, value interface{}) error { + return o.delegate.Set(key, value) +} + +func (o *oldToNewStoreAdapter) Get(ctx context.Context, key interface{}) interface{} { + return o.delegate.Get(key) +} + +func (o *oldToNewStoreAdapter) Delete(ctx context.Context, key interface{}) error { + return o.delegate.Delete(key) +} + +func (o *oldToNewStoreAdapter) SessionID(ctx context.Context) string { + return o.delegate.SessionID() +} + +func (o *oldToNewStoreAdapter) SessionRelease(ctx context.Context, w http.ResponseWriter) { + o.delegate.SessionRelease(w) +} + +func (o *oldToNewStoreAdapter) Flush(ctx context.Context) error { + return o.delegate.Flush() +} diff --git a/pkg/infrastructure/session/sess_cookie.go b/pkg/infrastructure/session/sess_cookie.go index ffb19fb7..649f6510 100644 --- a/pkg/infrastructure/session/sess_cookie.go +++ b/pkg/infrastructure/session/sess_cookie.go @@ -172,7 +172,7 @@ func (pder *CookieProvider) SessionAll(context.Context) int { } // SessionUpdate Implement method, no used. -func (pder *CookieProvider) SessionUpdate(sid string) error { +func (pder *CookieProvider) SessionUpdate(ctx context.Context, sid string) error { return nil } diff --git a/pkg/infrastructure/session/sess_mem.go b/pkg/infrastructure/session/sess_mem.go index 9a27c331..27e24c73 100644 --- a/pkg/infrastructure/session/sess_mem.go +++ b/pkg/infrastructure/session/sess_mem.go @@ -96,7 +96,7 @@ func (pder *MemProvider) SessionInit(ctx context.Context, maxlifetime int64, sav func (pder *MemProvider) SessionRead(ctx context.Context, sid string) (Store, error) { pder.lock.RLock() if element, ok := pder.sessions[sid]; ok { - go pder.SessionUpdate(sid) + go pder.SessionUpdate(nil, sid) pder.lock.RUnlock() return element.Value.(*MemSessionStore), nil } @@ -123,7 +123,7 @@ func (pder *MemProvider) SessionExist(ctx context.Context, sid string) (bool, er func (pder *MemProvider) SessionRegenerate(ctx context.Context, oldsid, sid string) (Store, error) { pder.lock.RLock() if element, ok := pder.sessions[oldsid]; ok { - go pder.SessionUpdate(oldsid) + go pder.SessionUpdate(nil, oldsid) pder.lock.RUnlock() pder.lock.Lock() element.Value.(*MemSessionStore).sid = sid @@ -181,7 +181,7 @@ func (pder *MemProvider) SessionAll(context.Context) int { } // SessionUpdate expand time of session store by id in memory session -func (pder *MemProvider) SessionUpdate(sid string) error { +func (pder *MemProvider) SessionUpdate(ctx context.Context, sid string) error { pder.lock.Lock() defer pder.lock.Unlock() if element, ok := pder.sessions[sid]; ok {