136 lines
4.8 KiB
Go
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
|
|
}
|