2023-10-23 12:05:20 +00:00
|
|
|
// Package memory implements a pure in-memory store for secrets which
|
|
|
|
// is suitable for testing and should not be used for productive use
|
|
|
|
package memory
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Luzifer/ots/pkg/storage"
|
|
|
|
"github.com/gofrs/uuid"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
|
|
|
memStorageSecret struct {
|
|
|
|
Expiry time.Time
|
|
|
|
Secret string
|
|
|
|
}
|
|
|
|
|
|
|
|
storageMem struct {
|
|
|
|
sync.RWMutex
|
2024-10-27 11:33:53 +00:00
|
|
|
store map[string]memStorageSecret
|
|
|
|
storePruneTimer *time.Ticker
|
2023-10-23 12:05:20 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
// New creates a new In-Mem storage
|
|
|
|
func New() storage.Storage {
|
2024-10-27 11:33:53 +00:00
|
|
|
store := &storageMem{
|
|
|
|
store: make(map[string]memStorageSecret),
|
|
|
|
storePruneTimer: time.NewTicker(time.Minute),
|
|
|
|
}
|
|
|
|
|
|
|
|
go store.storePruner()
|
|
|
|
|
|
|
|
return store
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageMem) storePruner() {
|
|
|
|
for range s.storePruneTimer.C {
|
|
|
|
s.pruneStore()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageMem) pruneStore() {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
for k, v := range s.store {
|
|
|
|
if v.hasExpired() {
|
|
|
|
delete(s.store, k)
|
|
|
|
}
|
2023-10-23 12:05:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageMem) Count() (int64, error) {
|
|
|
|
s.RLock()
|
|
|
|
defer s.RUnlock()
|
|
|
|
|
|
|
|
return int64(len(s.store)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageMem) Create(secret string, expireIn time.Duration) (string, error) {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
var (
|
|
|
|
expire time.Time
|
|
|
|
id = uuid.Must(uuid.NewV4()).String()
|
|
|
|
)
|
|
|
|
|
|
|
|
if expireIn > 0 {
|
|
|
|
expire = time.Now().Add(expireIn)
|
|
|
|
}
|
|
|
|
|
|
|
|
s.store[id] = memStorageSecret{
|
|
|
|
Expiry: expire,
|
|
|
|
Secret: secret,
|
|
|
|
}
|
|
|
|
|
|
|
|
return id, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *storageMem) ReadAndDestroy(id string) (string, error) {
|
|
|
|
s.Lock()
|
|
|
|
defer s.Unlock()
|
|
|
|
|
|
|
|
secret, ok := s.store[id]
|
|
|
|
if !ok {
|
|
|
|
return "", storage.ErrSecretNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
defer delete(s.store, id)
|
|
|
|
|
2024-10-27 11:33:53 +00:00
|
|
|
// Still check to see if the secret has expired in order to prevent a
|
|
|
|
// race condition where a secret has expired but the the store pruner has
|
|
|
|
// not yet been invoked.
|
|
|
|
if secret.hasExpired() {
|
2023-10-23 12:05:20 +00:00
|
|
|
return "", storage.ErrSecretNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
return secret.Secret, nil
|
|
|
|
}
|
2024-10-27 11:33:53 +00:00
|
|
|
|
|
|
|
func (m *memStorageSecret) hasExpired() bool {
|
|
|
|
return !m.Expiry.IsZero() && m.Expiry.Before(time.Now())
|
|
|
|
}
|