winrm 1.7.1 → 1.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +10 -10
  3. data/.rspec +3 -3
  4. data/.rubocop.yml +12 -12
  5. data/.travis.yml +12 -12
  6. data/Gemfile +9 -9
  7. data/LICENSE +202 -202
  8. data/README.md +194 -194
  9. data/Rakefile +36 -36
  10. data/Vagrantfile +9 -9
  11. data/appveyor.yml +42 -42
  12. data/bin/rwinrm +97 -97
  13. data/changelog.md +77 -74
  14. data/lib/winrm.rb +42 -42
  15. data/lib/winrm/command_executor.rb +224 -224
  16. data/lib/winrm/command_output_decoder.rb +53 -0
  17. data/lib/winrm/exceptions/exceptions.rb +57 -57
  18. data/lib/winrm/helpers/iso8601_duration.rb +58 -58
  19. data/lib/winrm/helpers/powershell_script.rb +42 -42
  20. data/lib/winrm/http/response_handler.rb +82 -82
  21. data/lib/winrm/http/transport.rb +421 -421
  22. data/lib/winrm/output.rb +43 -43
  23. data/lib/winrm/soap_provider.rb +39 -39
  24. data/lib/winrm/version.rb +7 -7
  25. data/lib/winrm/winrm_service.rb +547 -556
  26. data/preamble +17 -17
  27. data/spec/auth_timeout_spec.rb +16 -16
  28. data/spec/cmd_spec.rb +102 -102
  29. data/spec/command_executor_spec.rb +429 -429
  30. data/spec/command_output_decoder_spec.rb +37 -0
  31. data/spec/config-example.yml +19 -19
  32. data/spec/exception_spec.rb +50 -50
  33. data/spec/issue_184_spec.rb +67 -67
  34. data/spec/issue_59_spec.rb +23 -23
  35. data/spec/matchers.rb +74 -74
  36. data/spec/output_spec.rb +110 -110
  37. data/spec/powershell_spec.rb +97 -97
  38. data/spec/response_handler_spec.rb +59 -59
  39. data/spec/spec_helper.rb +73 -73
  40. data/spec/stubs/responses/get_command_output_response.xml.erb +13 -13
  41. data/spec/stubs/responses/open_shell_v1.xml +19 -19
  42. data/spec/stubs/responses/open_shell_v2.xml +20 -20
  43. data/spec/stubs/responses/soap_fault_v1.xml +36 -36
  44. data/spec/stubs/responses/soap_fault_v2.xml +42 -42
  45. data/spec/stubs/responses/wmi_error_v2.xml +41 -41
  46. data/spec/transport_spec.rb +124 -124
  47. data/spec/winrm_options_spec.rb +76 -76
  48. data/spec/winrm_primitives_spec.rb +51 -51
  49. data/spec/wql_spec.rb +14 -14
  50. data/winrm.gemspec +40 -40
  51. metadata +5 -3
