diff options
Diffstat (limited to 'spec/integration')
-rw-r--r-- | spec/integration/provider/ssh_authorized_key_spec.rb | 219 | ||||
-rw-r--r-- | spec/integration/provider/sshkey_spec.rb | 159 |
2 files changed, 378 insertions, 0 deletions
diff --git a/spec/integration/provider/ssh_authorized_key_spec.rb b/spec/integration/provider/ssh_authorized_key_spec.rb new file mode 100644 index 0000000..14af2de --- /dev/null +++ b/spec/integration/provider/ssh_authorized_key_spec.rb @@ -0,0 +1,219 @@ +#! /usr/bin/env ruby + +require 'spec_helper' +require 'puppet/file_bucket/dipper' + +describe Puppet::Type.type(:ssh_authorized_key).provider(:parsed), '(integration)', :unless => Puppet.features.microsoft_windows? do + include PuppetSpec::Files + + let :fake_userfile do + tmpfile('authorized_keys.user') + end + + let :fake_rootfile do + tmpfile('authorized_keys.root') + end + + let :sample_rsa_keys do + [ + 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQCi18JBZOq10X3w4f67nVhO0O3s5Y1vHH4UgMSM3ZnQwbC5hjGyYSi9UULOoQQoQynI/a0I9NL423/Xk/XJVIKCHcS8q6V2Wmjd+fLNelOjxxoW6mbIytEt9rDvwgq3Mof3/m21L3t2byvegR00a+ikKbmInPmKwjeWZpexCIsHzQ==', # 1024 bit + 'AAAAB3NzaC1yc2EAAAADAQABAAAAgQDLClyvi3CsJw5Id6khZs2/+s11qOH4Gdp6iDioDsrIp0m8kSiPr71VGyQYAfPzzvHemHS7Xg0NkG1Kc8u9tRqBQfTvz7ubq0AT/g01+4P2hQ/soFkuwlUG/HVnnaYb6N0Qp5SHWvD5vBE2nFFQVpP5GrSctPtHSjzJq/i+6LYhmQ==', # 1024 bit + 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDLygAO6txXkh9FNV8xSsBkATeqLbHzS7sFjGI3gt0Dx6q3LjyKwbhQ1RLf28kd5G6VWiXmClU/RtiPdUz8nrGuun++2mrxzrXrvpR9dq1lygLQ2wn2cI35dN5bjRMtXy3decs6HUhFo9MoNwX250rUWfdCyNPhGIp6OOfmjdy+UeLGNxq9wDx6i4bT5tVVSqVRtsEfw9+ICXchzl85QudjneVVpP+thriPZXfXA5eaGwAo/dmoKOIhUwF96gpdLqzNtrGQuxPbV80PTbGv9ZtAtTictxaDz8muXO7he9pXmchUpxUKtMFjHkL0FAZ9tRPmv3RA30sEr2fZ8+LKvnE50w0' #2048 Bit + ] + end + + let :sample_dsa_keys do + [ + 'AAAAB3NzaC1kc3MAAACBAOPck2O8MIDSqxPSnvENt6tzRrKJ5oOhB6Nc6oEcWm+VEH1gvuxdiRqwoMgRwyEf1yUd+UAcLw3a6Jn+EtFyEBN/5WF+4Tt4xTxZ0Pfik2Wc5uqHbQ2dkmOoXiAOYPiD3JUQ1Xwm/J0CgetjitoLfzAGdCNhMqguqAuHcVJ78ZZbAAAAFQCIBKFYZ+I18I+dtgteirXh+VVEEwAAAIEAs1yvQ/wnLLrRCM660pF4kBiw3D6dJfMdCXWQpn0hZmkBQSIzZv4Wuk3giei5luxscDxNc+y3CTXtnyG4Kt1Yi2sOdvhRI3rX8tD+ejn8GHazM05l5VIo9uu4AQPIE32iV63IqgApSBbJ6vDJW91oDH0J492WdLCar4BS/KE3cRwAAACBAN0uSDyJqYLRsfYcFn4HyVf6TJxQm1IcwEt6GcJVzgjri9VtW7FqY5iBqa9B9Zdh5XXAYJ0XLsWQCcrmMHM2XGHGpA4gL9VlCJ/0QvOcXxD2uK7IXwAVUA7g4V4bw8EVnFv2Flufozhsp+4soo1xiYc5jiFVHwVlk21sMhAtKAeF' # 1024 Bit + ] + end + + let :sample_lines do + [ + "ssh-rsa #{sample_rsa_keys[1]} root@someotherhost", + "ssh-dss #{sample_dsa_keys[0]} root@anywhere", + "ssh-rsa #{sample_rsa_keys[2]} paul", + "ssh-rsa #{sample_rsa_keys[2]} dummy" + ] + end + + let :dummy do + Puppet::Type.type(:ssh_authorized_key).new( + :name => 'dummy', + :target => fake_userfile, + :user => 'nobody', + :ensure => :absent + ) + end + + before :each do + File.stubs(:chown) + File.stubs(:chmod) + Puppet::Util::SUIDManager.stubs(:asuser).yields + end + + after :each do + described_class.clear # Work around bug #6628 + end + + def create_fake_key(username, content) + filename = (username == :root ? fake_rootfile : fake_userfile ) + File.open(filename, 'w') do |f| + content.each do |line| + f.puts line + end + end + end + + def check_fake_key(username, expected_content) + filename = (username == :root ? fake_rootfile : fake_userfile ) + content = File.readlines(filename).map(&:chomp).sort.reject{ |x| x =~ /^# HEADER:/ } + expect(content.join("\n")).to eq(expected_content.sort.join("\n")) + end + + def run_in_catalog(*resources) + Puppet::FileBucket::Dipper.any_instance.stubs(:backup) # Don't backup to the filebucket + catalog = Puppet::Resource::Catalog.new + catalog.host_config = false + resources.each do |resource| + resource.expects(:err).never + catalog.add_resource(resource) + end + catalog.apply + end + + it "should not complain about empty lines and comments" do + described_class.expects(:flush).never + sample = ['',sample_lines[0],' ',sample_lines[1],'# just a comment','#and another'] + create_fake_key(:user,sample) + run_in_catalog(dummy) + check_fake_key(:user, sample) + end + + it "should keep empty lines and comments when modifying a file" do + create_fake_key(:user, ['',sample_lines[0],' ',sample_lines[3],'# just a comment','#and another']) + run_in_catalog(dummy) + check_fake_key(:user, ['',sample_lines[0],' ','# just a comment','#and another']) + end + + describe "when managing one resource" do + + describe "with ensure set to absent" do + let :resource do + Puppet::Type.type(:ssh_authorized_key).new( + :name => 'root@hostname', + :type => :rsa, + :key => sample_rsa_keys[0], + :target => fake_rootfile, + :user => 'root', + :ensure => :absent + ) + end + + it "should not modify root's keyfile if resource is currently not present" do + create_fake_key(:root, sample_lines) + run_in_catalog(resource) + check_fake_key(:root, sample_lines) + end + + it "remove the key from root's keyfile if resource is currently present" do + create_fake_key(:root, sample_lines + ["ssh-rsa #{sample_rsa_keys[0]} root@hostname"]) + run_in_catalog(resource) + check_fake_key(:root, sample_lines) + end + end + + describe "when ensure is present" do + let :resource do + Puppet::Type.type(:ssh_authorized_key).new( + :name => 'root@hostname', + :type => :rsa, + :key => sample_rsa_keys[0], + :target => fake_rootfile, + :user => 'root', + :ensure => :present + ) + end + + # just a dummy so the parsedfile provider is aware + # of the user's authorized_keys file + + it "should add the key if it is not present" do + create_fake_key(:root, sample_lines) + run_in_catalog(resource) + check_fake_key(:root, sample_lines + ["ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + end + + it "should modify the type if type is out of sync" do + create_fake_key(:root,sample_lines + [ "ssh-dss #{sample_rsa_keys[0]} root@hostname" ]) + run_in_catalog(resource) + check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + end + + it "should modify the key if key is out of sync" do + create_fake_key(:root,sample_lines + [ "ssh-rsa #{sample_rsa_keys[1]} root@hostname" ]) + run_in_catalog(resource) + check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + end + + it "should remove the key from old file if target is out of sync" do + create_fake_key(:user, [ sample_lines[0], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + create_fake_key(:root, [ sample_lines[1], sample_lines[2] ]) + run_in_catalog(resource, dummy) + check_fake_key(:user, [ sample_lines[0] ]) + #check_fake_key(:root, [ sample_lines[1], sample_lines[2], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + end + + it "should add the key to new file if target is out of sync" do + create_fake_key(:user, [ sample_lines[0], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + create_fake_key(:root, [ sample_lines[1], sample_lines[2] ]) + run_in_catalog(resource, dummy) + #check_fake_key(:user, [ sample_lines[0] ]) + check_fake_key(:root, [ sample_lines[1], sample_lines[2], "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + end + + it "should modify options if options are out of sync" do + resource[:options]=[ 'from="*.domain1,host1.domain2"', 'no-port-forwarding', 'no-pty' ] + create_fake_key(:root, sample_lines + [ "from=\"*.false,*.false2\",no-port-forwarding,no-pty ssh-rsa #{sample_rsa_keys[0]} root@hostname"]) + run_in_catalog(resource) + check_fake_key(:root, sample_lines + [ "from=\"*.domain1,host1.domain2\",no-port-forwarding,no-pty ssh-rsa #{sample_rsa_keys[0]} root@hostname"] ) + end + end + end + + describe "when managing two resource" do + let :examples do + resources = [] + resources << Puppet::Type.type(:ssh_authorized_key).new( + :name => 'root@hostname', + :type => :rsa, + :key => sample_rsa_keys[0], + :target => fake_rootfile, + :user => 'root', + :ensure => :present + ) + resources << Puppet::Type.type(:ssh_authorized_key).new( + :name => 'user@hostname', + :key => sample_rsa_keys[1], + :type => :rsa, + :target => fake_userfile, + :user => 'nobody', + :ensure => :present + ) + resources + end + + describe "and both keys are absent" do + before :each do + create_fake_key(:root, sample_lines) + create_fake_key(:user, sample_lines) + end + + it "should add both keys" do + run_in_catalog(*examples) + check_fake_key(:root, sample_lines + [ "ssh-rsa #{sample_rsa_keys[0]} root@hostname" ]) + check_fake_key(:user, sample_lines + [ "ssh-rsa #{sample_rsa_keys[1]} user@hostname" ]) + end + end + end +end diff --git a/spec/integration/provider/sshkey_spec.rb b/spec/integration/provider/sshkey_spec.rb new file mode 100644 index 0000000..f461460 --- /dev/null +++ b/spec/integration/provider/sshkey_spec.rb @@ -0,0 +1,159 @@ +#!/usr/bin/env ruby + +require 'spec_helper' +require 'puppet/file_bucket/dipper' +require 'puppet_spec/files' +require 'puppet_spec/compiler' + +describe Puppet::Type.type(:sshkey).provider(:parsed), '(integration)', + :unless => Puppet.features.microsoft_windows? do + include PuppetSpec::Files + include PuppetSpec::Compiler + + before :each do + # Don't backup to filebucket + Puppet::FileBucket::Dipper.any_instance.stubs(:backup) + # We don't want to execute anything + described_class.stubs(:filetype). + returns Puppet::Util::FileType::FileTypeFlat + + @sshkey_file = tmpfile('sshkey_integration_specs') + FileUtils.cp(my_fixture('sample'), @sshkey_file) + end + + after :each do + # sshkey provider class + described_class.clear + end + + let(:type_under_test) { 'sshkey' } + + describe "when managing a ssh known hosts file it..." do + + let(:super_unique) { "my.super.unique.host" } + it "should create a new known_hosts file with mode 0644" do + target = tmpfile('ssh_known_hosts') + manifest = "#{type_under_test} { '#{super_unique}': + ensure => 'present', + type => 'rsa', + key => 'TESTKEY', + target => '#{target}' }" + apply_with_error_check(manifest) + expect_file_mode(target, "644") + end + + it "should create an SSH host key entry (ensure present)" do + manifest = "#{type_under_test} { '#{super_unique}': + ensure => 'present', + type => 'rsa', + key => 'mykey', + target => '#{@sshkey_file}' }" + apply_with_error_check(manifest) + expect(File.read(@sshkey_file)).to match(/#{super_unique}.*mykey/) + end + + let(:sshkey_name) { 'kirby.madstop.com' } + it "should delete an entry for an SSH host key" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'absent', + target => '#{@sshkey_file}' }" + apply_with_error_check(manifest) + expect(File.read(@sshkey_file)).not_to match(/#{sshkey_name}.*Yqk0=/) + end + + it "should update an entry for an SSH host key" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'present', + type => 'rsa', + key => 'mynewshinykey', + target => '#{@sshkey_file}' }" + apply_with_error_check(manifest) + expect(File.read(@sshkey_file)).to match(/#{sshkey_name}.*mynewshinykey/) + expect(File.read(@sshkey_file)).not_to match(/#{sshkey_name}.*Yqk0=/) + end + + # test all key types + types = ["ssh-dss", "dsa", + "ssh-ed25519", "ed25519", + "ssh-rsa", "rsa", + "ecdsa-sha2-nistp256", + "ecdsa-sha2-nistp384", + "ecdsa-sha2-nistp521"] + # these types are treated as aliases for sshkey <ahem> type + # so they are populated as the *values* below + aliases = {"dsa" => "ssh-dss", + "ed25519" => "ssh-ed25519", + "rsa" => "ssh-rsa"} + types.each do |type| + it "should update an entry with #{type} type" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'present', + type => '#{type}', + key => 'mynewshinykey', + target => '#{@sshkey_file}' }" + + apply_with_error_check(manifest) + if aliases.has_key?(type) + full_type = aliases[type] + expect(File.read(@sshkey_file)). + to match(/#{sshkey_name}.*#{full_type}.*mynew/) + else + expect(File.read(@sshkey_file)). + to match(/#{sshkey_name}.*#{type}.*mynew/) + end + end + end + + # test unknown key type fails + let(:invalid_type) { 'ssh-er0ck' } + it "should raise an error with an unknown type" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'present', + type => '#{invalid_type}', + key => 'mynewshinykey', + target => '#{@sshkey_file}' }" + expect { + apply_compiled_manifest(manifest) + }.to raise_error(Puppet::ResourceError, /Invalid value "#{invalid_type}"/) + end + + #single host_alias + let(:host_alias) { 'r0ckdata.com' } + it "should update an entry with new host_alias" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'present', + host_aliases => '#{host_alias}', + target => '#{@sshkey_file}' }" + apply_with_error_check(manifest) + expect(File.read(@sshkey_file)).to match(/#{sshkey_name},#{host_alias}\s/) + expect(File.read(@sshkey_file)).not_to match(/#{sshkey_name}\s/) + end + + #array host_alias + let(:host_aliases) { "r0ckdata.com,erict.net" } + it "should update an entry with new host_alias" do + manifest = "#{type_under_test} { '#{sshkey_name}': + ensure => 'present', + host_aliases => '#{host_alias}', + target => '#{@sshkey_file}' }" + apply_with_error_check(manifest) + expect(File.read(@sshkey_file)).to match(/#{sshkey_name},#{host_alias}\s/) + expect(File.read(@sshkey_file)).not_to match(/#{sshkey_name}\s/) + end + + #puppet resource sshkey + it "should fetch an entry from resources" do + @resource_app = Puppet::Application[:resource] + @resource_app.preinit + @resource_app.command_line.stubs(:args). + returns([type_under_test, sshkey_name, "target=#{@sshkey_file}"]) + + @resource_app.expects(:puts).with do |args| + expect(args).to match(/#{sshkey_name}/) + end + @resource_app.main + end + + end + +end |