rework password expiration cleaning

This commit is contained in:
1e99 2024-10-30 11:47:17 +01:00
parent e3805b1a04
commit aaa1715f32
4 changed files with 69 additions and 90 deletions

15
main.go
View file

@ -22,7 +22,20 @@ func run() error {
return err 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 := http.NewServeMux()
mux.Handle("GET /", routes.ServeFiles(embedFS, "static")) mux.Handle("GET /", routes.ServeFiles(embedFS, "static"))

View file

@ -8,23 +8,16 @@ import (
"time" "time"
) )
func NewDirStore(clearInterval time.Duration, path string) Store { func NewDirStore(path string) Store {
store := &dir{ store := &dir{
clearInterval: clearInterval, path: path,
timeLayout: time.RFC3339Nano,
path: path,
close: make(chan bool),
} }
go store.clearExpired()
return store return store
} }
type dir struct { type dir struct {
clearInterval time.Duration path string
timeLayout string
path string
close chan bool
} }
func (store *dir) Create(password []byte, expiresAt time.Time) (string, error) { func (store *dir) Create(password []byte, expiresAt time.Time) (string, error) {
@ -98,56 +91,45 @@ func (store *dir) Delete(id string) error {
return nil return nil
} }
func (store *dir) Close() error { func (store *dir) ClearExpired() error {
store.close <- true now := time.Now()
return nil
}
func (store *dir) clearExpired() error { entries, err := os.ReadDir(store.path)
ticker := time.NewTicker(store.clearInterval) if err != nil {
defer ticker.Stop() return err
}
for { for _, file := range entries {
select { id := file.Name()
case <-store.close: path := store.getPath(id)
return nil file, err := os.OpenFile(
case <-ticker.C: path,
// TODO: Error handling? os.O_RDONLY,
now := time.Now() 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 { if err != nil {
continue return err
}
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 nil
} }
func (store *dir) getPath(id string) string { func (store *dir) getPath(id string) string {

View file

@ -5,23 +5,18 @@ import (
"time" "time"
) )
func NewRamStore(clearInterval time.Duration) Store { func NewRamStore() Store {
store := &ram{ store := &ram{
clearInterval: clearInterval, passwords: make(map[string]entry),
passwords: make(map[string]entry), lock: sync.Mutex{},
lock: sync.Mutex{},
close: make(chan bool),
} }
go store.clearExpired()
return store return store
} }
type ram struct { type ram struct {
clearInterval time.Duration passwords map[string]entry
passwords map[string]entry lock sync.Mutex
lock sync.Mutex
close chan bool
} }
func (store *ram) Create(password []byte, expiresAt time.Time) (string, error) { func (store *ram) Create(password []byte, expiresAt time.Time) (string, error) {
@ -63,30 +58,19 @@ func (store *ram) Delete(id string) error {
return nil return nil
} }
func (store *ram) Close() error { func (store *ram) ClearExpired() error {
store.close <- true store.lock.Lock()
return nil defer store.lock.Unlock()
} time := time.Now()
func (store *ram) clearExpired() error { for id, password := range store.passwords {
ticker := time.NewTicker(store.clearInterval) if time.After(password.ExpiresAt) {
defer ticker.Stop() err := store.Delete(id)
if err != nil {
for { return err
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)
}
} }
store.lock.Unlock()
} }
} }
return nil
} }

View file

@ -23,7 +23,7 @@ type Store interface {
Create(password []byte, expiresAt time.Time) (string, error) Create(password []byte, expiresAt time.Time) (string, error)
Get(id string) ([]byte, error) Get(id string) ([]byte, error)
Delete(id string) error Delete(id string) error
Close() error ClearExpired() error
} }
func NewStore() (Store, error) { func NewStore() (Store, error) {
@ -32,7 +32,7 @@ func NewStore() (Store, error) {
switch storeType { switch storeType {
case "ram": case "ram":
return NewRamStore(20 * time.Second), nil return NewRamStore(), nil
case "dir": case "dir":
path := os.Getenv("PASSED_STORE_DIR_PATH") path := os.Getenv("PASSED_STORE_DIR_PATH")
if path == "" { if path == "" {
@ -40,11 +40,11 @@ func NewStore() (Store, error) {
path = "passwords" path = "passwords"
} }
return NewDirStore(60*time.Second, path), nil return NewDirStore(path), nil
default: default:
log.Printf("No PASSED_STORE_TYPE provided, defaulting to memory store.") log.Printf("No PASSED_STORE_TYPE provided, defaulting to memory store.")
return NewRamStore(20 * time.Second), nil return NewRamStore(), nil
} }
} }