github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/instance/store.go (about) 1 package instance 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 "github.com/cozy/cozy-stack/pkg/config/config" 9 "github.com/cozy/cozy-stack/pkg/prefixer" 10 "github.com/redis/go-redis/v9" 11 ) 12 13 // Store is an object to store and retrieve session codes. 14 type Store interface { 15 SaveSessionCode(db prefixer.Prefixer, code string) error 16 SaveEmailVerfiedCode(db prefixer.Prefixer, code string) error 17 CheckAndClearSessionCode(db prefixer.Prefixer, code string) bool 18 CheckEmailVerifiedCode(db prefixer.Prefixer, code string) bool 19 } 20 21 // sessionCodeTTL is the time an entry for a session_code stays alive (1 week) 22 var sessionCodeTTL = 7 * 24 * time.Hour 23 24 // emailVerifiedCodeTTL is the time an entry for an email_verified_code stays alive 25 var emailVerifiedCodeTTL = 15 * time.Minute 26 27 // storeCleanInterval is the time interval between each cleanup. 28 var storeCleanInterval = 1 * time.Hour 29 30 var mu sync.Mutex 31 var globalStore Store 32 33 // GetStore returns the store for temporary move objects. 34 func GetStore() Store { 35 mu.Lock() 36 defer mu.Unlock() 37 if globalStore != nil { 38 return globalStore 39 } 40 cli := config.GetConfig().SessionStorage 41 if cli == nil { 42 globalStore = newMemStore() 43 } else { 44 ctx := context.Background() 45 globalStore = &redisStore{cli, ctx} 46 } 47 return globalStore 48 } 49 50 func newMemStore() Store { 51 store := &memStore{vals: make(map[string]time.Time)} 52 go store.cleaner() 53 return store 54 } 55 56 type memStore struct { 57 mu sync.Mutex 58 vals map[string]time.Time // session_code -> expiration time 59 } 60 61 func (s *memStore) cleaner() { 62 for range time.Tick(storeCleanInterval) { 63 now := time.Now() 64 for k, v := range s.vals { 65 if now.After(v) { 66 delete(s.vals, k) 67 } 68 } 69 } 70 } 71 72 func (s *memStore) SaveSessionCode(db prefixer.Prefixer, code string) error { 73 s.mu.Lock() 74 defer s.mu.Unlock() 75 key := sessionCodeKey(db, code) 76 s.vals[key] = time.Now().Add(sessionCodeTTL) 77 return nil 78 } 79 80 func (s *memStore) SaveEmailVerfiedCode(db prefixer.Prefixer, code string) error { 81 s.mu.Lock() 82 defer s.mu.Unlock() 83 key := emailVerifiedCodeKey(db, code) 84 s.vals[key] = time.Now().Add(emailVerifiedCodeTTL) 85 return nil 86 } 87 88 func (s *memStore) CheckAndClearSessionCode(db prefixer.Prefixer, code string) bool { 89 s.mu.Lock() 90 defer s.mu.Unlock() 91 key := sessionCodeKey(db, code) 92 exp, ok := s.vals[key] 93 if !ok { 94 return false 95 } 96 delete(s.vals, key) 97 return time.Now().Before(exp) 98 } 99 100 func (s *memStore) CheckEmailVerifiedCode(db prefixer.Prefixer, code string) bool { 101 s.mu.Lock() 102 defer s.mu.Unlock() 103 key := emailVerifiedCodeKey(db, code) 104 exp, ok := s.vals[key] 105 if !ok { 106 return false 107 } 108 if time.Now().After(exp) { 109 delete(s.vals, key) 110 return false 111 } 112 return true 113 } 114 115 type redisStore struct { 116 c redis.UniversalClient 117 ctx context.Context 118 } 119 120 func (s *redisStore) SaveSessionCode(db prefixer.Prefixer, code string) error { 121 key := sessionCodeKey(db, code) 122 return s.c.Set(s.ctx, key, "1", sessionCodeTTL).Err() 123 } 124 125 func (s *redisStore) SaveEmailVerfiedCode(db prefixer.Prefixer, code string) error { 126 key := emailVerifiedCodeKey(db, code) 127 return s.c.Set(s.ctx, key, "1", emailVerifiedCodeTTL).Err() 128 } 129 130 func (s *redisStore) CheckAndClearSessionCode(db prefixer.Prefixer, code string) bool { 131 key := sessionCodeKey(db, code) 132 n, err := s.c.Del(s.ctx, key).Result() 133 return err == nil && n > 0 134 } 135 136 func (s *redisStore) CheckEmailVerifiedCode(db prefixer.Prefixer, code string) bool { 137 key := emailVerifiedCodeKey(db, code) 138 n, err := s.c.Exists(s.ctx, key).Result() 139 return err == nil && n > 0 140 } 141 142 func sessionCodeKey(db prefixer.Prefixer, suffix string) string { 143 return db.DBPrefix() + ":sessioncode:" + suffix 144 } 145 146 func emailVerifiedCodeKey(db prefixer.Prefixer, suffix string) string { 147 return db.DBPrefix() + ":emailverifiedcode:" + suffix 148 }