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
@@ -35,10 +35,12 @@ class Chef
35
35
  use_multipackage_api
36
36
  use_package_name_for_source
37
37
 
38
- provides :package, platform_family: %w{rhel fedora amazon} do
38
+ provides :package, platform_family: %w{fedora amazon} do
39
39
  which("dnf") && shell_out("rpm -q dnf").stdout =~ /^dnf-[1-9]/
40
40
  end
41
41
 
42
+ provides :package, platform_family: %w{rhel}, platform_version: ">= 8"
43
+
42
44
  provides :dnf_package, os: "linux"
43
45
 
44
46
  #
@@ -29,6 +29,25 @@ class Chef
29
29
  super
30
30
  end
31
31
 
32
+ def define_resource_requirements
33
+ [ new_resource.remote_user, new_resource.remote_domain,
34
+ new_resource.remote_password ].each do |prop|
35
+ requirements.assert(:all_actions) do |a|
36
+ a.assertion do
37
+ if prop
38
+ node[:platform_family] == "windows"
39
+ else
40
+ true
41
+ end
42
+ end
43
+ a.failure_message Chef::Exceptions::UnsupportedPlatform, "'remote_user', 'remote_domain' and 'remote_password' properties are supported only for Windows platform"
44
+ a.whyrun("Assuming that the platform is Windows while passing 'remote_user', 'remote_domain' and 'remote_password' properties")
45
+ end
46
+ end
47
+
48
+ super
49
+ end
50
+
32
51
  def load_current_resource
33
52
  @current_resource = Chef::Resource::RemoteFile.new(new_resource.name)
34
53
  super
@@ -24,6 +24,9 @@ class Chef
24
24
 
25
25
  def self.for_resource(uri, new_resource, current_resource)
26
26
  if network_share?(uri)
27
+ if !Chef::Platform.windows?
28
+ raise Exceptions::UnsupportedPlatform, "Fetching the file on a network share is supported only on the Windows platform. Please change your source: #{uri}"
29
+ end
27
30
  Chef::Provider::RemoteFile::NetworkFile.new(uri, new_resource, current_resource)
28
31
  else
29
32
  case uri.scheme
@@ -19,14 +19,18 @@
19
19
  require "uri"
20
20
  require "tempfile"
21
21
  require "chef/provider/remote_file"
22
+ require "chef/mixin/user_context"
22
23
 
23
24
  class Chef
24
25
  class Provider
25
26
  class RemoteFile
26
27
  class NetworkFile
28
+ include Chef::Mixin::UserContext
27
29
 
28
30
  attr_reader :new_resource
29
31
 
32
+ TRANSFER_CHUNK_SIZE = 1048576
33
+
30
34
  def initialize(source, new_resource, current_resource)
31
35
  @new_resource = new_resource
32
36
  @source = source
@@ -35,13 +39,22 @@ class Chef
35
39
  # Fetches the file on a network share, returning a Tempfile-like File handle
36
40
  # windows only
37
41
  def fetch
38
- tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
39
- Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}")
40
- FileUtils.cp(@source, tempfile.path)
41
- tempfile.close if tempfile
42
+ begin
43
+ tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile
44
+ Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}")
45
+
46
+ with_user_context(new_resource.remote_user, new_resource.remote_password, new_resource.remote_domain) do
47
+ ::File.open(@source, "rb") do |remote_file|
48
+ while data = remote_file.read(TRANSFER_CHUNK_SIZE)
49
+ tempfile.write(data)
50
+ end
51
+ end
52
+ end
53
+ ensure
54
+ tempfile.close if tempfile
55
+ end
42
56
  tempfile
43
57
  end
44
-
45
58
  end
46
59
  end
47
60
  end
@@ -52,17 +52,18 @@ class Chef
52
52
  @plist_size = 0
53
53
  @plist = @new_resource.plist ? @new_resource.plist : find_service_plist
