puppet 6.22.1-universal-darwin → 6.23.0-universal-darwin

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 (129) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +14 -14
  3. data/ext/osx/puppet.plist +2 -0
  4. data/lib/puppet/application/agent.rb +12 -5
  5. data/lib/puppet/application/apply.rb +2 -1
  6. data/lib/puppet/application/device.rb +2 -1
  7. data/lib/puppet/application/resource.rb +2 -1
  8. data/lib/puppet/application/script.rb +2 -1
  9. data/lib/puppet/configurer/downloader.rb +2 -1
  10. data/lib/puppet/defaults.rb +5 -3
  11. data/lib/puppet/file_serving/fileset.rb +14 -2
  12. data/lib/puppet/functions/all.rb +1 -1
  13. data/lib/puppet/functions/camelcase.rb +1 -1
  14. data/lib/puppet/functions/capitalize.rb +2 -2
  15. data/lib/puppet/functions/downcase.rb +2 -2
  16. data/lib/puppet/functions/get.rb +5 -5
  17. data/lib/puppet/functions/group_by.rb +13 -5
  18. data/lib/puppet/functions/lest.rb +1 -1
  19. data/lib/puppet/functions/new.rb +100 -100
  20. data/lib/puppet/functions/partition.rb +4 -4
  21. data/lib/puppet/functions/require.rb +5 -5
  22. data/lib/puppet/functions/sort.rb +3 -3
  23. data/lib/puppet/functions/tree_each.rb +7 -9
  24. data/lib/puppet/functions/type.rb +4 -4
  25. data/lib/puppet/functions/upcase.rb +2 -2
  26. data/lib/puppet/http/resolver/server_list.rb +15 -4
  27. data/lib/puppet/http/service/compiler.rb +69 -0
  28. data/lib/puppet/http/service/file_server.rb +2 -1
  29. data/lib/puppet/indirector/catalog/compiler.rb +1 -0
  30. data/lib/puppet/indirector/file_metadata/rest.rb +1 -0
  31. data/lib/puppet/parser/functions/fqdn_rand.rb +14 -6
  32. data/lib/puppet/pops/types/p_sem_ver_type.rb +8 -2
  33. data/lib/puppet/pops/types/p_sensitive_type.rb +10 -0
  34. data/lib/puppet/provider/package/nim.rb +11 -6
  35. data/lib/puppet/provider/service/systemd.rb +13 -3
  36. data/lib/puppet/provider/service/windows.rb +38 -0
  37. data/lib/puppet/provider/user/directoryservice.rb +25 -12
  38. data/lib/puppet/reference/configuration.rb +1 -1
  39. data/lib/puppet/transaction/additional_resource_generator.rb +1 -1
  40. data/lib/puppet/type/file/selcontext.rb +1 -1
  41. data/lib/puppet/type/file.rb +19 -1
  42. data/lib/puppet/type/service.rb +18 -38
  43. data/lib/puppet/type/tidy.rb +21 -2
  44. data/lib/puppet/type/user.rb +38 -20
  45. data/lib/puppet/util/selinux.rb +30 -4
  46. data/lib/puppet/version.rb +1 -1
  47. data/locales/puppet.pot +109 -101
  48. data/man/man5/puppet.conf.5 +272 -252
  49. data/man/man8/puppet-agent.8 +1 -1
  50. data/man/man8/puppet-apply.8 +1 -1
  51. data/man/man8/puppet-catalog.8 +1 -1
  52. data/man/man8/puppet-config.8 +1 -1
  53. data/man/man8/puppet-describe.8 +1 -1
  54. data/man/man8/puppet-device.8 +1 -1
  55. data/man/man8/puppet-doc.8 +1 -1
  56. data/man/man8/puppet-epp.8 +1 -1
  57. data/man/man8/puppet-facts.8 +1 -1
  58. data/man/man8/puppet-filebucket.8 +1 -1
  59. data/man/man8/puppet-generate.8 +1 -1
  60. data/man/man8/puppet-help.8 +1 -1
  61. data/man/man8/puppet-key.8 +1 -1
  62. data/man/man8/puppet-lookup.8 +1 -1
  63. data/man/man8/puppet-man.8 +1 -1
  64. data/man/man8/puppet-module.8 +1 -1
  65. data/man/man8/puppet-node.8 +1 -1
  66. data/man/man8/puppet-parser.8 +1 -1
  67. data/man/man8/puppet-plugin.8 +1 -1
  68. data/man/man8/puppet-report.8 +1 -1
  69. data/man/man8/puppet-resource.8 +1 -1
  70. data/man/man8/puppet-script.8 +1 -1
  71. data/man/man8/puppet-ssl.8 +1 -1
  72. data/man/man8/puppet-status.8 +1 -1
  73. data/man/man8/puppet.8 +2 -2
  74. data/spec/fixtures/ssl/127.0.0.1-key.pem +107 -57
  75. data/spec/fixtures/ssl/127.0.0.1.pem +52 -31
  76. data/spec/fixtures/ssl/bad-basic-constraints.pem +57 -35
  77. data/spec/fixtures/ssl/bad-int-basic-constraints.pem +57 -35
  78. data/spec/fixtures/ssl/ca.pem +57 -35
  79. data/spec/fixtures/ssl/crl.pem +28 -18
  80. data/spec/fixtures/ssl/ec-key.pem +11 -11
  81. data/spec/fixtures/ssl/ec.pem +33 -24
  82. data/spec/fixtures/ssl/encrypted-ec-key.pem +12 -12
  83. data/spec/fixtures/ssl/encrypted-key.pem +108 -58
  84. data/spec/fixtures/ssl/intermediate-agent-crl.pem +28 -19
  85. data/spec/fixtures/ssl/intermediate-agent.pem +57 -36
  86. data/spec/fixtures/ssl/intermediate-crl.pem +31 -21
  87. data/spec/fixtures/ssl/intermediate.pem +57 -36
  88. data/spec/fixtures/ssl/pluto-key.pem +107 -57
  89. data/spec/fixtures/ssl/pluto.pem +52 -30
  90. data/spec/fixtures/ssl/request-key.pem +107 -57
  91. data/spec/fixtures/ssl/request.pem +47 -26
  92. data/spec/fixtures/ssl/revoked-key.pem +107 -57
  93. data/spec/fixtures/ssl/revoked.pem +52 -30
  94. data/spec/fixtures/ssl/signed-key.pem +107 -57
  95. data/spec/fixtures/ssl/signed.pem +52 -30
  96. data/spec/fixtures/ssl/tampered-cert.pem +52 -30
  97. data/spec/fixtures/ssl/tampered-csr.pem +47 -26
  98. data/spec/fixtures/ssl/unknown-127.0.0.1-key.pem +107 -57
  99. data/spec/fixtures/ssl/unknown-127.0.0.1.pem +50 -29
  100. data/spec/fixtures/ssl/unknown-ca-key.pem +107 -57
  101. data/spec/fixtures/ssl/unknown-ca.pem +55 -33
  102. data/spec/integration/application/resource_spec.rb +30 -0
  103. data/spec/lib/puppet/test_ca.rb +2 -2
  104. data/spec/unit/application/agent_spec.rb +7 -2
  105. data/spec/unit/configurer/downloader_spec.rb +6 -0
  106. data/spec/unit/configurer_spec.rb +23 -0
  107. data/spec/unit/file_serving/fileset_spec.rb +60 -0
  108. data/spec/unit/gettext/config_spec.rb +12 -0
  109. data/spec/unit/http/service/compiler_spec.rb +123 -0
  110. data/spec/unit/indirector/catalog/compiler_spec.rb +14 -10
  111. data/spec/unit/parser/functions/fqdn_rand_spec.rb +15 -1
  112. data/spec/unit/pops/types/p_sem_ver_type_spec.rb +18 -0
  113. data/spec/unit/pops/types/p_sensitive_type_spec.rb +18 -0
  114. data/spec/unit/provider/package/nim_spec.rb +42 -0
  115. data/spec/unit/provider/service/init_spec.rb +1 -0
  116. data/spec/unit/provider/service/openwrt_spec.rb +3 -1
  117. data/spec/unit/provider/service/systemd_spec.rb +42 -8
  118. data/spec/unit/provider/service/windows_spec.rb +202 -0
  119. data/spec/unit/provider/user/directoryservice_spec.rb +67 -35
  120. data/spec/unit/ssl/state_machine_spec.rb +19 -5
  121. data/spec/unit/transaction/additional_resource_generator_spec.rb +0 -2
  122. data/spec/unit/transaction_spec.rb +18 -20
  123. data/spec/unit/type/file/selinux_spec.rb +3 -3
  124. data/spec/unit/type/service_spec.rb +59 -188
  125. data/spec/unit/type/tidy_spec.rb +17 -7
  126. data/spec/unit/type/user_spec.rb +45 -0
  127. data/spec/unit/util/selinux_spec.rb +87 -16
  128. data/tasks/generate_cert_fixtures.rake +2 -2
  129. metadata +4 -2
