package otpmanagermdl import ( "errors" "time" "github.com/tidwall/gjson" "github.com/tidwall/sjson" "corelab.mkcl.org/MKCLOS/coredevelopmentplatform/corepkgv2/cachemdl" ) // Entry is a data to be stored against a key. type Entry struct { Data gjson.Result `json:"data,omitempty"` Expiration int64 `json:"expiration,omitempty"` ExpiredAT int64 `json:"expiredAt,omitempty"` } const ( // keys for the entry object KEY_DATA = "data" KEY_EXPIREDAT = "expiredAt" KEY_EXPIRATION = "expiration" ) var store cachemdl.Cacher var ErrOtpNotFound = errors.New("OTP_NOT_FOUND") var ErrInvalidDataType = errors.New("INVALID_DATA_Type") // Init initializes otp manager with provided cache. Subsequent calls will not have any effect after first initialization. func Init(cache cachemdl.Cacher) { if store != nil { return } store = cache } // NewEntry prepares the object required to store data in session. // // The `exp` field interprets time in seconds. Ex. For 5 seconds, set `5` func NewOTPEntry(val gjson.Result, exp int64) Entry { duration := time.Duration(exp) * time.Second deadLine := time.Now().Add(duration).Unix() return Entry{ Data: val, Expiration: exp, ExpiredAT: deadLine, } } // NewRedisEntry prepares the entry for redis cache. This is required because redis accepts a byte array. func NewRedisEntry(entry Entry) string { var data string data, _ = sjson.Set(data, KEY_DATA, entry.Data.Value()) data, _ = sjson.Set(data, KEY_EXPIRATION, entry.Expiration) data, _ = sjson.Set(data, KEY_EXPIREDAT, entry.ExpiredAT) return data } // Store adds/ updates the entry against the provided key. func Store(key string, entry Entry) { duration := time.Duration(entry.Expiration) * time.Second // if otp manager uses redis cache, the data field (gjson.Result) is saved as is. // This adds irrelevant fields in redis cache and we get them on retrieve operation. // The following operation needs to be performed so that the data is marshaled correctly. Redis only accepts []byte{}. if store.Type() == cachemdl.TypeRedisCache { store.SetWithExpiration(key, NewRedisEntry(entry), duration) return } store.SetWithExpiration(key, entry, duration) } // // Delete removes the otp entry. If the key is not present, error `ErrOtpNotFound` will be thrown. Caller can ignore error if this is acceptable. func DeleteOTP(key string) error { _, ok := store.Get(key) if !ok { return ErrOtpNotFound } store.Delete(key) return nil } func GetOTP(key string) (Entry, bool) { data, ok := store.Get(key) if !ok { return Entry{}, false } switch v := data.(type) { case string: // for result from redis cache res := gjson.Parse(v) return Entry{ Data: res.Get(KEY_DATA), Expiration: res.Get(KEY_EXPIRATION).Int(), ExpiredAT: res.Get(KEY_EXPIREDAT).Int(), }, true case Entry: // for result from fastcache return v, true default: return Entry{}, false } }