54
54
  @service_label = find_service_label
55
- # LauchAgents should be loaded as the console user.
55
+ # LaunchAgents should be loaded as the console user.
56
56
  @console_user = @plist ? @plist.include?("LaunchAgents") : false
57
57
  @session_type = @new_resource.session_type
58
58
 
59
59
  if @console_user
60
- @console_user = Etc.getlogin
60
+ @console_user = Etc.getpwuid(::File.stat("/dev/console").uid).name
61
61
  Chef::Log.debug("#{new_resource} console_user: '#{@console_user}'")
62
62
  cmd = "su "
63
63
  param = this_version_or_newer?("10.10") ? "" : "-l "
64
+ param = "-l " if this_version_or_newer?("10.12")
64
65
  @base_user_cmd = cmd + param + "#{@console_user} -c"
65
- # Default LauchAgent session should be Aqua
66
+ # Default LaunchAgent session should be Aqua
66
67
  @session_type = "Aqua" if @session_type.nil?
67
68
  end
68
69
 
@@ -0,0 +1,62 @@
1
+ #
2
+ # Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
3
+ # Copyright:: Copyright 2008-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 "chef/mixin/windows_env_helper" if Chef::Platform.windows?
20
+ require "chef/mixin/wide_string"
21
+ require "chef/exceptions"
22
+
23
+ class Chef
24
+ class Provider
25
+ class WindowsPath < Chef::Provider
26
+
27
+ include Chef::Mixin::WindowsEnvHelper if Chef::Platform.windows?
28
+
29
+ def load_current_resource
30
+ @current_resource = Chef::Resource::WindowsPath.new(new_resource.name)
31
+ @current_resource.path(new_resource.path)
32
+ @current_resource
33
+ end
34
+
35
+ action :add do
36
+ # The windows Env provider does not correctly expand variables in
37
+ # the PATH environment variable. Ruby expects these to be expanded.
38
+ #
39
+ path = expand_path(new_resource.path)
40
+ converge_by "Adding #{new_resource.path} to path environment variable" do
41
+ declare_resource(:env, "path") do
42
+ action :modify
43
+ delim ::File::PATH_SEPARATOR
44
+ value path.tr("/", '\\')
45
+ end
46
+ end
47
+ end
48
+
49
+ action :remove do
50
+ # The windows Env provider does not correctly expand variables in
51
+ # the PATH environment variable. Ruby expects these to be expanded.
52
+ #
53
+ path = expand_path(new_resource.path)
54
+ declare_resource(:env, "path") do
55
+ action :delete
56
+ delim ::File::PATH_SEPARATOR
57
+ value path.tr("/", '\\')
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -57,7 +57,7 @@ class Chef
57
57
  end
58
58
 
59
59
  action :refresh do
60
- declare_resource(:execute, "zypper refresh #{escaped_repo_name}") do
60
+ declare_resource(:execute, "zypper#{' --gpg-auto-import-keys' if new_resource.gpgautoimportkeys} refresh #{escaped_repo_name}") do
61
61
  only_if "zypper lr #{escaped_repo_name}"
62
62
  end
63
63
  end
@@ -61,6 +61,7 @@ require "chef/provider/whyrun_safe_ruby_block"
61
61
  require "chef/provider/yum_repository"
62
62
  require "chef/provider/windows_task"
63
63
  require "chef/provider/zypper_repository"
64
+ require "chef/provider/windows_path"
64
65
 
65
66
  require "chef/provider/env/windows"
66
67
 
@@ -641,7 +641,11 @@ class Chef
641
641
 
642
642
  all_props = {}
643
643
  self.class.state_properties.map do |p|
644
- all_props[p.name.to_s] = p.sensitive? ? '"*sensitive value suppressed*"' : value_to_text(p.get(self))
644
+ begin
645
+ all_props[p.name.to_s] = p.sensitive? ? '"*sensitive value suppressed*"' : value_to_text(p.get(self))
646
+ rescue Chef::Exceptions::ValidationFailed
647
+ # This space left intentionally blank, the property was probably required or had an invalid default.
648
+ end
645
649
  end
