Keep sessions for 7 days

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-04-20 13:50:28 +02:00
parent 9f188a7b93
commit 4bd0ca4ac6
8 changed files with 122 additions and 33 deletions

View file

@ -6,7 +6,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/session"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/txt"
)
@ -27,7 +27,7 @@ func CreateSession(router *gin.RouterGroup, conf *config.Config) {
user := gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"}
token := session.Create(user)
token := service.Session().Create(user)
c.Header("X-Session-Token", token)
@ -42,7 +42,7 @@ func DeleteSession(router *gin.RouterGroup, conf *config.Config) {
router.DELETE("/session/:token", func(c *gin.Context) {
token := c.Param("token")
session.Delete(token)
service.Session().Delete(token)
c.JSON(http.StatusOK, gin.H{"status": "ok", "token": token})
})
@ -59,5 +59,5 @@ func Unauthorized(c *gin.Context, conf *config.Config) bool {
token := c.GetHeader("X-Session-Token")
// Check if session token is valid
return !session.Exists(token)
return !service.Session().Exists(token)
}

View file

@ -10,7 +10,7 @@ import (
"github.com/gorilla/websocket"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/session"
"github.com/photoprism/photoprism/internal/service"
"github.com/photoprism/photoprism/pkg/rnd"
)
@ -54,7 +54,7 @@ func wsReader(ws *websocket.Conn, writeMutex *sync.Mutex, connId string, conf *c
if err := json.Unmarshal(m, &info); err != nil {
log.Error(err)
} else {
if session.Exists(info.SessionToken) {
if service.Session().Exists(info.SessionToken) {
log.Debug("websocket: authenticated")
wsAuth.mutex.Lock()

View file

@ -5,6 +5,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/nsfw"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/session"
)
var conf *config.Config
@ -16,6 +17,7 @@ var services struct {
Convert *photoprism.Convert
Resample *photoprism.Resample
Classify *classify.TensorFlow
Session *session.Session
}
func SetConfig(c *config.Config) {

View file

@ -0,0 +1,21 @@
package service
import (
"sync"
"time"
"github.com/photoprism/photoprism/internal/session"
)
var onceSession sync.Once
func initSession() {
// keep sessions for 7 days by default
services.Session = session.New(168*time.Hour, Config().CachePath())
}
func Session() *session.Session {
onceSession.Do(initSession)
return services.Session
}

View file

@ -8,7 +8,46 @@ https://github.com/photoprism/photoprism/wiki
package session
import (
"encoding/json"
"io/ioutil"
"path"
"time"
gc "github.com/patrickmn/go-cache"
"github.com/photoprism/photoprism/internal/event"
)
var log = event.Log
// Session represents a session store.
type Session struct {
cacheFile string
cache *gc.Cache
}
// New returns a new session store with an optional cachePath.
func New(expiration time.Duration, cachePath string) *Session {
s := &Session{}
cleanupInterval := 15 * time.Minute
if cachePath != "" {
var items map[string]gc.Item
s.cacheFile = path.Join(cachePath, "sessions.json")
if cached, err := ioutil.ReadFile(s.cacheFile); err != nil {
log.Infof("session: %s", err)
} else if err := json.Unmarshal(cached, &items); err != nil {
log.Errorf("session: %s", err)
} else {
s.cache = gc.NewFrom(expiration, cleanupInterval, items)
}
}
if s.cache == nil {
s.cache = gc.New(expiration, cleanupInterval)
}
return s
}

View file

@ -1,31 +1,51 @@
package session
import (
"time"
"encoding/json"
"io/ioutil"
gc "github.com/patrickmn/go-cache"
)
var cache = gc.New(72*time.Hour, 30*time.Minute)
func Create(data interface{}) string {
func (s *Session) Create(data interface{}) string {
token := Token()
cache.Set(token, data, gc.DefaultExpiration)
s.cache.Set(token, data, gc.DefaultExpiration)
log.Debugf("session: created")
if err := s.Save(); err != nil {
log.Errorf("session: %s", err)
}
return token
}
func Delete(token string) {
cache.Delete(token)
func (s *Session) Delete(token string) {
s.cache.Delete(token)
log.Debugf("session: deleted")
if err := s.Save(); err != nil {
log.Errorf("session: %s", err)
}
}
func Get(token string) (data interface{}, exists bool) {
return cache.Get(token)
func (s *Session) Get(token string) (data interface{}, exists bool) {
return s.cache.Get(token)
}
func Exists(token string) bool {
_, found := cache.Get(token)
func (s *Session) Exists(token string) bool {
_, found := s.cache.Get(token)
return found
}
func (s *Session) Save() error {
if s.cacheFile == "" {
return nil
} else if serialized, err := json.MarshalIndent(s.cache.Items(), "", " "); err != nil {
return err
} else if err = ioutil.WriteFile(s.cacheFile, serialized, 0600); err != nil {
return err
}
return nil
}

View file

@ -2,44 +2,49 @@ package session
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestCreate(t *testing.T) {
token := Create(23)
func TestSession_Create(t *testing.T) {
s := New(time.Hour, "testdata")
token := s.Create(23)
t.Logf("token: %s", token)
assert.Equal(t, 48, len(token))
}
func TestDelete(t *testing.T) {
Delete("abc")
func TestSession_Delete(t *testing.T) {
s := New(time.Hour, "testdata")
s.Delete("abc")
}
func TestGet(t *testing.T) {
token := Create(42)
func TestSession_Get(t *testing.T) {
s := New(time.Hour, "testdata")
token := s.Create(42)
t.Logf("token: %s", token)
assert.Equal(t, 48, len(token))
data, exists := Get(token)
data, exists := s.Get(token)
assert.Equal(t, 42, data)
assert.True(t, exists)
Delete(token)
s.Delete(token)
data, exists = Get(token)
data, exists = s.Get(token)
assert.Nil(t, data)
assert.False(t, Exists(token))
assert.False(t, s.Exists(token))
}
func TestExists(t *testing.T) {
assert.False(t, Exists("xyz"))
token := Create(23)
func TestSession_Exists(t *testing.T) {
s := New(time.Hour, "testdata")
assert.False(t, s.Exists("xyz"))
token := s.Create(23)
t.Logf("token: %s", token)
assert.Equal(t, 48, len(token))
assert.True(t, Exists(token))
Delete(token)
assert.False(t, Exists(token))
assert.True(t, s.Exists(token))
s.Delete(token)
assert.False(t, s.Exists(token))
}

2
internal/session/testdata/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
*
!.gitignore