diff --git a/Modulefile b/Modulefile index 7ca5bcf4..bc88d0d3 100644 --- a/Modulefile +++ b/Modulefile @@ -1,5 +1,5 @@ name 'puppetlabs-java_ks' -version '0.0.6' +version '1.0.0' source 'https://github.com/puppetlabs/puppetlabs-java_ks.git' author 'puppetlabs' license 'ASL 2.0' diff --git a/lib/puppet/provider/java_ks/keytool.rb b/lib/puppet/provider/java_ks/keytool.rb index 9309904f..6552518d 100644 --- a/lib/puppet/provider/java_ks/keytool.rb +++ b/lib/puppet/provider/java_ks/keytool.rb @@ -20,13 +20,21 @@ def to_pkcs12 tmpfile = Tempfile.new("#{@resource[:name]}.") tmpfile.write(@resource[:password]) tmpfile.flush - output = Puppet::Util.execute( - cmd, - :stdinfile => tmpfile.path, - :failonfail => true, - :combine => true - ) + + # To maintain backwards compatibility with Puppet 2.7.x, resort to ugly + # code to make sure RANDFILE is passed as an environment variable to the + # openssl command but not retained in the Puppet process environment. + randfile = Tempfile.new("#{@resource[:name]}.") + if Puppet::Util::Execution.respond_to?(:withenv) + withenv = Puppet::Util::Execution.method(:withenv) + else + withenv = Puppet::Util.method(:withenv) + end + output = withenv.call('RANDFILE' => randfile.path) do + run_command(cmd, false, tmpfile) + end tmpfile.close! + randfile.close! return output end @@ -50,7 +58,7 @@ def import_ks tmpfile.write("#{@resource[:password]}\n#{@resource[:password]}\n#{@resource[:password]}") end tmpfile.flush - run_keystore_command(cmd, @resource[:target], tmpfile) + run_command(cmd, @resource[:target], tmpfile) tmppk12.close! tmpfile.close! end @@ -66,12 +74,7 @@ def exists? tmpfile = Tempfile.new("#{@resource[:name]}.") tmpfile.write(@resource[:password]) tmpfile.flush - Puppet::Util.execute( - cmd, - :stdinfile => tmpfile.path, - :failonfail => true, - :combine => true - ) + run_command(cmd, false, tmpfile) tmpfile.close! return true rescue @@ -86,7 +89,7 @@ def latest 'x509', '-fingerprint', '-md5', '-noout', '-in', @resource[:certificate] ] - output = Puppet::Util.execute(cmd) + output = run_command(cmd) latest = output.scan(/MD5 Fingerprint=(.*)/)[0][0] return latest end @@ -96,21 +99,16 @@ def current output = '' cmd = [ command(:keytool), - '-list', + '-list', '-v', '-keystore', @resource[:target], '-alias', @resource[:name] ] tmpfile = Tempfile.new("#{@resource[:name]}.") tmpfile.write(@resource[:password]) tmpfile.flush - output = Puppet::Util.execute( - cmd, - :stdinfile => tmpfile.path, - :failonfail => true, - :combine => true - ) + output = run_command(cmd, false, tmpfile) tmpfile.close! - current = output.scan(/Certificate fingerprint \(MD5\): (.*)/)[0][0] + current = output.scan(/Certificate fingerprints:\n\s+MD5: (.*)/)[0][0] return current end @@ -137,7 +135,7 @@ def create tmpfile.write("#{@resource[:password]}\n#{@resource[:password]}") end tmpfile.flush - run_keystore_command(cmd, @resource[:target], tmpfile) + run_command(cmd, @resource[:target], tmpfile) tmpfile.close! end end @@ -152,12 +150,7 @@ def destroy tmpfile = Tempfile.new("#{@resource[:name]}.") tmpfile.write(@resource[:password]) tmpfile.flush - Puppet::Util.execute( - cmd, - :stdinfile => tmpfile.path, - :failonfail => true, - :combine => true - ) + run_command(cmd, false, tmpfile) tmpfile.close! end @@ -167,38 +160,56 @@ def update create end - def run_keystore_command(cmd, target, stdinfile) + def run_command(cmd, target=false, stdinfile=false) + + # The Puppet::Util::Execution.execute method is deparcated in Puppet 3.x + # but we need this to work on 2.7.x too. + if Puppet::Util::Execution.respond_to?(:execute) + exec_method = Puppet::Util::Execution.method(:execute) + else + exec_method = Puppet::Util.method(:execute) + end + # the java keytool will not correctly deal with an empty target keystore # file. If we encounter an empty keystore target file, preserve the mode, # owner and group, and delete the empty file. - if File.exists?(target) and File.zero?(target) + if target and (File.exists?(target) and File.zero?(target)) stat = File.stat(target) File.delete(target) end - # There's a problem in IBM java wherein stdin cannot be used (trivially) - # pass in the keystore passwords. This makes the provider work on SLES - # with minimal effort. + # There's a problem in IBM java keytool wherein stdin cannot be used + # (trivially) to pass in the keystore passwords. The below hack makes the + # provider work on SLES with minimal effort at the cost of letting the + # passphrase to the keystore show up in the process list as an argument. + # From a best practice standpoint the keystore should be protected by file + # permissions and not just the passphrase so "making it work on SLES" + # trumps. if Facter.value('osfamily') == 'Suse' and @resource[:password] - cmd << '-srcstorepass' << @resource[:password] - cmd << '-deststorepass' << @resource[:password] + cmd_to_run = cmd.is_a?(String) ? cmd.split(/\s/).first : cmd.first + if cmd_to_run == command(:keytool) + cmd << '-srcstorepass' << @resource[:password] + cmd << '-deststorepass' << @resource[:password] + end end # Now run the command - Puppet::Util.execute( - cmd, - :stdinfile => stdinfile.path, - :failonfail => true, - :combine => true - ) + options = { :failonfail => true, :combine => true } + output = if stdinfile + exec_method.call(cmd, options.merge(:stdinfile => stdinfile.path)) + else + exec_method.call(cmd, options) + end # for previously empty files, restore the mode, owner and group. The funky # double-take check is because on Suse defined? doesn't seem to behave # quite the same as on Debian, RedHat - if defined? stat and stat + if target and (defined? stat and stat) File.chmod(stat.mode, target) File.chown(stat.uid, stat.gid, target) end + + return output end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c53b0170..2c6f5664 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1 @@ -require 'pathname' -dir = Pathname.new(__FILE__).parent -$LOAD_PATH.unshift(dir, dir + 'lib', dir + '../lib') - -require 'mocha' -require 'puppet' -gem 'rspec', '>=2.0.0' -require 'rspec/expectations' - -RSpec.configure do |config| - config.mock_with :mocha -end - -# We need this because the RAL uses 'should' as a method. This -# allows us the same behaviour but with a different method name. -class Object - alias :must :should -end +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/spec/provider/java_ks/keytool_spec.rb b/spec/unit/puppet/provider/java_ks/keytool_spec.rb similarity index 93% rename from spec/provider/java_ks/keytool_spec.rb rename to spec/unit/puppet/provider/java_ks/keytool_spec.rb index 5005c060..e5b6196e 100644 --- a/spec/provider/java_ks/keytool_spec.rb +++ b/spec/unit/puppet/provider/java_ks/keytool_spec.rb @@ -47,7 +47,7 @@ describe 'when importing a private key and certifcate' do it 'should execute openssl and keytool with specific options' do - Puppet::Util.expects(:execute).with do |args| + provider.expects(:run_command).with do |*args| args[0] == [ 'myopenssl', 'pkcs12', '-export', '-passout', 'stdin', '-in', resource[:certificate], @@ -55,7 +55,7 @@ '-name', resource[:name] ] end - Puppet::Util.expects(:execute).with do |args| + provider.expects(:run_command).with do |*args| args[0] == [ 'mykeytool', '-importkeystore', '-srcstoretype', 'PKCS12', '-destkeystore', resource[:target], @@ -76,7 +76,7 @@ it 'should call keytool with specific options if only certificate is provided' do no_pk = resource.dup no_pk.delete(:private_key) - Puppet::Util.expects(:execute).with do |args| + provider.expects(:run_command).with do |*args| args[0] == [ 'mykeytool', '-importcert', '-noprompt', '-alias', no_pk[:name], @@ -91,7 +91,7 @@ describe 'when removing entries from keytool' do it 'should execute keytool with a specific set of options' do - Puppet::Util.expects(:execute).with do |args| + provider.expects(:run_command).with do |*args| args[0] == [ 'mykeytool', '-delete', '-alias', resource[:name], diff --git a/spec/type/java_ks_spec.rb b/spec/unit/puppet/type/java_ks_spec.rb similarity index 100% rename from spec/type/java_ks_spec.rb rename to spec/unit/puppet/type/java_ks_spec.rb