646
650
 
647
651
  ivars = instance_variables.map { |ivar| ivar.to_sym } - HIDDEN_IVARS
@@ -33,7 +33,7 @@ class Chef
33
33
  # whether or not to add the repository as a source repo, too
34
34
  property :deb_src, [TrueClass, FalseClass], default: false
35
35
  property :keyserver, [String, nil, false], default: "keyserver.ubuntu.com", nillable: true, coerce: proc { |x| x ? x : nil }
36
- property :key, [String, nil, false], default: nil, nillable: true, coerce: proc { |x| x ? x : nil }
36
+ property :key, [String, Array, nil, false], default: [], coerce: proc { |x| x ? Array(x) : nil }
37
37
  property :key_proxy, [String, nil, false], default: nil, nillable: true, coerce: proc { |x| x ? x : nil }
38
38
 
39
39
  property :cookbook, [String, nil, false], default: nil, desired_state: false, nillable: true, coerce: proc { |x| x ? x : nil }
@@ -44,6 +44,10 @@ class Chef
44
44
  @network = nil
45
45
  @bootproto = nil
46
46
  @onparent = nil
47
+ @ethtool_opts = nil
48
+ @bonding_opts = nil
49
+ @master = nil
50
+ @slave = nil
47
51
  end
48
52
 
49
53
  def target(arg = nil)
@@ -141,6 +145,38 @@ class Chef
141
145
  :kind_of => String
142
146
  )
143
147
  end
148
+
149
+ def ethtool_opts(arg = nil)
150
+ set_or_return(
151
+ :ethtool_opts,
152
+ arg,
153
+ :kind_of => String
154
+ )
155
+ end
156
+
157
+ def bonding_opts(arg = nil)
158
+ set_or_return(
159
+ :bonding_opts,
160
+ arg,
161
+ :kind_of => String
162
+ )
163
+ end
164
+
165
+ def master(arg = nil)
166
+ set_or_return(
167
+ :master,
168
+ arg,
169
+ :kind_of => String
170
+ )
171
+ end
172
+
173
+ def slave(arg = nil)
174
+ set_or_return(
175
+ :slave,
176
+ arg,
177
+ :kind_of => String
178
+ )
179
+ end
144
180
  end
145
181
 
146
182
  end
@@ -131,6 +131,66 @@ class Chef
131
131
  )
132
132
  end
133
133
 
134
+ property :remote_user, String
135
+
136
+ property :remote_domain, String
137
+
138
+ property :remote_password, String, sensitive: true
139
+
140
+ def after_created
141
+ validate_identity_platform(remote_user, remote_password, remote_domain)
142
+ identity = qualify_user(remote_user, remote_password, remote_domain)
143
+ remote_domain(identity[:domain])
144
+ remote_user(identity[:user])
145
+ end
146
+
147
+ def validate_identity_platform(specified_user, password = nil, specified_domain = nil)
148
+ if node[:platform_family] == "windows"
149
+ if specified_user && password.nil?
150
+ raise ArgumentError, "A value for `remote_password` must be specified when a value for `user` is specified on the Windows platform"
151
+ end
152
+ end
153
+ end
154
+
155
+ def qualify_user(specified_user, password = nil, specified_domain = nil)
156
+ domain = specified_domain
157
+ user = specified_user
158
+
159
+ if specified_user.nil? && ! specified_domain.nil?
160
+ raise ArgumentError, "The domain `#{specified_domain}` was specified, but no user name was given"
161
+ end
162
+
163
+ # if domain is provided in both username and domain
164
+ if specified_user && ((specified_user.include? '\\') || (specified_user.include? "@")) && specified_domain
165
+ raise ArgumentError, "The domain is provided twice. Username: `#{specified_user}`, Domain: `#{specified_domain}`. Please specify domain only once."
166
+ end
167
+
168
+ if ! specified_user.nil? && specified_domain.nil?
169
+ # Splitting username of format: Domain\Username
170
+ domain_and_user = user.split('\\')
171
+
172
+ if domain_and_user.length == 2
173
+ domain = domain_and_user[0]
174
+ user = domain_and_user[1]
175
+ elsif domain_and_user.length == 1
176
+ # Splitting username of format: Username@Domain
177
+ domain_and_user = user.split("@")
178
+ if domain_and_user.length == 2
179
+ domain = domain_and_user[1]
180
+ user = domain_and_user[0]
181
+ elsif domain_and_user.length != 1
182
+ raise ArgumentError, "The specified user name `#{user}` is not a syntactically valid user name"
183
+ end
184
+ end
185
+ end
186
+
187
+ if ( password || domain ) && user.nil?
188
+ raise ArgumentError, "A value for `password` or `domain` was specified without specification of a value for `user`"
189
+ end
190
+
191
+ { domain: domain, user: user }
192
+ end
193
+
134
194
  private
