package postgres import ( "fmt" "os" "path/filepath" "strings" "testing" "time" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/log" "github.com/grafana/grafana/pkg/tsdb/grafana-postgresql-datasource/sqleng" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Test getTLSSettings. func TestGetTLSSettings(t *testing.T) { mockValidateCertFilePaths() t.Cleanup(resetValidateCertFilePaths) updatedTime := time.Now() testCases := []struct { desc string expErr string jsonData sqleng.JsonData secureJSONData map[string]string uid string tlsSettings tlsSettings updated time.Time }{ { desc: "Custom TLS authentication disabled", updated: updatedTime, jsonData: sqleng.JsonData{ Mode: "disable", RootCertFile: "i/am/coding/ca.crt", CertFile: "i/am/coding/client.crt", CertKeyFile: "i/am/coding/client.key", ConfigurationMethod: "file-path", }, tlsSettings: tlsSettings{Mode: "disable"}, }, { desc: "Custom TLS authentication with file path", updated: updatedTime.Add(time.Minute), jsonData: sqleng.JsonData{ Mode: "verify-full", ConfigurationMethod: "file-path", RootCertFile: "i/am/coding/ca.crt", CertFile: "i/am/coding/client.crt", CertKeyFile: "i/am/coding/client.key", }, tlsSettings: tlsSettings{ Mode: "verify-full", ConfigurationMethod: "file-path", RootCertFile: "i/am/coding/ca.crt", CertFile: "i/am/coding/client.crt", CertKeyFile: "i/am/coding/client.key", }, }, } for _, tt := range testCases { t.Run(tt.desc, func(t *testing.T) { var settings tlsSettings var err error mng := tlsManager{ logger: backend.NewLoggerWith("logger", "tsdb.postgres"), } ds := sqleng.DataSourceInfo{ JsonData: tt.jsonData, DecryptedSecureJSONData: tt.secureJSONData, UID: tt.uid, Updated: tt.updated, } settings, err = mng.getTLSSettings(ds) if tt.expErr == "" { require.NoError(t, err, tt.desc) assert.Equal(t, tt.tlsSettings, settings) } else { require.Error(t, err, tt.desc) assert.True(t, strings.HasPrefix(err.Error(), tt.expErr), fmt.Sprintf("%s: %q doesn't start with %q", tt.desc, err, tt.expErr)) } }) } } func mockValidateCertFilePaths() { validateCertFunc = func(rootCert, clientCert, clientKey string) error { return nil } } func resetValidateCertFilePaths() { validateCertFunc = validateCertFilePaths } func TestTLSManager_GetTLSSettings(t *testing.T) { logger := log.New() tlsManager := newTLSManager(logger) dsInfo := sqleng.DataSourceInfo{ JsonData: sqleng.JsonData{ Mode: "require", ConfigurationMethod: "file-content", }, DecryptedSecureJSONData: map[string]string{ "tlsCACert": "root-cert-content", "tlsClientCert": "client-cert-content", "tlsClientKey": "client-key-content", }, } tlsConfig, err := tlsManager.getTLSSettings(dsInfo) require.NoError(t, err) assert.Equal(t, "require", tlsConfig.Mode) assert.NotEmpty(t, tlsConfig.RootCertFile) assert.NotEmpty(t, tlsConfig.CertFile) assert.NotEmpty(t, tlsConfig.CertKeyFile) // Cleanup temporary files tlsManager.cleanupCertFiles(tlsConfig) assert.NoFileExists(t, tlsConfig.RootCertFile) assert.NoFileExists(t, tlsConfig.CertFile) assert.NoFileExists(t, tlsConfig.CertKeyFile) } func TestTLSManager_CleanupCertFiles_FilePath(t *testing.T) { logger := log.New() tlsManager := newTLSManager(logger) // Create temporary files for testing rootCertFile, err := tlsManager.writeCertFile("root-*.crt", "root-cert-content") require.NoError(t, err) clientCertFile, err := tlsManager.writeCertFile("client-*.crt", "client-cert-content") require.NoError(t, err) clientKeyFile, err := tlsManager.writeCertFile("client-*.key", "client-key-content") require.NoError(t, err) // Simulate a configuration where the method is "file-path" tlsConfig := tlsSettings{ ConfigurationMethod: "file-path", RootCertFile: rootCertFile, CertFile: clientCertFile, CertKeyFile: clientKeyFile, } // Call cleanupCertFiles tlsManager.cleanupCertFiles(tlsConfig) // Verify the files are NOT deleted assert.FileExists(t, rootCertFile, "Root certificate file should not be deleted") assert.FileExists(t, clientCertFile, "Client certificate file should not be deleted") assert.FileExists(t, clientKeyFile, "Client key file should not be deleted") // Cleanup the files manually err = os.Remove(rootCertFile) require.NoError(t, err) err = os.Remove(clientCertFile) require.NoError(t, err) err = os.Remove(clientKeyFile) require.NoError(t, err) } func TestTLSManager_CreateCertFiles(t *testing.T) { logger := log.New() tlsManager := newTLSManager(logger) dsInfo := sqleng.DataSourceInfo{ DecryptedSecureJSONData: map[string]string{ "tlsCACert": "root-cert-content", "tlsClientCert": "client-cert-content", "tlsClientKey": "client-key-content", }, } tlsConfig := tlsSettings{ ConfigurationMethod: "file-content", } err := tlsManager.createCertFiles(dsInfo, &tlsConfig) require.NoError(t, err) assert.FileExists(t, tlsConfig.RootCertFile) assert.FileExists(t, tlsConfig.CertFile) assert.FileExists(t, tlsConfig.CertKeyFile) // Cleanup temporary files tlsManager.cleanupCertFiles(tlsConfig) assert.NoFileExists(t, tlsConfig.RootCertFile) assert.NoFileExists(t, tlsConfig.CertFile) assert.NoFileExists(t, tlsConfig.CertKeyFile) } func TestTLSManager_WriteCertFile(t *testing.T) { logger := log.New() tlsManager := newTLSManager(logger) // Test writing a valid certificate file filePath, err := tlsManager.writeCertFile("test-*.crt", "test-cert-content") require.NoError(t, err) assert.FileExists(t, filePath) content, err := os.ReadFile(filepath.Clean(filePath)) require.NoError(t, err) assert.Equal(t, "test-cert-content", string(content)) // Cleanup the file err = os.Remove(filePath) require.NoError(t, err) assert.NoFileExists(t, filePath) } func TestTLSManager_CleanupCertFiles(t *testing.T) { logger := log.New() tlsManager := newTLSManager(logger) // Create temporary files for testing rootCertFile, err := tlsManager.writeCertFile("root-*.crt", "root-cert-content") require.NoError(t, err) clientCertFile, err := tlsManager.writeCertFile("client-*.crt", "client-cert-content") require.NoError(t, err) clientKeyFile, err := tlsManager.writeCertFile("client-*.key", "client-key-content") require.NoError(t, err) tlsConfig := tlsSettings{ ConfigurationMethod: "file-content", RootCertFile: rootCertFile, CertFile: clientCertFile, CertKeyFile: clientKeyFile, } // Cleanup the files tlsManager.cleanupCertFiles(tlsConfig) // Verify the files are deleted assert.NoFileExists(t, rootCertFile) assert.NoFileExists(t, clientCertFile) assert.NoFileExists(t, clientKeyFile) }