Files
mq/security.go
2025-09-18 18:26:35 +05:45

681 lines
17 KiB
Go

package mq
import (
"context"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"fmt"
"net"
"strings"
"sync"
"time"
)
// SecurityManager handles authentication, authorization, and security policies
type SecurityManager struct {
authProviders map[string]AuthProvider
roleManager *RoleManager
rateLimiter *SecurityRateLimiter
auditLogger *AuditLogger
sessionManager *SessionManager
encryptionKey []byte
mu sync.RWMutex
}
// AuthProvider interface for different authentication methods
type AuthProvider interface {
Name() string
Authenticate(ctx context.Context, credentials map[string]any) (*User, error)
ValidateToken(token string) (*User, error)
}
// User represents an authenticated user
type User struct {
ID string `json:"id"`
Username string `json:"username"`
Roles []string `json:"roles"`
Permissions []string `json:"permissions"`
Metadata map[string]any `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
LastLoginAt *time.Time `json:"last_login_at,omitempty"`
}
// RoleManager manages user roles and permissions
type RoleManager struct {
roles map[string]*Role
permissions map[string]*Permission
mu sync.RWMutex
}
// Role represents a user role with associated permissions
type Role struct {
Name string `json:"name"`
Description string `json:"description"`
Permissions []string `json:"permissions"`
CreatedAt time.Time `json:"created_at"`
}
// Permission represents a specific permission
type Permission struct {
Name string `json:"name"`
Resource string `json:"resource"`
Action string `json:"action"`
Description string `json:"description"`
CreatedAt time.Time `json:"created_at"`
}
// SecurityRateLimiter implements rate limiting for security operations
type SecurityRateLimiter struct {
attempts map[string]*RateLimitEntry
maxAttempts int
window time.Duration
mu sync.RWMutex
}
// RateLimitEntry tracks rate limiting for a specific key
type RateLimitEntry struct {
Count int
WindowStart time.Time
}
// AuditLogger logs security-related events
type AuditLogger struct {
events []AuditEvent
maxEvents int
mu sync.RWMutex
}
// AuditEvent represents a security audit event
type AuditEvent struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
EventType string `json:"event_type"`
UserID string `json:"user_id,omitempty"`
Resource string `json:"resource"`
Action string `json:"action"`
IPAddress string `json:"ip_address,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
Success bool `json:"success"`
Details map[string]any `json:"details,omitempty"`
}
// SessionManager manages user sessions
type SessionManager struct {
sessions map[string]*Session
maxAge time.Duration
mu sync.RWMutex
}
// Session represents a user session
type Session struct {
ID string `json:"id"`
UserID string `json:"user_id"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at"`
IPAddress string `json:"ip_address"`
UserAgent string `json:"user_agent"`
Data map[string]any `json:"data,omitempty"`
}
// NewSecurityManager creates a new security manager
func NewSecurityManager() *SecurityManager {
key := make([]byte, 32)
rand.Read(key)
return &SecurityManager{
authProviders: make(map[string]AuthProvider),
roleManager: NewRoleManager(),
rateLimiter: NewSecurityRateLimiter(5, time.Minute*15), // 5 attempts per 15 minutes
auditLogger: NewAuditLogger(10000),
sessionManager: NewSessionManager(time.Hour * 24), // 24 hour sessions
encryptionKey: key,
}
}
// NewRoleManager creates a new role manager
func NewRoleManager() *RoleManager {
rm := &RoleManager{
roles: make(map[string]*Role),
permissions: make(map[string]*Permission),
}
// Initialize default permissions
rm.AddPermission(&Permission{
Name: "task.publish",
Resource: "task",
Action: "publish",
Description: "Publish tasks to queues",
CreatedAt: time.Now(),
})
rm.AddPermission(&Permission{
Name: "task.consume",
Resource: "task",
Action: "consume",
Description: "Consume tasks from queues",
CreatedAt: time.Now(),
})
rm.AddPermission(&Permission{
Name: "queue.manage",
Resource: "queue",
Action: "manage",
Description: "Manage queues",
CreatedAt: time.Now(),
})
rm.AddPermission(&Permission{
Name: "admin.system",
Resource: "system",
Action: "admin",
Description: "System administration",
CreatedAt: time.Now(),
})
// Initialize default roles
rm.AddRole(&Role{
Name: "publisher",
Description: "Can publish tasks",
Permissions: []string{"task.publish"},
CreatedAt: time.Now(),
})
rm.AddRole(&Role{
Name: "consumer",
Description: "Can consume tasks",
Permissions: []string{"task.consume"},
CreatedAt: time.Now(),
})
rm.AddRole(&Role{
Name: "admin",
Description: "Full system access",
Permissions: []string{"task.publish", "task.consume", "queue.manage", "admin.system"},
CreatedAt: time.Now(),
})
return rm
}
// AddPermission adds a permission to the role manager
func (rm *RoleManager) AddPermission(perm *Permission) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.permissions[perm.Name] = perm
}
// AddRole adds a role to the role manager
func (rm *RoleManager) AddRole(role *Role) {
rm.mu.Lock()
defer rm.mu.Unlock()
rm.roles[role.Name] = role
}
// HasPermission checks if a user has a specific permission
func (rm *RoleManager) HasPermission(user *User, permission string) bool {
rm.mu.RLock()
defer rm.mu.RUnlock()
for _, roleName := range user.Roles {
if role, exists := rm.roles[roleName]; exists {
for _, perm := range role.Permissions {
if perm == permission {
return true
}
}
}
}
return false
}
// GetUserPermissions returns all permissions for a user
func (rm *RoleManager) GetUserPermissions(user *User) []string {
rm.mu.RLock()
defer rm.mu.RUnlock()
permissions := make(map[string]bool)
for _, roleName := range user.Roles {
if role, exists := rm.roles[roleName]; exists {
for _, perm := range role.Permissions {
permissions[perm] = true
}
}
}
result := make([]string, 0, len(permissions))
for perm := range permissions {
result = append(result, perm)
}
return result
}
// NewSecurityRateLimiter creates a new security rate limiter
func NewSecurityRateLimiter(maxAttempts int, window time.Duration) *SecurityRateLimiter {
return &SecurityRateLimiter{
attempts: make(map[string]*RateLimitEntry),
maxAttempts: maxAttempts,
window: window,
}
}
// IsAllowed checks if an action is allowed based on rate limiting
func (rl *SecurityRateLimiter) IsAllowed(key string) bool {
rl.mu.Lock()
defer rl.mu.Unlock()
now := time.Now()
entry, exists := rl.attempts[key]
if !exists {
rl.attempts[key] = &RateLimitEntry{
Count: 1,
WindowStart: now,
}
return true
}
// Check if we're in a new window
if now.Sub(entry.WindowStart) >= rl.window {
entry.Count = 1
entry.WindowStart = now
return true
}
// Check if we've exceeded the limit
if entry.Count >= rl.maxAttempts {
return false
}
entry.Count++
return true
}
// Reset resets the rate limit for a key
func (rl *SecurityRateLimiter) Reset(key string) {
rl.mu.Lock()
defer rl.mu.Unlock()
delete(rl.attempts, key)
}
// NewAuditLogger creates a new audit logger
func NewAuditLogger(maxEvents int) *AuditLogger {
return &AuditLogger{
events: make([]AuditEvent, 0),
maxEvents: maxEvents,
}
}
// LogEvent logs a security event
func (al *AuditLogger) LogEvent(event AuditEvent) {
al.mu.Lock()
defer al.mu.Unlock()
event.ID = generateID()
event.Timestamp = time.Now()
al.events = append(al.events, event)
// Keep only the most recent events
if len(al.events) > al.maxEvents {
al.events = al.events[len(al.events)-al.maxEvents:]
}
}
// GetEvents returns audit events with optional filtering
func (al *AuditLogger) GetEvents(userID, eventType string, limit int) []AuditEvent {
al.mu.RLock()
defer al.mu.RUnlock()
var filtered []AuditEvent
for _, event := range al.events {
if (userID == "" || event.UserID == userID) &&
(eventType == "" || event.EventType == eventType) {
filtered = append(filtered, event)
}
}
// Return most recent events
if len(filtered) > limit && limit > 0 {
start := len(filtered) - limit
return filtered[start:]
}
return filtered
}
// NewSessionManager creates a new session manager
func NewSessionManager(maxAge time.Duration) *SessionManager {
sm := &SessionManager{
sessions: make(map[string]*Session),
maxAge: maxAge,
}
// Start cleanup routine
go sm.cleanupRoutine()
return sm
}
// CreateSession creates a new session for a user
func (sm *SessionManager) CreateSession(userID, ipAddress, userAgent string) *Session {
sm.mu.Lock()
defer sm.mu.Unlock()
session := &Session{
ID: generateID(),
UserID: userID,
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(sm.maxAge),
IPAddress: ipAddress,
UserAgent: userAgent,
Data: make(map[string]any),
}
sm.sessions[session.ID] = session
return session
}
// GetSession retrieves a session by ID
func (sm *SessionManager) GetSession(sessionID string) (*Session, bool) {
sm.mu.RLock()
defer sm.mu.RUnlock()
session, exists := sm.sessions[sessionID]
if !exists {
return nil, false
}
// Check if session has expired
if time.Now().After(session.ExpiresAt) {
return nil, false
}
return session, true
}
// DeleteSession deletes a session
func (sm *SessionManager) DeleteSession(sessionID string) {
sm.mu.Lock()
defer sm.mu.Unlock()
delete(sm.sessions, sessionID)
}
// cleanupRoutine periodically cleans up expired sessions
func (sm *SessionManager) cleanupRoutine() {
ticker := time.NewTicker(time.Hour)
defer ticker.Stop()
for range ticker.C {
sm.mu.Lock()
now := time.Now()
for id, session := range sm.sessions {
if now.After(session.ExpiresAt) {
delete(sm.sessions, id)
}
}
sm.mu.Unlock()
}
}
// AddAuthProvider adds an authentication provider
func (sm *SecurityManager) AddAuthProvider(provider AuthProvider) {
sm.mu.Lock()
defer sm.mu.Unlock()
sm.authProviders[provider.Name()] = provider
}
// Authenticate authenticates a user using available providers
func (sm *SecurityManager) Authenticate(ctx context.Context, credentials map[string]any) (*User, error) {
sm.mu.RLock()
providers := make(map[string]AuthProvider)
for name, provider := range sm.authProviders {
providers[name] = provider
}
sm.mu.RUnlock()
var lastErr error
for _, provider := range providers {
user, err := provider.Authenticate(ctx, credentials)
if err == nil {
// Log successful authentication
sm.auditLogger.LogEvent(AuditEvent{
EventType: "authentication",
UserID: user.ID,
Action: "login",
Success: true,
Details: map[string]any{
"provider": provider.Name(),
},
})
// Update user permissions
user.Permissions = sm.roleManager.GetUserPermissions(user)
return user, nil
}
lastErr = err
}
// Log failed authentication
sm.auditLogger.LogEvent(AuditEvent{
EventType: "authentication",
Action: "login",
Success: false,
Details: map[string]any{
"error": lastErr.Error(),
},
})
return nil, fmt.Errorf("authentication failed: %w", lastErr)
}
// Authorize checks if a user is authorized for an action
func (sm *SecurityManager) Authorize(user *User, resource, action string) error {
permission := fmt.Sprintf("%s.%s", resource, action)
if !sm.roleManager.HasPermission(user, permission) {
// Log authorization failure
sm.auditLogger.LogEvent(AuditEvent{
EventType: "authorization",
UserID: user.ID,
Resource: resource,
Action: action,
Success: false,
})
return fmt.Errorf("user %s does not have permission %s", user.Username, permission)
}
// Log successful authorization
sm.auditLogger.LogEvent(AuditEvent{
EventType: "authorization",
UserID: user.ID,
Resource: resource,
Action: action,
Success: true,
})
return nil
}
// ValidateSession validates a session token
func (sm *SecurityManager) ValidateSession(sessionID string) (*User, error) {
session, exists := sm.sessionManager.GetSession(sessionID)
if !exists {
return nil, fmt.Errorf("invalid session")
}
// Create a user object from session data
user := &User{
ID: session.UserID,
Username: session.UserID, // In a real implementation, you'd fetch from database
}
// Update user permissions
user.Permissions = sm.roleManager.GetUserPermissions(user)
return user, nil
}
// CheckRateLimit checks if an action is rate limited
func (sm *SecurityManager) CheckRateLimit(key string) error {
if !sm.rateLimiter.IsAllowed(key) {
sm.auditLogger.LogEvent(AuditEvent{
EventType: "rate_limit",
Action: "exceeded",
Success: false,
Details: map[string]any{
"key": key,
},
})
return fmt.Errorf("rate limit exceeded for key: %s", key)
}
return nil
}
// Encrypt encrypts data using the security manager's key
func (sm *SecurityManager) Encrypt(data []byte) ([]byte, error) {
// Simple encryption using SHA256 hash of key + data
// In production, use proper encryption like AES
hash := sha256.Sum256(append(sm.encryptionKey, data...))
return hash[:], nil
}
// Decrypt decrypts data (placeholder for proper decryption)
func (sm *SecurityManager) Decrypt(data []byte) ([]byte, error) {
// Placeholder - in production, implement proper decryption
return data, nil
}
// BasicAuthProvider implements basic username/password authentication
type BasicAuthProvider struct {
users map[string]*User
mu sync.RWMutex
}
func NewBasicAuthProvider() *BasicAuthProvider {
return &BasicAuthProvider{
users: make(map[string]*User),
}
}
func (bap *BasicAuthProvider) Name() string {
return "basic"
}
func (bap *BasicAuthProvider) Authenticate(ctx context.Context, credentials map[string]any) (*User, error) {
username, ok := credentials["username"].(string)
if !ok {
return nil, fmt.Errorf("username required")
}
password, ok := credentials["password"].(string)
if !ok {
return nil, fmt.Errorf("password required")
}
bap.mu.RLock()
user, exists := bap.users[username]
bap.mu.RUnlock()
if !exists {
return nil, fmt.Errorf("user not found")
}
// In production, compare hashed passwords
if password != "password" { // Placeholder
return nil, fmt.Errorf("invalid password")
}
// Update last login
now := time.Now()
user.LastLoginAt = &now
return user, nil
}
func (bap *BasicAuthProvider) ValidateToken(token string) (*User, error) {
// Basic token validation - in production, use JWT or similar
parts := strings.Split(token, ":")
if len(parts) != 2 {
return nil, fmt.Errorf("invalid token format")
}
username := parts[0]
return bap.Authenticate(context.Background(), map[string]any{
"username": username,
"password": "token", // Placeholder
})
}
func (bap *BasicAuthProvider) AddUser(user *User, password string) error {
bap.mu.Lock()
defer bap.mu.Unlock()
// In production, hash the password
user.CreatedAt = time.Now()
bap.users[user.Username] = user
return nil
}
// generateID generates a random ID
func generateID() string {
bytes := make([]byte, 16)
rand.Read(bytes)
return hex.EncodeToString(bytes)
}
// SecurityMiddleware provides security middleware for HTTP handlers
type SecurityMiddleware struct {
securityManager *SecurityManager
}
// NewSecurityMiddleware creates a new security middleware
func NewSecurityMiddleware(sm *SecurityManager) *SecurityMiddleware {
return &SecurityMiddleware{
securityManager: sm,
}
}
// AuthenticateRequest authenticates a request with credentials
func (sm *SecurityMiddleware) AuthenticateRequest(credentials map[string]any, ipAddress string) (*User, error) {
user, err := sm.securityManager.Authenticate(context.Background(), credentials)
if err != nil {
// Log failed authentication attempt
sm.securityManager.auditLogger.LogEvent(AuditEvent{
EventType: "authentication",
Action: "login",
Success: false,
Details: map[string]any{
"ip_address": ipAddress,
"error": err.Error(),
},
})
return nil, err
}
return user, nil
}
// AuthorizeRequest authorizes a request
func (sm *SecurityMiddleware) AuthorizeRequest(user *User, resource, action string) error {
return sm.securityManager.Authorize(user, resource, action)
}
// GetClientIP gets the real client IP from a network connection
func GetClientIP(conn net.Conn) string {
if conn == nil {
return ""
}
host, _, err := net.SplitHostPort(conn.RemoteAddr().String())
if err != nil {
return conn.RemoteAddr().String()
}
return host
}