@@ -1,3 +1,4 @@
1
+
1
2
  # coding: utf-8
2
3
  require 'spec_helper'
3
4
  require 'puppet/http'
@@ -258,6 +259,128 @@ describe Puppet::HTTP::Service::Compiler do
258
259
  end
259
260
  end
260
261
 
262
+ context 'when posting for a v4 catalog' do
263
+ let(:uri) {"https://compiler.example.com:8140/puppet/v4/catalog"}
264
+ let(:persistence) {{ facts: true, catalog: true }}
265
+ let(:facts) {{ 'foo' => 'bar' }}
266
+ let(:trusted_facts) {{}}
267
+ let(:uuid) { "ec3d2844-b236-4287-b0ad-632fbb4d1ff0" }
268
+ let(:job_id) { "1" }
269
+ let(:payload) {{
270
+ environment: environment,
271
+ persistence: persistence,
272
+ facts: facts,
273
+ trusted_facts: trusted_facts,
274
+ transaction_uuid: uuid,
275
+ job_id: job_id,
276
+ options: {
277
+ prefer_requested_environment: false,
278
+ capture_logs: false
279
+ }
280
+ }}
281
+ let(:serialized_catalog) {{ 'catalog' => catalog.to_data_hash }.to_json}
282
+ let(:catalog_response) {{ body: serialized_catalog, headers: {'Content-Type' => formatter.mime }}}
283
+
284
+ it 'includes default HTTP headers' do
285
+ stub_request(:post, uri).with do |request|
286
+ expect(request.headers).to include({'X-Puppet-Version' => /./, 'User-Agent' => /./})
287
+ expect(request.headers).to_not include('X-Puppet-Profiling')
288
+ end.to_return(**catalog_response)
289
+
290
+ subject.post_catalog4(certname, **payload)
291
+ end
292
+
293
+ it 'defaults the server and port based on settings' do
294
+ Puppet[:server] = 'compiler2.example.com'
295
+ Puppet[:serverport] = 8141
296
+
297
+ stub_request(:post, "https://compiler2.example.com:8141/puppet/v4/catalog")
298
+ .to_return(**catalog_response)
299
+
300
+ subject.post_catalog4(certname, **payload)
301
+ end
302
+
303
+ it 'includes puppet headers set via the :http_extra_headers and :profile settings' do
304
+ stub_request(:post, uri).with(headers: {'Example-Header' => 'real-thing', 'another' => 'thing', 'X-Puppet-Profiling' => 'true'}).
305
+ to_return(**catalog_response)
306
+
307
+ Puppet[:http_extra_headers] = 'Example-Header:real-thing,another:thing'
308
+ Puppet[:profile] = true
309
+
310
+ subject.post_catalog4(certname, **payload)
311
+ end
312
+
313
+ it 'returns a deserialized catalog' do
314
+ stub_request(:post, uri)
315
+ .to_return(**catalog_response)
316
+
317
+ _, cat, _ = subject.post_catalog4(certname, **payload)
318
+ expect(cat).to be_a(Puppet::Resource::Catalog)
319
+ expect(cat.name).to eq(certname)
320
+ end
321
+
322
+ it 'returns the request response' do
323
+ stub_request(:post, uri)
324
+ .to_return(**catalog_response)
325
+
326
+ resp, _, _ = subject.post_catalog4(certname, **payload)
327
+ expect(resp).to be_a(Puppet::HTTP::Response)
328
+ end
329
+
330
+ it 'raises a response error if unsuccessful' do
331
+ stub_request(:post, uri)
332
+ .to_return(status: [500, "Server Error"])
333
+
334
+ expect {
335
+ subject.post_catalog4(certname, **payload)
336
+ }.to raise_error do |err|
337
+ expect(err).to be_an_instance_of(Puppet::HTTP::ResponseError)
338
+ expect(err.message).to eq('Server Error')
339
+ expect(err.response.code).to eq(500)
340
+ end
341
+ end
342
+
343
+ it 'raises a response error when server response is not JSON' do
344
+ stub_request(:post, uri)
345
+ .to_return(body: "this isn't valid JSON", headers: {'Content-Type' => 'application/json'})
346
+
347
+ expect {
348
+ subject.post_catalog4(certname, **payload)
349
+ }.to raise_error do |err|
350
+ expect(err).to be_an_instance_of(Puppet::HTTP::SerializationError)
351
+ expect(err.message).to match(/Failed to deserialize catalog from puppetserver response/)
352
+ end
353
+ end
354
+
355
+ it 'raises a response error when server response a JSON serialized catalog' do
356
+ stub_request(:post, uri)
357
+ .to_return(body: {oops: 'bad response data'}.to_json, headers: {'Content-Type' => 'application/json'})
358
+
359
+ expect {
360
+ subject.post_catalog4(certname, **payload)
361
+ }.to raise_error do |err|
362
+ expect(err).to be_an_instance_of(Puppet::HTTP::SerializationError)
363
+ expect(err.message).to match(/Failed to deserialize catalog from puppetserver response/)
364
+ end
365
+ end
366
+
367
+ it 'raises ArgumentError when the `persistence` hash does not contain required keys' do
368
+ payload[:persistence].delete(:facts)
369
+ expect { subject.post_catalog4(certname, **payload) }.to raise_error do |err|
370
+ expect(err).to be_an_instance_of(ArgumentError)
371
+ expect(err.message).to match(/The 'persistence' hash is missing the keys: facts/)
372
+ end
373
+ end
374
+
375
+ it 'raises ArgumentError when `facts` are not a Hash' do
376
+ payload[:facts] = Puppet::Node::Facts.new(certname)
377
+ expect { subject.post_catalog4(certname, **payload) }.to raise_error do |err|
378
+ expect(err).to be_an_instance_of(ArgumentError)
379
+ expect(err.message).to match(/Facts must be a Hash not a Puppet::Node::Facts/)
380
+ end
381
+ end
382
+ end
383
+
261
384
  context 'when getting a node' do
