From aaa1715f327fc91b15ff29c66d644a8a7246e584 Mon Sep 17 00:00:00 2001 From: 1e99 <1e99@1e99.eu> Date: Wed, 30 Oct 2024 11:47:17 +0100 Subject: [PATCH] rework password expiration cleaning --- main.go | 15 +++++++- storage/dir.go | 88 ++++++++++++++++++---------------------------- storage/ram.go | 48 +++++++++---------------- storage/storage.go | 8 ++--- 4 files changed, 69 insertions(+), 90 deletions(-) diff --git a/main.go b/main.go index 9092167..718278e 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,20 @@ func run() error { return err } - defer storage.Close() + go func() { + ticker := time.Tick(20 * time.Second) + for { + <-ticker + + err := storage.ClearExpired() + if err != nil { + log.Printf("Failed to clear expired passwords: %s", err) + continue + } + + log.Printf("Cleared expired passwords.") + } + }() mux := http.NewServeMux() mux.Handle("GET /", routes.ServeFiles(embedFS, "static")) diff --git a/storage/dir.go b/storage/dir.go index aab0d5c..4c4fd39 100644 --- a/storage/dir.go +++ b/storage/dir.go @@ -8,23 +8,16 @@ import ( "time" ) -func NewDirStore(clearInterval time.Duration, path string) Store { +func NewDirStore(path string) Store { store := &dir{ - clearInterval: clearInterval, - timeLayout: time.RFC3339Nano, - path: path, - close: make(chan bool), + path: path, } - go store.clearExpired() return store } type dir struct { - clearInterval time.Duration - timeLayout string - path string - close chan bool + path string } func (store *dir) Create(password []byte, expiresAt time.Time) (string, error) { @@ -98,56 +91,45 @@ func (store *dir) Delete(id string) error { return nil } -func (store *dir) Close() error { - store.close <- true - return nil -} +func (store *dir) ClearExpired() error { + now := time.Now() -func (store *dir) clearExpired() error { - ticker := time.NewTicker(store.clearInterval) - defer ticker.Stop() + entries, err := os.ReadDir(store.path) + if err != nil { + return err + } - for { - select { - case <-store.close: - return nil - case <-ticker.C: - // TODO: Error handling? - now := time.Now() + for _, file := range entries { + id := file.Name() + path := store.getPath(id) + file, err := os.OpenFile( + path, + os.O_RDONLY, + 0, + ) + if err != nil { + return err + } - entries, err := os.ReadDir(store.path) + defer file.Close() + + var entry entry + err = gob.NewDecoder(file).Decode(&entry) + if err != nil { + return err + } + + if now.After(entry.ExpiresAt) { + // Close file early as we need to delete it + file.Close() + err = store.Delete(id) if err != nil { - continue - } - - for _, file := range entries { - id := file.Name() - path := store.getPath(id) - file, err := os.OpenFile( - path, - os.O_RDONLY, - 0, - ) - if err != nil { - continue - } - - defer file.Close() - - var entry entry - err = gob.NewDecoder(file).Decode(&entry) - if err != nil { - continue - } - - if now.After(entry.ExpiresAt) { - // Close file early as we need to delete it - file.Close() - store.Delete(id) - } + return err } } } + + return nil } func (store *dir) getPath(id string) string { diff --git a/storage/ram.go b/storage/ram.go index 774fa8b..597361d 100644 --- a/storage/ram.go +++ b/storage/ram.go @@ -5,23 +5,18 @@ import ( "time" ) -func NewRamStore(clearInterval time.Duration) Store { +func NewRamStore() Store { store := &ram{ - clearInterval: clearInterval, - passwords: make(map[string]entry), - lock: sync.Mutex{}, - close: make(chan bool), + passwords: make(map[string]entry), + lock: sync.Mutex{}, } - go store.clearExpired() return store } type ram struct { - clearInterval time.Duration - passwords map[string]entry - lock sync.Mutex - close chan bool + passwords map[string]entry + lock sync.Mutex } func (store *ram) Create(password []byte, expiresAt time.Time) (string, error) { @@ -63,30 +58,19 @@ func (store *ram) Delete(id string) error { return nil } -func (store *ram) Close() error { - store.close <- true - return nil -} +func (store *ram) ClearExpired() error { + store.lock.Lock() + defer store.lock.Unlock() + time := time.Now() -func (store *ram) clearExpired() error { - ticker := time.NewTicker(store.clearInterval) - defer ticker.Stop() - - for { - select { - case <-store.close: - return nil - case <-ticker.C: - store.lock.Lock() - time := time.Now() - - for id, password := range store.passwords { - if time.After(password.ExpiresAt) { - store.Delete(id) - } + for id, password := range store.passwords { + if time.After(password.ExpiresAt) { + err := store.Delete(id) + if err != nil { + return err } - - store.lock.Unlock() } } + + return nil } diff --git a/storage/storage.go b/storage/storage.go index db3f316..d3e1d8f 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -23,7 +23,7 @@ type Store interface { Create(password []byte, expiresAt time.Time) (string, error) Get(id string) ([]byte, error) Delete(id string) error - Close() error + ClearExpired() error } func NewStore() (Store, error) { @@ -32,7 +32,7 @@ func NewStore() (Store, error) { switch storeType { case "ram": - return NewRamStore(20 * time.Second), nil + return NewRamStore(), nil case "dir": path := os.Getenv("PASSED_STORE_DIR_PATH") if path == "" { @@ -40,11 +40,11 @@ func NewStore() (Store, error) { path = "passwords" } - return NewDirStore(60*time.Second, path), nil + return NewDirStore(path), nil default: log.Printf("No PASSED_STORE_TYPE provided, defaulting to memory store.") - return NewRamStore(20 * time.Second), nil + return NewRamStore(), nil } }