rbvmomi 1.11.3 → 3.0.0
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.
- checksums.yaml +5 -5
- data/README.md +2 -2
- data/exe/rbvmomish +5 -5
- data/lib/rbvmomi/connection.rb +13 -13
- data/lib/rbvmomi/deserialization.rb +1 -2
- data/lib/rbvmomi/{trollop.rb → optimist.rb} +7 -7
- data/lib/rbvmomi/pbm.rb +1 -1
- data/lib/rbvmomi/sms.rb +1 -1
- data/lib/rbvmomi/sso.rb +313 -0
- data/lib/rbvmomi/trivial_soap.rb +8 -5
- data/lib/rbvmomi/type_loader.rb +1 -1
- data/lib/rbvmomi/utils/deploy.rb +1 -1
- data/lib/rbvmomi/version.rb +2 -2
- data/lib/rbvmomi/vim/Folder.rb +1 -1
- data/lib/rbvmomi/vim/HostSystem.rb +2 -2
- data/lib/rbvmomi/vim/ManagedObject.rb +1 -1
- data/lib/rbvmomi/vim/OvfManager.rb +1 -1
- data/lib/rbvmomi/vim/VirtualMachine.rb +9 -11
- data/lib/rbvmomi/vim.rb +9 -10
- data/lib/rbvmomi.rb +11 -9
- data/vmodl.db +0 -0
- metadata +45 -61
- data/.gitignore +0 -13
- data/.travis.yml +0 -11
- data/.yardopts +0 -6
- data/CONTRIBUTORS.md +0 -42
- data/Gemfile +0 -10
- data/Rakefile +0 -16
- data/devel/analyze-vim-declarations.rb +0 -217
- data/devel/analyze-xml.rb +0 -49
- data/devel/benchmark.rb +0 -121
- data/devel/collisions.rb +0 -22
- data/devel/merge-internal-vmodl.rb +0 -63
- data/devel/merge-manual-vmodl.rb +0 -36
- data/examples/annotate.rb +0 -57
- data/examples/cached_ovf_deploy.rb +0 -124
- data/examples/clone_vm.rb +0 -88
- data/examples/create_vm-1.9.rb +0 -97
- data/examples/create_vm.rb +0 -97
- data/examples/extraConfig.rb +0 -57
- data/examples/lease_tool.rb +0 -106
- data/examples/logbundle.rb +0 -66
- data/examples/logtail.rb +0 -63
- data/examples/nfs_datastore.rb +0 -99
- data/examples/power.rb +0 -62
- data/examples/readme-1.rb +0 -38
- data/examples/readme-2.rb +0 -54
- data/examples/run.sh +0 -41
- data/examples/screenshot.rb +0 -51
- data/examples/vdf.rb +0 -84
- data/examples/vm_drs_behavior.rb +0 -80
- data/rbvmomi.gemspec +0 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a94fe8a2f4faf96128119f1108856f73cd48ad979cb7f55da44d85628f7aae7a
|
4
|
+
data.tar.gz: 58f4de40f316bab9631062b8f5cad76f12eed1bf420ff9287b95788e7f1b58f5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08f70868de310a5b3c6b23d376058b02cfba807de7b3dd7bef71f2b976b7400ea1b48ad8e63f1461227a5f8b75eaf7cace3c7f4e73c6acf03378fffe55aaa635'
|
7
|
+
data.tar.gz: 6d49a75c34cb17b774a05eb6319818bd56a2762ee9f84da62730f70abf7a143ecf281dfe70884c1bb32040faa680443555f68a3770be0a25b201178d7f4ea93e
|
data/README.md
CHANGED
@@ -56,7 +56,7 @@ require 'rbvmomi'
|
|
56
56
|
|
57
57
|
vim = RbVmomi::VIM.connect(host: 'foo', user: 'bar', password: 'baz')
|
58
58
|
root_folder = vim.serviceInstance.content.rootFolder
|
59
|
-
dc =
|
59
|
+
dc = root_folder.childEntity.grep(RbVmomi::VIM::Datacenter).find { |x| x.name == 'mydatacenter' } || fail('datacenter not found')
|
60
60
|
vm = dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine).find { |x| x.name == 'my_vm' } || fail('VM not found')
|
61
61
|
task = vm.PowerOnVM_Task
|
62
62
|
filter = vim.propertyCollector.CreateFilter(
|
@@ -89,7 +89,7 @@ A few important points:
|
|
89
89
|
* Data object types can usually be inferred from context, so you may use a hash instead.
|
90
90
|
* Enumeration values are simply strings.
|
91
91
|
* Example code is included in the examples/ directory.
|
92
|
-
* A set of helper methods for
|
92
|
+
* A set of helper methods for Optimist is included to speed up development of
|
93
93
|
command line apps. See the included examples for usage.
|
94
94
|
* If you don't have trusted SSL certificates installed on the host you're
|
95
95
|
connecting to, you'll get an `OpenSSL::SSL::SSLError` "certificate verify
|
data/exe/rbvmomish
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
# TODO keepalive
|
3
3
|
# TODO rc file
|
4
4
|
# TODO proxy support?
|
5
|
-
require '
|
5
|
+
require 'optimist'
|
6
6
|
require 'readline'
|
7
7
|
require 'rbvmomi'
|
8
|
-
require 'rbvmomi/
|
8
|
+
require 'rbvmomi/optimist'
|
9
9
|
|
10
10
|
VIM = RbVmomi::VIM
|
11
11
|
|
12
|
-
opts =
|
12
|
+
opts = Optimist.options do
|
13
13
|
banner <<-EOS
|
14
14
|
vSphere API console.
|
15
15
|
|
@@ -35,7 +35,7 @@ VIM connection options:
|
|
35
35
|
Other options:
|
36
36
|
EOS
|
37
37
|
|
38
|
-
$
|
38
|
+
$optimist = self
|
39
39
|
end
|
40
40
|
|
41
41
|
begin
|
@@ -102,7 +102,7 @@ def si
|
|
102
102
|
end
|
103
103
|
|
104
104
|
def help
|
105
|
-
$
|
105
|
+
$optimist.educate
|
106
106
|
:no_result
|
107
107
|
end
|
108
108
|
|
data/lib/rbvmomi/connection.rb
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
|
4
4
|
require 'time'
|
5
5
|
require 'date'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
require_relative 'trivial_soap'
|
7
|
+
require_relative 'basic_types'
|
8
|
+
require_relative 'fault'
|
9
|
+
require_relative 'type_loader'
|
10
|
+
require_relative 'deserialization'
|
11
11
|
|
12
12
|
module RbVmomi
|
13
13
|
|
@@ -23,7 +23,7 @@ class Connection < TrivialSoap
|
|
23
23
|
attr_reader :profile_summary
|
24
24
|
attr_accessor :profiling
|
25
25
|
attr_reader :deserializer
|
26
|
-
|
26
|
+
|
27
27
|
def initialize opts
|
28
28
|
@ns = opts[:ns] or fail "no namespace specified"
|
29
29
|
@rev = opts[:rev] or fail "no revision specified"
|
@@ -32,7 +32,7 @@ class Connection < TrivialSoap
|
|
32
32
|
@profiling = false
|
33
33
|
super opts
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
def reset_profiling
|
37
37
|
@profile = {}
|
38
38
|
@profile_summary = {:network_latency => 0, :request_emit => 0, :response_parse => 0, :num_calls => 0}
|
@@ -90,7 +90,7 @@ class Connection < TrivialSoap
|
|
90
90
|
|
91
91
|
t3 = Time.now
|
92
92
|
out = parse_response resp, desc['result']
|
93
|
-
|
93
|
+
|
94
94
|
if @profiling
|
95
95
|
t4 = Time.now
|
96
96
|
@profile[method] ||= []
|
@@ -98,8 +98,8 @@ class Connection < TrivialSoap
|
|
98
98
|
:network_latency => (t3 - t2),
|
99
99
|
:request_emit => t2 - t1,
|
100
100
|
:response_parse => t4 - t3,
|
101
|
-
:params => params,
|
102
|
-
:obj => this,
|
101
|
+
:params => params,
|
102
|
+
:obj => this,
|
103
103
|
:backtrace => caller,
|
104
104
|
:request_size => body.length,
|
105
105
|
:response_size => resp_size,
|
@@ -110,7 +110,7 @@ class Connection < TrivialSoap
|
|
110
110
|
@profile_summary[:request_emit] += profile_info[:request_emit]
|
111
111
|
@profile_summary[:num_calls] += 1
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
out
|
115
115
|
end
|
116
116
|
|
@@ -139,7 +139,7 @@ class Connection < TrivialSoap
|
|
139
139
|
when BasicTypes::DataObject
|
140
140
|
if expected and not expected >= o.class and not expected == BasicTypes::AnyType
|
141
141
|
fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}"
|
142
|
-
end
|
142
|
+
end
|
143
143
|
xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do
|
144
144
|
o.class.full_props_desc.each do |desc|
|
145
145
|
if o.props.member? desc['name'].to_sym
|
@@ -223,7 +223,7 @@ class Connection < TrivialSoap
|
|
223
223
|
def type name
|
224
224
|
self.class.type name
|
225
225
|
end
|
226
|
-
|
226
|
+
|
227
227
|
def instanceUuid
|
228
228
|
nil
|
229
229
|
end
|
@@ -101,7 +101,6 @@ class NewDeserializer
|
|
101
101
|
obj = klass.new nil
|
102
102
|
props = obj.props
|
103
103
|
children = node.children.select(&:element?)
|
104
|
-
n = children.size
|
105
104
|
i = 0
|
106
105
|
|
107
106
|
klass.full_props_desc.each do |desc|
|
@@ -152,7 +151,7 @@ class NewDeserializer
|
|
152
151
|
h[child.name] = child.content
|
153
152
|
end
|
154
153
|
[h['key'], h['value']]
|
155
|
-
end
|
154
|
+
end
|
156
155
|
end
|
157
156
|
|
158
157
|
class OldDeserializer
|
@@ -1,19 +1,19 @@
|
|
1
1
|
# Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved.
|
2
2
|
# SPDX-License-Identifier: MIT
|
3
3
|
|
4
|
-
require '
|
4
|
+
require 'optimist'
|
5
5
|
|
6
|
-
# Convenience methods for
|
7
|
-
# @see http://
|
8
|
-
# @see
|
9
|
-
module
|
6
|
+
# Convenience methods for Optimist, Ruby's premier option parser.
|
7
|
+
# @see http://optimist.rubyforge.org/
|
8
|
+
# @see Optimist::Parser
|
9
|
+
module Optimist
|
10
10
|
|
11
|
-
# Convenience methods for
|
11
|
+
# Convenience methods for Optimist, Ruby's premier option parser.
|
12
12
|
#
|
13
13
|
# See the examples directory for sample code.
|
14
14
|
# Descriptions are of the form:
|
15
15
|
# <key>: <options> <environment variable> (<default>)
|
16
|
-
# @see http://
|
16
|
+
# @see http://optimist.rubyforge.org/
|
17
17
|
class Parser
|
18
18
|
# Options used by VIM.connect
|
19
19
|
#
|
data/lib/rbvmomi/pbm.rb
CHANGED
data/lib/rbvmomi/sms.rb
CHANGED
data/lib/rbvmomi/sso.rb
ADDED
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'net/https'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'openssl'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'time'
|
7
|
+
|
8
|
+
module RbVmomi
|
9
|
+
# Provides access to vCenter Single Sign-On
|
10
|
+
class SSO
|
11
|
+
BST_PROFILE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'.freeze
|
12
|
+
C14N_CLASS = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
|
13
|
+
C14N_METHOD = 'http://www.w3.org/2001/10/xml-exc-c14n#'.freeze
|
14
|
+
DIGEST_METHOD = 'http://www.w3.org/2001/04/xmlenc#sha512'.freeze
|
15
|
+
ENCODING_METHOD = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'.freeze
|
16
|
+
SIGNATURE_METHOD = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'.freeze
|
17
|
+
STS_PATH = '/sts/STSService'.freeze
|
18
|
+
TOKEN_TYPE = 'urn:oasis:names:tc:SAML:2.0:assertion'.freeze
|
19
|
+
TOKEN_PROFILE = 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0'.freeze
|
20
|
+
NAMESPACES = {
|
21
|
+
:ds => 'http://www.w3.org/2000/09/xmldsig#',
|
22
|
+
:soap => 'http://schemas.xmlsoap.org/soap/envelope/',
|
23
|
+
:wsse => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
|
24
|
+
:wsse11 => 'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd',
|
25
|
+
:wst => 'http://docs.oasis-open.org/ws-sx/ws-trust/200512',
|
26
|
+
:wsu => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
attr_reader :assertion,
|
30
|
+
:assertion_id,
|
31
|
+
:certificate,
|
32
|
+
:host,
|
33
|
+
:user,
|
34
|
+
:password,
|
35
|
+
:path,
|
36
|
+
:port,
|
37
|
+
:private_key
|
38
|
+
|
39
|
+
# Creates an instance of an SSO object
|
40
|
+
#
|
41
|
+
# @param [Hash] opts the options to create the object with
|
42
|
+
# @option opts [String] :host the host to connect to
|
43
|
+
# @option opts [Fixnum] :port (443) the port to connect to
|
44
|
+
# @option opts [String] :path the path to call
|
45
|
+
# @option opts [String] :user the user to authenticate with
|
46
|
+
# @option opts [String] :password the password to authenticate with
|
47
|
+
# @option opts [String] :private_key the private key to use
|
48
|
+
# @option opts [String] :certificate the certificate to use
|
49
|
+
# @option opts [Boolean] :insecure (false) whether to connect insecurely
|
50
|
+
def initialize(opts = {})
|
51
|
+
@host = opts[:host]
|
52
|
+
@insecure = opts.fetch(:insecure, false)
|
53
|
+
@password = opts[:password]
|
54
|
+
@path = opts.fetch(:path, STS_PATH)
|
55
|
+
@port = opts.fetch(:port, 443)
|
56
|
+
@user = opts[:user]
|
57
|
+
|
58
|
+
load_x509(opts[:private_key], opts[:certificate])
|
59
|
+
end
|
60
|
+
|
61
|
+
def request_token
|
62
|
+
req = sso_call(hok_token_request)
|
63
|
+
|
64
|
+
unless req.is_a?(Net::HTTPSuccess)
|
65
|
+
resp = Nokogiri::XML(req.body)
|
66
|
+
resp.remove_namespaces!
|
67
|
+
raise(resp.at_xpath('//Envelope/Body/Fault/faultstring/text()'))
|
68
|
+
end
|
69
|
+
|
70
|
+
extract_assertion(req.body)
|
71
|
+
end
|
72
|
+
|
73
|
+
def sign_request(request)
|
74
|
+
raise('Need SAML2 assertion') unless @assertion
|
75
|
+
raise('No SAML2 assertion ID') unless @assertion_id
|
76
|
+
|
77
|
+
request_id = generate_id
|
78
|
+
timestamp_id = generate_id
|
79
|
+
|
80
|
+
request = request.is_a?(String) ? Nokogiri::XML(request) : request
|
81
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
82
|
+
xml[:soap].Header(Hash[NAMESPACES.map { |ns, uri| ["xmlns:#{ns}", uri] }]) do
|
83
|
+
xml[:wsse].Security do
|
84
|
+
wsu_timestamp(xml, timestamp_id)
|
85
|
+
ds_signature(xml, request_id, timestamp_id) do |x|
|
86
|
+
x[:wsse].SecurityTokenReference('wsse11:TokenType' => TOKEN_PROFILE) do
|
87
|
+
x[:wsse].KeyIdentifier(
|
88
|
+
@assertion_id,
|
89
|
+
'ValueType' => 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID'
|
90
|
+
)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# To avoid Nokogiri mangling the token, we replace it as a string
|
98
|
+
# later on. Figure out a way around this.
|
99
|
+
builder.doc.at_xpath('//soap:Header/wsse:Security/wsu:Timestamp').add_previous_sibling(Nokogiri::XML::Text.new('SAML_ASSERTION_PLACEHOLDER', builder.doc))
|
100
|
+
|
101
|
+
request.at_xpath('//soap:Envelope', NAMESPACES).tap do |e|
|
102
|
+
NAMESPACES.each do |ns, uri|
|
103
|
+
e.add_namespace(ns.to_s, uri)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
request.xpath('//soap:Envelope/soap:Body').each do |body|
|
107
|
+
body.add_previous_sibling(builder.doc.root)
|
108
|
+
body.add_namespace('wsu', NAMESPACES[:wsu])
|
109
|
+
body['wsu:Id'] = request_id
|
110
|
+
end
|
111
|
+
|
112
|
+
signed = sign(request)
|
113
|
+
signed.gsub!('SAML_ASSERTION_PLACEHOLDER', @assertion.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip)
|
114
|
+
|
115
|
+
signed
|
116
|
+
end
|
117
|
+
|
118
|
+
# We default to Issue, since that's all we currently need.
|
119
|
+
def sso_call(body)
|
120
|
+
sso_url = URI::HTTPS.build(:host => @host, :port => @port, :path => @path)
|
121
|
+
http = Net::HTTP.new(sso_url.host, sso_url.port)
|
122
|
+
http.use_ssl = true
|
123
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @insecure
|
124
|
+
|
125
|
+
req = Net::HTTP::Post.new(sso_url.request_uri)
|
126
|
+
req.add_field('Accept', 'text/xml, multipart/related')
|
127
|
+
req.add_field('User-Agent', "VMware/RbVmomi #{RbVmomi::VERSION}")
|
128
|
+
req.add_field('SOAPAction', 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue')
|
129
|
+
req.content_type = 'text/xml; charset="UTF-8"'
|
130
|
+
req.body = body
|
131
|
+
|
132
|
+
http.request(req)
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def hok_token_request
|
138
|
+
request_id = generate_id
|
139
|
+
security_token_id = generate_id
|
140
|
+
signature_id = generate_id
|
141
|
+
timestamp_id = generate_id
|
142
|
+
|
143
|
+
datum = Time.now.utc
|
144
|
+
created_at = datum.iso8601
|
145
|
+
token_expires_at = (datum + 1800).iso8601
|
146
|
+
|
147
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
148
|
+
xml[:soap].Envelope(Hash[NAMESPACES.map { |ns, uri| ["xmlns:#{ns}", uri] }]) do
|
149
|
+
xml[:soap].Header do
|
150
|
+
xml[:wsse].Security do
|
151
|
+
wsu_timestamp(xml, timestamp_id, datum)
|
152
|
+
wsse_username_token(xml)
|
153
|
+
wsse_binary_security_token(xml, security_token_id)
|
154
|
+
ds_signature(xml, request_id, timestamp_id, signature_id) do |x|
|
155
|
+
x[:wsse].SecurityTokenReference do
|
156
|
+
x[:wsse].Reference(
|
157
|
+
'URI' => "##{security_token_id}",
|
158
|
+
'ValueType' => BST_PROFILE
|
159
|
+
)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
xml[:soap].Body('wsu:Id' => request_id) do
|
165
|
+
xml[:wst].RequestSecurityToken do
|
166
|
+
xml[:wst].TokenType(TOKEN_TYPE)
|
167
|
+
xml[:wst].RequestType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue')
|
168
|
+
xml[:wst].Lifetime do
|
169
|
+
xml[:wsu].Created(created_at)
|
170
|
+
xml[:wsu].Expires(token_expires_at)
|
171
|
+
end
|
172
|
+
xml[:wst].Renewing('Allow' => 'false', 'OK' => 'false')
|
173
|
+
xml[:wst].KeyType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey')
|
174
|
+
xml[:wst].SignatureAlgorithm(SIGNATURE_METHOD)
|
175
|
+
xml[:wst].Delegatable('false')
|
176
|
+
end
|
177
|
+
xml[:wst].UseKey('Sig' => signature_id)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
sign(builder.doc)
|
183
|
+
end
|
184
|
+
|
185
|
+
def extract_assertion(sso_response)
|
186
|
+
sso_response = Nokogiri::XML(sso_response) if sso_response.is_a?(String)
|
187
|
+
namespaces = sso_response.collect_namespaces
|
188
|
+
|
189
|
+
# Doesn't matter that usually there's more than one NS with the same
|
190
|
+
# URI - either will work for XPath. We just don't want to hardcode
|
191
|
+
# xmlns:saml2.
|
192
|
+
token_ns = namespaces.find { |_, uri| uri == TOKEN_TYPE }.first.gsub(/^xmlns:/, '')
|
193
|
+
|
194
|
+
@assertion = sso_response.at_xpath("//#{token_ns}:Assertion", namespaces)
|
195
|
+
@assertion_id = @assertion.at_xpath("//#{token_ns}:Assertion/@ID", namespaces).value
|
196
|
+
end
|
197
|
+
|
198
|
+
def sign(doc)
|
199
|
+
signature_digest_references = doc.xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference/@URI', doc.collect_namespaces).map { |a| a.value.sub(/^#/, '') }
|
200
|
+
signature_digest_references.each do |ref|
|
201
|
+
data = doc.at_xpath("//*[@wsu:Id='#{ref}']", doc.collect_namespaces)
|
202
|
+
digest = Base64.strict_encode64(Digest::SHA2.new(512).digest(data.canonicalize(C14N_CLASS)))
|
203
|
+
digest_tag = doc.at_xpath("/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference[@URI='##{ref}']/ds:DigestValue", doc.collect_namespaces)
|
204
|
+
digest_tag.add_child(Nokogiri::XML::Text.new(digest, doc))
|
205
|
+
end
|
206
|
+
|
207
|
+
signed_info = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo', doc.collect_namespaces)
|
208
|
+
signature = Base64.strict_encode64(@private_key.sign(OpenSSL::Digest::SHA512.new, signed_info.canonicalize(C14N_CLASS)))
|
209
|
+
signature_value_tag = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignatureValue', doc.collect_namespaces)
|
210
|
+
signature_value_tag.add_child(Nokogiri::XML::Text.new(signature, doc))
|
211
|
+
|
212
|
+
doc.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip
|
213
|
+
end
|
214
|
+
|
215
|
+
def load_x509(private_key, certificate)
|
216
|
+
@private_key = private_key ? private_key : OpenSSL::PKey::RSA.new(2048)
|
217
|
+
if @private_key.is_a? String
|
218
|
+
@private_key = OpenSSL::PKey::RSA.new(@private_key)
|
219
|
+
end
|
220
|
+
|
221
|
+
@certificate = certificate
|
222
|
+
if @certificate && !private_key
|
223
|
+
raise(ArgumentError, "Can't generate private key from a certificate")
|
224
|
+
end
|
225
|
+
|
226
|
+
if @certificate.is_a? String
|
227
|
+
@certificate = OpenSSL::X509::Certificate.new(@certificate)
|
228
|
+
end
|
229
|
+
# If only a private key is specified, we will generate a certificate.
|
230
|
+
unless @certificate
|
231
|
+
timestamp = Time.now.utc
|
232
|
+
@certificate = OpenSSL::X509::Certificate.new
|
233
|
+
@certificate.not_before = timestamp
|
234
|
+
@certificate.not_after = timestamp + 3600 # 3600 is 1 hour
|
235
|
+
@certificate.subject = OpenSSL::X509::Name.new([
|
236
|
+
%w[O VMware],
|
237
|
+
%w[OU RbVmomi],
|
238
|
+
%W[CN #{@user}]
|
239
|
+
])
|
240
|
+
@certificate.issuer = @certificate.subject
|
241
|
+
@certificate.serial = rand(2**160)
|
242
|
+
@certificate.public_key = @private_key.public_key
|
243
|
+
@certificate.sign(@private_key, OpenSSL::Digest::SHA512.new)
|
244
|
+
end
|
245
|
+
|
246
|
+
true
|
247
|
+
end
|
248
|
+
|
249
|
+
def ds_signature(xml, request_id, timestamp_id, id = nil)
|
250
|
+
signature_id = {}
|
251
|
+
signature_id['Id'] = id if id
|
252
|
+
xml[:ds].Signature(signature_id) do
|
253
|
+
ds_signed_info(xml, request_id, timestamp_id)
|
254
|
+
xml[:ds].SignatureValue
|
255
|
+
xml[:ds].KeyInfo do
|
256
|
+
yield xml
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def ds_signed_info(xml, request_id, timestamp_id)
|
262
|
+
xml[:ds].SignedInfo do
|
263
|
+
xml[:ds].CanonicalizationMethod('Algorithm' => C14N_METHOD)
|
264
|
+
xml[:ds].SignatureMethod('Algorithm' => SIGNATURE_METHOD)
|
265
|
+
xml[:ds].Reference('URI' => "##{request_id}") do
|
266
|
+
xml[:ds].Transforms do
|
267
|
+
xml[:ds].Transform('Algorithm' => C14N_METHOD)
|
268
|
+
end
|
269
|
+
xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD)
|
270
|
+
xml[:ds].DigestValue
|
271
|
+
end
|
272
|
+
xml[:ds].Reference('URI' => "##{timestamp_id}") do
|
273
|
+
xml[:ds].Transforms do
|
274
|
+
xml[:ds].Transform('Algorithm' => C14N_METHOD)
|
275
|
+
end
|
276
|
+
xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD)
|
277
|
+
xml[:ds].DigestValue
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def wsu_timestamp(xml, id, datum = nil)
|
283
|
+
datum ||= Time.now.utc
|
284
|
+
created_at = datum.iso8601
|
285
|
+
expires_at = (datum + 600).iso8601
|
286
|
+
|
287
|
+
xml[:wsu].Timestamp('wsu:Id' => id) do
|
288
|
+
xml[:wsu].Created(created_at)
|
289
|
+
xml[:wsu].Expires(expires_at)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def wsse_username_token(xml)
|
294
|
+
xml[:wsse].UsernameToken do
|
295
|
+
xml[:wsse].Username(@user)
|
296
|
+
xml[:wsse].Password(@password)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def wsse_binary_security_token(xml, id)
|
301
|
+
xml[:wsse].BinarySecurityToken(
|
302
|
+
Base64.strict_encode64(@certificate.to_der),
|
303
|
+
'EncodingType' => ENCODING_METHOD,
|
304
|
+
'ValueType' => BST_PROFILE,
|
305
|
+
'wsu:Id' => id
|
306
|
+
)
|
307
|
+
end
|
308
|
+
|
309
|
+
def generate_id
|
310
|
+
"_#{SecureRandom.uuid}"
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
data/lib/rbvmomi/trivial_soap.rb
CHANGED
@@ -17,6 +17,7 @@ class RbVmomi::TrivialSoap
|
|
17
17
|
return unless @opts[:host] # for testcases
|
18
18
|
@debug = @opts[:debug]
|
19
19
|
@cookie = @opts[:cookie]
|
20
|
+
@sso = @opts[:sso]
|
20
21
|
@operation_id = @opts[:operation_id]
|
21
22
|
@lock = Mutex.new
|
22
23
|
@http = nil
|
@@ -42,11 +43,8 @@ class RbVmomi::TrivialSoap
|
|
42
43
|
if @opts[:ssl]
|
43
44
|
require 'net/https'
|
44
45
|
@http.use_ssl = true
|
45
|
-
if @opts[:insecure]
|
46
|
-
|
47
|
-
else
|
48
|
-
@http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
49
|
-
end
|
46
|
+
@http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @opts[:insecure]
|
47
|
+
@http.ca_file = @opts[:ca_file] if @opts[:ca_file]
|
50
48
|
@http.cert = OpenSSL::X509::Certificate.new(@opts[:cert]) if @opts[:cert]
|
51
49
|
@http.key = OpenSSL::PKey::RSA.new(@opts[:key]) if @opts[:key]
|
52
50
|
end
|
@@ -89,6 +87,11 @@ class RbVmomi::TrivialSoap
|
|
89
87
|
$stderr.puts
|
90
88
|
end
|
91
89
|
|
90
|
+
if @cookie.nil? && @sso
|
91
|
+
@sso.request_token unless @sso.assertion_id
|
92
|
+
body = @sso.sign_request(body)
|
93
|
+
end
|
94
|
+
|
92
95
|
start_time = Time.now
|
93
96
|
response = @lock.synchronize do
|
94
97
|
begin
|
data/lib/rbvmomi/type_loader.rb
CHANGED
data/lib/rbvmomi/utils/deploy.rb
CHANGED
data/lib/rbvmomi/version.rb
CHANGED
data/lib/rbvmomi/vim/Folder.rb
CHANGED
@@ -67,7 +67,7 @@ class RbVmomi::VIM::Folder
|
|
67
67
|
propSpecs = {
|
68
68
|
:entity => self, :inventoryPath => path
|
69
69
|
}
|
70
|
-
|
70
|
+
_connection.searchIndex.FindByInventoryPath(propSpecs)
|
71
71
|
end
|
72
72
|
|
73
73
|
# Alias to <tt>traverse path, type, true</tt>
|
@@ -115,12 +115,12 @@ class VIM::EsxcliNamespace
|
|
115
115
|
conn.type(@type_info.wsdlName).new(conn, @instance)
|
116
116
|
end
|
117
117
|
|
118
|
-
def method_missing
|
118
|
+
def method_missing(name, *args)
|
119
119
|
name = name.to_s
|
120
120
|
if @namespaces.member? name and args.empty?
|
121
121
|
@namespaces[name]
|
122
122
|
elsif @commands.member? name
|
123
|
-
@commands[name].call
|
123
|
+
@commands[name].call(*args)
|
124
124
|
else
|
125
125
|
raise NoMethodError
|
126
126
|
end
|
@@ -52,7 +52,7 @@ class RbVmomi::VIM::ManagedObject
|
|
52
52
|
# @return [Array] Property values in same order as +pathSet+, or the return
|
53
53
|
# value from the block if it is given.
|
54
54
|
def collect *pathSet
|
55
|
-
h = collect!
|
55
|
+
h = collect!(*pathSet)
|
56
56
|
a = pathSet.map { |k| h[k.to_s] }
|
57
57
|
if block_given?
|
58
58
|
yield a
|
@@ -139,7 +139,7 @@ class RbVmomi::VIM::OvfManager
|
|
139
139
|
# to the uploadCmd. It is not clear to me why, but that leads to
|
140
140
|
# trucation of the uploaded disk. Without this option curl can't tell
|
141
141
|
# the progress, but who cares
|
142
|
-
system("#{downloadCmd} | #{uploadCmd}",
|
142
|
+
system("#{downloadCmd} | #{uploadCmd}", :out => "/dev/null")
|
143
143
|
|
144
144
|
keepAliveThread.kill
|
145
145
|
keepAliveThread.join
|