262
385
  let(:uri) { %r{/puppet/v3/node/ziggy} }
263
386
  let(:node_response) { { body: formatter.render(node), headers: {'Content-Type' => formatter.mime } } }
@@ -909,9 +909,10 @@ describe Puppet::Resource::Catalog::Compiler do
909
909
  it "inlines child metadata" do
910
910
  catalog = compile_to_catalog(<<-MANIFEST, node)
911
911
  file { '#{path}':
912
- ensure => directory,
913
- recurse => true,
914
- source => '#{source_dir}'
912
+ ensure => directory,
913
+ recurse => true,
914
+ source => '#{source_dir}',
915
+ max_files => 1234,
915
916
  }
916
917
  MANIFEST
917
918
 
@@ -925,6 +926,7 @@ describe Puppet::Resource::Catalog::Compiler do
925
926
  :source_permissions => :ignore,
926
927
  :recurse => true,
927
928
  :recurselimit => nil,
929
+ :max_files => 1234,
928
930
  :ignore => nil,
929
931
  }
930
932
  expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, options).and_return([metadata, child_metadata])
@@ -938,14 +940,15 @@ describe Puppet::Resource::Catalog::Compiler do
938
940
  it "uses resource parameters when inlining metadata" do
939
941
  catalog = compile_to_catalog(<<-MANIFEST, node)
