diff --git a/internal/api/session.go b/internal/api/session.go index ec2128e08..66ccbc984 100644 --- a/internal/api/session.go +++ b/internal/api/session.go @@ -4,9 +4,9 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/patrickmn/go-cache" "github.com/photoprism/photoprism/internal/config" "github.com/photoprism/photoprism/internal/form" + "github.com/photoprism/photoprism/internal/session" "github.com/photoprism/photoprism/internal/util" ) @@ -25,15 +25,13 @@ func CreateSession(router *gin.RouterGroup, conf *config.Config) { return } - token := util.RandomToken(16) + user := gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"} + + token := session.Create(user) c.Header("X-Session-Token", token) - gc := conf.Cache() - - gc.Set(token, 1, cache.DefaultExpiration) - - s := gin.H{"token": token, "user": gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"}} + s := gin.H{"token": token, "user": user} c.JSON(http.StatusOK, s) }) @@ -44,9 +42,7 @@ func DeleteSession(router *gin.RouterGroup, conf *config.Config) { router.DELETE("/session/:token", func(c *gin.Context) { token := c.Param("token") - gc := conf.Cache() - - gc.Delete(token) + session.Delete(token) c.JSON(http.StatusOK, gin.H{"status": "ok", "token": token}) }) @@ -61,11 +57,7 @@ func Unauthorized(c *gin.Context, conf *config.Config) bool { // Get session token from HTTP header token := c.GetHeader("X-Session-Token") - log.Debugf("X-Session-Token: %s", token) // Check if session token is valid - gc := conf.Cache() - _, found := gc.Get(token) - - return !found + return !session.Exists(token) } diff --git a/internal/config/config.go b/internal/config/config.go index 007d543f2..de8e18f44 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -573,11 +573,11 @@ func (c *Config) ClientConfig() ClientConfig { var albums []*entity.Album var position struct { - PhotoUUID string `json:"photo"` - LocationID uint64 `json:"location"` - TakenAt time.Time `json:"utc"` - PhotoLat float64 `json:"lat"` - PhotoLng float64 `json:"lng"` + PhotoUUID string `json:"photo"` + LocationID uint64 `json:"location"` + TakenAt time.Time `json:"utc"` + PhotoLat float64 `json:"lat"` + PhotoLng float64 `json:"lng"` } db.Table("photos"). diff --git a/internal/session/session.go b/internal/session/session.go new file mode 100644 index 000000000..4e5cb2732 --- /dev/null +++ b/internal/session/session.go @@ -0,0 +1,14 @@ +/* +This package encapsulates session storage. + +Additional information can be found in our Developer Guide: + +https://github.com/photoprism/photoprism/wiki +*/ +package session + +import ( + "github.com/photoprism/photoprism/internal/event" +) + +var log = event.Log diff --git a/internal/session/store.go b/internal/session/store.go new file mode 100644 index 000000000..3e0844059 --- /dev/null +++ b/internal/session/store.go @@ -0,0 +1,31 @@ +package session + +import ( + "time" + + gc "github.com/patrickmn/go-cache" +) + +var cache = gc.New(72*time.Hour, 30*time.Minute) + +func Create(data interface{}) string { + token := Token() + cache.Set(token, data, gc.DefaultExpiration) + log.Debugf("session: created") + return token +} + +func Delete(token string) { + cache.Delete(token) + log.Debugf("session: deleted") +} + +func Get(token string) (data interface{}, exists bool) { + return cache.Get(token) +} + +func Exists(token string) bool { + _, found := cache.Get(token) + + return found +} diff --git a/internal/session/store_test.go b/internal/session/store_test.go new file mode 100644 index 000000000..b0be4d856 --- /dev/null +++ b/internal/session/store_test.go @@ -0,0 +1,45 @@ +package session + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCreate(t *testing.T) { + token := Create(23) + t.Logf("token: %s", token) + assert.Equal(t, 48, len(token)) +} + +func TestDelete(t *testing.T) { + Delete("abc") +} + +func TestGet(t *testing.T) { + token := Create(42) + t.Logf("token: %s", token) + assert.Equal(t, 48, len(token)) + + data, exists := Get(token) + + assert.Equal(t, 42, data) + assert.True(t, exists) + + Delete(token) + + data, exists = Get(token) + + assert.Nil(t, data) + assert.False(t, Exists(token)) +} + +func TestExists(t *testing.T) { + assert.False(t, Exists("xyz")) + token := 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)) +} diff --git a/internal/session/token.go b/internal/session/token.go new file mode 100644 index 000000000..1de738bca --- /dev/null +++ b/internal/session/token.go @@ -0,0 +1,16 @@ +package session + +import ( + "crypto/rand" + "fmt" +) + +func Token() string { + b := make([]byte, 24) + + if _, err := rand.Read(b); err != nil { + log.Fatal(err) + } + + return fmt.Sprintf("%x", b) +} diff --git a/internal/session/token_test.go b/internal/session/token_test.go new file mode 100644 index 000000000..bcae0f80c --- /dev/null +++ b/internal/session/token_test.go @@ -0,0 +1,15 @@ +package session + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestToken(t *testing.T) { + for n := 0; n < 5; n++ { + token := Token() + t.Logf("token: %s", token) + assert.Equal(t, 48, len(token)) + } +}