135
195
 
136
196
  include Chef::Mixin::Uris
@@ -0,0 +1,41 @@
1
+ #
2
+ # Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
3
+ # Copyright:: Copyright 2008-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 "chef/resource"
20
+
21
+ class Chef
22
+ class Resource
23
+ class WindowsPath < Chef::Resource
24
+
25
+ provides :windows_path, os: "windows"
26
+
27
+ allowed_actions :add, :remove
28
+ default_action :add
29
+
30
+ def initialize(name, run_context = nil)
31
+ super
32
+ @resource_name = :windows_path
33
+ @path = name
34
+ @provider = Chef::Provider::WindowsPath
35
+ @action = :add
36
+ end
37
+
38
+ property :path, String, name_property: true
39
+ end
40
+ end
41
+ end
@@ -39,6 +39,7 @@ class Chef
39
39
  property :mode, default: "0644"
40
40
  property :refresh_cache, [true, false], default: true
41
41
  property :source, String, regex: /.*/
42
+ property :gpgautoimportkeys, [true, false], default: true
42
43
 
43
44
  default_action :create
44
45
  allowed_actions :create, :remove, :add, :refresh
@@ -101,3 +101,4 @@ require "chef/resource/cab_package"
101
101
  require "chef/resource/powershell_package"
102
102
  require "chef/resource/msu_package"
103
103
  require "chef/resource/windows_task"
104
+ require "chef/resource/windows_path"
@@ -138,6 +138,7 @@ module Shell
138
138
  def self.session
139
139
  unless client_type.instance.node_built?
140
140
  puts "Session type: #{client_type.session_type}"
141
+ client_type.instance.json_configuration = @json_attribs
141
142
  client_type.instance.reset!
142
143
  end
143
144
  client_type.instance
@@ -38,7 +38,7 @@ module Shell
38
38
  @session_type
39
39
  end
40
40
 
41
- attr_accessor :node, :compile, :recipe, :run_context
41
+ attr_accessor :node, :compile, :recipe, :run_context, :json_configuration
42
42
  attr_reader :node_attributes, :client
43
43
  def initialize
44
44
  @node_built = false
@@ -151,7 +151,7 @@ module Shell
151
151
 
152
152
  def rebuild_node
153
153
  Chef::Config[:solo_legacy_mode] = true
154
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
154
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
155
155
  @client.run_ohai
156
156
  @client.load_node
157
157
  @client.build_node
@@ -183,7 +183,7 @@ module Shell
183
183
  def rebuild_node
184
184
  # Tell the client we're chef solo so it won't try to contact the server
185
185
  Chef::Config[:solo_legacy_mode] = true
186
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
186
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
187
187
  @client.run_ohai
188
188
  @client.load_node
189
189
  @client.build_node