940
942
  file { '#{path}':
941
- ensure => directory,
942
- recurse => true,
943
- source => '#{source_dir}',
944
- checksum => sha256,
943
+ ensure => directory,
944
+ recurse => true,
945
+ source => '#{source_dir}',
946
+ checksum => sha256,
945
947
  source_permissions => use_when_creating,
946
- recurselimit => 2,
947
- ignore => 'foo.+',
948
- links => follow,
948
+ recurselimit => 2,
949
+ max_files => 4321,
950
+ ignore => 'foo.+',
951
+ links => follow,
949
952
  }
950
953
  MANIFEST
951
954
 
@@ -956,6 +959,7 @@ describe Puppet::Resource::Catalog::Compiler do
956
959
  :source_permissions => :use_when_creating,
957
960
  :recurse => true,
958
961
  :recurselimit => 2,
962
+ :max_files => 4321,
959
963
  :ignore => 'foo.+',
960
964
  }
961
965
  expect(Puppet::FileServing::Metadata.indirection).to receive(:search).with(source_dir, options).and_return([metadata, child_metadata])
@@ -61,12 +61,26 @@ describe "the fqdn_rand function" do
61
61
  expect(fqdn_rand(5000, :extra_identifier => ['expensive job 33'])).to eql(2389)
62
62
  end
63
63
 
64
+ it "returns the same value if only host differs by case" do
65
+ val1 = fqdn_rand(1000000000, :host => "host.example.com", :extra_identifier => [nil, true])
66
+ val2 = fqdn_rand(1000000000, :host => "HOST.example.com", :extra_identifier => [nil, true])
67
+
68
+ expect(val1).to eql(val2)
69
+ end
70
+
71
+ it "returns the same value if only host differs by case and an initial seed is given" do
72
+ val1 = fqdn_rand(1000000000, :host => "host.example.com", :extra_identifier => ['a seed', true])
73
+ val2 = fqdn_rand(1000000000, :host => "HOST.example.com", :extra_identifier => ['a seed', true])
74
+
75
+ expect(val1).to eql(val2)
76
+ end
77
+
64
78
  def fqdn_rand(max, args = {})
65
79
  host = args[:host] || '127.0.0.1'
66
80
  extra = args[:extra_identifier] || []
67
81
 
68
82
  scope = create_test_scope_for_node('localhost')
69
- allow(scope).to receive(:[]).with("::fqdn").and_return(host)
83
+ scope.compiler.topscope['fqdn'] = host.freeze
70
84
 
71
85
  scope.function_fqdn_rand([max] + extra)
72
86
  end
@@ -125,6 +125,24 @@ describe 'Semantic Versions' do
125
125
  expect(eval_and_collect_notices(code)).to eql(['true', 'false'])
126
126
  end
127
127
 
