From bced62bda80b6f110ef6050e2179200bcf8561e9 Mon Sep 17 00:00:00 2001 From: Jochen Ehret Date: Mon, 2 Nov 2026 14:35:02 +0100 Subject: [PATCH] Fix STI subclass bug in FieldEncryptor#before_create ManagedServiceInstance and UserProvidedServiceInstance are STI subclasses of ServiceInstance, but only "ServiceInstance" is registered in encrypted_classes. The before_create hook checked only self.class.name, so precursor INSERTs for managed service instances never set encryption_key_label, leaving it NULL permanently when the failure path skipped the credentials= setter. Fix: walk the ancestor chain instead of checking the class name directly. --- lib/cloud_controller/encryptor.rb | 4 +- .../lib/cloud_controller/encryptor_spec.rb | 56 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/cloud_controller/encryptor.rb b/lib/cloud_controller/encryptor.rb index 05dff457c1a..52b19b72cb5 100644 --- a/lib/cloud_controller/encryptor.rb +++ b/lib/cloud_controller/encryptor.rb @@ -102,10 +102,10 @@ module FieldEncryptor extend ActiveSupport::Concern def before_create - if Encryptor.encrypted_classes.include?(self.class.name) && Encryptor.current_encryption_key_label.present? + if self.class.ancestors.any? { |a| Encryptor.encrypted_classes.include?(a.name) } && Encryptor.current_encryption_key_label.present? self.encryption_key_label = Encryptor.current_encryption_key_label end - super + super if defined?(super) end private diff --git a/spec/unit/lib/cloud_controller/encryptor_spec.rb b/spec/unit/lib/cloud_controller/encryptor_spec.rb index 89ed4a1d5ce..afb2115567b 100644 --- a/spec/unit/lib/cloud_controller/encryptor_spec.rb +++ b/spec/unit/lib/cloud_controller/encryptor_spec.rb @@ -453,6 +453,62 @@ def self.name end end + describe '#before_create' do + let(:columns) { %i[sekret salt encryption_key_label encryption_iterations] } + + let(:sti_parent_class) do + Class.new(base_class) do + set_field_as_encrypted :sekret + + def db; end + + def self.name + 'StiParentClass' + end + end + end + + let(:sti_child_class) do + Class.new(sti_parent_class) do + def self.name + 'StiChildClass' + end + end + end + + before do + allow(Encryptor).to receive_messages(current_encryption_key_label: 'some-label', encrypted_classes: ['StiParentClass']) + end + + context 'when the class itself is registered in encrypted_classes' do + it 'sets encryption_key_label' do + instance = sti_parent_class.new + instance.before_create + expect(instance.encryption_key_label).to eq('some-label') + end + end + + context 'when an STI subclass is not directly registered in encrypted_classes' do + it 'still sets encryption_key_label via ancestor lookup' do + instance = sti_child_class.new + instance.before_create + expect(instance.encryption_key_label).to eq('some-label') + end + end + + context 'when no current encryption key label is set' do + before do + allow(Encryptor).to receive(:current_encryption_key_label).and_return(nil) + end + + it 'does not set encryption_key_label' do + instance = sti_child_class.new + instance.before_create + expect(instance.encryption_key_label).to be_nil + end + end + end + describe 'key rotation' do let(:salt) { Encryptor.generate_salt } let(:unencrypted_string) { 'unencrypted' }