Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,109 @@
// codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/service/user/publiclookup.go
package user
import (
"context"
"encoding/base64"
"strings"
"go.uber.org/zap"
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
uc_user "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/usecase/user"
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/httperror"
"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/validation"
)
type UserPublicLookupRequestDTO struct {
Email string `json:"email"`
}
type UserPublicLookupResponseDTO struct {
UserID string `json:"user_id"`
Email string `json:"email"`
Name string `json:"name"` // Optional: for display
PublicKeyInBase64 string `json:"public_key_in_base64"` // Base64 encoded
VerificationID string `json:"verification_id"`
}
type UserPublicLookupService interface {
Execute(ctx context.Context, req *UserPublicLookupRequestDTO) (*UserPublicLookupResponseDTO, error)
}
type userPublicLookupServiceImpl struct {
config *config.Config
logger *zap.Logger
userGetByEmailUC uc_user.UserGetByEmailUseCase
}
func NewUserPublicLookupService(
cfg *config.Config,
logger *zap.Logger,
userGetByEmailUC uc_user.UserGetByEmailUseCase,
) UserPublicLookupService {
logger = logger.Named("UserPublicLookupService")
return &userPublicLookupServiceImpl{cfg, logger, userGetByEmailUC}
}
func (svc *userPublicLookupServiceImpl) Execute(ctx context.Context, req *UserPublicLookupRequestDTO) (*UserPublicLookupResponseDTO, error) {
//
// STEP 1: Sanitization of the input.
//
// Defensive Code: For security purposes we need to perform some sanitization on the inputs.
req.Email = strings.ToLower(req.Email)
req.Email = strings.ReplaceAll(req.Email, " ", "")
req.Email = strings.ReplaceAll(req.Email, "\t", "")
req.Email = strings.TrimSpace(req.Email)
svc.logger.Debug("sanitized email",
zap.String("email", validation.MaskEmail(req.Email)))
//
// STEP 2: Validation of input.
//
e := make(map[string]string)
if req.Email == "" {
e["email"] = "Email is required"
}
if len(req.Email) > 255 {
e["email"] = "Email is too long"
}
if len(e) != 0 {
svc.logger.Warn("failed validating",
zap.Any("e", e))
return nil, httperror.NewForBadRequest(&e)
}
//
// STEP 3: Lookup user by email
//
// Lookup the user in our database, else return a `400 Bad Request` error.
// Note: We return a generic error message to prevent user enumeration attacks.
u, err := svc.userGetByEmailUC.Execute(ctx, req.Email)
if err != nil {
svc.logger.Error("failed getting user by email from database",
zap.Any("error", err))
return nil, httperror.NewForBadRequestWithSingleField("email", "Unable to complete lookup")
}
if u == nil {
svc.logger.Warn("user lookup attempted for non-existent email",
zap.String("email", validation.MaskEmail(req.Email)))
// Return same error message as above to prevent user enumeration
return nil, httperror.NewForBadRequestWithSingleField("email", "Unable to complete lookup")
}
// STEP 4: Build response DTO
dto := &UserPublicLookupResponseDTO{
UserID: u.ID.String(),
Email: u.Email,
Name: u.Name,
PublicKeyInBase64: base64.StdEncoding.EncodeToString(u.SecurityData.PublicKey.Key),
VerificationID: u.SecurityData.VerificationID,
}
return dto, nil
}