package model import ( "encoding/json" "strings" "time" "gorm.io/gorm" ) // SecurityEvent represents a security event in the system type SecurityEvent struct { BaseModel EventType string `json:"eventType" gorm:"not null;type:varchar(100);index"` Severity string `json:"severity" gorm:"not null;type:varchar(20);index"` UserID string `json:"userId" gorm:"type:varchar(36);index"` IPAddress string `json:"ipAddress" gorm:"type:varchar(45);index"` UserAgent string `json:"userAgent" gorm:"type:text"` Resource string `json:"resource" gorm:"type:varchar(100)"` Action string `json:"action" gorm:"type:varchar(100)"` Success bool `json:"success" gorm:"index"` Blocked bool `json:"blocked" gorm:"default:false;index"` Message string `json:"message" gorm:"type:text"` Details map[string]interface{} `json:"details" gorm:"type:text"` SessionID string `json:"sessionId,omitempty" gorm:"type:varchar(255)"` RequestID string `json:"requestId,omitempty" gorm:"type:varchar(255)"` CountryCode string `json:"countryCode,omitempty" gorm:"type:varchar(2)"` City string `json:"city,omitempty" gorm:"type:varchar(100)"` Resolved bool `json:"resolved" gorm:"default:false;index"` ResolvedBy string `json:"resolvedBy,omitempty" gorm:"type:varchar(36)"` ResolvedAt *time.Time `json:"resolvedAt,omitempty"` Notes string `json:"notes,omitempty" gorm:"type:text"` User *User `json:"user,omitempty" gorm:"foreignKey:UserID"` Resolver *User `json:"resolver,omitempty" gorm:"foreignKey:ResolvedBy"` } // SecurityEventCreateRequest represents the request to create a new security event type SecurityEventCreateRequest struct { EventType string `json:"eventType" validate:"required,max=100"` Severity string `json:"severity" validate:"required,oneof=low medium high critical"` UserID string `json:"userId"` IPAddress string `json:"ipAddress" validate:"max=45"` UserAgent string `json:"userAgent"` Resource string `json:"resource" validate:"max=100"` Action string `json:"action" validate:"max=100"` Success bool `json:"success"` Blocked bool `json:"blocked"` Message string `json:"message" validate:"required"` Details map[string]interface{} `json:"details"` SessionID string `json:"sessionId"` RequestID string `json:"requestId"` CountryCode string `json:"countryCode" validate:"max=2"` City string `json:"city" validate:"max=100"` } // SecurityEventUpdateRequest represents the request to update a security event type SecurityEventUpdateRequest struct { Resolved *bool `json:"resolved,omitempty"` ResolvedBy *string `json:"resolvedBy,omitempty"` Notes *string `json:"notes,omitempty"` } // SecurityEventInfo represents public security event information type SecurityEventInfo struct { ID string `json:"id"` EventType string `json:"eventType"` Severity string `json:"severity"` UserID string `json:"userId"` UserEmail string `json:"userEmail,omitempty"` UserName string `json:"userName,omitempty"` IPAddress string `json:"ipAddress"` UserAgent string `json:"userAgent"` Resource string `json:"resource"` Action string `json:"action"` Success bool `json:"success"` Blocked bool `json:"blocked"` Message string `json:"message"` Details map[string]interface{} `json:"details"` SessionID string `json:"sessionId,omitempty"` RequestID string `json:"requestId,omitempty"` CountryCode string `json:"countryCode,omitempty"` City string `json:"city,omitempty"` Resolved bool `json:"resolved"` ResolvedBy string `json:"resolvedBy,omitempty"` ResolverEmail string `json:"resolverEmail,omitempty"` ResolverName string `json:"resolverName,omitempty"` ResolvedAt *time.Time `json:"resolvedAt,omitempty"` Notes string `json:"notes,omitempty"` DateCreated string `json:"dateCreated"` } // BeforeCreate is called before creating a security event func (se *SecurityEvent) BeforeCreate(tx *gorm.DB) error { se.BaseModel.BeforeCreate() // Normalize fields se.EventType = strings.ToLower(strings.TrimSpace(se.EventType)) se.Severity = strings.ToLower(strings.TrimSpace(se.Severity)) se.IPAddress = strings.TrimSpace(se.IPAddress) se.UserAgent = strings.TrimSpace(se.UserAgent) se.Resource = strings.ToLower(strings.TrimSpace(se.Resource)) se.Action = strings.ToLower(strings.TrimSpace(se.Action)) se.Message = strings.TrimSpace(se.Message) return se.Validate() } // BeforeUpdate is called before updating a security event func (se *SecurityEvent) BeforeUpdate(tx *gorm.DB) error { se.BaseModel.BeforeUpdate() // If resolving the event, set resolved timestamp if se.Resolved && se.ResolvedAt == nil { now := time.Now() se.ResolvedAt = &now } return nil } // Validate validates security event data func (se *SecurityEvent) Validate() error { validSeverities := []string{"low", "medium", "high", "critical"} isValidSeverity := false for _, severity := range validSeverities { if se.Severity == severity { isValidSeverity = true break } } if !isValidSeverity { return gorm.ErrInvalidValue } return nil } // SetDetails sets the details field from a map func (se *SecurityEvent) SetDetails(details map[string]interface{}) error { se.Details = details return nil } // GetDetails returns the details as a map func (se *SecurityEvent) GetDetails() map[string]interface{} { if se.Details == nil { return make(map[string]interface{}) } return se.Details } // SetDetailsFromJSON sets the details field from a JSON string func (se *SecurityEvent) SetDetailsFromJSON(jsonStr string) error { if jsonStr == "" { se.Details = make(map[string]interface{}) return nil } var details map[string]interface{} if err := json.Unmarshal([]byte(jsonStr), &details); err != nil { return err } se.Details = details return nil } // GetDetailsAsJSON returns the details as a JSON string func (se *SecurityEvent) GetDetailsAsJSON() (string, error) { if se.Details == nil || len(se.Details) == 0 { return "{}", nil } bytes, err := json.Marshal(se.Details) if err != nil { return "", err } return string(bytes), nil } // ToSecurityEventInfo converts SecurityEvent to SecurityEventInfo (public information) func (se *SecurityEvent) ToSecurityEventInfo() SecurityEventInfo { info := SecurityEventInfo{ ID: se.ID, EventType: se.EventType, Severity: se.Severity, UserID: se.UserID, IPAddress: se.IPAddress, UserAgent: se.UserAgent, Resource: se.Resource, Action: se.Action, Success: se.Success, Blocked: se.Blocked, Message: se.Message, Details: se.GetDetails(), SessionID: se.SessionID, RequestID: se.RequestID, CountryCode: se.CountryCode, City: se.City, Resolved: se.Resolved, ResolvedBy: se.ResolvedBy, ResolvedAt: se.ResolvedAt, Notes: se.Notes, DateCreated: se.DateCreated.Format("2006-01-02T15:04:05Z"), } // Include user information if available if se.User != nil { info.UserEmail = se.User.Email info.UserName = se.User.Name } // Include resolver information if available if se.Resolver != nil { info.ResolverEmail = se.Resolver.Email info.ResolverName = se.Resolver.Name } return info } // AddDetail adds a single detail to the details map func (se *SecurityEvent) AddDetail(key string, value interface{}) { if se.Details == nil { se.Details = make(map[string]interface{}) } se.Details[key] = value } // GetDetail gets a single detail from the details map func (se *SecurityEvent) GetDetail(key string) (interface{}, bool) { if se.Details == nil { return nil, false } value, exists := se.Details[key] return value, exists } // Resolve resolves the security event func (se *SecurityEvent) Resolve(resolverID, notes string) { se.Resolved = true se.ResolvedBy = resolverID se.Notes = notes now := time.Now() se.ResolvedAt = &now } // Unresolve unresolves the security event func (se *SecurityEvent) Unresolve() { se.Resolved = false se.ResolvedBy = "" se.ResolvedAt = nil } // IsResolved returns whether the security event is resolved func (se *SecurityEvent) IsResolved() bool { return se.Resolved } // IsCritical returns whether the security event is critical func (se *SecurityEvent) IsCritical() bool { return se.Severity == SecuritySeverityCritical } // IsHigh returns whether the security event is high severity func (se *SecurityEvent) IsHigh() bool { return se.Severity == SecuritySeverityHigh } // IsBlocked returns whether the security event was blocked func (se *SecurityEvent) IsBlocked() bool { return se.Blocked } // Security event types const ( SecurityEventLoginAttempt = "login_attempt" SecurityEventLoginFailure = "login_failure" SecurityEventLoginSuccess = "login_success" SecurityEventBruteForce = "brute_force" SecurityEventAccountLockout = "account_lockout" SecurityEventUnauthorizedAccess = "unauthorized_access" SecurityEventPrivilegeEscalation = "privilege_escalation" SecurityEventSuspiciousActivity = "suspicious_activity" SecurityEventRateLimitExceeded = "rate_limit_exceeded" SecurityEventInvalidToken = "invalid_token" SecurityEventTokenExpired = "token_expired" SecurityEventPasswordChange = "password_change" SecurityEventEmailVerification = "email_verification" SecurityEventTwoFactorAuth = "two_factor_auth" SecurityEventDataExfiltration = "data_exfiltration" SecurityEventMaliciousRequest = "malicious_request" SecurityEventSystemAccess = "system_access" SecurityEventConfigChange = "config_change" SecurityEventFileAccess = "file_access" SecurityEventDatabaseAccess = "database_access" ) // Security severity levels const ( SecuritySeverityLow = "low" SecuritySeverityMedium = "medium" SecuritySeverityHigh = "high" SecuritySeverityCritical = "critical" ) // CreateSecurityEvent creates a new security event func CreateSecurityEvent(eventType, severity, message string) *SecurityEvent { event := &SecurityEvent{ EventType: eventType, Severity: severity, Message: message, Details: make(map[string]interface{}), } event.Init() return event } // CreateSecurityEventWithUser creates a new security event with user information func CreateSecurityEventWithUser(eventType, severity, message, userID string) *SecurityEvent { event := CreateSecurityEvent(eventType, severity, message) event.UserID = userID return event } // CreateSecurityEventWithDetails creates a new security event with details func CreateSecurityEventWithDetails(eventType, severity, message string, details map[string]interface{}) *SecurityEvent { event := CreateSecurityEvent(eventType, severity, message) event.Details = details return event } // SetRequestInfo sets request-related information func (se *SecurityEvent) SetRequestInfo(ipAddress, userAgent, sessionID, requestID string) { se.IPAddress = ipAddress se.UserAgent = userAgent se.SessionID = sessionID se.RequestID = requestID } // SetLocationInfo sets location-related information func (se *SecurityEvent) SetLocationInfo(countryCode, city string) { se.CountryCode = countryCode se.City = city } // SetResourceAction sets resource and action information func (se *SecurityEvent) SetResourceAction(resource, action string) { se.Resource = resource se.Action = action } // MarkAsBlocked marks the security event as blocked func (se *SecurityEvent) MarkAsBlocked() { se.Blocked = true } // MarkAsSuccess marks the security event as successful func (se *SecurityEvent) MarkAsSuccess() { se.Success = true } // MarkAsFailure marks the security event as failed func (se *SecurityEvent) MarkAsFailure() { se.Success = false }