puppet 6.3.0-x86-mingw32 → 6.4.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/CODEOWNERS +30 -0
  3. data/Gemfile.lock +9 -9
  4. data/lib/puppet.rb +13 -0
  5. data/lib/puppet/application/agent.rb +8 -12
  6. data/lib/puppet/application/device.rb +2 -3
  7. data/lib/puppet/application/filebucket.rb +6 -1
  8. data/lib/puppet/application/ssl.rb +102 -55
  9. data/lib/puppet/configurer.rb +8 -7
  10. data/lib/puppet/defaults.rb +3 -1
  11. data/lib/puppet/file_system.rb +24 -4
  12. data/lib/puppet/file_system/file_impl.rb +25 -0
  13. data/lib/puppet/file_system/jruby.rb +23 -0
  14. data/lib/puppet/file_system/windows.rb +84 -0
  15. data/lib/puppet/indirector/rest.rb +4 -2
  16. data/lib/puppet/loaders.rb +1 -0
  17. data/lib/puppet/network/http.rb +1 -0
  18. data/lib/puppet/network/http/base_pool.rb +18 -0
  19. data/lib/puppet/network/http/connection.rb +49 -17
  20. data/lib/puppet/network/http/nocache_pool.rb +9 -4
  21. data/lib/puppet/network/http/pool.rb +10 -11
  22. data/lib/puppet/network/http/session.rb +3 -2
  23. data/lib/puppet/network/http_pool.rb +32 -0
  24. data/lib/puppet/pops/loader/generic_plan_instantiator.rb +28 -0
  25. data/lib/puppet/pops/loader/loader_paths.rb +46 -10
  26. data/lib/puppet/pops/loader/module_loaders.rb +10 -3
  27. data/lib/puppet/provider/file/windows.rb +49 -1
  28. data/lib/puppet/provider/package/windows.rb +5 -1
  29. data/lib/puppet/reports/http.rb +2 -1
  30. data/lib/puppet/rest/client.rb +7 -3
  31. data/lib/puppet/rest/routes.rb +9 -44
  32. data/lib/puppet/ssl.rb +6 -0
  33. data/lib/puppet/ssl/error.rb +26 -0
  34. data/lib/puppet/ssl/host.rb +9 -92
  35. data/lib/puppet/ssl/ssl_context.rb +30 -0
  36. data/lib/puppet/ssl/ssl_provider.rb +232 -0
  37. data/lib/puppet/ssl/state_machine.rb +261 -0
  38. data/lib/puppet/ssl/validator.rb +1 -0
  39. data/lib/puppet/ssl/validator/default_validator.rb +1 -0
  40. data/lib/puppet/ssl/validator/no_validator.rb +2 -0
  41. data/lib/puppet/ssl/verifier.rb +134 -0
  42. data/lib/puppet/ssl/verifier_adapter.rb +48 -0
  43. data/lib/puppet/test/test_helper.rb +2 -1
  44. data/lib/puppet/type/exec.rb +30 -6
  45. data/lib/puppet/type/file/mode.rb +6 -1
  46. data/lib/puppet/type/file/source.rb +2 -2
  47. data/lib/puppet/type/filebucket.rb +12 -8
  48. data/lib/puppet/type/user.rb +14 -1
  49. data/lib/puppet/util/connection.rb +10 -5
  50. data/lib/puppet/util/feature.rb +11 -2
  51. data/lib/puppet/util/http_proxy.rb +3 -2
  52. data/lib/puppet/util/pidlock.rb +1 -1
  53. data/lib/puppet/util/ssl.rb +1 -10
  54. data/lib/puppet/util/windows/security.rb +29 -8
  55. data/lib/puppet/version.rb +1 -1
  56. data/lib/puppet/x509.rb +7 -0
  57. data/lib/puppet/x509/cert_provider.rb +286 -0
  58. data/lib/puppet/x509/pem_store.rb +55 -0
  59. data/locales/ja/puppet.po +740 -590
  60. data/locales/puppet.pot +433 -208
  61. data/man/man5/puppet.conf.5 +6 -3
  62. data/man/man8/puppet-agent.8 +1 -1
  63. data/man/man8/puppet-apply.8 +1 -1
  64. data/man/man8/puppet-catalog.8 +1 -1
  65. data/man/man8/puppet-config.8 +1 -1
  66. data/man/man8/puppet-describe.8 +1 -1
  67. data/man/man8/puppet-device.8 +1 -1
  68. data/man/man8/puppet-doc.8 +1 -1
  69. data/man/man8/puppet-epp.8 +1 -1
  70. data/man/man8/puppet-facts.8 +1 -1
  71. data/man/man8/puppet-filebucket.8 +6 -2
  72. data/man/man8/puppet-generate.8 +1 -1
  73. data/man/man8/puppet-help.8 +1 -1
  74. data/man/man8/puppet-key.8 +1 -1
  75. data/man/man8/puppet-lookup.8 +1 -1
  76. data/man/man8/puppet-man.8 +1 -1
  77. data/man/man8/puppet-module.8 +1 -1
  78. data/man/man8/puppet-node.8 +1 -1
  79. data/man/man8/puppet-parser.8 +1 -1
  80. data/man/man8/puppet-plugin.8 +1 -1
  81. data/man/man8/puppet-report.8 +1 -1
  82. data/man/man8/puppet-resource.8 +1 -1
  83. data/man/man8/puppet-script.8 +1 -1
  84. data/man/man8/puppet-ssl.8 +5 -1
  85. data/man/man8/puppet-status.8 +1 -1
  86. data/man/man8/puppet.8 +2 -2
  87. data/spec/fixtures/ssl/127.0.0.1-key.pem +67 -0
  88. data/spec/fixtures/ssl/127.0.0.1.pem +48 -0
  89. data/spec/fixtures/ssl/bad-basic-constraints.pem +59 -0
  90. data/spec/fixtures/ssl/bad-int-basic-constraints.pem +59 -0
  91. data/spec/fixtures/ssl/ca.pem +59 -0
  92. data/spec/fixtures/ssl/crl.pem +30 -0
  93. data/spec/fixtures/ssl/encrypted-key.pem +70 -0
  94. data/spec/fixtures/ssl/intermediate-agent-crl.pem +31 -0
  95. data/spec/fixtures/ssl/intermediate-agent.pem +60 -0
  96. data/spec/fixtures/ssl/intermediate-crl.pem +36 -0
  97. data/spec/fixtures/ssl/intermediate.pem +60 -0
  98. data/spec/fixtures/ssl/netlock-arany-utf8.pem +23 -0
  99. data/spec/fixtures/ssl/pluto-key.pem +67 -0
  100. data/spec/fixtures/ssl/pluto.pem +44 -0
  101. data/spec/fixtures/ssl/request-key.pem +67 -0
  102. data/spec/fixtures/ssl/request.pem +39 -0
  103. data/spec/fixtures/ssl/revoked-key.pem +67 -0
  104. data/spec/fixtures/ssl/revoked.pem +44 -0
  105. data/spec/fixtures/ssl/signed-key.pem +67 -0
  106. data/spec/fixtures/ssl/signed.pem +44 -0
  107. data/spec/fixtures/ssl/tampered-cert.pem +44 -0
  108. data/spec/fixtures/ssl/tampered-csr.pem +39 -0
  109. data/spec/integration/network/http_pool_spec.rb +222 -0
  110. data/spec/integration/provider/file/windows_spec.rb +162 -0
  111. data/spec/integration/rest/client_spec.rb +73 -0
  112. data/spec/integration/type/file_spec.rb +0 -19
  113. data/spec/lib/puppet/test_ca.rb +87 -50
  114. data/spec/lib/puppet_spec/fixtures.rb +20 -0
  115. data/spec/lib/puppet_spec/https.rb +84 -0
  116. data/spec/unit/application/agent_spec.rb +29 -30
  117. data/spec/unit/application/device_spec.rb +12 -49
  118. data/spec/unit/application/ssl_spec.rb +24 -38
  119. data/spec/unit/configurer_spec.rb +11 -11
  120. data/spec/unit/file_system/uniquefile_spec.rb +6 -0
  121. data/spec/unit/file_system_spec.rb +214 -0
  122. data/spec/unit/indirector/rest_spec.rb +3 -3
  123. data/spec/unit/network/http/connection_spec.rb +30 -90
  124. data/spec/unit/network/http/factory_spec.rb +1 -0
  125. data/spec/unit/network/http/nocache_pool_spec.rb +8 -8
  126. data/spec/unit/network/http/pool_spec.rb +63 -33
  127. data/spec/unit/network/http/session_spec.rb +8 -1
  128. data/spec/unit/network/http_pool_spec.rb +36 -0
  129. data/spec/unit/pops/loaders/loader_spec.rb +26 -1
  130. data/spec/unit/provider/package/windows_spec.rb +12 -1
  131. data/spec/unit/reports/http_spec.rb +7 -7
  132. data/spec/unit/rest/client_spec.rb +4 -6
  133. data/spec/unit/ssl/host_spec.rb +39 -33
  134. data/spec/unit/ssl/ssl_provider_spec.rb +428 -0
  135. data/spec/unit/ssl/state_machine_spec.rb +502 -0
  136. data/spec/unit/ssl/verifier_spec.rb +123 -0
  137. data/spec/unit/type/exec_spec.rb +63 -0
  138. data/spec/unit/type/file/source_spec.rb +5 -5
  139. data/spec/unit/type/filebucket_spec.rb +8 -6
  140. data/spec/unit/util/feature_spec.rb +2 -2
  141. data/spec/unit/util/storage_spec.rb +19 -19
  142. data/spec/unit/x509/cert_provider_spec.rb +527 -0
  143. data/spec/unit/x509/pem_store_spec.rb +160 -0
  144. data/tasks/generate_cert_fixtures.rake +158 -0
  145. metadata +78 -4
  146. data/MAINTAINERS +0 -47
  147. data/lib/puppet/rest/ssl_context.rb +0 -13
