mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-09 08:00:49 +02:00
- 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.
332 lines
9.2 KiB
Go
332 lines
9.2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/starfleetcptn/gomft/internal/encryption"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// EncryptionMiddleware handles automatic encryption and decryption of model fields
|
|
type EncryptionMiddleware struct {
|
|
encryptor *encryption.CredentialEncryptor
|
|
enabled bool
|
|
}
|
|
|
|
// NewEncryptionMiddleware creates a new middleware instance for encrypting/decrypting fields
|
|
func NewEncryptionMiddleware() (*EncryptionMiddleware, error) {
|
|
// Get the global credential encryptor
|
|
encryptor, err := encryption.GetGlobalCredentialEncryptor()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to initialize encryption middleware: %w", err)
|
|
}
|
|
|
|
return &EncryptionMiddleware{
|
|
encryptor: encryptor,
|
|
enabled: true,
|
|
}, nil
|
|
}
|
|
|
|
// Enable turns on automatic encryption/decryption
|
|
func (m *EncryptionMiddleware) Enable() {
|
|
m.enabled = true
|
|
}
|
|
|
|
// Disable turns off automatic encryption/decryption
|
|
func (m *EncryptionMiddleware) Disable() {
|
|
m.enabled = false
|
|
}
|
|
|
|
// IsEnabled returns whether the middleware is enabled
|
|
func (m *EncryptionMiddleware) IsEnabled() bool {
|
|
return m.enabled
|
|
}
|
|
|
|
// RegisterHooks registers the encryption/decryption hooks with the GORM instance
|
|
func (m *EncryptionMiddleware) RegisterHooks(db *gorm.DB) {
|
|
// Register BeforeSave hook to encrypt sensitive fields
|
|
db.Callback().Create().Before("gorm:create").Register("encrypt_before_create", m.encryptBeforeSave)
|
|
db.Callback().Update().Before("gorm:update").Register("encrypt_before_update", m.encryptBeforeSave)
|
|
|
|
// Register AfterFind hook to decrypt sensitive fields
|
|
db.Callback().Query().After("gorm:after_query").Register("decrypt_after_find", m.decryptAfterFind)
|
|
}
|
|
|
|
// encryptBeforeSave encrypts sensitive fields before saving to the database
|
|
func (m *EncryptionMiddleware) encryptBeforeSave(db *gorm.DB) {
|
|
if !m.enabled {
|
|
return
|
|
}
|
|
|
|
// Get the model value
|
|
value := db.Statement.ReflectValue
|
|
if value.Kind() == reflect.Ptr {
|
|
value = value.Elem()
|
|
}
|
|
|
|
// Skip if the value is not a struct
|
|
if value.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
// Process the model
|
|
if err := m.processModelForEncryption(value); err != nil {
|
|
db.AddError(fmt.Errorf("encryption middleware error: %w", err))
|
|
}
|
|
}
|
|
|
|
// decryptAfterFind decrypts sensitive fields after retrieving from the database
|
|
func (m *EncryptionMiddleware) decryptAfterFind(db *gorm.DB) {
|
|
if !m.enabled {
|
|
return
|
|
}
|
|
|
|
// Get the model value
|
|
value := db.Statement.ReflectValue
|
|
if value.Kind() == reflect.Ptr {
|
|
value = value.Elem()
|
|
}
|
|
|
|
// Handle slice of models
|
|
if value.Kind() == reflect.Slice {
|
|
for i := 0; i < value.Len(); i++ {
|
|
item := value.Index(i)
|
|
if item.Kind() == reflect.Ptr {
|
|
item = item.Elem()
|
|
}
|
|
|
|
if item.Kind() == reflect.Struct {
|
|
if err := m.processModelForDecryption(item); err != nil {
|
|
db.AddError(fmt.Errorf("decryption middleware error [index %d]: %w", i, err))
|
|
return
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Skip if the value is not a struct
|
|
if value.Kind() != reflect.Struct {
|
|
return
|
|
}
|
|
|
|
// Process the model
|
|
if err := m.processModelForDecryption(value); err != nil {
|
|
db.AddError(fmt.Errorf("decryption middleware error: %w", err))
|
|
}
|
|
}
|
|
|
|
// processModelForEncryption encrypts sensitive fields in a model
|
|
func (m *EncryptionMiddleware) processModelForEncryption(value reflect.Value) error {
|
|
modelType := value.Type()
|
|
|
|
// Special handling for StorageProvider type
|
|
if modelType.Name() == "StorageProvider" {
|
|
return m.encryptStorageProvider(value)
|
|
}
|
|
|
|
// Generic handling for models with encryptable fields
|
|
for i := 0; i < modelType.NumField(); i++ {
|
|
field := modelType.Field(i)
|
|
|
|
// Check if field requires encryption based on its name
|
|
fieldName := field.Name
|
|
if requiresEncryption, credType := encryption.RequiresEncryption(fieldName); requiresEncryption {
|
|
// Get the field value
|
|
fieldValue := value.Field(i)
|
|
if !fieldValue.CanInterface() || !fieldValue.CanSet() {
|
|
continue
|
|
}
|
|
|
|
// Get string value
|
|
strValue, ok := fieldValue.Interface().(string)
|
|
if !ok || strValue == "" {
|
|
continue
|
|
}
|
|
|
|
// If already encrypted, skip
|
|
if m.encryptor.IsEncrypted(strValue) {
|
|
continue
|
|
}
|
|
|
|
// Encrypt the field
|
|
encryptedValue, err := m.encryptor.Encrypt(strValue, credType)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to encrypt field %s: %w", fieldName, err)
|
|
}
|
|
|
|
// Find the corresponding encrypted field
|
|
encryptedFieldName := "Encrypted" + fieldName
|
|
encryptedField := value.FieldByName(encryptedFieldName)
|
|
|
|
// If encrypted field exists and can be set, set it
|
|
if encryptedField.IsValid() && encryptedField.CanSet() {
|
|
encryptedField.SetString(encryptedValue)
|
|
|
|
// If the original field is marked with gorm:"-", we should clear it to prevent leaking it
|
|
if field.Tag.Get("gorm") == "-" {
|
|
fieldValue.SetString("")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// processModelForDecryption decrypts encrypted fields in a model
|
|
func (m *EncryptionMiddleware) processModelForDecryption(value reflect.Value) error {
|
|
modelType := value.Type()
|
|
|
|
// Special handling for StorageProvider type
|
|
if modelType.Name() == "StorageProvider" {
|
|
return m.decryptStorageProvider(value)
|
|
}
|
|
|
|
// Generic handling for models with encrypted fields
|
|
for i := 0; i < modelType.NumField(); i++ {
|
|
field := modelType.Field(i)
|
|
|
|
// Look for encrypted fields based on naming pattern
|
|
fieldName := field.Name
|
|
if strings.HasPrefix(fieldName, "Encrypted") {
|
|
originalFieldName := strings.TrimPrefix(fieldName, "Encrypted")
|
|
|
|
// Get the encrypted field value
|
|
encryptedFieldValue := value.Field(i)
|
|
if !encryptedFieldValue.CanInterface() {
|
|
continue
|
|
}
|
|
|
|
// Get encrypted string value
|
|
encryptedValue, ok := encryptedFieldValue.Interface().(string)
|
|
if !ok || encryptedValue == "" {
|
|
continue
|
|
}
|
|
|
|
// Decrypt the field
|
|
decryptedValue, err := m.encryptor.DecryptField(encryptedValue)
|
|
if err != nil {
|
|
// Log the error but continue
|
|
fmt.Printf("Warning: failed to decrypt field %s: %v\n", fieldName, err)
|
|
continue
|
|
}
|
|
|
|
// Find the corresponding original field
|
|
originalField := value.FieldByName(originalFieldName)
|
|
|
|
// If original field exists and can be set, set it
|
|
if originalField.IsValid() && originalField.CanSet() {
|
|
originalField.SetString(decryptedValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// encryptStorageProvider handles encryption for StorageProvider model fields
|
|
func (m *EncryptionMiddleware) encryptStorageProvider(value reflect.Value) error {
|
|
// Check if model implements GetSensitiveFields method
|
|
modelInterface := value.Addr().Interface()
|
|
|
|
// Type assertion to access the GetSensitiveFields method
|
|
model, ok := modelInterface.(interface {
|
|
GetSensitiveFields() map[string]string
|
|
})
|
|
|
|
if !ok {
|
|
return errors.New("StorageProvider model does not implement GetSensitiveFields")
|
|
}
|
|
|
|
// Get sensitive fields that need encryption
|
|
sensitiveFields := model.GetSensitiveFields()
|
|
|
|
// Encrypt each sensitive field
|
|
for fieldName, fieldValue := range sensitiveFields {
|
|
if fieldValue == "" {
|
|
continue
|
|
}
|
|
|
|
// Skip already encrypted values
|
|
if m.encryptor.IsEncrypted(fieldValue) {
|
|
continue
|
|
}
|
|
|
|
// Determine the credential type based on field name
|
|
_, credType := encryption.RequiresEncryption(fieldName)
|
|
|
|
// Encrypt the value
|
|
encryptedValue, err := m.encryptor.Encrypt(fieldValue, credType)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to encrypt StorageProvider field %s: %w", fieldName, err)
|
|
}
|
|
|
|
// Find the corresponding encrypted field
|
|
encryptedFieldName := "Encrypted" + fieldName
|
|
encryptedField := value.FieldByName(encryptedFieldName)
|
|
|
|
// Set the encrypted value
|
|
if encryptedField.IsValid() && encryptedField.CanSet() {
|
|
encryptedField.SetString(encryptedValue)
|
|
|
|
// Clear the original field if it shouldn't be stored
|
|
originalField := value.FieldByName(fieldName)
|
|
if originalField.IsValid() && originalField.CanSet() {
|
|
// Find the field in the struct type to check its gorm tag
|
|
modelType := reflect.TypeOf(model).Elem()
|
|
if field, found := modelType.FieldByName(fieldName); found && field.Tag.Get("gorm") == "-" {
|
|
originalField.SetString("")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// decryptStorageProvider handles decryption for StorageProvider model fields
|
|
func (m *EncryptionMiddleware) decryptStorageProvider(value reflect.Value) error {
|
|
// Fields to decrypt
|
|
encryptedFields := []string{
|
|
"EncryptedPassword",
|
|
"EncryptedSecretKey",
|
|
"EncryptedClientSecret",
|
|
"EncryptedRefreshToken",
|
|
}
|
|
|
|
// Process each encrypted field
|
|
for _, fieldName := range encryptedFields {
|
|
encryptedField := value.FieldByName(fieldName)
|
|
if !encryptedField.IsValid() || !encryptedField.CanInterface() {
|
|
continue
|
|
}
|
|
|
|
// Get encrypted value
|
|
encryptedValue, ok := encryptedField.Interface().(string)
|
|
if !ok || encryptedValue == "" {
|
|
continue
|
|
}
|
|
|
|
// Decrypt value
|
|
decryptedValue, err := m.encryptor.DecryptField(encryptedValue)
|
|
if err != nil {
|
|
// Log warning but continue with other fields
|
|
fmt.Printf("Warning: failed to decrypt StorageProvider field %s: %v\n", fieldName, err)
|
|
continue
|
|
}
|
|
|
|
// Set decrypted value to the original field
|
|
originalFieldName := strings.TrimPrefix(fieldName, "Encrypted")
|
|
originalField := value.FieldByName(originalFieldName)
|
|
if originalField.IsValid() && originalField.CanSet() {
|
|
originalField.SetString(decryptedValue)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|