chef 13.3.42 → 13.4.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/VERSION +1 -1
  4. data/lib/chef/knife/core/ui.rb +1 -1
  5. data/lib/chef/mash.rb +6 -0
  6. data/lib/chef/mixin/deep_merge.rb +1 -1
  7. data/lib/chef/mixin/user_context.rb +52 -0
  8. data/lib/chef/node/attribute.rb +80 -14
  9. data/lib/chef/node/immutable_collections.rb +16 -19
  10. data/lib/chef/provider/apt_repository.rb +12 -10
  11. data/lib/chef/provider/git.rb +20 -3
  12. data/lib/chef/provider/ifconfig/redhat.rb +4 -0
  13. data/lib/chef/provider/launchd.rb +20 -0
  14. data/lib/chef/provider/package/dnf.rb +3 -1
  15. data/lib/chef/provider/remote_file.rb +19 -0
  16. data/lib/chef/provider/remote_file/fetcher.rb +3 -0
  17. data/lib/chef/provider/remote_file/network_file.rb +18 -5
  18. data/lib/chef/provider/service/macosx.rb +4 -3
  19. data/lib/chef/provider/windows_path.rb +62 -0
  20. data/lib/chef/provider/zypper_repository.rb +1 -1
  21. data/lib/chef/providers.rb +1 -0
  22. data/lib/chef/resource.rb +5 -1
  23. data/lib/chef/resource/apt_repository.rb +1 -1
  24. data/lib/chef/resource/ifconfig.rb +36 -0
  25. data/lib/chef/resource/remote_file.rb +60 -0
  26. data/lib/chef/resource/windows_path.rb +41 -0
  27. data/lib/chef/resource/zypper_repository.rb +1 -0
  28. data/lib/chef/resources.rb +1 -0
  29. data/lib/chef/shell.rb +1 -0
  30. data/lib/chef/shell/shell_session.rb +4 -4
  31. data/lib/chef/util/windows/logon_session.rb +126 -0
  32. data/lib/chef/version.rb +4 -3
  33. data/lib/chef/win32/api/security.rb +2 -0
  34. data/spec/functional/mixin/user_context_spec.rb +117 -0
  35. data/spec/functional/resource/remote_file_spec.rb +171 -0
  36. data/spec/functional/resource/windows_path_spec.rb +64 -0
  37. data/spec/unit/knife/client_delete_spec.rb +1 -1
  38. data/spec/unit/mixin/user_context_spec.rb +109 -0
  39. data/spec/unit/node/immutable_collections_spec.rb +12 -4
  40. data/spec/unit/node_spec.rb +7 -0
  41. data/spec/unit/provider/git_spec.rb +55 -0
  42. data/spec/unit/provider/ifconfig/redhat_spec.rb +8 -0
  43. data/spec/unit/provider/remote_file/fetcher_spec.rb +1 -0
  44. data/spec/unit/provider/remote_file/network_file_spec.rb +7 -2
  45. data/spec/unit/provider/service/macosx_spec.rb +4 -1
  46. data/spec/unit/provider/windows_path_spec.rb +65 -0
  47. data/spec/unit/resource/windows_path_spec.rb +38 -0
  48. data/spec/unit/resource_spec.rb +8 -0
  49. data/spec/unit/shell/shell_session_spec.rb +82 -58
  50. data/spec/unit/util/windows/logon_session_spec.rb +284 -0
  51. data/tasks/maintainers.rb +3 -3
  52. metadata +15 -5
@@ -14,15 +14,16 @@
14
14
  # limitations under the License.
15
15
 
16
16
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
17
- # NOTE: This file is modified via the `.expeditor/update_version.sh` script, which
18
- # is triggered automatically by Chef Expeditor when a Pull Request is merged.
17
+ # NOTE: This file is generated by running `rake version` in the top level of
18
+ # this repo. Do not edit this manually. Edit the VERSION file and run the rake
19
+ # task instead.
19
20
  #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20
21
 
21
22
  require "chef/version_string"
22
23
 
23
24
  class Chef
24
25
  CHEF_ROOT = File.expand_path("../..", __FILE__)
25
- VERSION = Chef::VersionString.new("13.3.42")
26
+ VERSION = Chef::VersionString.new("13.4.19")
26
27
  end
27
28
 
28
29
  #
@@ -453,6 +453,8 @@ class Chef
453
453
  safe_attach_function :SetSecurityDescriptorSacl, [ :pointer, :BOOL, :pointer, :BOOL ], :BOOL
454
454
  safe_attach_function :GetTokenInformation, [ :HANDLE, :TOKEN_INFORMATION_CLASS, :pointer, :DWORD, :PDWORD ], :BOOL
