first commit
This commit is contained in:
369
framework/configstore/encryption.go
Normal file
369
framework/configstore/encryption.go
Normal file
@@ -0,0 +1,369 @@
|
||||
package configstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/maximhq/bifrost/framework/configstore/tables"
|
||||
"github.com/maximhq/bifrost/framework/encrypt"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
encryptionStatusPlainText = "plain_text"
|
||||
encryptionStatusEncrypted = "encrypted"
|
||||
encryptionBatchSize = 100
|
||||
)
|
||||
|
||||
// EncryptPlaintextRows encrypts all rows with encryption_status='plain_text'
|
||||
// across all sensitive tables. Called during startup when encryption is enabled.
|
||||
// Each table's GORM BeforeSave hook handles the actual encryption.
|
||||
func (s *RDBConfigStore) EncryptPlaintextRows(ctx context.Context) error {
|
||||
if !encrypt.IsEnabled() {
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalEncrypted int
|
||||
|
||||
// config_keys
|
||||
count, err := s.encryptPlaintextKeys(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt config_keys: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// governance_virtual_keys
|
||||
count, err = s.encryptPlaintextVirtualKeys(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt virtual_keys: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// sessions
|
||||
count, err = s.encryptPlaintextSessions(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt sessions: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// oauth_tokens
|
||||
count, err = s.encryptPlaintextOAuthTokens(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt oauth_tokens: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// oauth_configs
|
||||
count, err = s.encryptPlaintextOAuthConfigs(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt oauth_configs: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// config_mcp_clients
|
||||
count, err = s.encryptPlaintextMCPClients(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt mcp_clients: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// config_providers (proxy config)
|
||||
count, err = s.encryptPlaintextProviderProxies(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt provider proxy configs: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// config_vector_store
|
||||
count, err = s.encryptPlaintextVectorStoreConfigs(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt vector_store configs: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
// config_plugins
|
||||
count, err = s.encryptPlaintextPlugins(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to encrypt plugin configs: %w", err)
|
||||
}
|
||||
totalEncrypted += count
|
||||
|
||||
if totalEncrypted > 0 && s.logger != nil {
|
||||
s.logger.Info(fmt.Sprintf("encrypted %d plaintext rows across all tables", totalEncrypted))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// encryptPlaintextKeys finds all config_keys rows with plaintext encryption status and
|
||||
// re-saves them in batches. The TableKey.BeforeSave hook handles the actual encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextKeys(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var keys []tables.TableKey
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("encryption_status = ? OR encryption_status IS NULL OR encryption_status = ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&keys).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range keys {
|
||||
if err := tx.Save(&keys[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(keys)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextVirtualKeys finds all governance_virtual_keys rows with plaintext encryption
|
||||
// status and re-saves them in batches. The TableVirtualKey.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextVirtualKeys(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var vks []tables.TableVirtualKey
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND value != ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&vks).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(vks) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range vks {
|
||||
if err := tx.Save(&vks[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(vks)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextSessions finds all sessions rows with plaintext encryption status and
|
||||
// re-saves them in batches. The SessionsTable.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextSessions(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var sessions []tables.SessionsTable
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND token != ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&sessions).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(sessions) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range sessions {
|
||||
if err := tx.Save(&sessions[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(sessions)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextOAuthTokens finds all oauth_tokens rows with plaintext encryption status
|
||||
// and re-saves them in batches. The TableOauthToken.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextOAuthTokens(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var tokens []tables.TableOauthToken
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("encryption_status = ? OR encryption_status IS NULL OR encryption_status = ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&tokens).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(tokens) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range tokens {
|
||||
if err := tx.Save(&tokens[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(tokens)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextOAuthConfigs finds all oauth_configs rows with plaintext encryption status
|
||||
// and re-saves them in batches. The TableOauthConfig.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextOAuthConfigs(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var configs []tables.TableOauthConfig
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND (client_secret != '' OR code_verifier != '')", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&configs).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(configs) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range configs {
|
||||
if err := tx.Save(&configs[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(configs)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextMCPClients finds all config_mcp_clients rows with plaintext encryption
|
||||
// status and re-saves them in batches. The TableMCPClient.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextMCPClients(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var clients []tables.TableMCPClient
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("encryption_status = ? OR encryption_status IS NULL OR encryption_status = ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&clients).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(clients) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range clients {
|
||||
if err := tx.Save(&clients[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(clients)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextProviderProxies finds all config_providers rows that have a non-empty
|
||||
// proxy config with plaintext encryption status and re-saves them in batches. The
|
||||
// TableProvider.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextProviderProxies(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var providers []tables.TableProvider
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND proxy_config_json != '' AND proxy_config_json IS NOT NULL", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&providers).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(providers) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range providers {
|
||||
if err := tx.Save(&providers[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(providers)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextVectorStoreConfigs finds all config_vector_store rows that have a non-empty
|
||||
// config with plaintext encryption status and re-saves them in batches. The
|
||||
// TableVectorStoreConfig.BeforeSave hook handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextVectorStoreConfigs(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var configs []tables.TableVectorStoreConfig
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND config IS NOT NULL AND config != ''", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&configs).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(configs) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range configs {
|
||||
if err := tx.Save(&configs[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(configs)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// encryptPlaintextPlugins finds all config_plugins rows that have a non-empty config with
|
||||
// plaintext encryption status and re-saves them in batches. The TablePlugin.BeforeSave hook
|
||||
// handles encryption.
|
||||
func (s *RDBConfigStore) encryptPlaintextPlugins(ctx context.Context) (int, error) {
|
||||
var count int
|
||||
for {
|
||||
var plugins []tables.TablePlugin
|
||||
if err := s.DB().WithContext(ctx).
|
||||
Where("(encryption_status = ? OR encryption_status IS NULL OR encryption_status = '') AND config_json != '' AND config_json != '{}'", encryptionStatusPlainText).
|
||||
Limit(encryptionBatchSize).
|
||||
Find(&plugins).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
if len(plugins) == 0 {
|
||||
break
|
||||
}
|
||||
if err := s.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
for i := range plugins {
|
||||
if err := tx.Save(&plugins[i]).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return count, err
|
||||
}
|
||||
count += len(plugins)
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
Reference in New Issue
Block a user