summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Edwards <adamed@opscode.com>2015-10-11 22:43:33 -0700
committerAdam Edwards <adamed@opscode.com>2015-10-11 22:43:33 -0700
commitcc839a7f6f332e3f1c4f3b90d5099384b3904766 (patch)
tree54227c7098d0c85c74ec519baa56225f51b443df
parentc36bce07f4fc3ae3fc322f5271a83ac620b5b691 (diff)
downloadchef-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.rb37
-rw-r--r--lib/chef/resource/remote_file.rb51
-rw-r--r--lib/chef/util/windows/logon_session.rb22
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'