otpmanagermdl.go 2.89 KiB
Newer Older
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
	}
}