@@ -218,7 +218,7 @@ module Shell
218
218
  def rebuild_node
219
219
  # Make sure the client knows this is not chef solo
220
220
  Chef::Config[:solo_legacy_mode] = false
221
- @client = Chef::Client.new(nil, Chef::Config[:shell_config])
221
+ @client = Chef::Client.new(json_configuration, Chef::Config[:shell_config])
222
222
  @client.run_ohai
223
223
  @client.register
224
224
  @client.load_node
@@ -0,0 +1,126 @@
1
+ #
2
+ # Author:: Adam Edwards (<adamed@chef.io>)
3
+ #
4
+ # Copyright:: Copyright (c) 2015 Chef Software, Inc.
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 "chef/win32/api/security" if Chef::Platform.windows?
20
+ require "chef/mixin/wide_string"
21
+
22
+ class Chef
23
+ class Util
24
+ class Windows
25
+ class LogonSession
26
+ include Chef::Mixin::WideString
27
+
28
+ def initialize(username, password, domain = nil)
29
+ if username.nil? || password.nil?
30
+ raise ArgumentError, "The logon session must be initialize with non-nil user name and password parameters"
31
+ end
32
+
33
+ @original_username = username
34
+ @original_password = password
35
+ @original_domain = domain
36
+ @token = FFI::Buffer.new(:pointer)
37
+ @session_opened = false
38
+ @impersonating = false
39
+ end
40
+
41
+ def open
42
+ if session_opened
43
+ raise "Attempted to open a logon session that was already open."
44
+ end
45
+
46
+ username = wstring(original_username)
47
+ password = wstring(original_password)
48
+ domain = wstring(original_domain)
49
+
50
+ status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, token)
51
+
52
+ if !status
53
+ last_error = FFI::LastError.error
54
+ raise Chef::Exceptions::Win32APIError, "Logon for user `#{original_username}` failed with Win32 status #{last_error}."
55
+ end
56
+
57
+ @session_opened = true
58
+ end
59
+
60
+ def close
61
+ validate_session_open!
62
+
63
+ if impersonating
64
+ restore_user_context
65
+ end
66
+
67
+ Chef::ReservedNames::Win32::API::System.CloseHandle(token.read_ulong)
68
+ @token = nil
69
+ @session_opened = false
70
+ end
71
+
72
+ def set_user_context
73
+ validate_session_open!
74
+
75
+ if ! session_opened
76
+ raise "Attempted to set the user context before opening a session."
77
+ end
78
+
79
+ if impersonating
80
+ raise "Attempt to set the user context when the user context is already set."
81
+ end
82
+
83
+ status = Chef::ReservedNames::Win32::API::Security.ImpersonateLoggedOnUser(token.read_ulong)
84
+
85
+ if !status
86
+ last_error = FFI::LastError.error
87
+ raise Chef::Exceptions::Win32APIError, "Attempt to impersonate user `#{original_username}` failed with Win32 status #{last_error}."
88
+ end
89
+
90
+ @impersonating = true
91
+ end
92
+
93
+ def restore_user_context
94
+ validate_session_open!
95
+
96
+ if impersonating
97
+ status = Chef::ReservedNames::Win32::API::Security.RevertToSelf
98
+
99
+ if !status
100
+ last_error = FFI::LastError.error
101
+ raise Chef::Exceptions::Win32APIError, "Unable to restore user context with Win32 status #{last_error}."
102
+ end
103
+ end
104
+
105
+ @impersonating = false
106
+ end
107
+
108
+ protected
109
+
110
+ attr_reader :original_username
111
+ attr_reader :original_password
112
+ attr_reader :original_domain
113
+
114
+ attr_reader :token
115
+ attr_reader :session_opened
116
+ attr_reader :impersonating
117
+
118
+ def validate_session_open!
119
+ if ! session_opened
120
+ raise "Attempted to set the user context before opening a session."
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end