177 lines
5.6 KiB
Go
177 lines
5.6 KiB
Go
// File Path: monorepo/cloud/maplepress-backend/internal/service/securityevent/logger.go
|
|
package securityevent
|
|
|
|
import (
|
|
"context"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"codeberg.org/mapleopentech/monorepo/cloud/maplepress-backend/internal/domain/securityevent"
|
|
)
|
|
|
|
// Logger handles logging of security events
|
|
// CWE-778: Ensures sufficient logging of security events for audit and forensics
|
|
type Logger interface {
|
|
// LogEvent logs a security event
|
|
LogEvent(ctx context.Context, event *securityevent.SecurityEvent) error
|
|
|
|
// LogAccountLocked logs an account lockout event
|
|
LogAccountLocked(ctx context.Context, emailHash, clientIP string, failedAttempts int, lockoutDuration string) error
|
|
|
|
// LogAccountUnlocked logs an account unlock event
|
|
LogAccountUnlocked(ctx context.Context, emailHash, unlockedBy string) error
|
|
|
|
// LogFailedLogin logs a failed login attempt
|
|
LogFailedLogin(ctx context.Context, emailHash, clientIP string, remainingAttempts int) error
|
|
|
|
// LogExcessiveFailedLogin logs excessive failed login attempts
|
|
LogExcessiveFailedLogin(ctx context.Context, emailHash, clientIP string, attemptCount int) error
|
|
|
|
// LogSuccessfulLogin logs a successful login
|
|
LogSuccessfulLogin(ctx context.Context, emailHash, clientIP string) error
|
|
|
|
// LogIPRateLimitExceeded logs IP rate limit exceeded
|
|
LogIPRateLimitExceeded(ctx context.Context, clientIP string) error
|
|
}
|
|
|
|
type securityEventLogger struct {
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewSecurityEventLogger creates a new security event logger
|
|
func NewSecurityEventLogger(logger *zap.Logger) Logger {
|
|
return &securityEventLogger{
|
|
logger: logger.Named("security-events"),
|
|
}
|
|
}
|
|
|
|
// ProvideSecurityEventLogger provides a SecurityEventLogger for dependency injection
|
|
func ProvideSecurityEventLogger(logger *zap.Logger) Logger {
|
|
return NewSecurityEventLogger(logger)
|
|
}
|
|
|
|
// LogEvent logs a security event
|
|
func (s *securityEventLogger) LogEvent(ctx context.Context, event *securityevent.SecurityEvent) error {
|
|
// Map severity to log level
|
|
logFunc := s.logger.Info
|
|
switch event.Severity {
|
|
case securityevent.SeverityLow:
|
|
logFunc = s.logger.Info
|
|
case securityevent.SeverityMedium:
|
|
logFunc = s.logger.Warn
|
|
case securityevent.SeverityHigh, securityevent.SeverityCritical:
|
|
logFunc = s.logger.Error
|
|
}
|
|
|
|
// Build log fields
|
|
fields := []zap.Field{
|
|
zap.String("event_id", event.ID),
|
|
zap.String("event_type", string(event.EventType)),
|
|
zap.String("severity", string(event.Severity)),
|
|
zap.String("email_hash", event.EmailHash),
|
|
zap.String("client_ip", event.ClientIP),
|
|
zap.Time("timestamp", event.Timestamp),
|
|
}
|
|
|
|
if event.UserAgent != "" {
|
|
fields = append(fields, zap.String("user_agent", event.UserAgent))
|
|
}
|
|
|
|
// Add metadata fields
|
|
for key, value := range event.Metadata {
|
|
fields = append(fields, zap.Any(key, value))
|
|
}
|
|
|
|
logFunc(event.Message, fields...)
|
|
|
|
// TODO: In production, also persist to a security event database/SIEM
|
|
// This could be implemented as a repository pattern:
|
|
// - Store in Cassandra for long-term retention
|
|
// - Send to SIEM (Splunk, ELK, etc.) for analysis
|
|
// - Send to monitoring/alerting system
|
|
|
|
return nil
|
|
}
|
|
|
|
// LogAccountLocked logs an account lockout event
|
|
func (s *securityEventLogger) LogAccountLocked(ctx context.Context, emailHash, clientIP string, failedAttempts int, lockoutDuration string) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeAccountLocked,
|
|
securityevent.SeverityHigh,
|
|
emailHash,
|
|
clientIP,
|
|
"Account locked due to excessive failed login attempts",
|
|
)
|
|
event.WithMetadata("failed_attempts", failedAttempts)
|
|
event.WithMetadata("lockout_duration", lockoutDuration)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|
|
|
|
// LogAccountUnlocked logs an account unlock event
|
|
func (s *securityEventLogger) LogAccountUnlocked(ctx context.Context, emailHash, unlockedBy string) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeAccountUnlocked,
|
|
securityevent.SeverityMedium,
|
|
emailHash,
|
|
"",
|
|
"Account manually unlocked by administrator",
|
|
)
|
|
event.WithMetadata("unlocked_by", unlockedBy)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|
|
|
|
// LogFailedLogin logs a failed login attempt
|
|
func (s *securityEventLogger) LogFailedLogin(ctx context.Context, emailHash, clientIP string, remainingAttempts int) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeFailedLogin,
|
|
securityevent.SeverityMedium,
|
|
emailHash,
|
|
clientIP,
|
|
"Failed login attempt - invalid credentials",
|
|
)
|
|
event.WithMetadata("remaining_attempts", remainingAttempts)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|
|
|
|
// LogExcessiveFailedLogin logs excessive failed login attempts
|
|
func (s *securityEventLogger) LogExcessiveFailedLogin(ctx context.Context, emailHash, clientIP string, attemptCount int) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeExcessiveFailedLogin,
|
|
securityevent.SeverityHigh,
|
|
emailHash,
|
|
clientIP,
|
|
"Excessive failed login attempts detected",
|
|
)
|
|
event.WithMetadata("attempt_count", attemptCount)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|
|
|
|
// LogSuccessfulLogin logs a successful login
|
|
func (s *securityEventLogger) LogSuccessfulLogin(ctx context.Context, emailHash, clientIP string) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeSuccessfulLogin,
|
|
securityevent.SeverityLow,
|
|
emailHash,
|
|
clientIP,
|
|
"Successful login",
|
|
)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|
|
|
|
// LogIPRateLimitExceeded logs IP rate limit exceeded
|
|
func (s *securityEventLogger) LogIPRateLimitExceeded(ctx context.Context, clientIP string) error {
|
|
event := securityevent.NewSecurityEvent(
|
|
securityevent.EventTypeIPRateLimitExceeded,
|
|
securityevent.SeverityMedium,
|
|
"",
|
|
clientIP,
|
|
"IP rate limit exceeded for login attempts",
|
|
)
|
|
|
|
return s.LogEvent(ctx, event)
|
|
}
|