2023-10-17 06:03:42 +03:00
package api
import (
"context"
"encoding/gob"
2023-12-05 00:13:50 +02:00
"fmt"
2023-10-17 06:03:42 +03:00
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
2024-02-01 06:34:07 +02:00
zerr "zotregistry.dev/zot/errors"
"zotregistry.dev/zot/pkg/scheduler"
"zotregistry.dev/zot/pkg/storage"
storageConstants "zotregistry.dev/zot/pkg/storage/constants"
2023-10-17 06:03:42 +03:00
)
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 {
2024-02-01 19:15:53 +02:00
sch . SubmitGenerator (
& SessionCleanup { rootDir : c . rootDir } ,
cookiesMaxAge * time . Second ,
scheduler . LowPriority ,
)
2023-10-17 06:03:42 +03:00
}
}
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
}
2024-02-20 00:43:30 +02:00
func IsExpiredSession ( dirEntry fs . DirEntry ) bool {
fileInfo , err := dirEntry . Info ( )
if err != nil { // may have been deleted in the meantime
return false
}
if ! strings . HasPrefix ( fileInfo . Name ( ) , "session_" ) {
return false
}
if fileInfo . ModTime ( ) . Add ( cookiesMaxAge * time . Second ) . After ( time . Now ( ) ) {
return false
}
return true
}
2023-10-17 06:03:42 +03:00
func getExpiredSessions ( dir string ) ( [ ] string , error ) {
sessions := make ( [ ] string , 0 )
2024-02-20 00:43:30 +02:00
err := filepath . WalkDir ( dir , func ( filePath string , dirEntry fs . DirEntry , err error ) error {
2023-10-17 06:03:42 +03:00
if err != nil {
return err
}
2024-02-20 00:43:30 +02:00
if IsExpiredSession ( dirEntry ) {
sessions = append ( sessions , filePath )
2023-10-17 06:03:42 +03:00
}
return nil
} )
2024-01-22 19:15:27 +02:00
if os . IsNotExist ( err ) {
return sessions , nil
}
2023-10-17 06:03:42 +03:00
return sessions , err
}
type SessionCleanup struct {
rootDir string
done bool
}
2024-02-01 19:15:53 +02:00
func ( gen * SessionCleanup ) Name ( ) string {
return "SessionCleanupGenerator"
}
2023-10-17 06:03:42 +03:00
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 {
2023-12-11 20:00:34 +02:00
if ! os . IsNotExist ( err ) {
return err
}
2023-10-17 06:03:42 +03:00
}
}
return nil
}
2023-12-05 00:13:50 +02:00
func ( cleanTask * CleanTask ) String ( ) string {
return fmt . Sprintf ( "{Name: %s, sessions: %s}" ,
cleanTask . Name ( ) , cleanTask . sessions )
}
func ( cleanTask * CleanTask ) Name ( ) string {
return "SessionCleanupTask"
}