128
+ it 'can be compared to another instance created from arguments' do
129
+ code = <<-CODE
130
+ $x = SemVer('1.2.3-rc4+5')
131
+ $y = SemVer(1, 2, 3, 'rc4', '5')
132
+ notice($x == $y)
133
+ CODE
134
+ expect(eval_and_collect_notices(code)).to eql(['true'])
135
+ end
136
+
137
+ it 'can be compared to another instance created from a hash' do
138
+ code = <<-CODE
139
+ $x = SemVer('1.2.3-rc4+5')
140
+ $y = SemVer(major => 1, minor => 2, patch => 3, prerelease => 'rc4', build => '5')
141
+ notice($x == $y)
142
+ CODE
143
+ expect(eval_and_collect_notices(code)).to eql(['true'])
144
+ end
145
+
128
146
  it 'can be compared to another instance for magnitude' do
129
147
  code = <<-CODE
130
148
  $x = SemVer('1.1.1')
@@ -113,6 +113,24 @@ describe 'Sensitive Type' do
113
113
  expect(eval_and_collect_notices(code)).to eq(['Sensitive[Integer] != Sensitive[String]'])
114
114
  end
115
115
 
116
+ it 'equals another instance with the same value' do
117
+ code =<<-CODE
118
+ $i = Sensitive('secret')
119
+ $o = Sensitive('secret')
120
+ notice($i == $o)
121
+ CODE
122
+ expect(eval_and_collect_notices(code)).to eq(['true'])
123
+ end
124
+
125
+ it 'has equal hash keys for same values' do
126
+ code =<<-CODE
127
+ $i = Sensitive('secret')
128
+ $o = Sensitive('secret')
129
+ notice({$i => 1} == {$o => 1})
130
+ CODE
131
+ expect(eval_and_collect_notices(code)).to eq(['true'])
132
+ end
133
+
116
134
  it 'can be created from another sensitive instance ' do
117
135
  code =<<-CODE
118
136
  $o = Sensitive("hunter2")
@@ -191,6 +191,27 @@ OUTPUT
191
191
  expect(versions[version]).to eq(:rpm)
192
192
  end
193
193
  end
194
+
195
+ it "should be able to parse RPM package listings with letters in version" do
196
+ showres_output = <<END
197
+ cairo ALL @@R:cairo _all_filesets
198
+ @@R:cairo-1.14.6-2waixX11 1.14.6-2waixX11
199
+ END
200
+ packages = subject.send(:parse_showres_output, showres_output)
201
+ expect(Set.new(packages.keys)).to eq(Set.new(['cairo']))
202
+ versions = packages['cairo']
203
+ expect(versions.has_key?('1.14.6-2waixX11')).to eq(true)
204
+ expect(versions['1.14.6-2waixX11']).to eq(:rpm)
205
+ end
206
+
207
+ it "should raise error when parsing invalid RPM package listings" do
208
+ showres_output = <<END
209
+ cairo ALL @@R:cairo _all_filesets
210
+ @@R:cairo-invalid_version invalid_version
211
+ END
212
+ expect{ subject.send(:parse_showres_output, showres_output) }.to raise_error(Puppet::Error,
213
+ /Unable to parse output from nimclient showres: package string does not match expected rpm package string format/)
214
+ end
194
215
  end
195
216
 
196
217
  context "#determine_latest_version" do
@@ -220,6 +241,27 @@ END
220
241
  it "should return :installp for installp/bff packages" do
221
242
  expect(subject.send(:determine_package_type, bff_showres_output, 'mypackage.foo', '1.2.3.4')).to eq(:installp)
222
243
  end
244
+
245
+ it "should return :installp for security updates" do
246
+ nimclient_showres_output = <<END
247
+ bos.net ALL @@S:bos.net _all_filesets
248
+ + 7.2.0.1 TCP/IP ntp Applications @@S:bos.net.tcp.ntp 7.2.0.1
249
+ + 7.2.0.2 TCP/IP ntp Applications @@S:bos.net.tcp.ntp 7.2.0.2
250
+
251
+ END
252
+ expect(subject.send(:determine_package_type, nimclient_showres_output, 'bos.net.tcp.ntp', '7.2.0.2')).to eq(:installp)
253
+ end
254
+
255
+ it "should raise error when invalid header format is given" do
256
+ nimclient_showres_output = <<END
257
+ bos.net ALL @@INVALID_TYPE:bos.net _all_filesets
258
+ + 7.2.0.1 TCP/IP ntp Applications @@INVALID_TYPE:bos.net.tcp.ntp 7.2.0.1
259
+ + 7.2.0.2 TCP/IP ntp Applications @@INVALID_TYPE:bos.net.tcp.ntp 7.2.0.2
260
+
261
+ END
262
+ expect{ subject.send(:determine_package_type, nimclient_showres_output, 'bos.net.tcp.ntp', '7.2.0.2') }.to raise_error(
263
+ Puppet::Error, /Unable to parse output from nimclient showres: line does not match expected package header format/)
264
+ end
223
265
  end
224
266
  end
225
267
  end
@@ -83,6 +83,7 @@ describe 'Puppet::Type::Service::Provider::Init',
83
83
  allow(provider_class).to receive(:defpath).and_return('tmp')
