mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-23 18:25:36 -04:00
2be1f651a9
* feat(#450): add Redis SSL/TLS configuration support Add comprehensive SSL/TLS support for Redis connections with configurable certificate verification modes. Introduces new environment variables for SSL configuration including REDIS_USE_SSL, REDIS_SSL_CERT_REQS (supporting CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED), and REDIS_SSL_CA_CERTS for custom CA certificates. Changes: - Add Redis SSL configuration options to .env.example - Implement RedisTLSConfig() method to build tls.Config based on environment settings - Pass TLS config to both standard Redis and Sentinel mode initializers - Support custom CA certificate loading and verification modes - Set minimum TLS version to 1.2 for security - Minor whitespace cleanup in existing config comments This enables secure Redis connections in production environments with flexible certificate verification options. * fix(#450): prevent reference cycle in TLS config and simplify SSL setup - Capture only RootCAs in VerifyConnection closure to avoid retaining entire tlsConf and potential reference cycles - Remove redundant nil checks for tlsConf in Redis client initialization since tlsConf is guaranteed to be non-nil when useSsl is true - Update comments to reflect actual behavior and constraints * fix(#450): improve Redis TLS certificate verification logic for optional certificates * fix(#450): simplify Redis TLS certificate verification logic for optional and required certificates * docs(#450): add note for CA certificate file path in Redis SSL configuration * test(#450): add comprehensive tests for Redis TLS configuration * fix(#450): enhance Redis SSL configuration documentation and enforce CA cert requirement * fix(#450): add nil TLS parameter to InitRedisClient calls in tests Update all InitRedisClient function calls across test files to include the new nil parameter for TLS configuration. This change maintains backward compatibility by explicitly passing nil for TLS settings in non-TLS test scenarios. * fix(#450): add default TLS configuration for Redis client when no tlsConf is provided
243 lines
6.1 KiB
Go
243 lines
6.1 KiB
Go
package persistence
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"testing"
|
|
|
|
cloudoss "github.com/langgenius/dify-cloud-kit/oss"
|
|
"github.com/langgenius/dify-cloud-kit/oss/factory"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/db"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/utils/cache"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/utils/strings"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestPersistenceStoreAndLoad(t *testing.T) {
|
|
err := cache.InitRedisClient("localhost:6379", "", "difyai123456", false, 0, nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to init redis client: %v", err)
|
|
}
|
|
defer cache.Close()
|
|
|
|
db.Init(&app.Config{
|
|
DBType: app.DB_TYPE_POSTGRESQL,
|
|
DBUsername: "postgres",
|
|
DBPassword: "difyai123456",
|
|
DBHost: "localhost",
|
|
DBDefaultDatabase: "postgres",
|
|
DBPort: 5432,
|
|
DBDatabase: "dify_plugin_daemon",
|
|
DBSslMode: "disable",
|
|
})
|
|
defer db.Close()
|
|
|
|
oss, err := factory.Load("local", cloudoss.OSSArgs{
|
|
Local: &cloudoss.Local{
|
|
Path: "./storage",
|
|
},
|
|
},
|
|
)
|
|
if err != nil {
|
|
t.Error("failed to load local storage", err.Error())
|
|
}
|
|
|
|
InitPersistence(oss, &app.Config{
|
|
PersistenceStoragePath: "./persistence_storage",
|
|
PersistenceStorageMaxSize: 1024 * 1024 * 1024,
|
|
})
|
|
|
|
key := strings.RandomString(10)
|
|
|
|
assert.Nil(t, persistence.Save("tenant_id", "plugin_checksum", -1, key, []byte("data")))
|
|
|
|
data, err := persistence.Load("tenant_id", "plugin_checksum", key)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, string(data), "data")
|
|
|
|
// check if the file exists
|
|
if _, err := oss.Load("./persistence_storage/tenant_id/plugin_checksum/" + key); err != nil {
|
|
t.Fatalf("File not found: %v", err)
|
|
}
|
|
|
|
// check if cache is updated
|
|
cacheData, err := cache.GetString("persistence:cache:tenant_id:plugin_checksum:" + key)
|
|
assert.Nil(t, err)
|
|
|
|
cacheDataBytes, err := hex.DecodeString(cacheData)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, string(cacheDataBytes), "data")
|
|
}
|
|
|
|
func TestPersistenceSaveAndLoadWithLongKey(t *testing.T) {
|
|
err := cache.InitRedisClient("localhost:6379", "", "difyai123456", false, 0, nil)
|
|
assert.Nil(t, err)
|
|
defer cache.Close()
|
|
db.Init(&app.Config{
|
|
DBType: app.DB_TYPE_POSTGRESQL,
|
|
DBUsername: "postgres",
|
|
DBPassword: "difyai123456",
|
|
DBHost: "localhost",
|
|
DBPort: 5432,
|
|
DBDatabase: "dify_plugin_daemon",
|
|
DBSslMode: "disable",
|
|
})
|
|
defer db.Close()
|
|
|
|
oss, err := factory.Load("local", cloudoss.OSSArgs{
|
|
Local: &cloudoss.Local{
|
|
Path: "./storage",
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
|
|
InitPersistence(oss, &app.Config{
|
|
PersistenceStoragePath: "./persistence_storage",
|
|
PersistenceStorageMaxSize: 1024 * 1024 * 1024,
|
|
})
|
|
|
|
key := strings.RandomString(257)
|
|
|
|
if err := persistence.Save("tenant_id", "plugin_checksum", -1, key, []byte("data")); err == nil {
|
|
t.Fatalf("Expected error, got nil")
|
|
}
|
|
}
|
|
|
|
func TestPersistenceDelete(t *testing.T) {
|
|
err := cache.InitRedisClient("localhost:6379", "", "difyai123456", false, 0, nil)
|
|
assert.Nil(t, err)
|
|
defer cache.Close()
|
|
db.Init(&app.Config{
|
|
DBType: app.DB_TYPE_POSTGRESQL,
|
|
DBUsername: "postgres",
|
|
DBPassword: "difyai123456",
|
|
DBHost: "localhost",
|
|
DBPort: 5432,
|
|
DBDatabase: "dify_plugin_daemon",
|
|
DBSslMode: "disable",
|
|
})
|
|
defer db.Close()
|
|
|
|
oss, err := factory.Load("local", cloudoss.OSSArgs{
|
|
Local: &cloudoss.Local{
|
|
Path: "./storage",
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
|
|
InitPersistence(oss, &app.Config{
|
|
PersistenceStoragePath: "./persistence_storage",
|
|
PersistenceStorageMaxSize: 1024 * 1024 * 1024,
|
|
})
|
|
|
|
key := strings.RandomString(10)
|
|
|
|
if err := persistence.Save("tenant_id", "plugin_checksum", -1, key, []byte("data")); err != nil {
|
|
t.Fatalf("Failed to save data: %v", err)
|
|
}
|
|
|
|
if _, err := persistence.Delete("tenant_id", "plugin_checksum", key); err != nil {
|
|
t.Fatalf("Failed to delete data: %v", err)
|
|
}
|
|
|
|
// check if the file exists
|
|
if _, err := oss.Load("./persistence_storage/tenant_id/plugin_checksum/" + key); err == nil {
|
|
t.Fatalf("File not deleted: %v", err)
|
|
}
|
|
|
|
// check if cache is updated
|
|
_, err = cache.GetString("persistence:cache:tenant_id:plugin_checksum:" + key)
|
|
assert.Equal(t, err, cache.ErrNotFound)
|
|
}
|
|
|
|
func TestPersistencePathTraversal(t *testing.T) {
|
|
err := cache.InitRedisClient("localhost:6379", "", "difyai123456", false, 0, nil)
|
|
if err != nil {
|
|
t.Fatalf("Failed to init redis client: %v", err)
|
|
}
|
|
defer cache.Close()
|
|
|
|
db.Init(&app.Config{
|
|
DBType: app.DB_TYPE_POSTGRESQL,
|
|
DBUsername: "postgres",
|
|
DBPassword: "difyai123456",
|
|
DBHost: "localhost",
|
|
DBDefaultDatabase: "postgres",
|
|
DBPort: 5432,
|
|
DBDatabase: "dify_plugin_daemon",
|
|
DBSslMode: "disable",
|
|
})
|
|
defer db.Close()
|
|
|
|
oss, err := factory.Load("local", cloudoss.OSSArgs{
|
|
Local: &cloudoss.Local{
|
|
Path: "./storage",
|
|
},
|
|
})
|
|
assert.Nil(t, err)
|
|
|
|
InitPersistence(oss, &app.Config{
|
|
PersistenceStoragePath: "./persistence_storage",
|
|
PersistenceStorageMaxSize: 1024 * 1024 * 1024,
|
|
})
|
|
|
|
// Test cases for path traversal
|
|
testCases := []struct {
|
|
name string
|
|
key string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "normal key",
|
|
key: "test.txt",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "parent directory traversal",
|
|
key: "../test.txt",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "multiple parent directory traversal",
|
|
key: "../../test.txt",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "double slash",
|
|
key: "test//test.txt",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "backslash",
|
|
key: "test\\test.txt",
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "mixed traversal",
|
|
key: "test/../test.txt",
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "absolute path",
|
|
key: "/etc/passwd",
|
|
wantErr: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Test Save
|
|
err := persistence.Save("tenant_id", "plugin_checksum", -1, tc.key, []byte("data"))
|
|
assert.Equal(t, err != nil, tc.wantErr)
|
|
|
|
// Test Load
|
|
_, err = persistence.Load("tenant_id", "plugin_checksum", tc.key)
|
|
assert.Equal(t, err != nil, tc.wantErr)
|
|
|
|
// Test Delete
|
|
_, err = persistence.Delete("tenant_id", "plugin_checksum", tc.key)
|
|
assert.Equal(t, err != nil, tc.wantErr)
|
|
})
|
|
}
|
|
}
|