455
455
  safe_attach_function :LogonUserW, [:LPTSTR, :LPTSTR, :LPTSTR, :DWORD, :DWORD, :PHANDLE], :BOOL
456
+ safe_attach_function :ImpersonateLoggedOnUser, [:HANDLE], :BOOL
457
+ safe_attach_function :RevertToSelf, [], :BOOL
456
458
 
457
459
  end
458
460
  end
@@ -0,0 +1,117 @@
1
+ #
2
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
3
+ # License:: Apache License, Version 2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+
18
+ require "spec_helper"
19
+
20
+ require "chef/win32/api" if Chef::Platform.windows?
21
+ require "chef/win32/api/error" if Chef::Platform.windows?
22
+ require "chef/mixin/user_context"
23
+
24
+ describe Chef::Mixin::UserContext, windows_only: true do
25
+ include Chef::Mixin::UserContext
26
+
27
+ let(:get_user_name_a) do
28
+ FFI.ffi_lib "advapi32.dll"
29
+ FFI.attach_function :GetUserNameA, [ :pointer, :pointer ], :bool
30
+ end
31
+
32
+ let(:process_username) do
33
+ name_size = FFI::Buffer.new(:long).write_long(0)
34
+ succeeded = get_user_name_a.call(nil, name_size)
35
+ last_error = FFI::LastError.error
36
+ if succeeded || last_error != Chef::ReservedNames::Win32::API::Error::ERROR_INSUFFICIENT_BUFFER
37
+ raise Chef::Exceptions::Win32APIError, "Expected ERROR_INSUFFICIENT_BUFFER from GetUserNameA but it returned the following error: #{last_error}"
38
+ end
39
+ user_name = FFI::MemoryPointer.new :char, (name_size.read_long)
40
+ succeeded = get_user_name_a.call(user_name, name_size)
41
+ last_error = FFI::LastError.error
42
+ if succeeded == 0 || last_error != 0
43
+ raise Chef::Exceptions::Win32APIError, "GetUserNameA failed with #{lasterror}"
44
+ end
45
+ user_name.read_string
46
+ end
47
+
48
+ let(:test_user) { "chefuserctx3" }
49
+ let(:test_domain) { windows_nonadmin_user_domain }
50
+ let(:test_password) { "j823jfxK3;2Xe1" }
51
+
52
+ let(:username_domain_qualification) { nil }
53
+ let(:username_with_conditional_domain) { username_domain_qualification.nil? ? username_to_impersonate : "#{username_domain_qualification}\\#{username_to_impersonate}" }
54
+
55
+ let(:windows_nonadmin_user) { test_user }
56
+ let(:windows_nonadmin_user_password) { test_password }
57
+
58
+ let(:username_while_impersonating) do
59
+ username = nil
60
+ with_user_context(username_with_conditional_domain, username_to_impersonate_password, domain_to_impersonate) do
61
+ username = process_username
62
+ end
63
+ username
64
+ end
65
+
66
+ before do
67
+ allow_any_instance_of(described_class).to receive(:node).and_return({ "platform_family" => "windows" })
68
+ end
69
+
70
+ shared_examples_for "method that executes the block while impersonating the alternate user" do
71
+ it "uses different credentials for other network connections" do
72
+ allow_any_instance_of(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
73
+ expect(username_while_impersonating.downcase).not_to eq(username_to_impersonate.downcase)
74
+ end
75
+ end
76
+
77
+ describe "#with_user_context" do
78
+ context "when the user and domain are both nil" do
79
+ let(:username_to_impersonate) { nil }
80
+ let(:domain_to_impersonate) { nil }
81
+ let(:username_to_impersonate_password) { nil }
82
+
83
+ it "has the same token and username as the process" do
84
+ expect(username_while_impersonating.downcase).to eq(ENV["username"].downcase)
85
+ end
86
+ end
87
+
88
+ context "when a non-nil user is specified" do
89
+ include_context "a non-admin Windows user"
90
+ context "when a username different than the process user is specified" do
91
+ let(:username_to_impersonate) { test_user }
92
+ let(:username_to_impersonate_password) { test_password }
93
+ context "when an explicit domain is given with a valid password" do
94
+ let(:domain_to_impersonate) { test_domain }
95
+ it "uses different credentials for other network connections" do
96
+ expect(username_while_impersonating.downcase).not_to eq(username_to_impersonate.downcase)
97
+ end
98
+ end
99
+
100
+ context "when a valid password and a non-qualified user is given and no domain is specified" do
101
+ let(:domain_to_impersonate) { "." }
102
+ it_behaves_like "method that executes the block while impersonating the alternate user"
103
+ end
104
+
105
+ it "raises an error user if specified with the wrong password" do
106
+ expect { with_user_context(username_to_impersonate, username_to_impersonate_password + "1", nil) }.to raise_error(ArgumentError)
107
+ end
108
+ end
109
+ end
110
+
111
+ context "when invalid arguments are passed" do
112
+ it "raises an ArgumentError exception if the password is not specified but the user is specified" do
113
+ expect { with_user_context(test_user, nil, nil) }.to raise_error(ArgumentError)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -123,6 +123,177 @@ describe Chef::Resource::RemoteFile do
123
123
 
124
124
  end
125
125
 
126
+ context "when running on Windows", :windows_only do
127
+ describe "when fetching files over SMB" do
128
+ include Chef::Mixin::ShellOut
129
+ let(:smb_share_root_directory) { directory = File.join(Dir.tmpdir, make_tmpname("windows_script_test")); Dir.mkdir(directory); directory }
130
+ let(:smb_file_local_file_name) { "smb_file.txt" }
131
+ let(:smb_file_local_path) { File.join( smb_share_root_directory, smb_file_local_file_name ) }
132
+ let(:smb_share_name) { "chef_smb_test" }
133
+ let(:smb_remote_path) { File.join("//#{ENV['COMPUTERNAME']}", smb_share_name, smb_file_local_file_name).gsub(/\//, "\\") }
134
+ let(:smb_file_content) { "hellofun" }
135
+ let(:local_destination_path) { File.join(Dir.tmpdir, make_tmpname("chef_remote_file")) }
136
+ let(:windows_current_user) { ENV["USERNAME"] }
137
+ let(:windows_current_user_domain) { ENV["USERDOMAIN"] || ENV["COMPUTERNAME"] }
138
+ let(:windows_current_user_qualified) { "#{windows_current_user_domain}\\#{windows_current_user}" }
139
+
140
+ let(:remote_domain) { nil }
141
+ let(:remote_user) { nil }
142
+ let(:remote_password) { nil }
143
+
144
+ let(:resource) do
145
+ node = Chef::Node.new
146
+ events = Chef::EventDispatch::Dispatcher.new
147
+ run_context = Chef::RunContext.new(node, {}, events)
148
+ resource = Chef::Resource::RemoteFile.new(path, run_context)
149
+ end
150
+
151
+ before do
152
+ shell_out("net.exe share #{smb_share_name} /delete")
153
+ File.write(smb_file_local_path, smb_file_content )
154
+ shell_out!("net.exe share #{smb_share_name}=\"#{smb_share_root_directory.gsub(/\//, '\\')}\" /grant:\"authenticated users\",read")
155
+ end
156
+
157
+ after do
158
+ shell_out("net.exe share #{smb_share_name} /delete")
159
+ File.delete(smb_file_local_path) if File.exist?(smb_file_local_path)
160
+ File.delete(local_destination_path) if File.exist?(local_destination_path)
161
+ Dir.rmdir(smb_share_root_directory)
162
+ end
163
+
164
+ context "when configuring the Windows identity used to access the remote file" do
165
+ before do
166
+ resource.path(local_destination_path)
167
+ resource.source(smb_remote_path)
168
+ resource.remote_domain(remote_domain)
169
+ resource.remote_user(remote_user)
170
+ resource.remote_password(remote_password)
171
+ resource.node.default["platform_family"] = "windows"
172
+ allow_any_instance_of(Chef::Provider::RemoteFile::NetworkFile).to receive(:node).and_return({ "platform_family" => "windows" })
173
+ end
174
+
175
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user has access" do
176
+ it "has the same content as the original file" do
177
+ expect { resource.run_action(:create) }.not_to raise_error
178
+ expect(::File.read(local_destination_path).chomp).to eq smb_file_content
179
+ end
180
+ end
181
+
182
+ shared_examples_for "a remote_file resource accessing a remote file to which the specified user does not have access" do
183
+ it "causes an error to be raised" do
184
+ expect { resource.run_action(:create) }.to raise_error(Errno::EACCES)
185
+ end
186
+ end
187
+
188
+ shared_examples_for "a remote_file resource accessing a remote file with invalid user" do
189
+ it "causes an error to be raised" do
190
+ allow(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
191
+ expect { resource.run_action(:create) }.to raise_error(Chef::Exceptions::Win32APIError)
192
+ end
193
+ end
194
+
195
+ context "when the file is accessible to non-admin users only as the current identity" do
196
+ before do
197
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
198
+ end
199
+
200
+ context "when the resource is accessed using the current user's identity" do
201
+ let(:remote_user) { nil }
202
+ let(:remote_domain) { nil }
203
+ let(:remote_password) { nil }
204
+
205
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
206
+
207
+ describe "uses the ::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE constant to chunk the file" do
208
+ let(:invalid_chunk_size) { -1 }
209
+ before do
210
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", invalid_chunk_size)
211
+ end
212
+
213
+ it "raises an ArgumentError when the chunk size is negative" do
214
+ expect(::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE).to eq(invalid_chunk_size)
215
+ expect { resource.run_action(:create) }.to raise_error(ArgumentError)
216
+ end
217
+ end
218
+
219
+ context "when the file must be transferred in more than one chunk" do
220
+ before do
221
+ stub_const("::Chef::Provider::RemoteFile::NetworkFile::TRANSFER_CHUNK_SIZE", 3)
222
+ end
223
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
224
+ end
225
+ end
226
+
227
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
228
+ let (:windows_nonadmin_user) { "chefremfile1" }
229
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe1" }
230
+ include_context "a non-admin Windows user"
231
+
232
+ before do
233
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /deny \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
234
+ end
235
+
236
+ let(:remote_user) { windows_nonadmin_user }
237
+ let(:remote_domain) { windows_nonadmin_user_domain }
238
+ let(:remote_password) { windows_nonadmin_user_password }
239
+
240
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
241
+ end
242
+ end
243
+
244
+ context "when the the file is only accessible as a specific alternate identity" do
245
+ let (:windows_nonadmin_user) { "chefremfile2" }
246
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe2" }
247
+ include_context "a non-admin Windows user"
248
+
249
+ before do
250
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_current_user_qualified}:(R)\" /inheritance:r")
251
+ end
252
+
253
+ context "when the resource is accessed using the specific non-qualified alternate user identity with access" do
254
+ let(:remote_user) { windows_nonadmin_user }
255
+ let(:remote_domain) { "." }
256
+ let(:remote_password) { windows_nonadmin_user_password }
257
+
258
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
259
+ end
260
+
261
+ context "when the resource is accessed using the specific alternate user identity with access and the domain is specified" do
262
+ let(:remote_user) { windows_nonadmin_user }
263
+ let(:remote_domain) { windows_nonadmin_user_domain }
264
+ let(:remote_password) { windows_nonadmin_user_password }
265
+
266
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user has access"
267
+ end
268
+
269
+ context "when the resource is accessed using the current user's identity" do
270
+ before do
271
+ shell_out!("icacls #{smb_file_local_path} /grant:r \"authenticated users:(W)\" /grant \"#{windows_nonadmin_user_qualified}:(R)\" /deny #{windows_current_user_qualified}:(R) /inheritance:r")
272
+ end
273
+
274
+ it_behaves_like "a remote_file resource accessing a remote file to which the specified user does not have access"
275
+ end
276
+
277
+ context "when the resource is accessed using an alternate user's identity with no access to the file" do
278
+ let (:windows_nonadmin_user) { "chefremfile3" }
279
+ let (:windows_nonadmin_user_password) { "j82ajfxK3;2Xe3" }
280
+ include_context "a non-admin Windows user"
281
+
282
+ let(:remote_user) { windows_nonadmin_user_qualified }
283
+ let(:remote_domain) { nil }
284
+ let(:remote_password) { windows_nonadmin_user_password }
285
+
286
+ before do
287
+ allow_any_instance_of(Chef::Util::Windows::LogonSession).to receive(:validate_session_open!).and_return(true)
288
+ end
289
+
290
+ it_behaves_like "a remote_file resource accessing a remote file with invalid user"
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
296
+
126
297
  context "when dealing with content length checking" do
127
298
  before(:each) do
128
299
  start_tiny_server
@@ -0,0 +1,64 @@
1
+ #
2
+ # Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
3
+ # Copyright:: Copyright (c) 2017 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+
21
+ describe Chef::Resource::WindowsPath, :windows_only do
22
+ let(:path) { "test_path" }
23
+
24
+ before(:all) do
25
+ @old_path = ENV["PATH"].dup
26
+ end
27
+
28
+ after(:all) do
29
+ ENV["PATH"] = @old_path
30
+ end
31
+
32
+ subject do
33
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
34
+ new_resource
35
+ end
36
+
37
+ describe "adding path" do
38
+ after { remove_path }
39
+
40
+ it "appends the user given path in the Environment variable Path" do
41
+ subject.run_action(:add)
42
+ expect(ENV["PATH"]).to include(path)
43
+ end
44
+ end
45
+
46
+ describe "removing path" do
47
+ before { add_path }
48
+
49
+ it "removes the user given path from the Environment variable Path" do
50
+ subject.run_action(:remove)
51
+ expect(ENV["PATH"]).not_to include(path)
52
+ end
53
+ end
54
+
55
+ def remove_path
56
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
57
+ new_resource.run_action(:remove)
58
+ end
59
+
60
+ def add_path
61
+ new_resource = Chef::Resource::WindowsPath.new(path, run_context)
62
+ new_resource.run_action(:add)
63
+ end
64
+ end
@@ -35,7 +35,7 @@ describe Chef::Knife::ClientDelete do
35
35
  end
36
36
 
37
37
  context "receives multiple clients" do
38
- let(:clients) { %w{ "adam", "ben", "charlie" } }
38
+ let(:clients) { %w{ adam ben charlie } }
39
39
 
40
40
  before(:each) do
41
41
  @knife.name_args = clients
@@ -0,0 +1,109 @@
1
+ #
2
+ # Author:: Adam Edwards (<adamed@chef.io>)
3
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require "spec_helper"
20
+ require "chef/mixin/user_context"
21
+ require "chef/util/windows/logon_session"
22
+
23
+ describe "a class that mixes in user_context" do
24
+ let(:instance_with_user_context) do
25
+ class UserContextConsumer
26
+ include ::Chef::Mixin::UserContext
27
+ def with_context(user, domain, password, &block)
28
+ with_user_context(user, password, domain, &block)
29
+ end
30
+ end
31
+ UserContextConsumer.new
32
+ end
33
+
34
+ shared_examples_for "a method that requires a block" do
35
+ it "raises an ArgumentError exception if a block is not supplied" do
36
+ expect { instance_with_user_context.with_context(nil, nil, nil) }.to raise_error(ArgumentError)
37
+ end
38
+ end
39
+
40
+ context "when running on Windows" do
41
+ before do
42
+ allow(::Chef::Platform).to receive(:windows?).and_return(true)
43
+ allow(::Chef::Util::Windows::LogonSession).to receive(:new).and_return(logon_session)
44
+ allow(instance_with_user_context).to receive(:node).and_return({ "platform_family" => "windows" })
45
+ end
46
+
47
+ let(:logon_session) { instance_double("::Chef::Util::Windows::LogonSession", :set_user_context => nil, :open => nil, :close => nil) }
48
+
49
+ it "does not raise an exception when the user and all parameters are nil" do
50
+ expect { instance_with_user_context.with_context(nil, nil, nil) {} }.not_to raise_error
51
+ end
52
+
53
+ context "when given valid user credentials" do
54
+ before do
55
+ expect(::Chef::Util::Windows::LogonSession).to receive(:new).and_return(logon_session)
56
+ end
57
+
58
+ let(:block_object) do
59
+ class BlockClass
60
+ def block_method
61
+ end
62
+ end
63
+ BlockClass.new
64
+ end
65
+
66
+ let(:block_parameter) { Proc.new { block_object.block_method } }
67
+
68
+ context "when the block doesn't raise an exception" do
69
+ before do
70
+ expect( block_object ).to receive(:block_method)
71
+ end
72
+ it "calls the supplied block" do
73
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error
74
+ end
75
+
76
+ it "does not raise an exception if the user, password, and domain are specified" do
77
+ expect { instance_with_user_context.with_context("kamilah", "xanadu", "chef4life", &block_parameter) }.not_to raise_error
78
+ end
79
+ end
80
+
81
+ context "when the block raises an exception" do
82
+ class UserContextTestException < RuntimeError
83
+ end
84
+ let(:block_parameter) { Proc.new { raise UserContextTextException } }
85
+
86
+ it "raises the exception raised by the block" do
87
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error(UserContextTestException)
88
+ end
89
+
90
+ it "closes the logon session so resources are not leaked" do
91
+ expect(logon_session).to receive(:close)
92
+ expect { instance_with_user_context.with_context("kamilah", nil, "chef4life", &block_parameter) }.not_to raise_error(UserContextTestException)
93
+ end
94
+ end
95
+ end
96
+
97
+ it_behaves_like "a method that requires a block"
98
+ end
99
+
100
+ context "when not running on Windows" do
101
+ before do
102
+ allow(instance_with_user_context).to receive(:node).and_return({ "platform_family" => "ubuntu" })
103
+ end
104
+
105
+ it "raises a ::Chef::Exceptions::UnsupportedPlatform exception" do
106
+ expect { instance_with_user_context.with_context(nil, nil, nil) {} }.to raise_error(::Chef::Exceptions::UnsupportedPlatform)
107
+ end
108
+ end
109
+ end