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) { // Получаем ID автора из Redis authorIdString, err := c.store.Get(c.ctx, fmt.Sprintf("author:user:%s", userId)).Result() if err != nil { return "", err } // Преобразуем ID автора из строки в int var authorId int err = json.Unmarshal([]byte(authorIdString), &authorId) if err != nil { return "", err } // Получаем профиль автора из Redis authorProfileString, err := c.store.Get(c.ctx, fmt.Sprintf("author:id:%d", authorId)).Result() if err != nil { return "", err } // Парсим профиль пользователя в map var authorProfileMap map[string]interface{} err = json.Unmarshal([]byte(authorProfileString), &authorProfileMap) if err != nil { return "", err } // Начинаем сбор данных в общий 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 }