mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-20 13:30:51 +02: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:
@@ -0,0 +1,331 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"github.com/starfleetcptn/gomft/internal/encryption"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// TestModel is a simple model for testing encryption middleware
|
||||
type TestModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
Name string `gorm:"not null"`
|
||||
Password string `gorm:"-"` // Not stored in DB, only for form input
|
||||
EncryptedPassword string `gorm:"column:encrypted_password"`
|
||||
APIKey string `gorm:"-"` // Not stored in DB, only for form input
|
||||
EncryptedAPIKey string `gorm:"column:encrypted_api_key"`
|
||||
CreatedAt time.Time `gorm:"not null"`
|
||||
UpdatedAt time.Time `gorm:"not null"`
|
||||
}
|
||||
|
||||
// StorageProvider is a simplified version of the real model for testing
|
||||
type StorageProvider struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
Name string `gorm:"not null"`
|
||||
Type string `gorm:"not null"`
|
||||
Password string `gorm:"-"` // Not stored in DB
|
||||
EncryptedPassword string `gorm:"column:encrypted_password"`
|
||||
SecretKey string `gorm:"-"` // Not stored in DB
|
||||
EncryptedSecretKey string `gorm:"column:encrypted_secret_key"`
|
||||
ClientSecret string `gorm:"-"` // Not stored in DB
|
||||
EncryptedClientSecret string `gorm:"column:encrypted_client_secret"`
|
||||
RefreshToken string `gorm:"-"` // Not stored in DB
|
||||
EncryptedRefreshToken string `gorm:"column:encrypted_refresh_token"`
|
||||
CreatedAt time.Time `gorm:"not null"`
|
||||
UpdatedAt time.Time `gorm:"not null"`
|
||||
}
|
||||
|
||||
// GetSensitiveFields returns a map of field names to values that need encryption
|
||||
func (sp *StorageProvider) GetSensitiveFields() map[string]string {
|
||||
sensitiveFields := make(map[string]string)
|
||||
|
||||
if sp.Password != "" {
|
||||
sensitiveFields["Password"] = sp.Password
|
||||
}
|
||||
if sp.SecretKey != "" {
|
||||
sensitiveFields["SecretKey"] = sp.SecretKey
|
||||
}
|
||||
if sp.ClientSecret != "" {
|
||||
sensitiveFields["ClientSecret"] = sp.ClientSecret
|
||||
}
|
||||
if sp.RefreshToken != "" {
|
||||
sensitiveFields["RefreshToken"] = sp.RefreshToken
|
||||
}
|
||||
|
||||
return sensitiveFields
|
||||
}
|
||||
|
||||
func setupTestDB(t *testing.T) *gorm.DB {
|
||||
// Initialize in-memory SQLite database
|
||||
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
|
||||
require.NoError(t, err, "Failed to connect to in-memory database")
|
||||
|
||||
// Migrate the test models
|
||||
err = db.AutoMigrate(&TestModel{}, &StorageProvider{})
|
||||
require.NoError(t, err, "Failed to migrate test models")
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func setupEncryptionMiddleware(t *testing.T) (*EncryptionMiddleware, error) {
|
||||
// Initialize the encryption key manager for testing
|
||||
err := encryption.InitializeKeyManager("test-key")
|
||||
require.NoError(t, err, "Failed to initialize key manager")
|
||||
|
||||
// Create the encryption middleware
|
||||
return NewEncryptionMiddleware()
|
||||
}
|
||||
|
||||
func TestEncryptionMiddlewareWithGenericModel(t *testing.T) {
|
||||
// Setup
|
||||
db := setupTestDB(t)
|
||||
middleware, err := setupEncryptionMiddleware(t)
|
||||
require.NoError(t, err, "Failed to setup encryption middleware")
|
||||
|
||||
// Register hooks with GORM
|
||||
middleware.RegisterHooks(db)
|
||||
|
||||
// Create a test model
|
||||
testModel := &TestModel{
|
||||
Name: "Test User",
|
||||
Password: "securePassword123",
|
||||
APIKey: "api-key-12345",
|
||||
}
|
||||
|
||||
// Save the model - should trigger encryption
|
||||
err = db.Create(testModel).Error
|
||||
require.NoError(t, err, "Failed to save test model")
|
||||
|
||||
// Verify encrypted fields are set and original fields are cleared
|
||||
assert.Empty(t, testModel.Password, "Password should be cleared after save")
|
||||
assert.Empty(t, testModel.APIKey, "APIKey should be cleared after save")
|
||||
assert.NotEmpty(t, testModel.EncryptedPassword, "EncryptedPassword should be set")
|
||||
assert.NotEmpty(t, testModel.EncryptedAPIKey, "EncryptedAPIKey should be set")
|
||||
assert.True(t, strings.HasPrefix(testModel.EncryptedPassword, encryption.EncryptedPrefix), "EncryptedPassword should have encryption prefix")
|
||||
assert.True(t, strings.HasPrefix(testModel.EncryptedAPIKey, encryption.EncryptedPrefix), "EncryptedAPIKey should have encryption prefix")
|
||||
|
||||
// Test retrieval and automatic decryption
|
||||
retrievedModel := new(TestModel)
|
||||
err = db.First(retrievedModel, testModel.ID).Error
|
||||
require.NoError(t, err, "Failed to retrieve test model")
|
||||
|
||||
// Verify decryption
|
||||
assert.Equal(t, "securePassword123", retrievedModel.Password, "Password should be automatically decrypted")
|
||||
assert.Equal(t, "api-key-12345", retrievedModel.APIKey, "APIKey should be automatically decrypted")
|
||||
assert.NotEmpty(t, retrievedModel.EncryptedPassword, "EncryptedPassword should remain set")
|
||||
assert.NotEmpty(t, retrievedModel.EncryptedAPIKey, "EncryptedAPIKey should remain set")
|
||||
}
|
||||
|
||||
func TestEncryptionMiddlewareWithStorageProvider(t *testing.T) {
|
||||
// Setup
|
||||
db := setupTestDB(t)
|
||||
middleware, err := setupEncryptionMiddleware(t)
|
||||
require.NoError(t, err, "Failed to setup encryption middleware")
|
||||
|
||||
// Register hooks with GORM
|
||||
middleware.RegisterHooks(db)
|
||||
|
||||
// Create a storage provider
|
||||
provider := &StorageProvider{
|
||||
Name: "Test S3",
|
||||
Type: "s3",
|
||||
Password: "testPassword",
|
||||
SecretKey: "testSecretKey",
|
||||
ClientSecret: "testClientSecret",
|
||||
RefreshToken: "testRefreshToken",
|
||||
}
|
||||
|
||||
// Save the provider - should trigger encryption
|
||||
err = db.Create(provider).Error
|
||||
require.NoError(t, err, "Failed to save storage provider")
|
||||
|
||||
// Verify encrypted fields are set and original fields are cleared
|
||||
assert.Empty(t, provider.Password, "Password should be cleared after save")
|
||||
assert.Empty(t, provider.SecretKey, "SecretKey should be cleared after save")
|
||||
assert.Empty(t, provider.ClientSecret, "ClientSecret should be cleared after save")
|
||||
assert.Empty(t, provider.RefreshToken, "RefreshToken should be cleared after save")
|
||||
assert.NotEmpty(t, provider.EncryptedPassword, "EncryptedPassword should be set")
|
||||
assert.NotEmpty(t, provider.EncryptedSecretKey, "EncryptedSecretKey should be set")
|
||||
assert.NotEmpty(t, provider.EncryptedClientSecret, "EncryptedClientSecret should be set")
|
||||
assert.NotEmpty(t, provider.EncryptedRefreshToken, "EncryptedRefreshToken should be set")
|
||||
|
||||
// Test retrieval and automatic decryption
|
||||
retrievedProvider := new(StorageProvider)
|
||||
err = db.First(retrievedProvider, provider.ID).Error
|
||||
require.NoError(t, err, "Failed to retrieve storage provider")
|
||||
|
||||
// Verify decryption
|
||||
assert.Equal(t, "testPassword", retrievedProvider.Password, "Password should be automatically decrypted")
|
||||
assert.Equal(t, "testSecretKey", retrievedProvider.SecretKey, "SecretKey should be automatically decrypted")
|
||||
assert.Equal(t, "testClientSecret", retrievedProvider.ClientSecret, "ClientSecret should be automatically decrypted")
|
||||
assert.Equal(t, "testRefreshToken", retrievedProvider.RefreshToken, "RefreshToken should be automatically decrypted")
|
||||
}
|
||||
|
||||
func TestEncryptionMiddlewareWithMultipleRecords(t *testing.T) {
|
||||
// Setup
|
||||
db := setupTestDB(t)
|
||||
middleware, err := setupEncryptionMiddleware(t)
|
||||
require.NoError(t, err, "Failed to setup encryption middleware")
|
||||
|
||||
// Register hooks with GORM
|
||||
middleware.RegisterHooks(db)
|
||||
|
||||
// Create multiple test models
|
||||
models := []TestModel{
|
||||
{Name: "User 1", Password: "password1", APIKey: "apikey1"},
|
||||
{Name: "User 2", Password: "password2", APIKey: "apikey2"},
|
||||
{Name: "User 3", Password: "password3", APIKey: "apikey3"},
|
||||
}
|
||||
|
||||
// Save all models
|
||||
err = db.Create(&models).Error
|
||||
require.NoError(t, err, "Failed to save multiple test models")
|
||||
|
||||
// Retrieve all models
|
||||
var retrievedModels []TestModel
|
||||
err = db.Find(&retrievedModels).Error
|
||||
require.NoError(t, err, "Failed to retrieve all test models")
|
||||
|
||||
// Verify count
|
||||
assert.Equal(t, 3, len(retrievedModels), "Should retrieve 3 models")
|
||||
|
||||
// Verify each model was properly decrypted
|
||||
expectedPasswords := []string{"password1", "password2", "password3"}
|
||||
expectedAPIKeys := []string{"apikey1", "apikey2", "apikey3"}
|
||||
|
||||
for i, model := range retrievedModels {
|
||||
assert.Equal(t, expectedPasswords[i], model.Password, "Password should be automatically decrypted")
|
||||
assert.Equal(t, expectedAPIKeys[i], model.APIKey, "APIKey should be automatically decrypted")
|
||||
assert.NotEmpty(t, model.EncryptedPassword, "EncryptedPassword should remain set")
|
||||
assert.NotEmpty(t, model.EncryptedAPIKey, "EncryptedAPIKey should remain set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptionMiddlewareDisabled(t *testing.T) {
|
||||
// Setup
|
||||
db := setupTestDB(t)
|
||||
middleware, err := setupEncryptionMiddleware(t)
|
||||
require.NoError(t, err, "Failed to setup encryption middleware")
|
||||
|
||||
// Register hooks with GORM
|
||||
middleware.RegisterHooks(db)
|
||||
|
||||
// Disable the middleware
|
||||
middleware.Disable()
|
||||
assert.False(t, middleware.IsEnabled(), "Middleware should be disabled")
|
||||
|
||||
// Create a test model
|
||||
testModel := &TestModel{
|
||||
Name: "Test User",
|
||||
Password: "securePassword123",
|
||||
APIKey: "api-key-12345",
|
||||
}
|
||||
|
||||
// Save the model - should NOT trigger encryption since middleware is disabled
|
||||
err = db.Create(testModel).Error
|
||||
require.NoError(t, err, "Failed to save test model")
|
||||
|
||||
// Verify sensitive fields are NOT encrypted
|
||||
assert.Equal(t, "securePassword123", testModel.Password, "Password should not be cleared when middleware is disabled")
|
||||
assert.Equal(t, "api-key-12345", testModel.APIKey, "APIKey should not be cleared when middleware is disabled")
|
||||
assert.Empty(t, testModel.EncryptedPassword, "EncryptedPassword should not be set when middleware is disabled")
|
||||
assert.Empty(t, testModel.EncryptedAPIKey, "EncryptedAPIKey should not be set when middleware is disabled")
|
||||
|
||||
// Re-enable the middleware for subsequent operations
|
||||
middleware.Enable()
|
||||
assert.True(t, middleware.IsEnabled(), "Middleware should be enabled")
|
||||
|
||||
// Update the model - should now trigger encryption
|
||||
testModel.Password = "newPassword456"
|
||||
err = db.Save(testModel).Error
|
||||
require.NoError(t, err, "Failed to update test model")
|
||||
|
||||
// Verify encryption now happened
|
||||
assert.Empty(t, testModel.Password, "Password should be cleared after update with middleware enabled")
|
||||
assert.NotEmpty(t, testModel.EncryptedPassword, "EncryptedPassword should be set after update with middleware enabled")
|
||||
}
|
||||
Reference in New Issue
Block a user