mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-10-27 07:20:27 +01:00
feat: Implement storage provider management functionality
- Added new routes and handlers for managing storage providers, including creation, editing, and deletion. - Introduced a new StorageProvider form component for user input. - Enhanced the database schema to support storage provider references in transfer configurations. - Implemented encryption for sensitive fields in storage provider data. - Added tests for storage provider API endpoints and integration with the database. - Updated frontend components to support storage provider selection and testing.
This commit is contained in:
1 parent
88d0ac815a
commit
31871bd16e
59 files changed
+15842
-594
No files matched your search
@@ -0,0 +1,400 @@
|
||||
package audit
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/starfleetcptn/gomft/internal/encryption"
|
||||
)
|
||||
|
||||
// EventType represents the type of encryption-related event
|
||||
type EventType string
|
||||
|
||||
// Event types for encryption operations
|
||||
const (
|
||||
EventEncrypt EventType = "encrypt"
|
||||
EventDecrypt EventType = "decrypt"
|
||||
EventKeyAccess EventType = "key_access"
|
||||
EventKeyRotation EventType = "key_rotation"
|
||||
EventKeyGeneration EventType = "key_generation"
|
||||
EventDecryptionFailure EventType = "decryption_failure"
|
||||
EventEncryptionFailure EventType = "encryption_failure"
|
||||
)
|
||||
|
||||
// SecurityLevel represents the severity/importance of an audit event
|
||||
type SecurityLevel string
|
||||
|
||||
// Security levels for events
|
||||
const (
|
||||
LevelInfo SecurityLevel = "info"
|
||||
LevelWarning SecurityLevel = "warning"
|
||||
LevelAlert SecurityLevel = "alert"
|
||||
LevelError SecurityLevel = "error"
|
||||
)
|
||||
|
||||
// AuditEvent represents a single encryption-related security event
|
||||
type AuditEvent struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
EventType EventType `json:"event_type"`
|
||||
Level SecurityLevel `json:"level"`
|
||||
Operation string `json:"operation"`
|
||||
FieldType string `json:"field_type,omitempty"`
|
||||
ModelType string `json:"model_type,omitempty"`
|
||||
Description string `json:"description"`
|
||||
Success bool `json:"success"`
|
||||
Error string `json:"error,omitempty"`
|
||||
KeyVersion string `json:"key_version,omitempty"`
|
||||
UserID uint `json:"user_id,omitempty"`
|
||||
RemoteIP string `json:"remote_ip,omitempty"`
|
||||
Duration int64 `json:"duration_ns,omitempty"` // Operation duration in nanoseconds
|
||||
}
|
||||
|
||||
// SecurityAuditor is responsible for logging security-related events
|
||||
type SecurityAuditor struct {
|
||||
enabled bool
|
||||
logWriter io.Writer
|
||||
errorWriter io.Writer
|
||||
mutex sync.Mutex
|
||||
detailedMode bool
|
||||
logFilePath string
|
||||
errorFilePath string
|
||||
}
|
||||
|
||||
// New creates a new SecurityAuditor with default configuration
|
||||
func New() (*SecurityAuditor, error) {
|
||||
return &SecurityAuditor{
|
||||
enabled: true,
|
||||
logWriter: os.Stdout, // Default to stdout for regular logs
|
||||
errorWriter: os.Stderr, // Default to stderr for error logs
|
||||
detailedMode: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewWithFileLogging creates a new SecurityAuditor with file-based logging
|
||||
func NewWithFileLogging(logFilePath, errorFilePath string) (*SecurityAuditor, error) {
|
||||
logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
|
||||
var errorWriter io.Writer
|
||||
if errorFilePath == logFilePath {
|
||||
errorWriter = logFile
|
||||
} else {
|
||||
errorFile, err := os.OpenFile(errorFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
logFile.Close()
|
||||
return nil, fmt.Errorf("failed to open error log file: %w", err)
|
||||
}
|
||||
errorWriter = errorFile
|
||||
}
|
||||
|
||||
return &SecurityAuditor{
|
||||
enabled: true,
|
||||
logWriter: logFile,
|
||||
errorWriter: errorWriter,
|
||||
logFilePath: logFilePath,
|
||||
errorFilePath: errorFilePath,
|
||||
detailedMode: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close properly closes any open resources
|
||||
func (a *SecurityAuditor) Close() error {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
// Check if we need to close file writers
|
||||
if closer, ok := a.logWriter.(io.Closer); ok {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Don't close errorWriter if it's the same as logWriter
|
||||
if a.errorFilePath != a.logFilePath {
|
||||
if closer, ok := a.errorWriter.(io.Closer); ok {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enable turns on the auditor
|
||||
func (a *SecurityAuditor) Enable() {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.enabled = true
|
||||
}
|
||||
|
||||
// Disable turns off the auditor
|
||||
func (a *SecurityAuditor) Disable() {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.enabled = false
|
||||
}
|
||||
|
||||
// SetDetailedMode toggles detailed logging mode
|
||||
func (a *SecurityAuditor) SetDetailedMode(detailed bool) {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
a.detailedMode = detailed
|
||||
}
|
||||
|
||||
// IsEnabled returns whether auditing is enabled
|
||||
func (a *SecurityAuditor) IsEnabled() bool {
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
return a.enabled
|
||||
}
|
||||
|
||||
// LogEvent records a security event to the audit log
|
||||
func (a *SecurityAuditor) LogEvent(event AuditEvent) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
a.mutex.Lock()
|
||||
defer a.mutex.Unlock()
|
||||
|
||||
// Ensure timestamp is set
|
||||
if event.Timestamp.IsZero() {
|
||||
event.Timestamp = time.Now()
|
||||
}
|
||||
|
||||
// Convert the event to JSON
|
||||
jsonData, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
fmt.Fprintf(a.errorWriter, "Error marshaling audit event: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Choose the right writer based on event level
|
||||
writer := a.logWriter
|
||||
if event.Level == LevelError || event.Level == LevelAlert {
|
||||
writer = a.errorWriter
|
||||
}
|
||||
|
||||
// Write to the appropriate log
|
||||
fmt.Fprintln(writer, string(jsonData))
|
||||
}
|
||||
|
||||
// LogEncryptionEvent logs an encryption operation event
|
||||
func (a *SecurityAuditor) LogEncryptionEvent(operation string, fieldType, modelType string, success bool, err error, keyVersion string, userID uint, duration time.Duration) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventEncrypt,
|
||||
Level: LevelInfo,
|
||||
Operation: operation,
|
||||
FieldType: fieldType,
|
||||
ModelType: modelType,
|
||||
Success: success,
|
||||
KeyVersion: keyVersion,
|
||||
UserID: userID,
|
||||
Duration: duration.Nanoseconds(),
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.EventType = EventEncryptionFailure
|
||||
event.Level = LevelWarning
|
||||
if err != nil {
|
||||
event.Error = encryption.SanitizeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// LogDecryptionEvent logs a decryption operation event
|
||||
func (a *SecurityAuditor) LogDecryptionEvent(operation string, fieldType, modelType string, success bool, err error, keyVersion string, userID uint, duration time.Duration) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventDecrypt,
|
||||
Level: LevelInfo,
|
||||
Operation: operation,
|
||||
FieldType: fieldType,
|
||||
ModelType: modelType,
|
||||
Success: success,
|
||||
KeyVersion: keyVersion,
|
||||
UserID: userID,
|
||||
Duration: duration.Nanoseconds(),
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.EventType = EventDecryptionFailure
|
||||
event.Level = LevelWarning
|
||||
if err != nil {
|
||||
event.Error = encryption.SanitizeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// LogKeyAccessEvent logs when an encryption key is accessed
|
||||
func (a *SecurityAuditor) LogKeyAccessEvent(keyVersion string, success bool, err error, userID uint) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventKeyAccess,
|
||||
Level: LevelInfo,
|
||||
Operation: "key_access",
|
||||
Success: success,
|
||||
KeyVersion: keyVersion,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.Level = LevelAlert
|
||||
if err != nil {
|
||||
event.Error = encryption.SanitizeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Key access failures are security-critical and should be logged at a higher level
|
||||
if !success {
|
||||
event.Description = "Failed key access attempt"
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// LogKeyRotationEvent logs when encryption keys are rotated
|
||||
func (a *SecurityAuditor) LogKeyRotationEvent(oldVersion, newVersion string, success bool, err error, userID uint) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventKeyRotation,
|
||||
Level: LevelInfo,
|
||||
Operation: "key_rotation",
|
||||
Description: fmt.Sprintf("Key rotation from version %s to %s", oldVersion, newVersion),
|
||||
Success: success,
|
||||
KeyVersion: newVersion,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.Level = LevelError
|
||||
if err != nil {
|
||||
event.Error = encryption.SanitizeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// LogKeyRotationEventWithDescription logs when encryption keys are rotated with a custom description
|
||||
func (a *SecurityAuditor) LogKeyRotationEventWithDescription(oldVersion, newVersion string, success bool, description string, userID uint) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventKeyRotation,
|
||||
Level: LevelInfo,
|
||||
Operation: "key_rotation",
|
||||
Description: description,
|
||||
Success: success,
|
||||
KeyVersion: newVersion,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.Level = LevelError
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// LogKeyGenerationEvent logs when a new encryption key is generated
|
||||
func (a *SecurityAuditor) LogKeyGenerationEvent(keyVersion string, success bool, err error, userID uint) {
|
||||
if !a.IsEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
event := AuditEvent{
|
||||
Timestamp: time.Now(),
|
||||
EventType: EventKeyGeneration,
|
||||
Level: LevelInfo,
|
||||
Operation: "key_generation",
|
||||
Description: "New encryption key generated",
|
||||
Success: success,
|
||||
KeyVersion: keyVersion,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
if !success {
|
||||
event.Level = LevelError
|
||||
if err != nil {
|
||||
event.Error = encryption.SanitizeError(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
a.LogEvent(event)
|
||||
}
|
||||
|
||||
// global is the default security auditor instance
|
||||
var global *SecurityAuditor
|
||||
var globalOnce sync.Once
|
||||
|
||||
// GetGlobalAuditor returns the global security auditor instance
|
||||
func GetGlobalAuditor() *SecurityAuditor {
|
||||
globalOnce.Do(func() {
|
||||
var err error
|
||||
global, err = New()
|
||||
if err != nil {
|
||||
// Fall back to a disabled auditor if there's an error
|
||||
global = &SecurityAuditor{enabled: false}
|
||||
}
|
||||
})
|
||||
return global
|
||||
}
|
||||
|
||||
// InitializeWithFileLogging initializes the global auditor with file logging
|
||||
func InitializeWithFileLogging(logFilePath, errorFilePath string) error {
|
||||
auditor, err := NewWithFileLogging(logFilePath, errorFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
globalOnce.Do(func() {
|
||||
global = auditor
|
||||
})
|
||||
|
||||
// If global auditor was already initialized, replace it
|
||||
if global != auditor {
|
||||
if closer, ok := global.logWriter.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
if global.errorFilePath != global.logFilePath {
|
||||
if closer, ok := global.errorWriter.(io.Closer); ok {
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
global = auditor
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user