84
84
 
85
85
  @services = ['one', 'two', 'three', 'four', 'umountfs']
86
+ allow(Dir).to receive(:entries).and_call_original
86
87
  allow(Dir).to receive(:entries).with('tmp').and_return(@services)
87
88
  expect(FileTest).to receive(:directory?).with('tmp').and_return(true)
88
89
  allow(FileTest).to receive(:executable?).and_return(true)
@@ -32,6 +32,7 @@ describe 'Puppet::Type::Service::Provider::Openwrt',
32
32
  allow(File).to receive(:directory?).with('/etc/init.d').and_return(true)
33
33
 
34
34
  allow(Puppet::FileSystem).to receive(:exist?).with('/etc/init.d/myservice').and_return(true)
35
+ allow(FileTest).to receive(:file?).and_call_original
35
36
  allow(FileTest).to receive(:file?).with('/etc/init.d/myservice').and_return(true)
36
37
  allow(FileTest).to receive(:executable?).with('/etc/init.d/myservice').and_return(true)
37
38
  end
@@ -47,7 +48,8 @@ describe 'Puppet::Type::Service::Provider::Openwrt',
47
48
  let(:services) {['dnsmasq', 'dropbear', 'firewall', 'led', 'puppet', 'uhttpd' ]}
48
49
 
49
50
  before :each do
50
- allow(Dir).to receive(:entries).and_return(services)
51
+ allow(Dir).to receive(:entries).and_call_original
52
+ allow(Dir).to receive(:entries).with('/etc/init.d').and_return(services)
51
53
  allow(FileTest).to receive(:directory?).and_return(true)
52
54
  allow(FileTest).to receive(:executable?).and_return(true)
53
55
  end
@@ -357,6 +357,9 @@ Jun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= a
357
357
  describe "#mask" do
358
358
  it "should run systemctl to disable and mask a service" do
359
359
  provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service'))
360
+ expect(provider).to receive(:execute).
361
+ with(['/bin/systemctl','cat', '--', 'sshd.service'], :failonfail => false).
362
+ and_return(Puppet::Util::Execution::ProcessOutput.new("# /lib/systemd/system/sshd.service\n...", 0))
360
363
  # :disable is the only call in the provider that uses a symbol instead of
361
364
  # a string.
362
365
  # This should be made consistent in the future and all tests updated.
@@ -364,6 +367,15 @@ Jun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= a
364
367
  expect(provider).to receive(:systemctl).with(:mask, '--', 'sshd.service')
365
368
  provider.mask
366
369
  end
370
+
371
+ it "masks a service that doesn't exist" do
372
+ provider = provider_class.new(Puppet::Type.type(:service).new(:name => 'doesnotexist.service'))
373
+ expect(provider).to receive(:execute).
374
+ with(['/bin/systemctl','cat', '--', 'doesnotexist.service'], :failonfail => false).
375
+ and_return(Puppet::Util::Execution::ProcessOutput.new("No files found for doesnotexist.service.\n", 1))
376
+ expect(provider).to receive(:systemctl).with(:mask, '--', 'doesnotexist.service')
377
+ provider.mask
378
+ end
367
379
  end
368
380
 
369
381
  # Note: systemd provider does not care about hasstatus or a custom status
@@ -467,17 +479,39 @@ Jun 14 21:43:23 foo.example.com systemd[1]: sshd.service lacks both ExecStart= a
467
479
  context 'when service state is static' do
468
480
  let(:service_state) { 'static' }
469
481
 
470
- it 'is always enabled_insync even if current value is the same as expected' do
471
- expect(provider).to be_enabled_insync(:false)
472
- end
482
+ context 'when enable is not mask' do
483
+ it 'is always enabled_insync even if current value is the same as expected' do
484
+ expect(provider).to be_enabled_insync(:false)
485
+ end
473
486
 
474
- it 'is always enabled_insync even if current value is not the same as expected' do
475
- expect(provider).to be_enabled_insync(:true)
487
+ it 'is always enabled_insync even if current value is not the same as expected' do
488
+ expect(provider).to be_enabled_insync(:true)
489
+ end
490
+
491
+ it 'logs a debug messsage' do
492
+ expect(Puppet).to receive(:debug).with("Unable to enable or disable static service sshd.service")
493
+ provider.enabled_insync?(:true)
494
+ end
476
495
  end
477
496
 
478
- it 'logs a debug messsage' do
479
- expect(Puppet).to receive(:debug).with("Unable to enable or disable static service sshd.service")
480
- provider.enabled_insync?(:true)
497
+ context 'when enable is mask' do
498
+ let(:provider) do
499
+ provider_class.new(Puppet::Type.type(:service).new(:name => 'sshd.service',
500
+ :enable => 'mask'))
501
+ end
502
+
503
+ it 'is enabled_insync if current value is the same as expected' do
504
+ expect(provider).to be_enabled_insync(:mask)
505
+ end
506
+
507
+ it 'is not enabled_insync if current value is not the same as expected' do
508
+ expect(provider).not_to be_enabled_insync(:true)
509
+ end
510
+
511
+ it 'logs no debug messsage' do
512
+ expect(Puppet).not_to receive(:debug)
513
+ provider.enabled_insync?(:true)
514
+ end
481
515
  end
