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 }