@@ -0,0 +1,53 @@
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Copyright 2016 Shawn Neal <sneal@sneal.net>
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
+ require 'base64'
18
+
19
+ module WinRM
20
+ # Handles decoding a raw output response
21
+ class CommandOutputDecoder
22
+ # Decode the raw SOAP output into decoded and human consumable text,
23
+ # Decodes and replaces invalid unicode characters.
24
+ # @param raw_output [String] The raw encoded output
25
+ # @return [String] The decoded output
26
+ def decode(raw_output)
27
+ decoded_text = decode_raw_output(raw_output)
28
+ decoded_text = handle_invalid_encoding(decoded_text)
29
+ decoded_text = remove_bom(decoded_text)
30
+ decoded_text
31
+ end
32
+
33
+ private
34
+
35
+ def decode_raw_output(raw_output)
36
+ Base64.decode64(raw_output).force_encoding('utf-8')
37
+ end
38
+
39
+ def handle_invalid_encoding(decoded_text)
40
+ return decoded_text if decoded_text.valid_encoding?
41
+ if decoded_text.respond_to?(:scrub)
42
+ decoded_text.scrub
43
+ else
44
+ decoded_text.encode('utf-16', invalid: :replace, undef: :replace).encode('utf-8')
45
+ end
46
+ end
47
+
48
+ def remove_bom(decoded_text)
49
+ # remove BOM which 2008R2 applies
50
+ decoded_text.sub("\xEF\xBB\xBF", '')
51
+ end
52
+ end
53
+ end
@@ -1,57 +1,57 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
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
- module WinRM
18
- # WinRM base class for errors
19
- class WinRMError < StandardError; end
20
-
21
- # Authorization Error
22
- class WinRMAuthorizationError < WinRMError; end
23
-
24
- # A Fault returned in the SOAP response. The XML node is a WSManFault
25
- class WinRMWSManFault < WinRMError
26
- attr_reader :fault_code
27
- attr_reader :fault_description
28
-
29
- def initialize(fault_description, fault_code)
30
- @fault_description = fault_description
31
- @fault_code = fault_code
32
- super("[WSMAN ERROR CODE: #{fault_code}]: #{fault_description}")
33
- end
34
- end
35
-
36
- # A Fault returned in the SOAP response. The XML node is a MSFT_WmiError
37
- class WinRMWMIError < WinRMError
38
- attr_reader :error_code
39
- attr_reader :error
40
-
41
- def initialize(error, error_code)
42
- @error = error
43
- @error_code = error_code
44
- super("[WMI ERROR CODE: #{error_code}]: #{error}")
45
- end
46
- end
47
-
48
- # non-200 response without a SOAP fault
49
- class WinRMHTTPTransportError < WinRMError
50
- attr_reader :status_code
51
-
52
- def initialize(msg, status_code)
53
- @status_code = status_code
54
- super(msg + " (#{status_code}).")
55
- end
56
- end
57
- end
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
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
+ module WinRM
18
+ # WinRM base class for errors
19
+ class WinRMError < StandardError; end
20
+
21
+ # Authorization Error
22
+ class WinRMAuthorizationError < WinRMError; end
23
+
24
+ # A Fault returned in the SOAP response. The XML node is a WSManFault
25
+ class WinRMWSManFault < WinRMError
26
+ attr_reader :fault_code
27
+ attr_reader :fault_description
28
+
29
+ def initialize(fault_description, fault_code)
30
+ @fault_description = fault_description
31
+ @fault_code = fault_code
32
+ super("[WSMAN ERROR CODE: #{fault_code}]: #{fault_description}")
33
+ end
34
+ end
35
+
36
+ # A Fault returned in the SOAP response. The XML node is a MSFT_WmiError
37
+ class WinRMWMIError < WinRMError
38
+ attr_reader :error_code
39
+ attr_reader :error
40
+
41
+ def initialize(error, error_code)
42
+ @error = error
43
+ @error_code = error_code
44
+ super("[WMI ERROR CODE: #{error_code}]: #{error}")
45
+ end
46
+ end
47
+
48
+ # non-200 response without a SOAP fault
49
+ class WinRMHTTPTransportError < WinRMError
50
+ attr_reader :status_code
51
+
52
+ def initialize(msg, status_code)
53
+ @status_code = status_code
54
+ super(msg + " (#{status_code}).")
55
+ end
56
+ end
57
+ end
@@ -1,58 +1,58 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
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
- # rubocop:disable Metrics/MethodLength
18
- # rubocop:disable Metrics/AbcSize
19
-
20
- # Format an ISO8601 Duration
21
- module Iso8601Duration
22
- # Convert the number of seconds to an ISO8601 duration format
23
- # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
24
- # @param [Fixnum] seconds The amount of seconds for this duration
25
- def self.sec_to_dur(seconds)
26
- seconds = seconds.to_i
27
- iso_str = 'P'
28
- if seconds > 604_800 # more than a week
29
- weeks = seconds / 604_800
30
- seconds -= (604_800 * weeks)
31
- iso_str << "#{weeks}W"
32
- end
33
- if seconds > 86_400 # more than a day
34
- days = seconds / 86_400
35
- seconds -= (86_400 * days)
36
- iso_str << "#{days}D"
37
- end
38
- if seconds > 0
39
- iso_str << 'T'
40
- if seconds > 3600 # more than an hour
41
- hours = seconds / 3600
42
- seconds -= (3600 * hours)
43
- iso_str << "#{hours}H"
44
- end
45
- if seconds > 60 # more than a minute
46
- minutes = seconds / 60
47
- seconds -= (60 * minutes)
48
- iso_str << "#{minutes}M"
49
- end
50
- iso_str << "#{seconds}S"
51
- end
52
-
53
- iso_str
54
- end
55
- end
56
-
57
- # rubocop:enable Metrics/MethodLength
58
- # rubocop:enable Metrics/AbcSize
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2010 Dan Wanek <dan.wanek@gmail.com>
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
+ # rubocop:disable Metrics/MethodLength
18
+ # rubocop:disable Metrics/AbcSize
19
+
20
+ # Format an ISO8601 Duration
21
+ module Iso8601Duration
22
+ # Convert the number of seconds to an ISO8601 duration format
23
+ # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
24
+ # @param [Fixnum] seconds The amount of seconds for this duration
25
+ def self.sec_to_dur(seconds)
26
+ seconds = seconds.to_i
27
+ iso_str = 'P'
28
+ if seconds > 604_800 # more than a week
29
+ weeks = seconds / 604_800
30
+ seconds -= (604_800 * weeks)
31
+ iso_str << "#{weeks}W"
32
+ end
33
+ if seconds > 86_400 # more than a day
34
+ days = seconds / 86_400
35
+ seconds -= (86_400 * days)
36
+ iso_str << "#{days}D"
37
+ end
38
+ if seconds > 0
39
+ iso_str << 'T'
40
+ if seconds > 3600 # more than an hour
41
+ hours = seconds / 3600
42
+ seconds -= (3600 * hours)
43
+ iso_str << "#{hours}H"
44
+ end
45
+ if seconds > 60 # more than a minute
46
+ minutes = seconds / 60
47
+ seconds -= (60 * minutes)
48
+ iso_str << "#{minutes}M"
49
+ end
50
+ iso_str << "#{seconds}S"
51
+ end
52
+
53
+ iso_str
54
+ end
55
+ end
56
+
57
+ # rubocop:enable Metrics/MethodLength
58
+ # rubocop:enable Metrics/AbcSize
@@ -1,42 +1,42 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2014 Shawn Neal <sneal@sneal.net>
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
- module WinRM
18
- # Wraps a PowerShell script to make it easy to Base64 encode for transport
19
- class PowershellScript
20
- attr_reader :text
21
-
22
- # Creates a new PowershellScript object which can be used to encode
23
- # PS scripts for safe transport over WinRM.
24
- # @param [String] The PS script text content
25
- def initialize(script)
26
- @text = script
27
- end
28
-
29
- # Encodes the script so that it can be passed to the PowerShell
30
- # --EncodedCommand argument.
31
- # @return [String] The UTF-16LE base64 encoded script
32
- def encoded
33
- encoded_script = safe_script(text).encode('UTF-16LE', 'UTF-8')
34
- Base64.strict_encode64(encoded_script)
35
- end
36
-
37
- # suppress the progress stream from leaking to stderr
38
- def safe_script(script)
39
- "$ProgressPreference='SilentlyContinue';" + script
40
- end
41
- end
42
- end
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2014 Shawn Neal <sneal@sneal.net>
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
+ module WinRM
18
+ # Wraps a PowerShell script to make it easy to Base64 encode for transport
19
+ class PowershellScript
20
+ attr_reader :text
21
+
22
+ # Creates a new PowershellScript object which can be used to encode
23
+ # PS scripts for safe transport over WinRM.
24
+ # @param [String] The PS script text content
25
+ def initialize(script)
26
+ @text = script
27
+ end
28
+
29
+ # Encodes the script so that it can be passed to the PowerShell
30
+ # --EncodedCommand argument.
31
+ # @return [String] The UTF-16LE base64 encoded script
32
+ def encoded
33
+ encoded_script = safe_script(text).encode('UTF-16LE', 'UTF-8')
34
+ Base64.strict_encode64(encoded_script)
35
+ end
36
+
37
+ # suppress the progress stream from leaking to stderr
38
+ def safe_script(script)
39
+ "$ProgressPreference='SilentlyContinue';" + script
40
+ end
41
+ end
42
+ end
@@ -1,82 +1,82 @@
1
- # encoding: UTF-8
2
- #
3
- # Copyright 2014 Shawn Neal <sneal@sneal.net>
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
- require 'rexml/document'
18
-
19
- module WinRM
20
- # Handles the raw WinRM HTTP response. Returns the body as an XML doc
21
- # or raises the appropriate WinRM error if the response is an error.
22
- class ResponseHandler
23
- # @param [String] The raw unparsed response body, if any
24
- # @param [Integer] The HTTP response status code
25
- def initialize(response_body, status_code)
26
- @response_body = response_body
27
- @status_code = status_code
28
- end
29
-
30
- # Processes the response from the WinRM service and either returns an XML
31
- # doc or raises an appropriate error.
32
- #
33
- # @returns [REXML::Document] The parsed response body
34
- def parse_to_xml
35
- raise_if_error
36
- response_xml
37
- end
38
-
39
- private
40
-
41
- def response_xml
42
- @response_xml ||= REXML::Document.new(@response_body)
43
- rescue REXML::ParseException => e
44
- raise WinRMHTTPTransportError.new(
45
- "Unable to parse WinRM response: #{e.message}", @status_code)
46
- end
47
-
48
- def raise_if_error
49
- return if @status_code == 200
50
- raise_if_auth_error
51
- raise_if_wsman_fault
52
- raise_if_wmi_error
53
- raise_transport_error
54
- end
55
-
56
- def raise_if_auth_error
57
- fail WinRMAuthorizationError if @status_code == 401
58
- end
59
-
60
- def raise_if_wsman_fault
61
- soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
62
- return if soap_errors.empty?
63
- fault = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_FAULT}:WSManFault")
64
- fail WinRMWSManFault.new(fault.to_s, fault.attributes['Code']) unless fault.nil?
65
- end
66
-
67
- def raise_if_wmi_error
68
- soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
69
- return if soap_errors.empty?
70
-
71
- error = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_MSFT}:MSFT_WmiError")
72
- return if error.nil?
73
-
74
- error_code = REXML::XPath.first(error, "//#{NS_WSMAN_MSFT}:error_Code").text
75
- fail WinRMWMIError.new(error.to_s, error_code)
76
- end
77
-
78
- def raise_transport_error
79
- fail WinRMHTTPTransportError.new('Bad HTTP response returned from server', @status_code)
80
- end
81
- end
82
- end
1
+ # encoding: UTF-8
2
+ #
3
+ # Copyright 2014 Shawn Neal <sneal@sneal.net>
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
+ require 'rexml/document'
18
+
19
+ module WinRM
20
+ # Handles the raw WinRM HTTP response. Returns the body as an XML doc
21
+ # or raises the appropriate WinRM error if the response is an error.
22
+ class ResponseHandler
23
+ # @param [String] The raw unparsed response body, if any
24
+ # @param [Integer] The HTTP response status code
25
+ def initialize(response_body, status_code)
26
+ @response_body = response_body
27
+ @status_code = status_code
28
+ end
29
+
30
+ # Processes the response from the WinRM service and either returns an XML
31
+ # doc or raises an appropriate error.
32
+ #
33
+ # @returns [REXML::Document] The parsed response body
34
+ def parse_to_xml
35
+ raise_if_error
36
+ response_xml
37
+ end
38
+
39
+ private
40
+
41
+ def response_xml
42
+ @response_xml ||= REXML::Document.new(@response_body)
43
+ rescue REXML::ParseException => e
44
+ raise WinRMHTTPTransportError.new(
45
+ "Unable to parse WinRM response: #{e.message}", @status_code)
46
+ end
47
+
48
+ def raise_if_error
49
+ return if @status_code == 200
50
+ raise_if_auth_error
51
+ raise_if_wsman_fault
52
+ raise_if_wmi_error
53
+ raise_transport_error
54
+ end
55
+
56
+ def raise_if_auth_error
57
+ fail WinRMAuthorizationError if @status_code == 401
58
+ end
59
+
60
+ def raise_if_wsman_fault
61
+ soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
62
+ return if soap_errors.empty?
63
+ fault = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_FAULT}:WSManFault")
64
+ fail WinRMWSManFault.new(fault.to_s, fault.attributes['Code']) unless fault.nil?
65
+ end
66
+
67
+ def raise_if_wmi_error
68
+ soap_errors = REXML::XPath.match(response_xml, "//#{NS_SOAP_ENV}:Body/#{NS_SOAP_ENV}:Fault/*")
69
+ return if soap_errors.empty?
70
+
71
+ error = REXML::XPath.first(soap_errors, "//#{NS_WSMAN_MSFT}:MSFT_WmiError")
72
+ return if error.nil?
73
+
74
+ error_code = REXML::XPath.first(error, "//#{NS_WSMAN_MSFT}:error_Code").text
75
+ fail WinRMWMIError.new(error.to_s, error_code)
76
+ end
77
+
78
+ def raise_transport_error
79
+ fail WinRMHTTPTransportError.new('Bad HTTP response returned from server', @status_code)
80
+ end
81
+ end
82
+ end