codeword/internal/services/recovery_service.go
2025-01-04 17:01:27 +03:00

136 lines
4.8 KiB
Go

package services
import (
"context"
"encoding/base64"
"gitea.pena/PenaSide/codeword/internal/adapters/client"
"gitea.pena/PenaSide/codeword/internal/models"
"gitea.pena/PenaSide/codeword/internal/utils/encrypt"
"go.uber.org/zap"
)
type CodewordRepository interface {
StoreRecoveryRecord(ctx context.Context, deps models.StoreRecDeps) (string, error)
InsertToQueue(ctx context.Context, deps models.RecEmailDeps) error
Ping(ctx context.Context) error
GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error)
}
type UserRepository interface {
FindByEmail(ctx context.Context, email string) (*models.User, error)
}
type Deps struct {
Logger *zap.Logger
CodewordRepository CodewordRepository
UserRepository UserRepository
Encrypt *encrypt.Encrypt
AuthClient *client.AuthClient
}
type RecoveryService struct {
logger *zap.Logger
repositoryCodeword CodewordRepository
repositoryUser UserRepository
encrypt *encrypt.Encrypt
authClient *client.AuthClient
}
func NewRecoveryService(deps Deps) *RecoveryService {
return &RecoveryService{
logger: deps.Logger,
repositoryCodeword: deps.CodewordRepository,
repositoryUser: deps.UserRepository,
encrypt: deps.Encrypt,
authClient: deps.AuthClient,
}
}
// GenerateKey генерирует ключ, используя шифрование на основе эллиптической кривой
func (s *RecoveryService) GenerateKey() ([]byte, error) {
key, err := s.encrypt.SignCommonSecret()
if err != nil {
s.logger.Error("Failed to generate unique key for user", zap.Error(err))
return nil, err
}
return key, nil
}
// вызывает пингование в бд
func (s *RecoveryService) Ping(ctx context.Context) error {
err := s.repositoryCodeword.Ping(ctx)
if err != nil {
s.logger.Error("Failed to ping database", zap.Error(err))
return err
}
return nil
}
// FindUserByEmail ищет пользователя по электронной почте
func (s *RecoveryService) FindUserByEmail(ctx context.Context, email string) (*models.User, error) {
user, err := s.repositoryUser.FindByEmail(ctx, email)
if err != nil {
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
return nil, err
}
return user, nil
}
// StoreRecoveryRecord сохраняет запись восстановления в базе данных
func (s *RecoveryService) StoreRecoveryRecord(ctx context.Context, deps models.StoreRecDeps) (string, error) {
id, err := s.repositoryCodeword.StoreRecoveryRecord(ctx, models.StoreRecDeps{UserID: deps.UserID, Email: deps.Email, Key: deps.Key, Url: deps.Url})
if err != nil {
s.logger.Error("Failed save data in mongoDB for email", zap.String("email", deps.Email), zap.Error(err))
return "", err
}
return id, nil
}
// RecoveryEmailTask посылает письмо для восстановления доступа пользователю
func (s *RecoveryService) RecoveryEmailTask(ctx context.Context, deps models.RecEmailDeps) error {
err := s.repositoryCodeword.InsertToQueue(ctx, models.RecEmailDeps{UserID: deps.UserID, Email: deps.Email, SignWithID: deps.SignWithID, ID: deps.ID})
if err != nil {
s.logger.Error("Failed creating a task to send a worker by email", zap.String("email", deps.Email), zap.Error(err))
return err
}
return nil
}
// GetRecoveryRecord получает запись восстановления из базы данных
func (s *RecoveryService) GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error) {
req, err := s.repositoryCodeword.GetRecoveryRecord(ctx, key)
if err != nil {
s.logger.Error("Failed to obtain signature recovery data", zap.String("signature", key), zap.Error(err))
return nil, err
}
byteKey, err := base64.URLEncoding.DecodeString(req.Sign)
if err != nil {
s.logger.Error("Failed to decode string signature to []byte format", zap.String("signature", key), zap.Error(err))
return nil, err
}
// сомнительный вариант но как я думаю верный, что false==err
result, err := s.encrypt.VerifySignature(byteKey)
if err != nil || !result {
s.logger.Error("Failed to verify signature", zap.String("signature", key), zap.Error(err))
return nil, err
}
return req, nil
}
// меняет подпись на токены идя в auth сервис
func (s *RecoveryService) ExchangeForTokens(userID string, signature string) (map[string]string, error) {
tokens, err := s.authClient.RefreshAuthToken(userID, signature)
if err != nil {
s.logger.Error("Failed to refresh auth token", zap.Error(err))
return nil, err
}
return map[string]string{
"accessToken": tokens.AccessToken,
"refreshToken": tokens.RefreshToken,
}, nil
}