482
516
  end
483
517
 
@@ -271,4 +271,206 @@ describe 'Puppet::Type::Service::Provider::Windows',
271
271
  }.to raise_error(Puppet::Error, /Cannot enable #{name}/)
272
272
  end
273
273
  end
274
+
275
+ describe "when managing logon credentials" do
276
+ before do
277
+ allow(Puppet::Util::Windows::ADSI).to receive(:computer_name).and_return(computer_name)
278
+ allow(Puppet::Util::Windows::SID).to receive(:name_to_principal).and_return(principal)
279
+ allow(Puppet::Util::Windows::Service).to receive(:set_startup_configuration).and_return(nil)
280
+ end
281
+
282
+ let(:computer_name) { 'myPC' }
283
+
284
+ describe "#logonaccount=" do
285
+ before do
286
+ allow(Puppet::Util::Windows::User).to receive(:password_is?).and_return(true)
287
+ resource[:logonaccount] = user_input
288
+ provider.logonaccount_insync?(user_input)
289
+ end
290
+
291
+ let(:user_input) { principal.account }
292
+ let(:principal) do
293
+ Puppet::Util::Windows::SID::Principal.new("myUser", nil, nil, computer_name, :SidTypeUser)
294
+ end
295
+
296
+ context "when given user is 'myUser'" do
297
+ it "should fail when the `Log On As A Service` right is missing from given user" do
298
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return("")
299
+ expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /".\\#{principal.account}" is missing the 'Log On As A Service' right./)
300
+ end
301
+
302
+ it "should fail when the `Log On As A Service` right is set to denied for given user" do
303
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return("SeDenyServiceLogonRight")
304
+ expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /".\\#{principal.account}" has the 'Log On As A Service' right set to denied./)
305
+ end
306
+
307
+ it "should not fail when given user has the `Log On As A Service` right" do
308
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return("SeServiceLogonRight")
309
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
310
+ end
311
+
312
+ ['myUser', 'myPC\\myUser', ".\\myUser", "MYPC\\mYuseR"].each do |user_input_variant|
313
+ let(:user_input) { user_input_variant }
314
+
315
+ it "should succesfully munge #{user_input_variant} to '.\\myUser'" do
316
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).with(principal.domain_account).and_return("SeServiceLogonRight")
317
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
318
+ expect(resource[:logonaccount]).to eq(".\\myUser")
319
+ end
320
+ end
321
+ end
322
+
323
+ context "when given user is a system account" do
324
+ before do
325
+ allow(Puppet::Util::Windows::User).to receive(:default_system_account?).and_return(true)
326
+ end
327
+
328
+ let(:user_input) { principal.account }
329
+ let(:principal) do
330
+ Puppet::Util::Windows::SID::Principal.new("LOCAL SERVICE", nil, nil, "NT AUTHORITY", :SidTypeUser)
331
+ end
332
+
333
+ it "should not fail when given user is a default system account even if the `Log On As A Service` right is missing" do
334
+ expect(Puppet::Util::Windows::User).not_to receive(:get_rights)
335
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
336
+ end
337
+
338
+ ['LocalSystem', '.\LocalSystem', 'myPC\LocalSystem', 'lOcALsysTem'].each do |user_input_variant|
339
+ let(:user_input) { user_input_variant }
340
+
341
+ it "should succesfully munge #{user_input_variant} to 'LocalSystem'" do
342
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
343
+ expect(resource[:logonaccount]).to eq('LocalSystem')
344
+ end
345
+ end
346
+ end
347
+
348
+ context "when domain is different from computer name" do
349
+ before do
350
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return("SeServiceLogonRight")
351
+ end
352
+
353
+ context "when given user is from AD" do
354
+ let(:user_input) { 'myRemoteUser' }
355
+ let(:principal) do
356
+ Puppet::Util::Windows::SID::Principal.new("myRemoteUser", nil, nil, "AD", :SidTypeUser)
357
+ end
358
+
359
+ it "should not raise any error" do
360
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
361
+ end
362
+
363
+ it "should succesfully be munged" do
364
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
365
+ expect(resource[:logonaccount]).to eq('AD\myRemoteUser')
366
+ end
367
+ end
368
+
369
+ context "when given user is LocalService" do
370
+ let(:user_input) { 'LocalService' }
371
+ let(:principal) do
372
+ Puppet::Util::Windows::SID::Principal.new("LOCAL SERVICE", nil, nil, "NT AUTHORITY", :SidTypeWellKnownGroup)
373
+ end
374
+
375
+ it "should succesfully munge well known user" do
376
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
377
+ expect(resource[:logonaccount]).to eq('NT AUTHORITY\LOCAL SERVICE')
378
+ end
379
+ end
380
+
381
+ context "when given user is in SID form" do
382
+ let(:user_input) { 'S-1-5-20' }
383
+ let(:principal) do
384
+ Puppet::Util::Windows::SID::Principal.new("NETWORK SERVICE", nil, nil, "NT AUTHORITY", :SidTypeUser)
385
+ end
386
+
387
+ it "should succesfully munge" do
388
+ expect { provider.logonaccount=(user_input) }.not_to raise_error
389
+ expect(resource[:logonaccount]).to eq('NT AUTHORITY\NETWORK SERVICE')
390
+ end
391
+ end
392
+
393
+ context "when given user is actually a group" do
394
+ let(:principal) do
395
+ Puppet::Util::Windows::SID::Principal.new("Administrators", nil, nil, "BUILTIN", :SidTypeAlias)
396
+ end
397
+ let(:user_input) { 'Administrators' }
398
+
399
+ it "should fail when sid type is not user or well known user" do
400
+ expect { provider.logonaccount=(user_input) }.to raise_error(Puppet::Error, /"BUILTIN\\#{user_input}" is not a valid account/)
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ describe "#logonpassword=" do
407
+ before do
408
+ allow(Puppet::Util::Windows::User).to receive(:get_rights).and_return('SeServiceLogonRight')
409
+ resource[:logonaccount] = account
410
+ resource[:logonpassword] = user_input
411
+ provider.logonaccount_insync?(account)
412
+ end
413
+
414
+ let(:account) { 'LocalSystem' }
415
+
416
+ describe "when given logonaccount is a predefined_local_account" do
417
+ let(:user_input) { 'pass' }
418
+ let(:principal) { nil }
419
+
420
+ it "should pass validation when given account is 'LocalSystem'" do
421
+ allow(Puppet::Util::Windows::User).to receive(:localsystem?).with('LocalSystem').and_return(true)
422
+ allow(Puppet::Util::Windows::User).to receive(:default_system_account?).with('LocalSystem').and_return(true)
423
+
424
+ expect(Puppet::Util::Windows::User).not_to receive(:password_is?)
425
+ expect { provider.logonpassword=(user_input) }.not_to raise_error
426
+ end
427
+
428
+ ['LOCAL SERVICE', 'NETWORK SERVICE', 'SYSTEM'].each do |predefined_local_account|
429
+ describe "when given account is #{predefined_local_account}" do
430
+ let(:account) { 'predefined_local_account' }
431
+ let(:principal) do
432
+ Puppet::Util::Windows::SID::Principal.new(account, nil, nil, "NT AUTHORITY", :SidTypeUser)
433
+ end
434
+
435
+ it "should pass validation" do
436
+ allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(principal.account).and_return(false)
437
+ allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(principal.domain_account).and_return(false)
438
+ expect(Puppet::Util::Windows::User).to receive(:default_system_account?).with(principal.domain_account).and_return(true).twice
439
+
440
+ expect(Puppet::Util::Windows::User).not_to receive(:password_is?)
441
+ expect { provider.logonpassword=(user_input) }.not_to raise_error
442
+ end
443
+ end
444
+ end
445
+ end
446
+
447
+ describe "when given logonaccount is not a predefined local account" do
448
+ before do
449
+ allow(Puppet::Util::Windows::User).to receive(:localsystem?).with(".\\#{principal.account}").and_return(false)
450
+ allow(Puppet::Util::Windows::User).to receive(:default_system_account?).with(".\\#{principal.account}").and_return(false)
451
+ end
452
+
453
+ let(:account) { 'myUser' }
454
+ let(:principal) do
455
+ Puppet::Util::Windows::SID::Principal.new(account, nil, nil, computer_name, :SidTypeUser)
456
+ end
457
+
458
+ describe "when password is proven correct" do
459
+ let(:user_input) { 'myPass' }
460
+ it "should pass validation" do
461
+ allow(Puppet::Util::Windows::User).to receive(:password_is?).with('myUser', 'myPass', '.').and_return(true)
462
+ expect { provider.logonpassword=(user_input) }.not_to raise_error
463
+ end
464
+ end
465
+
466
+ describe "when password is not proven correct" do
467
+ let(:user_input) { 'myWrongPass' }
468
+ it "should not pass validation" do
469
+ allow(Puppet::Util::Windows::User).to receive(:password_is?).with('myUser', 'myWrongPass', '.').and_return(false)
470
+ expect { provider.logonpassword=(user_input) }.to raise_error(Puppet::Error, /The given password is invalid for user '.\\myUser'/)
471
+ end
472
+ end
473
+ end
474
+ end
475
+ end
274
476
  end