Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
165
cloud/maplepress-backend/internal/service/gateway/login.go
Normal file
165
cloud/maplepress-backend/internal/service/gateway/login.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/service"
|
||||
gatewayuc "codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/usecase/gateway"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/logger"
|
||||
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/pkg/security/jwt"
|
||||
)
|
||||
|
||||
// LoginService handles user login operations
|
||||
type LoginService interface {
|
||||
Login(ctx context.Context, input *LoginInput) (*LoginResponse, error)
|
||||
}
|
||||
|
||||
// LoginInput represents the input for user login
|
||||
type LoginInput struct {
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
// LoginResponse represents the response after successful login
|
||||
type LoginResponse struct {
|
||||
// User details
|
||||
UserID string `json:"user_id"`
|
||||
UserEmail string `json:"user_email"`
|
||||
UserName string `json:"user_name"`
|
||||
UserRole string `json:"user_role"`
|
||||
|
||||
// Tenant details
|
||||
TenantID string `json:"tenant_id"`
|
||||
|
||||
// Session and tokens
|
||||
SessionID string `json:"session_id"`
|
||||
AccessToken string `json:"access_token"`
|
||||
AccessExpiry time.Time `json:"access_expiry"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
RefreshExpiry time.Time `json:"refresh_expiry"`
|
||||
|
||||
LoginAt time.Time `json:"login_at"`
|
||||
}
|
||||
|
||||
type loginService struct {
|
||||
loginUC *gatewayuc.LoginUseCase
|
||||
sessionService service.SessionService
|
||||
jwtProvider jwt.Provider
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewLoginService creates a new login service
|
||||
func NewLoginService(
|
||||
loginUC *gatewayuc.LoginUseCase,
|
||||
sessionService service.SessionService,
|
||||
jwtProvider jwt.Provider,
|
||||
logger *zap.Logger,
|
||||
) LoginService {
|
||||
return &loginService{
|
||||
loginUC: loginUC,
|
||||
sessionService: sessionService,
|
||||
jwtProvider: jwtProvider,
|
||||
logger: logger.Named("login-service"),
|
||||
}
|
||||
}
|
||||
|
||||
// Login handles the complete login flow
|
||||
func (s *loginService) Login(ctx context.Context, input *LoginInput) (*LoginResponse, error) {
|
||||
// CWE-532: Use hashed email to prevent PII in logs
|
||||
s.logger.Info("processing login request",
|
||||
logger.EmailHash(input.Email))
|
||||
|
||||
// Execute login use case (validates credentials)
|
||||
loginOutput, err := s.loginUC.Execute(ctx, &gatewayuc.LoginInput{
|
||||
Email: input.Email,
|
||||
Password: input.Password,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("login failed", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// CWE-532: Use hashed email to prevent PII in logs
|
||||
s.logger.Info("credentials validated successfully",
|
||||
zap.String("user_id", loginOutput.UserID),
|
||||
logger.EmailHash(loginOutput.UserEmail),
|
||||
zap.String("tenant_id", loginOutput.TenantID))
|
||||
|
||||
// Parse tenant ID to UUID
|
||||
tenantUUID, err := uuid.Parse(loginOutput.TenantID)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to parse tenant ID", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse user ID to UUID
|
||||
userUUID, err := uuid.Parse(loginOutput.UserID)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to parse user ID", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// CWE-384: Invalidate all existing sessions before creating new one (Session Fixation Prevention)
|
||||
// This ensures that any session IDs an attacker may have obtained are invalidated
|
||||
s.logger.Info("invalidating existing sessions for security",
|
||||
zap.String("user_uuid", userUUID.String()))
|
||||
if err := s.sessionService.InvalidateUserSessions(ctx, userUUID); err != nil {
|
||||
// Log warning but don't fail login - this is best effort cleanup
|
||||
s.logger.Warn("failed to invalidate existing sessions (non-fatal)",
|
||||
zap.String("user_uuid", userUUID.String()),
|
||||
zap.Error(err))
|
||||
}
|
||||
|
||||
// Create new session in two-tier cache
|
||||
session, err := s.sessionService.CreateSession(
|
||||
ctx,
|
||||
0, // UserID as uint64 - not used in our UUID-based system
|
||||
userUUID,
|
||||
loginOutput.UserEmail,
|
||||
loginOutput.UserName,
|
||||
loginOutput.UserRole,
|
||||
tenantUUID,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to create session", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.logger.Info("session created", zap.String("session_id", session.ID))
|
||||
|
||||
// Generate JWT access and refresh tokens
|
||||
accessToken, accessExpiry, refreshToken, refreshExpiry, err := s.jwtProvider.GenerateTokenPair(
|
||||
session.ID,
|
||||
AccessTokenDuration,
|
||||
RefreshTokenDuration,
|
||||
)
|
||||
if err != nil {
|
||||
s.logger.Error("failed to generate tokens", zap.Error(err))
|
||||
// Clean up session
|
||||
_ = s.sessionService.DeleteSession(ctx, session.ID)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.logger.Info("login completed successfully",
|
||||
zap.String("user_id", loginOutput.UserID),
|
||||
zap.String("tenant_id", loginOutput.TenantID),
|
||||
zap.String("session_id", session.ID))
|
||||
|
||||
return &LoginResponse{
|
||||
UserID: loginOutput.UserID,
|
||||
UserEmail: loginOutput.UserEmail,
|
||||
UserName: loginOutput.UserName,
|
||||
UserRole: loginOutput.UserRole,
|
||||
TenantID: loginOutput.TenantID,
|
||||
SessionID: session.ID,
|
||||
AccessToken: accessToken,
|
||||
AccessExpiry: accessExpiry,
|
||||
RefreshToken: refreshToken,
|
||||
RefreshExpiry: refreshExpiry,
|
||||
LoginAt: time.Now().UTC(),
|
||||
}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue