mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
feat: Add security handler with fraud detection, SIM swap verification, and alert management
- Add SecurityHandler with fraud analysis, alert filtering, and metrics endpoints - Add FraudAlert struct with ID, Type, Severity, ProfileID, RiskScore, Evidence, IPAddress, Status, and ActionsTaken fields - Add FraudMetrics struct with Period, TotalAlerts, ResolvedAlerts, ResolutionRate, FalsePositiveRate, ByType, and BySeverity fields - Add AnalyzeTransaction with risk scoring based on transaction amount and suspicious
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SecurityHandler handles security-related HTTP requests
|
||||
type SecurityHandler struct{}
|
||||
|
||||
// NewSecurityHandler creates a new security handler
|
||||
func NewSecurityHandler() *SecurityHandler {
|
||||
return &SecurityHandler{}
|
||||
}
|
||||
|
||||
// FraudAlert represents a fraud alert
|
||||
type FraudAlert struct {
|
||||
ID string `json:"id"`
|
||||
Type string `json:"type"`
|
||||
Severity string `json:"severity"`
|
||||
ProfileID string `json:"profile_id"`
|
||||
Description string `json:"description"`
|
||||
RiskScore float64 `json:"risk_score"`
|
||||
Evidence []string `json:"evidence"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
Status string `json:"status"`
|
||||
ActionsTaken []string `json:"actions_taken"`
|
||||
Metadata map[string]any `json:"metadata"`
|
||||
}
|
||||
|
||||
// FraudMetrics represents fraud detection metrics
|
||||
type FraudMetrics struct {
|
||||
Period string `json:"period"`
|
||||
TotalAlerts int64 `json:"total_alerts"`
|
||||
ResolvedAlerts int64 `json:"resolved_alerts"`
|
||||
FalsePositives int64 `json:"false_positives"`
|
||||
ResolutionRate float64 `json:"resolution_rate_pct"`
|
||||
FalsePositiveRate float64 `json:"false_positive_rate_pct"`
|
||||
ByType map[string]int64 `json:"by_type"`
|
||||
BySeverity map[string]int64 `json:"by_severity"`
|
||||
GeneratedAt time.Time `json:"generated_at"`
|
||||
}
|
||||
|
||||
// AnalyzeTransaction analyzes a transaction for fraud
|
||||
func (h *SecurityHandler) AnalyzeTransaction(c *gin.Context) {
|
||||
var req map[string]any
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
profileID, _ := req["profile_id"].(string)
|
||||
ipAddress, _ := req["ip_address"].(string)
|
||||
|
||||
// Simulated fraud analysis
|
||||
riskScore := 25.0 // Low risk by default
|
||||
|
||||
// Check for suspicious patterns
|
||||
if amount, ok := req["amount"].(float64); ok && amount > 1000 {
|
||||
riskScore += 20
|
||||
}
|
||||
|
||||
alert := FraudAlert{
|
||||
ID: "alert-" + time.Now().Format("20060102150405"),
|
||||
Type: "transaction_analysis",
|
||||
Severity: getSeverityFromScore(riskScore),
|
||||
ProfileID: profileID,
|
||||
Description: "Transaction analyzed for fraud indicators",
|
||||
RiskScore: riskScore,
|
||||
Evidence: []string{
|
||||
"Transaction amount within normal range",
|
||||
"IP address matches historical pattern",
|
||||
"Device fingerprint recognized",
|
||||
},
|
||||
IPAddress: ipAddress,
|
||||
Timestamp: time.Now(),
|
||||
Status: "analyzed",
|
||||
ActionsTaken: []string{"Logged for monitoring"},
|
||||
Metadata: req,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, alert)
|
||||
}
|
||||
|
||||
// GetFraudAlerts returns fraud alerts with filtering
|
||||
func (h *SecurityHandler) GetFraudAlerts(c *gin.Context) {
|
||||
var filter struct {
|
||||
Type string `json:"type"`
|
||||
Severity string `json:"severity"`
|
||||
Status string `json:"status"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
c.ShouldBindJSON(&filter)
|
||||
|
||||
if filter.Limit == 0 || filter.Limit > 100 {
|
||||
filter.Limit = 50
|
||||
}
|
||||
|
||||
// Simulated alerts
|
||||
alerts := []FraudAlert{
|
||||
{
|
||||
ID: "alert-001",
|
||||
Type: "account_takeover",
|
||||
Severity: "high",
|
||||
ProfileID: "profile-123",
|
||||
Description: "Multiple failed login attempts from new IP",
|
||||
RiskScore: 85.0,
|
||||
Evidence: []string{"10 failed logins", "New IP address", "Different country"},
|
||||
IPAddress: "192.168.1.100",
|
||||
Timestamp: time.Now().Add(-2 * time.Hour),
|
||||
Status: "new",
|
||||
},
|
||||
{
|
||||
ID: "alert-002",
|
||||
Type: "payment_fraud",
|
||||
Severity: "medium",
|
||||
ProfileID: "profile-456",
|
||||
Description: "Unusual payment pattern detected",
|
||||
RiskScore: 55.0,
|
||||
Evidence: []string{"Large transaction", "New payment method"},
|
||||
IPAddress: "10.0.0.50",
|
||||
Timestamp: time.Now().Add(-5 * time.Hour),
|
||||
Status: "investigating",
|
||||
},
|
||||
{
|
||||
ID: "alert-003",
|
||||
Type: "sim_swap",
|
||||
Severity: "critical",
|
||||
ProfileID: "profile-789",
|
||||
Description: "SIM swap attempt detected",
|
||||
RiskScore: 95.0,
|
||||
Evidence: []string{"SIM change request", "No prior notification", "High-value account"},
|
||||
IPAddress: "172.16.0.25",
|
||||
Timestamp: time.Now().Add(-30 * time.Minute),
|
||||
Status: "blocked",
|
||||
},
|
||||
}
|
||||
|
||||
// Filter alerts
|
||||
filtered := make([]FraudAlert, 0)
|
||||
for _, alert := range alerts {
|
||||
if filter.Type != "" && alert.Type != filter.Type {
|
||||
continue
|
||||
}
|
||||
if filter.Severity != "" && alert.Severity != filter.Severity {
|
||||
continue
|
||||
}
|
||||
if filter.Status != "" && alert.Status != filter.Status {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, alert)
|
||||
if len(filtered) >= filter.Limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, filtered)
|
||||
}
|
||||
|
||||
// UpdateAlertStatus updates a fraud alert status
|
||||
func (h *SecurityHandler) UpdateAlertStatus(c *gin.Context) {
|
||||
alertID := c.Param("id")
|
||||
|
||||
var req struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
Actions []string `json:"actions"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"id": alertID,
|
||||
"status": req.Status,
|
||||
"actions_taken": req.Actions,
|
||||
"updated_at": time.Now(),
|
||||
})
|
||||
}
|
||||
|
||||
// GetFraudMetrics returns fraud detection metrics
|
||||
func (h *SecurityHandler) GetFraudMetrics(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "monthly")
|
||||
|
||||
metrics := FraudMetrics{
|
||||
Period: period,
|
||||
TotalAlerts: 1250,
|
||||
ResolvedAlerts: 1100,
|
||||
FalsePositives: 125,
|
||||
ResolutionRate: 88.0,
|
||||
FalsePositiveRate: 10.0,
|
||||
ByType: map[string]int64{
|
||||
"account_takeover": 350,
|
||||
"subscription_fraud": 200,
|
||||
"payment_fraud": 400,
|
||||
"usage_anomaly": 200,
|
||||
"sim_swap": 100,
|
||||
},
|
||||
BySeverity: map[string]int64{
|
||||
"low": 400,
|
||||
"medium": 500,
|
||||
"high": 250,
|
||||
"critical": 100,
|
||||
},
|
||||
GeneratedAt: time.Now(),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, metrics)
|
||||
}
|
||||
|
||||
// GetFraudPatterns returns detected fraud patterns
|
||||
func (h *SecurityHandler) GetFraudPatterns(c *gin.Context) {
|
||||
patterns := []map[string]any{
|
||||
{
|
||||
"id": "pattern-1",
|
||||
"name": "Velocity Attack",
|
||||
"description": "Multiple rapid transactions from same source",
|
||||
"frequency": "high",
|
||||
"indicators": []string{"High transaction rate", "Same IP", "Different accounts"},
|
||||
"mitigation": "Rate limiting, IP blocking",
|
||||
},
|
||||
{
|
||||
"id": "pattern-2",
|
||||
"name": "Account Enumeration",
|
||||
"description": "Systematic probing of account existence",
|
||||
"frequency": "medium",
|
||||
"indicators": []string{"Sequential account checks", "Automated requests"},
|
||||
"mitigation": "CAPTCHA, rate limiting",
|
||||
},
|
||||
{
|
||||
"id": "pattern-3",
|
||||
"name": "SIM Swap Attack",
|
||||
"description": "Unauthorized SIM card replacement",
|
||||
"frequency": "low",
|
||||
"indicators": []string{"Sudden SIM change", "No customer contact", "High-value target"},
|
||||
"mitigation": "Multi-factor verification, cooling period",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"patterns": patterns})
|
||||
}
|
||||
|
||||
// VerifySIMSwap verifies a SIM swap request
|
||||
func (h *SecurityHandler) VerifySIMSwap(c *gin.Context) {
|
||||
var req struct {
|
||||
ProfileID string `json:"profile_id" binding:"required"`
|
||||
MSISDN string `json:"msisdn" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Simulated SIM swap verification
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"profile_id": req.ProfileID,
|
||||
"msisdn": req.MSISDN,
|
||||
"verified": true,
|
||||
"risk_score": 15.0,
|
||||
"risk_level": "low",
|
||||
"last_sim_swap": time.Now().AddDate(-1, 0, 0).Format("2006-01-02"),
|
||||
"checks_passed": []string{
|
||||
"Identity verified",
|
||||
"No recent SIM changes",
|
||||
"Account in good standing",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetSIMSwapHistory returns SIM swap history for a profile
|
||||
func (h *SecurityHandler) GetSIMSwapHistory(c *gin.Context) {
|
||||
profileID := c.Param("profile_id")
|
||||
|
||||
history := []map[string]any{
|
||||
{
|
||||
"id": "swap-001",
|
||||
"profile_id": profileID,
|
||||
"old_iccid": "8901234567890123456",
|
||||
"new_iccid": "8901234567890123457",
|
||||
"timestamp": time.Now().AddDate(-1, 0, 0),
|
||||
"reason": "Device upgrade",
|
||||
"verified": true,
|
||||
"status": "completed",
|
||||
},
|
||||
{
|
||||
"id": "swap-002",
|
||||
"profile_id": profileID,
|
||||
"old_iccid": "8901234567890123455",
|
||||
"new_iccid": "8901234567890123456",
|
||||
"timestamp": time.Now().AddDate(-2, 0, 0),
|
||||
"reason": "Lost device",
|
||||
"verified": true,
|
||||
"status": "completed",
|
||||
},
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"history": history})
|
||||
}
|
||||
|
||||
func getSeverityFromScore(score float64) string {
|
||||
switch {
|
||||
case score >= 80:
|
||||
return "critical"
|
||||
case score >= 60:
|
||||
return "high"
|
||||
case score >= 40:
|
||||
return "medium"
|
||||
default:
|
||||
return "low"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user