diff --git a/lib/puppet/resource_api.rb b/lib/puppet/resource_api.rb index 2f7aadcb..3e7efbc1 100644 --- a/lib/puppet/resource_api.rb +++ b/lib/puppet/resource_api.rb @@ -96,6 +96,11 @@ def initialize(attributes) # $stderr.puts "B: #{attributes.inspect}" attributes = my_provider.canonicalize(context, [attributes])[0] if type_definition.feature?('canonicalize') + # Re-wrap any sensitive values that canonicalize may have unwrapped + sensitives.each do |name| + attributes[name] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(attributes[name]) if attributes.key?(name) && !attributes[name].is_a?(Puppet::Pops::Types::PSensitiveType::Sensitive) + end + # the `Puppet::Resource::Ral.find` method, when `instances` does not return a match, uses a Hash with a `:name` key to create # an "absent" resource. This is often hit by `puppet resource`. This needs to work, even if the namevar is not called `name`. # This bit here relies on the default `title_patterns` (see below) to match the title back to the first (and often only) namevar @@ -104,8 +109,8 @@ def initialize(attributes) attributes[:title] = @title if attributes[:title].nil? && !type_definition.namevars.empty? end - attributes[:sensitive_parameters] = sensitives unless sensitives.empty? - super + super(attributes) + set_sensitive_parameters(sensitives) unless sensitives.empty? end # Override finish method to ensure scope tags (like class names) are properly inherited diff --git a/spec/puppet/resource_api_spec.rb b/spec/puppet/resource_api_spec.rb index f5c038bc..31b91002 100644 --- a/spec/puppet/resource_api_spec.rb +++ b/spec/puppet/resource_api_spec.rb @@ -526,6 +526,32 @@ def set(_context, _changes); end expect(instance.parameter(:secret).sensitive).not_to be true end end + + context 'when loading from a Puppet::Resource with sensitive parameters listed' do + let(:params) { instance_double('Puppet::Resource', 'resource') } + let(:provider_instance) { instance_double(provider_class, 'provider_instance') } + let(:catalog) { instance_double('Unknown', 'catalog') } + + before do + allow(provider_class).to receive(:new).with(no_args).and_return(provider_instance) + allow(provider_instance).to receive(:get).and_return([]) + allow(params).to receive(:is_a?).with(Puppet::Resource).and_return(true) + allow(params).to receive(:title).with(no_args).and_return('a title') + allow(params).to receive(:catalog).with(no_args).and_return(catalog) + allow(params).to receive(:sensitive_parameters).with(no_args).and_return([:secret]) + allow(params).to receive(:to_hash).with(no_args) + .and_return(title: 'test', secret: 'a password value') + allow(catalog).to receive(:host_config?).and_return(true) + end + + it 'preserves sensitive_parameters so Puppet::Parameter#sensitive returns true' do + expect(instance.parameter(:secret).sensitive).to be true + end + + it 're-wraps the unwrapped secret value as Sensitive' do + expect(instance[:secret]).to be_a Puppet::Pops::Types::PSensitiveType::Sensitive + end + end end end end