mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 23:50:48 +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.
330 lines
9.8 KiB
Go
330 lines
9.8 KiB
Go
package encryption
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func setupCredentialEncryptor(t *testing.T) *CredentialEncryptor {
|
|
encService := setupEncryptionService(t)
|
|
credEncryptor, err := NewCredentialEncryptor(encService)
|
|
require.NoError(t, err)
|
|
return credEncryptor
|
|
}
|
|
|
|
func TestNewCredentialEncryptor(t *testing.T) {
|
|
t.Run("Valid encryption service", func(t *testing.T) {
|
|
encService := setupEncryptionService(t)
|
|
credEncryptor, err := NewCredentialEncryptor(encService)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, credEncryptor)
|
|
})
|
|
|
|
t.Run("Nil encryption service", func(t *testing.T) {
|
|
credEncryptor, err := NewCredentialEncryptor(nil)
|
|
require.Error(t, err)
|
|
assert.Nil(t, credEncryptor)
|
|
})
|
|
}
|
|
|
|
func TestCredentialEncryptor_Encrypt(t *testing.T) {
|
|
credEncryptor := setupCredentialEncryptor(t)
|
|
|
|
t.Run("Encrypt password", func(t *testing.T) {
|
|
password := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(password)
|
|
require.NoError(t, err)
|
|
assert.True(t, strings.HasPrefix(encrypted, EncryptedPrefix))
|
|
|
|
// Check that we can decrypt it
|
|
decrypted, err := credEncryptor.Decrypt(encrypted)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, password, decrypted)
|
|
})
|
|
|
|
t.Run("Encrypt API key", func(t *testing.T) {
|
|
apiKey := "api_12345678901234567890abcdef"
|
|
encrypted, err := credEncryptor.EncryptAPIKey(apiKey)
|
|
require.NoError(t, err)
|
|
assert.True(t, strings.HasPrefix(encrypted, EncryptedPrefix))
|
|
|
|
// Check that we can decrypt it
|
|
decrypted, err := credEncryptor.Decrypt(encrypted)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, apiKey, decrypted)
|
|
})
|
|
|
|
t.Run("Encrypt empty value", func(t *testing.T) {
|
|
encrypted, err := credEncryptor.Encrypt("", TypePassword)
|
|
require.Error(t, err)
|
|
assert.Equal(t, ErrEmptyCredential, err)
|
|
assert.Empty(t, encrypted)
|
|
})
|
|
|
|
t.Run("Encrypt value with invalid type", func(t *testing.T) {
|
|
encrypted, err := credEncryptor.Encrypt("somevalue", "invalid_type")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), ErrUnsupportedType.Error())
|
|
assert.Empty(t, encrypted)
|
|
})
|
|
|
|
t.Run("Password validation", func(t *testing.T) {
|
|
shortPassword := "short"
|
|
encrypted, err := credEncryptor.EncryptPassword(shortPassword)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "password too short")
|
|
assert.Empty(t, encrypted)
|
|
})
|
|
|
|
t.Run("API key validation", func(t *testing.T) {
|
|
shortAPIKey := "short"
|
|
encrypted, err := credEncryptor.EncryptAPIKey(shortAPIKey)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "too short")
|
|
assert.Empty(t, encrypted)
|
|
})
|
|
|
|
t.Run("Already encrypted value", func(t *testing.T) {
|
|
password := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(password)
|
|
require.NoError(t, err)
|
|
|
|
// Try to encrypt again
|
|
doubleEncrypted, err := credEncryptor.Encrypt(encrypted, TypePassword)
|
|
require.Error(t, err)
|
|
assert.Equal(t, ErrAlreadyEncrypted, err)
|
|
assert.Empty(t, doubleEncrypted)
|
|
})
|
|
}
|
|
|
|
func TestCredentialEncryptor_Decrypt(t *testing.T) {
|
|
credEncryptor := setupCredentialEncryptor(t)
|
|
|
|
t.Run("Decrypt encrypted value", func(t *testing.T) {
|
|
original := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(original)
|
|
require.NoError(t, err)
|
|
|
|
decrypted, err := credEncryptor.Decrypt(encrypted)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, original, decrypted)
|
|
})
|
|
|
|
t.Run("Decrypt empty value", func(t *testing.T) {
|
|
decrypted, err := credEncryptor.Decrypt("")
|
|
require.Error(t, err)
|
|
assert.Equal(t, ErrEmptyCredential, err)
|
|
assert.Empty(t, decrypted)
|
|
})
|
|
|
|
t.Run("Decrypt non-encrypted value", func(t *testing.T) {
|
|
decrypted, err := credEncryptor.Decrypt("notEncrypted")
|
|
require.Error(t, err)
|
|
assert.Equal(t, ErrNotEncrypted, err)
|
|
assert.Empty(t, decrypted)
|
|
})
|
|
|
|
t.Run("Decrypt corrupted value", func(t *testing.T) {
|
|
original := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(original)
|
|
require.NoError(t, err)
|
|
|
|
// Remove the prefix for manipulation
|
|
encryptedWithoutPrefix := strings.TrimPrefix(encrypted, EncryptedPrefix)
|
|
|
|
// Base64 decode the encrypted content
|
|
decoded, err := base64.StdEncoding.DecodeString(encryptedWithoutPrefix)
|
|
require.NoError(t, err)
|
|
|
|
// Find position in the actual ciphertext (after the IV)
|
|
if len(decoded) > 20 {
|
|
// Corrupt a byte in the ciphertext portion (not in the IV)
|
|
decoded[20] ^= 0xFF // Flip all bits in this byte
|
|
|
|
// Re-encode to base64
|
|
corrupted := EncryptedPrefix + base64.StdEncoding.EncodeToString(decoded)
|
|
|
|
// This should fail to decrypt
|
|
decrypted, err := credEncryptor.Decrypt(corrupted)
|
|
require.Error(t, err, "Decryption should fail with corrupted data")
|
|
assert.Empty(t, decrypted)
|
|
} else {
|
|
t.Skip("Encrypted data too short to corrupt properly")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestCredentialEncryptor_EncryptField(t *testing.T) {
|
|
credEncryptor := setupCredentialEncryptor(t)
|
|
|
|
t.Run("Encrypt non-encrypted field", func(t *testing.T) {
|
|
field := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptField(field, TypePassword)
|
|
require.NoError(t, err)
|
|
assert.True(t, strings.HasPrefix(encrypted, EncryptedPrefix))
|
|
})
|
|
|
|
t.Run("Already encrypted field", func(t *testing.T) {
|
|
original := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(original)
|
|
require.NoError(t, err)
|
|
|
|
// Try to encrypt again using EncryptField
|
|
result, err := credEncryptor.EncryptField(encrypted, TypePassword)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, encrypted, result, "EncryptField should return the already encrypted value")
|
|
})
|
|
|
|
t.Run("Empty field", func(t *testing.T) {
|
|
result, err := credEncryptor.EncryptField("", TypePassword)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, result, "EncryptField should return empty for empty input")
|
|
})
|
|
}
|
|
|
|
func TestCredentialEncryptor_DecryptField(t *testing.T) {
|
|
credEncryptor := setupCredentialEncryptor(t)
|
|
|
|
t.Run("Decrypt encrypted field", func(t *testing.T) {
|
|
original := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(original)
|
|
require.NoError(t, err)
|
|
|
|
decrypted, err := credEncryptor.DecryptField(encrypted)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, original, decrypted)
|
|
})
|
|
|
|
t.Run("Non-encrypted field", func(t *testing.T) {
|
|
field := "plaintext"
|
|
result, err := credEncryptor.DecryptField(field)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, field, result, "DecryptField should return non-encrypted value as is")
|
|
})
|
|
|
|
t.Run("Empty field", func(t *testing.T) {
|
|
result, err := credEncryptor.DecryptField("")
|
|
require.NoError(t, err)
|
|
assert.Empty(t, result, "DecryptField should return empty for empty input")
|
|
})
|
|
}
|
|
|
|
func TestSanitizeCredential(t *testing.T) {
|
|
t.Run("Sanitize plaintext", func(t *testing.T) {
|
|
original := "plainTextPassword123"
|
|
sanitized := SanitizeCredential(original)
|
|
assert.NotEqual(t, original, sanitized)
|
|
assert.True(t, len(sanitized) < len(original))
|
|
assert.Contains(t, sanitized, "...")
|
|
})
|
|
|
|
t.Run("Sanitize encrypted value", func(t *testing.T) {
|
|
credEncryptor := setupCredentialEncryptor(t)
|
|
original := "securePassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(original)
|
|
require.NoError(t, err)
|
|
|
|
sanitized := SanitizeCredential(encrypted)
|
|
assert.NotEqual(t, encrypted, sanitized)
|
|
assert.True(t, strings.HasPrefix(sanitized, EncryptedPrefix))
|
|
assert.Contains(t, sanitized, "...")
|
|
})
|
|
|
|
t.Run("Sanitize empty value", func(t *testing.T) {
|
|
sanitized := SanitizeCredential("")
|
|
assert.Empty(t, sanitized)
|
|
})
|
|
|
|
t.Run("Sanitize short value", func(t *testing.T) {
|
|
sanitized := SanitizeCredential("short")
|
|
assert.Equal(t, "****", sanitized)
|
|
})
|
|
}
|
|
|
|
func TestRequiresEncryption(t *testing.T) {
|
|
testCases := []struct {
|
|
fieldName string
|
|
requiresEncryption bool
|
|
expectedType CredentialType
|
|
}{
|
|
{"password", true, TypePassword},
|
|
{"userPassword", true, TypePassword},
|
|
{"passwd", true, TypePassword},
|
|
{"pwd", true, TypePassword},
|
|
{"apiKey", true, TypeAPIKey},
|
|
{"api_key", true, TypeAPIKey},
|
|
{"secretKey", true, TypeSecretKey},
|
|
{"secret_key", true, TypeSecretKey},
|
|
{"accessToken", true, TypeAccessToken},
|
|
{"access_token", true, TypeAccessToken},
|
|
{"refreshToken", true, TypeRefreshToken},
|
|
{"refresh_token", true, TypeRefreshToken},
|
|
{"oauthToken", true, TypeOAuthToken},
|
|
{"oauth_refresh_token", true, TypeOAuthToken},
|
|
{"sshKey", true, TypeSSHKey},
|
|
{"ssh_key", true, TypeSSHKey},
|
|
{"privateKey", true, TypeSSHKey},
|
|
{"private_key", true, TypeSSHKey},
|
|
{"authToken", true, TypeGeneric},
|
|
{"secret", true, TypeGeneric},
|
|
{"key", true, TypeGeneric},
|
|
{"token", true, TypeGeneric},
|
|
{"username", false, ""},
|
|
{"email", false, ""},
|
|
{"address", false, ""},
|
|
{"name", false, ""},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.fieldName, func(t *testing.T) {
|
|
requires, credType := RequiresEncryption(tc.fieldName)
|
|
assert.Equal(t, tc.requiresEncryption, requires)
|
|
if tc.requiresEncryption {
|
|
assert.Equal(t, tc.expectedType, credType)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetGlobalCredentialEncryptor(t *testing.T) {
|
|
// Setup environment for global encryption service
|
|
testEnvVar := DefaultKeyEnvVar
|
|
validKey := make([]byte, AES256KeySize)
|
|
for i := range validKey {
|
|
validKey[i] = byte(i % 256)
|
|
}
|
|
validKeyBase64 := encodeBase64(validKey)
|
|
|
|
// Set a valid key in environment
|
|
setenv(t, testEnvVar, validKeyBase64)
|
|
|
|
// Get global credential encryptor
|
|
credEncryptor, err := GetGlobalCredentialEncryptor()
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, credEncryptor)
|
|
|
|
// Test that it works
|
|
testValue := "testPassword123"
|
|
encrypted, err := credEncryptor.EncryptPassword(testValue)
|
|
require.NoError(t, err)
|
|
assert.True(t, strings.HasPrefix(encrypted, EncryptedPrefix))
|
|
|
|
decrypted, err := credEncryptor.Decrypt(encrypted)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testValue, decrypted)
|
|
}
|
|
|
|
// Utility functions for testing
|
|
|
|
func encodeBase64(data []byte) string {
|
|
return base64.StdEncoding.EncodeToString(data)
|
|
}
|
|
|
|
func setenv(t *testing.T, key, value string) {
|
|
t.Setenv(key, value)
|
|
}
|