diff options
author | Adam Edwards <adamed@opscode.com> | 2015-10-11 22:43:33 -0700 |
---|---|---|
committer | Adam Edwards <adamed@opscode.com> | 2015-10-11 22:43:33 -0700 |
commit | cc839a7f6f332e3f1c4f3b90d5099384b3904766 (patch) | |
tree | 54227c7098d0c85c74ec519baa56225f51b443df | |
parent | c36bce07f4fc3ae3fc322f5271a83ac620b5b691 (diff) | |
download | chef-adamedx/remote-file-windows-user.tar.gz |
remote_file credential support for Windows UNC pathsadamedx/remote-file-windows-user
-rw-r--r-- | lib/chef/provider/remote_file/network_file.rb | 37 | ||||
-rw-r--r-- | lib/chef/resource/remote_file.rb | 51 | ||||
-rw-r--r-- | lib/chef/util/windows/logon_session.rb | 22 |
3 files changed, 97 insertions, 13 deletions
diff --git a/lib/chef/provider/remote_file/network_file.rb b/lib/chef/provider/remote_file/network_file.rb index 093a388d2a..70a8c074ad 100644 --- a/lib/chef/provider/remote_file/network_file.rb +++ b/lib/chef/provider/remote_file/network_file.rb @@ -19,6 +19,7 @@ require 'uri' require 'tempfile' require 'chef/provider/remote_file' +require 'chef/util/windows/logon_session' class Chef class Provider @@ -35,13 +36,39 @@ class Chef # Fetches the file on a network share, returning a Tempfile-like File handle # windows only def fetch - tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile - Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}") - FileUtils.cp(@source, tempfile.path) - tempfile.close if tempfile + remote_user = new_resource.remote_credentials && new_resource.remote_credentials[:user] + remote_user_domain = remote_user ? new_resource.remote_credentials[:domain] : nil + remote_user_secret = remote_user ? new_resource.remote_credentials[:secret] : nil + + session = nil + + if remote_user + session = Chef::Util::Windows::LogonSession.new(remote_user, remote_user_domain, remote_user_secret) + end + + tempfile = nil + + begin + tempfile = Chef::FileContentManagement::Tempfile.new(new_resource).tempfile + Chef::Log.debug("#{new_resource} staging #{@source} to #{tempfile.path}") + + if session + session.open + session.set_user_context + end + + ::File.open(@source, 'rb') do | remote_file | + while data = remote_file.read(1048576) + tempfile.write(data) + end + end + ensure + session.close! if session + tempfile.close if tempfile + end + tempfile end - end end end diff --git a/lib/chef/resource/remote_file.rb b/lib/chef/resource/remote_file.rb index b7a553cbe8..faf1abb0a0 100644 --- a/lib/chef/resource/remote_file.rb +++ b/lib/chef/resource/remote_file.rb @@ -23,6 +23,7 @@ require 'chef/provider/remote_file' require 'chef/mixin/securable' require 'chef/mixin/uris' + class Chef class Resource class RemoteFile < Chef::Resource::File @@ -36,6 +37,7 @@ class Chef @ftp_active_mode = false @headers = {} @provider = Chef::Provider::RemoteFile + @remote_credentials = nil end # source can take any of the following as arguments @@ -122,6 +124,24 @@ class Chef ) end + def remote_credentials(args=nil) + set_or_return( + :remote_credentials, + args, + { :kind_of => Hash, + :callbacks => { + :validate_credentials => method(:validate_credentials) + }}) + end + + def sensitive(arg=nil) + if remote_credentials && remote_credentials[:user] + true + else + super + end + end + private include Chef::Mixin::Uris @@ -138,6 +158,37 @@ class Chef true end + def validate_credentials(credentials) + if ! Chef::Platform.windows? + raise Exceptions::UnsupportedPlatform, + "The `remote_credentials` property is only supported on the Windows platform" + end + + if ! credentials.empty? + if ! credentials.has_key?(:user) + raise ArgumentError, "A non-empty `remote_credentials` property must specify `:user` key" + end + + if credentials[:user].nil? + raise ArgumentError, "A non-empty `remote_credentials` property must specify a non-nil value for the `:user` key" + end + end + + valid_keys = [:user, :domain, :secret] + invalid_arguments = credentials.keys - valid_keys + + if ! invalid_arguments.empty? + invalid_argument_list = invalid_arguments.map { | key | "`:#{key}`" }.join(', ') + raise ArgumentError, "Only the keys `:user`, `:domain`, and `:secret` are valid keys for the `remote_credentials` [Hash]. The following invalid keys were specified: #{invalid_argument_list}" + end + + credentials.keys.each do | key | + if (! credentials[key].nil?) && (! credentials[key].is_a? String) + raise ArgumentError, "The value of key :#{key} of the `remote_credentials` property must be of type [String], but it is of type [#{credentials[key].class}]" + end + end + end + def absolute_uri?(source) Chef::Provider::RemoteFile::Fetcher.network_share?(source) or (source.kind_of?(String) and as_uri(source).absolute?) rescue URI::InvalidURIError diff --git a/lib/chef/util/windows/logon_session.rb b/lib/chef/util/windows/logon_session.rb index cae7f406ff..35885cb4be 100644 --- a/lib/chef/util/windows/logon_session.rb +++ b/lib/chef/util/windows/logon_session.rb @@ -25,9 +25,10 @@ class Chef class LogonSession
include Chef::Mixin::WideString
- def initialize(username, password=nil)
+ def initialize(username, domain=nil, password=nil)
@username = username
@password = password
+ @domain = domain
@token = FFI::Buffer.new(:pointer)
@session_opened = false
@impersonating = false
@@ -40,8 +41,9 @@ class Chef username = wstring(@username)
password = wstring(@password)
+ domain = wstring(@domain)
- status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, nil, password, Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_INTERACTIVE, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, @token)
+ status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NETWORK, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, @token)
if ! status
raise 'logon failed'
@@ -50,20 +52,24 @@ class Chef @session_opened = true
end
- def close
- if ! @session_opened
- raise 'Session not open'
- end
-
+ def close!
if @impersonating
restore_user_context
end
- Chef::ReservedNames::Win32::API.CloseHandle(@token)
+ Chef::ReservedNames::Win32::API::System.CloseHandle(@token.read_ulong)
@token = nil
@session_opened = false
end
+ def close
+ if ! @session_opened
+ raise 'Session not open'
+ end
+
+ close!
+ end
+
def set_user_context
if ! @session_opened
raise 'Session not open'
|