authorizer/server/memorystore/providers/redis/store.go
Untone b8f5e9ebb9
Some checks failed
deploy / deploy (push) Failing after 1m3s
fixed-redis-cache-2
2024-06-05 21:27:51 +03:00

316 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package redis
import (
"fmt"
"strconv"
"time"
"encoding/json"
"github.com/authorizerdev/authorizer/server/constants"
log "github.com/sirupsen/logrus"
"github.com/redis/go-redis/v9"
)
var (
// state store prefix
stateStorePrefix = "authorizer_state:"
// env store prefix
envStorePrefix = "authorizer_env"
)
const mfaSessionPrefix = "mfa_sess_"
// SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
currentTime := time.Now()
expireTime := time.Unix(expiration, 0)
duration := expireTime.Sub(currentTime)
err := c.store.Set(c.ctx, fmt.Sprintf("%s:%s", userId, key), token, duration).Err()
if err != nil {
log.Debug("Error saving user session to redis: ", err)
return err
}
return nil
}
// GetUserSession returns the user session from redis store.
func (c *provider) GetUserSession(userId, key string) (string, error) {
data, err := c.store.Get(c.ctx, fmt.Sprintf("%s:%s", userId, key)).Result()
if err != nil {
return "", err
}
return data, nil
}
// DeleteUserSession deletes the user session from redis store.
func (c *provider) DeleteUserSession(userId, key string) error {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeSessionToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
// continue
}
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeAccessToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
// continue
}
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeRefreshToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
// continue
}
return nil
}
// DeleteAllUserSessions deletes all the user session from redis
func (c *provider) DeleteAllUserSessions(userID string) error {
res := c.store.Keys(c.ctx, fmt.Sprintf("*%s*", userID))
if res.Err() != nil {
log.Debug("Error getting all user sessions from redis: ", res.Err())
return res.Err()
}
keys := res.Val()
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debug("Error deleting all user sessions from redis: ", err)
continue
}
}
return nil
}
// DeleteSessionForNamespace to delete session for a given namespace example google,github
func (c *provider) DeleteSessionForNamespace(namespace string) error {
res := c.store.Keys(c.ctx, fmt.Sprintf("%s:*", namespace))
if res.Err() != nil {
log.Debug("Error getting all user sessions from redis: ", res.Err())
return res.Err()
}
keys := res.Val()
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debug("Error deleting all user sessions from redis: ", err)
continue
}
}
return nil
}
// SetMfaSession sets the mfa session with key and value of userId
func (c *provider) SetMfaSession(userId, key string, expiration int64) error {
currentTime := time.Now()
expireTime := time.Unix(expiration, 0)
duration := expireTime.Sub(currentTime)
err := c.store.Set(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key), userId, duration).Err()
if err != nil {
log.Debug("Error saving user session to redis: ", err)
return err
}
return nil
}
// GetMfaSession returns value of given mfa session
func (c *provider) GetMfaSession(userId, key string) (string, error) {
data, err := c.store.Get(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Result()
if err != nil {
return "", err
}
return data, nil
}
// DeleteMfaSession deletes given mfa session from in-memory store.
func (c *provider) DeleteMfaSession(userId, key string) error {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s%s:%s", mfaSessionPrefix, userId, key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err)
// continue
}
return nil
}
// SetState sets the state in redis store.
func (c *provider) SetState(key, value string) error {
err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err()
if err != nil {
log.Debug("Error saving redis token: ", err)
return err
}
return nil
}
// GetState gets the state from redis store.
func (c *provider) GetState(key string) (string, error) {
data, err := c.store.Get(c.ctx, stateStorePrefix+key).Result()
if err != nil {
log.Debug("error getting token from redis store: ", err)
return "", err
}
return data, err
}
// RemoveState removes the state from redis store.
func (c *provider) RemoveState(key string) error {
err := c.store.Del(c.ctx, stateStorePrefix+key).Err()
if err != nil {
log.Fatalln("Error deleting redis token: ", err)
return err
}
return nil
}
// UpdateEnvStore to update the whole env store object
func (c *provider) UpdateEnvStore(store map[string]interface{}) error {
for key, value := range store {
err := c.store.HSet(c.ctx, envStorePrefix, key, value).Err()
if err != nil {
return err
}
}
return nil
}
// GetEnvStore returns the whole env store object
func (c *provider) GetEnvStore() (map[string]interface{}, error) {
res := make(map[string]interface{})
data, err := c.store.HGetAll(c.ctx, envStorePrefix).Result()
if err != nil {
return nil, err
}
for key, value := range data {
if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyIsSMSServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure || key == constants.EnvKeyDisablePlayGround || key == constants.EnvKeyDisableTOTPLogin || key == constants.EnvKeyDisableMailOTPLogin {
boolValue, err := strconv.ParseBool(value)
if err != nil {
return res, err
}
res[key] = boolValue
} else {
res[key] = value
}
}
return res, nil
}
// UpdateEnvVariable to update the particular env variable
func (c *provider) UpdateEnvVariable(key string, value interface{}) error {
err := c.store.HSet(c.ctx, envStorePrefix, key, value).Err()
if err != nil {
log.Debug("Error saving redis token: ", err)
return err
}
return nil
}
// GetStringStoreEnvVariable to get the string env variable from env store
func (c *provider) GetStringStoreEnvVariable(key string) (string, error) {
data, err := c.store.HGet(c.ctx, envStorePrefix, key).Result()
if err != nil {
return "", nil
}
return data, nil
}
// GetBoolStoreEnvVariable to get the bool env variable from env store
func (c *provider) GetBoolStoreEnvVariable(key string) (bool, error) {
data, err := c.store.HGet(c.ctx, envStorePrefix, key).Result()
if err != nil {
return false, nil
}
return data == "1", nil
}
type AuthorProfile struct {
ID int `json:"id"`
// Add other fields as necessary
}
// GetUserAppDataFromRedis retrieves user profile and follows from Redis, combines them into a JSON format,
// and assigns the JSON string to the provided user's ID.
func (c *provider) GetUserAppDataFromRedis(userId string) (string, error) {
// Получаем профиль автора из Redis
authorProfileString, err := c.store.Get(c.ctx, fmt.Sprintf("author:user:%d", userId)).Result()
if err != nil {
return "", err
}
// Парсим профиль пользователя в map
var authorProfileMap map[string]interface{}
err = json.Unmarshal([]byte(authorProfileString), &authorProfileMap)
if err != nil {
return "", err
}
authorId, ok := authorProfileMap["id"].(int)
if !ok {
fmt.Println("Ошибка: id не найден или неверного типа")
return "", errors.New("id not found or is of incorrect type")
}
// Начинаем сбор данных в общий JSON
combinedData := map[string]interface{}{
"profile": authorProfileMap,
}
// Получаем подписки автора на других авторов
if authorsObjectsString, err := c.getFollowedObjectsString(fmt.Sprintf("author:follows-authors:%d", authorId), "author:id:%d"); err == nil {
combinedData["authors"] = authorsObjectsString
}
// Получаем подписки автора на темы
if topicsObjectString, err := c.getFollowedObjectsString(fmt.Sprintf("author:follows-topics:%d", authorId), "topic:id:%d"); err == nil {
combinedData["topics"] = topicsObjectString
}
// Получаем подписчиков автора
if authorFollowersObjectsString, err := c.getFollowedObjectsString(fmt.Sprintf("author:followers:%d", authorId), "author:id:%d"); err == nil {
combinedData["followers"] = authorFollowersObjectsString
}
// Преобразуем собранные данные в JSON строку
combinedDataString, err := json.Marshal(combinedData)
if err != nil {
return "", err
}
return string(combinedDataString), nil
}
// Универсальная функция для получения объектов по списку ID из Redis
func (c *provider) getFollowedObjectsString(followKey string, objectKeyPattern string) ([]map[string]interface{}, error) {
followsString, err := c.store.Get(c.ctx, followKey).Result()
if err != nil {
if err == redis.Nil {
return []map[string]interface{}{}, nil
}
return nil, err
}
var ids []int
err = json.Unmarshal([]byte(followsString), &ids)
if err != nil {
return nil, err
}
objects := make([]map[string]interface{}, 0, len(ids))
for _, id := range ids {
objectString, err := c.store.Get(c.ctx, fmt.Sprintf(objectKeyPattern, id)).Result()
if err == redis.Nil {
continue
} else if err != nil {
return nil, err
}
var object map[string]interface{}
err = json.Unmarshal([]byte(objectString), &object)
if err != nil {
return nil, err
}
objects = append(objects, object)
}
return objects, nil
}