@@ -0,0 +1,222 @@
1
+ require 'spec_helper'
2
+ require 'puppet_spec/https'
3
+ require 'puppet_spec/files'
4
+ require 'puppet/network/http_pool'
5
+
6
+ describe Puppet::Network::HttpPool, unless: Puppet::Util::Platform.jruby? do
7
+ include PuppetSpec::Files
8
+
9
+ before :all do
10
+ WebMock.disable!
11
+ end
12
+
13
+ after :all do
14
+ WebMock.enable!
15
+ end
16
+
17
+ before :each do
18
+ # make sure we don't take too long
19
+ Puppet[:http_connect_timeout] = '5s'
20
+ end
21
+
22
+ let(:hostname) { '127.0.0.1' }
23
+ let(:wrong_hostname) { 'localhost' }
24
+ let(:server) { PuppetSpec::HTTPSServer.new }
25
+
26
+ context "when calling deprecated HttpPool methods" do
27
+ let(:ssl_host) {
28
+ # use server's cert/key as the client cert/key
29
+ host = Puppet::SSL::Host.new
30
+ host.key = Puppet::SSL::Key.from_instance(server.server_key, host.name)
31
+ host.certificate = Puppet::SSL::Certificate.from_instance(server.server_cert, host.name)
32
+ host
33
+ }
34
+
35
+ before(:each) do
36
+ ssldir = tmpdir('http_pool')
37
+ Puppet[:ssldir] = ssldir
38
+ Puppet.settings.use(:main, :ssl)
39
+
40
+ File.write(Puppet[:localcacert], server.ca_cert.to_pem)
41
+ File.write(Puppet[:hostcrl], server.ca_crl.to_pem)
42
+ File.write(Puppet[:hostcert], server.server_cert.to_pem)
43
+ File.write(Puppet[:hostprivkey], server.server_key.to_pem)
44
+ end
45
+
46
+ # Can't use `around(:each)` because it will cause ssl_host to be
47
+ # created outside of any rspec example, and $confdir won't be set
48
+ before(:each) do
49
+ Puppet.push_context(ssl_host: ssl_host)
50
+ end
51
+
52
+ after (:each) do
53
+ Puppet.pop_context
54
+ end
55
+
56
+ def connection(host, port)
57
+ Puppet::Network::HttpPool.http_instance(host, port, use_ssl: true)
58
+ end
59
+
60
+ shared_examples_for 'HTTPS client' do
61
+ it "connects over SSL" do
62
+ server.start_server do |port|
63
+ http = connection(hostname, port)
64
+ res = http.get('/')
65
+ expect(res.code).to eq('200')
66
+ end
67
+ end
68
+
69
+ it "raises if the server's cert doesn't match the hostname we connected to" do
70
+ server.start_server do |port|
71
+ http = connection(wrong_hostname, port)
72
+ expect {
73
+ http.get('/')
74
+ }.to raise_error { |err|
75
+ pending("PUP-8213") if RUBY_VERSION.to_f >= 2.4
76
+
77
+ expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)
78
+ expect(err.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
79
+
80
+ md = err.message.match(/expected one of (.+)/)
81
+ expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
82
+ }
83
+ end
84
+ end
85
+
86
+ it "raises if the server's CA is unknown" do
87
+ # File must exist and by not empty so DefaultValidator doesn't
88
+ # downgrade to VERIFY_NONE, so use a different CA that didn't
89
+ # issue the server's cert
90
+ capath = tmpfile('empty')
91
+ File.write(capath, cert_fixture('netlock-arany-utf8.pem'))
92
+ Puppet[:localcacert] = capath
93
+ Puppet[:certificate_revocation] = false
94
+
95
+ server.start_server do |port|
96
+ http = connection(hostname, port)
97
+ expect {
98
+ http.get('/')
99
+ }.to raise_error(Puppet::Error,
100
+ %r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
101
+ end
102
+ end
103
+ end
104
+
105
+
106
+ context "when using single use HTTPS connections" do
107
+ include_examples 'HTTPS client'
108
+ end
109
+
110
+ context "when using persistent HTTPS connections" do
111
+ around :each do |example|
112
+ pool = Puppet::Network::HTTP::Pool.new
113
+ Puppet.override(:http_pool => pool) do
114
+ example.run
115
+ end
116
+ pool.close
117
+ end
118
+
119
+ include_examples 'HTTPS client'
120
+ end
121
+ end
122
+
123
+ context "when calling HttpPool.connection method" do
124
+ let(:ssl) { Puppet::SSL::SSLProvider.new }
125
+ let(:ssl_context) { ssl.create_root_context(cacerts: [server.ca_cert], crls: [server.ca_crl]) }
126
+
127
+ def connection(host, port, ssl_context:)
128
+ Puppet::Network::HttpPool.connection(host, port, ssl_context: ssl_context)
129
+ end
130
+
131
+ # Configure the server's SSLContext to require a client certificate. The `client_ca`
132
+ # setting allows the server to advertise which client CAs it will accept.
133
+ def require_client_certs(ctx)
134
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
135
+ ctx.client_ca = [cert_fixture('ca.pem')]
136
+ end
137
+
138
+ it "connects over SSL" do
139
+ server.start_server do |port|
140
+ http = connection(hostname, port, ssl_context: ssl_context)
141
+ res = http.get('/')
142
+ expect(res.code).to eq('200')
143
+ end
144
+ end
145
+
146
+ it "raises if the server's cert doesn't match the hostname we connected to" do
147
+ server.start_server do |port|
148
+ http = connection(wrong_hostname, port, ssl_context: ssl_context)
149
+ expect {
150
+ http.get('/')
151
+ }.to raise_error { |err|
152
+ expect(err).to be_instance_of(Puppet::SSL::CertMismatchError)
153
+ expect(err.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
154
+
155
+ md = err.message.match(/expected one of (.+)/)
156
+ expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
157
+ }
158
+ end
159
+ end
160
+
161
+ it "raises if the server's CA is unknown" do
162
+ server.start_server do |port|
163
+ ssl_context = ssl.create_root_context(cacerts: [cert_fixture('netlock-arany-utf8.pem')],
164
+ crls: [server.ca_crl])
165
+ http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
166
+ expect {
167
+ http.get('/')
168
+ }.to raise_error(Puppet::Error,
169
+ %r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
170
+ end
171
+ end
172
+
173
+ it "warns when client has an incomplete client cert chain" do
174
+ Puppet.expects(:warning).with("The issuer '/CN=Test CA Agent Subauthority' of certificate '/CN=pluto' cannot be found locally")
175
+
176
+ pluto = cert_fixture('pluto.pem')
177
+
178
+ ssl_context = ssl.create_context(
179
+ cacerts: [server.ca_cert], crls: [server.ca_crl],
180
+ client_cert: pluto, private_key: key_fixture('pluto-key.pem')
181
+ )
182
+
183
+ # verify client has incomplete chain
184
+ expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto.to_der])
185
+
186
+ # force server to require (not request) client certs
187
+ ctx_proc = -> (ctx) {
188
+ require_client_certs(ctx)
189
+
190
+ # server needs to trust the client's intermediate CA to complete the client's chain
191
+ ctx.cert_store.add_cert(cert_fixture('intermediate-agent.pem'))
192
+ }
193
+
194
+ server.start_server(ctx_proc: ctx_proc) do |port|
195
+ http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
196
+ res = http.get('/')
197
+ expect(res.code).to eq('200')
198
+ end
199
+ end
200
+
201
+ it "sends a complete client cert chain" do
202
+ pluto = cert_fixture('pluto.pem')
203
+ client_ca = cert_fixture('intermediate-agent.pem')
204
+
205
+ ssl_context = ssl.create_context(
206
+ cacerts: [server.ca_cert, client_ca],
207
+ crls: [server.ca_crl, crl_fixture('intermediate-agent-crl.pem')],
208
+ client_cert: pluto,
209
+ private_key: key_fixture('pluto-key.pem')
210
+ )
211
+
212
+ # verify client has complete chain from leaf to root
213
+ expect(ssl_context.client_chain.map(&:to_der)).to eq([pluto, client_ca, server.ca_cert].map(&:to_der))
214
+
215
+ server.start_server(ctx_proc: method(:require_client_certs)) do |port|
216
+ http = Puppet::Network::HttpPool.connection(hostname, port, ssl_context: ssl_context)
217
+ res = http.get('/')
218
+ expect(res.code).to eq('200')
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,162 @@
1
+ require 'spec_helper'
2
+ require 'puppet_spec/compiler'
3
+ require 'puppet_spec/files'
4
+
5
+ # For some reason the provider test will not filter out on windows when using the
6
+ # :if => Puppet.features.microsoft_windows? method of filtering the tests.
7
+ if Puppet.features.microsoft_windows?
8
+ require 'puppet/util/windows'
9
+ describe Puppet::Type.type(:file).provider(:windows), '(integration)' do
10
+ include PuppetSpec::Compiler
11
+ include PuppetSpec::Files
12
+
13
+ def create_temp_file(owner_sid, group_sid, initial_mode)
14
+ tmp_file = tmpfile('filewindowsprovider')
15
+ File.delete(tmp_file) if File.exist?(tmp_file)
16
+ File.open(tmp_file, 'w') { |file| file.write("rspec test") }
17
+
18
+ # There are other tests to ensure that these methods do indeed
19
+ # set the owner and group. Therefore it's ok to depend on them
20
+ # here
21
+ Puppet::Util::Windows::Security.set_owner(owner_sid, tmp_file) unless owner_sid.nil?
22
+ Puppet::Util::Windows::Security.set_group(group_sid, tmp_file) unless group_sid.nil?
23
+ # Pretend we are managing the owner and group to FORCE this mode, even if it's "bad"
24
+ Puppet::Util::Windows::Security.set_mode(initial_mode.to_i(8), tmp_file, true, true, true) unless initial_mode.nil?
25
+
26
+ tmp_file
27
+ end
28
+
29
+ def strip_sticky(value)
30
+ # For the purposes of these tests we don't care about the extra-ace bit in modes
31
+ # This function removes it
32
+ value & ~Puppet::Util::Windows::Security::S_IEXTRA
33
+ end
34
+
35
+ sids = {
36
+ :system => Puppet::Util::Windows::SID::LocalSystem,
37
+ :administrators => Puppet::Util::Windows::SID::BuiltinAdministrators,
38
+ :users => Puppet::Util::Windows::SID::BuiltinUsers,
39
+ :power_users => Puppet::Util::Windows::SID::PowerUsers,
40
+ :none => Puppet::Util::Windows::SID::Nobody,
41
+ :everyone => Puppet::Util::Windows::SID::Everyone
42
+ }
43
+
44
+ # Testcase Hash options
45
+ # create_* : These options are used when creating the initial test file
46
+ # create_owner (Required!)
47
+ # create_group (Required!)
48
+ # create_mode
49
+ #
50
+ # manifest_* : These options are used to craft the manifest which is applied to the test file after createion
51
+ # manifest_owner,
52
+ # manifest_group,
53
+ # manifest_mode (Required!)
54
+ #
55
+ # actual_* : These options are used to check the _actual_ values as opposed to the munged values from puppet
56
+ # actual_mode (Uses manifest_mode for checks if not set)
57
+
58
+ RSpec.shared_examples "a mungable file resource" do |testcase|
59
+
60
+ before(:each) do
61
+ @tmp_file = create_temp_file(sids[testcase[:create_owner]], sids[testcase[:create_group]], testcase[:create_mode])
62
+ raise "Could not create temporary file" if @tmp_file.nil?
63
+ end
64
+
65
+ after(:each) do
66
+ File.delete(@tmp_file) if File.exist?(@tmp_file)
67
+ end
68
+
69
+ context_name = "With initial owner '#{testcase[:create_owner]}' and initial group '#{testcase[:create_owner]}'"
70
+ context_name += " and initial mode of '#{testcase[:create_mode]}'" unless testcase[:create_mode].nil?
71
+ context_name += " and a mode of '#{testcase[:manifest_mode]}' in the manifest"
72
+ context_name += " and an owner of '#{testcase[:manifest_owner]}' in the manifest" unless testcase[:manifest_owner].nil?
73
+ context_name += " and a group of '#{testcase[:manifest_group]}' in the manifest" unless testcase[:manifest_group].nil?
74
+
75
+ context context_name do
76
+ is_idempotent = testcase[:is_idempotent].nil? || testcase[:is_idempotent]
77
+
78
+ let(:manifest) do
79
+ value = <<-MANIFEST
80
+ file { 'rspec_example':
81
+ ensure => present,
82
+ path => '#{@tmp_file}',
83
+ mode => '#{testcase[:manifest_mode]}',
84
+ MANIFEST
85
+ value += " owner => '#{testcase[:manifest_owner]}',\n" unless testcase[:manifest_owner].nil?
86
+ value += " group => '#{testcase[:manifest_group]}',\n" unless testcase[:manifest_group].nil?
87
+ value + "}"
88
+ end
89
+
90
+ it "should apply with no errors and have expected ACL" do
91
+ apply_with_error_check(manifest)
92
+ new_mode = strip_sticky(Puppet::Util::Windows::Security.get_mode(@tmp_file))
93
+ expect(new_mode.to_s(8)).to eq (testcase[:actual_mode].nil? ? testcase[:manifest_mode] : testcase[:actual_mode])
94
+ end
95
+
96
+ it "should be idempotent", :if => is_idempotent do
97
+ result = apply_with_error_check(manifest)
98
+ result = apply_with_error_check(manifest)
99
+ # Idempotent. Should be no changed resources
100
+ expect(result.changed?.count).to eq 0
101
+ end
102
+
103
+ it "should NOT be idempotent", :unless => is_idempotent do
104
+ result = apply_with_error_check(manifest)
105
+ result = apply_with_error_check(manifest)
106
+ result = apply_with_error_check(manifest)
107
+ result = apply_with_error_check(manifest)
108
+ # Not idempotent. Expect changed resources
109
+ expect(result.changed?.count).to be > 0
110
+ end
111
+ end
112
+ end
113
+
114
+ # These scenarios round-trip permissions and are idempotent
115
+ [
116
+ { :create_owner => :system, :create_group => :administrators, :manifest_mode => '760' },
117
+ { :create_owner => :administrators, :create_group => :administrators, :manifest_mode => '660' },
118
+ { :create_owner => :system, :create_group => :system, :manifest_mode => '770' },
119
+ ].each do |testcase|
120
+ # What happens if the owner and group are not managed
121
+ it_behaves_like "a mungable file resource", testcase
122
+ # What happens if the owner is managed
123
+ it_behaves_like "a mungable file resource", testcase.merge({ :manifest_owner => testcase[:create_owner]})
124
+ # What happens if the group is managed
125
+ it_behaves_like "a mungable file resource", testcase.merge({ :manifest_group => testcase[:create_group]})
126
+ # What happens if both the owner and group are managed
127
+ it_behaves_like "a mungable file resource", testcase.merge({
128
+ :manifest_owner => testcase[:create_owner],
129
+ :manifest_group => testcase[:create_group]
130
+ })
131
+ end
132
+
133
+ # SYSTEM is special in that when specifying less than mode 7, the owner and/or group MUST be managed
134
+ # otherwise it's munged to 7 behind the scenes and is not idempotent
135
+ both_system_testcase = { :create_owner => :system, :create_group => :system, :manifest_mode => '660', :actual_mode => '770', :is_idempotent => false }
136
+ # What happens if the owner and group are not managed
137
+ it_behaves_like "a mungable file resource", both_system_testcase.merge({ :is_idempotent => true })
138
+ # What happens if the owner is managed
139
+ it_behaves_like "a mungable file resource", both_system_testcase.merge({ :manifest_owner => both_system_testcase[:create_owner]})
140
+ # What happens if the group is managed
141
+ it_behaves_like "a mungable file resource", both_system_testcase.merge({ :manifest_group => both_system_testcase[:create_group]})
142
+
143
+ # However when we manage SYSTEM explicitly, then the modes lower than 7 stick and the file provider
144
+ # assumes it's insync (i.e. idempotent)
145
+ it_behaves_like "a mungable file resource", both_system_testcase.merge({
146
+ :manifest_owner => both_system_testcase[:create_owner],
147
+ :manifest_group => both_system_testcase[:create_group],
148
+ :actual_mode => both_system_testcase[:manifest_mode],
149
+ :is_idempotent => true
150
+ })
151
+
152
+ # What happens if we _create_ a file that SYSTEM is a part of, and is Full Control, but the manifest says it should not be Full Control
153
+ # Behind the scenes the mode should be changed to 7 and be idempotent
154
+ [
155
+ { :create_owner => :system, :create_group => :system, :manifest_mode => '660' },
156
+ { :create_owner => :administrators, :create_group => :system, :manifest_mode => '760' },
157
+ { :create_owner => :system, :create_group => :administrators, :manifest_mode => '670' },
158
+ ].each do |testcase|
159
+ it_behaves_like "a mungable file resource", testcase.merge({ :create_mode => '770', :actual_mode => '770'})
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'puppet_spec/https'
3
+ require 'puppet_spec/files'
4
+ require 'puppet/rest/client'
5
+
6
+ describe Puppet::Rest::Client, unless: Puppet::Util::Platform.jruby? do
7
+ include PuppetSpec::Files
8
+
9
+ before :all do
10
+ WebMock.disable!
11
+ end
12
+
13
+ after :all do
14
+ WebMock.enable!
15
+ end
16
+
17
+ let(:hostname) { '127.0.0.1' }
18
+ let(:wrong_hostname) { 'localhost' }
19
+ let(:server) { PuppetSpec::HTTPSServer.new }
20
+ let(:ssl) { Puppet::SSL::SSLProvider.new }
21
+ let(:ssl_context) { ssl.create_root_context(cacerts: [server.ca_cert], crls: [server.ca_crl]) }
22
+ let(:client) { Puppet::Rest::Client.new(ssl_context: ssl_context) }
23
+
24
+ before(:each) do
25
+ localcacert = tmpfile('rest_client')
26
+ File.write(localcacert, server.ca_cert.to_pem)
27
+ Puppet[:ssl_client_ca_auth] = localcacert
28
+
29
+ # make sure we don't take too long
30
+ Puppet[:http_connect_timeout] = '5s'
31
+ end
32
+
33
+ it "connects over SSL" do
34
+ server.start_server do |port|
35
+ uri = URI.parse("https://#{hostname}:#{port}/blah")
36
+ expect { |b|
37
+ client.get(uri, &b)
38
+ }.to yield_with_args('OK')
39
+ end
40
+ end
41
+
42
+ it 'provides a meaningful error message when cert validation fails' do
43
+ ssl_context = ssl.create_root_context(
44
+ cacerts: [cert_fixture('netlock-arany-utf8.pem')]
45
+ )
46
+ client = Puppet::Rest::Client.new(ssl_context: ssl_context)
47
+
48
+ server.start_server do |port|
49
+ uri = URI.parse("https://#{hostname}:#{port}/blah")
50
+ expect {
51
+ client.get(uri)
52
+ }.to raise_error(Puppet::Error,
53
+ %r{certificate verify failed.* .self signed certificate in certificate chain for /CN=Test CA.})
54
+ end
55
+ end
56
+
57
+ it 'provides valuable error message when cert names do not match' do
58
+ server.start_server do |port|
59
+ uri = URI.parse("https://#{wrong_hostname}:#{port}/blah")
60
+ expect {
61
+ client.get(uri)
62
+ }.to raise_error do |error|
63
+ pending("PUP-8213") if RUBY_VERSION.to_f >= 2.4 && !Puppet::Util::Platform.jruby?
64
+
65
+ expect(error).to be_instance_of(Puppet::SSL::CertMismatchError)
66
+ expect(error.message).to match(/\AServer hostname '#{wrong_hostname}' did not match server certificate; expected one of (.+)/)
67
+
68
+ md = error.message.match(/expected one of (.+)/)
69
+ expect(md[1].split(', ')).to contain_exactly('127.0.0.1', 'DNS:127.0.0.1', 'DNS:127.0.0.2')
70
+ end
71
+ end
72
+ end
73
+ end