mirror of
https://github.com/project-zot/zot.git
synced 2024-12-16 21:56:37 -05:00
160 lines
3.2 KiB
Go
160 lines
3.2 KiB
Go
|
package api
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"encoding/gob"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"path"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/gorilla/securecookie"
|
||
|
"github.com/gorilla/sessions"
|
||
|
|
||
|
zerr "zotregistry.io/zot/errors"
|
||
|
"zotregistry.io/zot/pkg/scheduler"
|
||
|
"zotregistry.io/zot/pkg/storage"
|
||
|
storageConstants "zotregistry.io/zot/pkg/storage/constants"
|
||
|
)
|
||
|
|
||
|
const cookiesMaxAge = 7200 // 2h
|
||
|
|
||
|
type CookieStore struct {
|
||
|
needsCleanup bool // if store should be periodically cleaned
|
||
|
rootDir string
|
||
|
sessions.Store
|
||
|
}
|
||
|
|
||
|
func (c *CookieStore) RunSessionCleaner(sch *scheduler.Scheduler) {
|
||
|
if c.needsCleanup {
|
||
|
sch.SubmitGenerator(&SessionCleanup{rootDir: c.rootDir}, cookiesMaxAge, scheduler.LowPriority)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func NewCookieStore(storeController storage.StoreController) (*CookieStore, error) {
|
||
|
// To store custom types in our cookies
|
||
|
// we must first register them using gob.Register
|
||
|
gob.Register(map[string]interface{}{})
|
||
|
|
||
|
hashKey, err := getHashKey()
|
||
|
if err != nil {
|
||
|
return &CookieStore{}, err
|
||
|
}
|
||
|
|
||
|
var store sessions.Store
|
||
|
|
||
|
var sessionsDir string
|
||
|
|
||
|
var needsCleanup bool
|
||
|
|
||
|
if storeController.DefaultStore.Name() == storageConstants.LocalStorageDriverName {
|
||
|
sessionsDir = path.Join(storeController.DefaultStore.RootDir(), "_sessions")
|
||
|
if err := os.MkdirAll(sessionsDir, storageConstants.DefaultDirPerms); err != nil {
|
||
|
return &CookieStore{}, err
|
||
|
}
|
||
|
|
||
|
localStore := sessions.NewFilesystemStore(sessionsDir, hashKey)
|
||
|
|
||
|
localStore.MaxAge(cookiesMaxAge)
|
||
|
|
||
|
store = localStore
|
||
|
needsCleanup = true
|
||
|
} else {
|
||
|
memStore := sessions.NewCookieStore(hashKey)
|
||
|
|
||
|
memStore.MaxAge(cookiesMaxAge)
|
||
|
|
||
|
store = memStore
|
||
|
}
|
||
|
|
||
|
return &CookieStore{
|
||
|
Store: store,
|
||
|
rootDir: sessionsDir,
|
||
|
needsCleanup: needsCleanup,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
func getHashKey() ([]byte, error) {
|
||
|
hashKey := securecookie.GenerateRandomKey(64)
|
||
|
if hashKey == nil {
|
||
|
return nil, zerr.ErrHashKeyNotCreated
|
||
|
}
|
||
|
|
||
|
return hashKey, nil
|
||
|
}
|
||
|
|
||
|
func getExpiredSessions(dir string) ([]string, error) {
|
||
|
sessions := make([]string, 0)
|
||
|
|
||
|
err := filepath.WalkDir(dir, func(_ string, dirEntry fs.DirEntry, err error) error {
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
fileInfo, err := dirEntry.Info()
|
||
|
if err != nil { // may have been deleted in the meantime
|
||
|
return nil //nolint: nilerr
|
||
|
}
|
||
|
|
||
|
if !strings.HasPrefix(fileInfo.Name(), "session_") {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
if fileInfo.ModTime().Add(cookiesMaxAge * time.Second).Before(time.Now()) {
|
||
|
sessions = append(sessions, path.Join(dir, fileInfo.Name()))
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
})
|
||
|
|
||
|
return sessions, err
|
||
|
}
|
||
|
|
||
|
type SessionCleanup struct {
|
||
|
rootDir string
|
||
|
done bool
|
||
|
}
|
||
|
|
||
|
func (gen *SessionCleanup) Next() (scheduler.Task, error) {
|
||
|
sessions, err := getExpiredSessions(gen.rootDir)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if len(sessions) == 0 {
|
||
|
gen.done = true
|
||
|
|
||
|
return nil, nil
|
||
|
}
|
||
|
|
||
|
return &CleanTask{sessions: sessions}, nil
|
||
|
}
|
||
|
|
||
|
func (gen *SessionCleanup) IsDone() bool {
|
||
|
return gen.done
|
||
|
}
|
||
|
|
||
|
func (gen *SessionCleanup) IsReady() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (gen *SessionCleanup) Reset() {
|
||
|
gen.done = false
|
||
|
}
|
||
|
|
||
|
type CleanTask struct {
|
||
|
sessions []string
|
||
|
}
|
||
|
|
||
|
func (cleanTask *CleanTask) DoWork(ctx context.Context) error {
|
||
|
for _, session := range cleanTask.sessions {
|
||
|
if err := os.Remove(session); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|