savon-xaop 0.7.2.1
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.
- data/CHANGELOG +124 -0
- data/README.textile +75 -0
- data/Rakefile +45 -0
- data/lib/savon/client.rb +84 -0
- data/lib/savon/core_ext/datetime.rb +8 -0
- data/lib/savon/core_ext/hash.rb +78 -0
- data/lib/savon/core_ext/net_http.rb +20 -0
- data/lib/savon/core_ext/object.rb +21 -0
- data/lib/savon/core_ext/string.rb +47 -0
- data/lib/savon/core_ext/symbol.rb +8 -0
- data/lib/savon/core_ext/uri.rb +10 -0
- data/lib/savon/core_ext.rb +3 -0
- data/lib/savon/request.rb +160 -0
- data/lib/savon/response.rb +108 -0
- data/lib/savon/soap.rb +176 -0
- data/lib/savon/wsdl.rb +122 -0
- data/lib/savon/wsse.rb +136 -0
- data/lib/savon.rb +34 -0
- data/spec/basic_spec_helper.rb +12 -0
- data/spec/endpoint_helper.rb +22 -0
- data/spec/fixtures/response/response_fixture.rb +36 -0
- data/spec/fixtures/response/xml/authentication.xml +14 -0
- data/spec/fixtures/response/xml/multi_ref.xml +39 -0
- data/spec/fixtures/response/xml/soap_fault.xml +8 -0
- data/spec/fixtures/response/xml/soap_fault12.xml +18 -0
- data/spec/fixtures/wsdl/wsdl_fixture.rb +37 -0
- data/spec/fixtures/wsdl/xml/authentication.xml +63 -0
- data/spec/fixtures/wsdl/xml/namespaced_actions.xml +307 -0
- data/spec/fixtures/wsdl/xml/no_namespace.xml +115 -0
- data/spec/http_stubs.rb +23 -0
- data/spec/integration/http_basic_auth_spec.rb +16 -0
- data/spec/integration/server.rb +51 -0
- data/spec/savon/client_spec.rb +77 -0
- data/spec/savon/core_ext/datetime_spec.rb +12 -0
- data/spec/savon/core_ext/hash_spec.rb +138 -0
- data/spec/savon/core_ext/net_http_spec.rb +38 -0
- data/spec/savon/core_ext/object_spec.rb +40 -0
- data/spec/savon/core_ext/string_spec.rb +68 -0
- data/spec/savon/core_ext/symbol_spec.rb +11 -0
- data/spec/savon/core_ext/uri_spec.rb +15 -0
- data/spec/savon/request_spec.rb +89 -0
- data/spec/savon/response_spec.rb +137 -0
- data/spec/savon/savon_spec.rb +23 -0
- data/spec/savon/soap_spec.rb +171 -0
- data/spec/savon/wsdl_spec.rb +84 -0
- data/spec/savon/wsse_spec.rb +132 -0
- data/spec/spec_helper.rb +5 -0
- metadata +175 -0
@@ -0,0 +1,160 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# == Savon::Request
|
4
|
+
#
|
5
|
+
# Handles both WSDL and SOAP HTTP requests.
|
6
|
+
class Request
|
7
|
+
|
8
|
+
# Content-Types by SOAP version.
|
9
|
+
ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
|
10
|
+
|
11
|
+
# Whether to log HTTP requests.
|
12
|
+
@@log = true
|
13
|
+
|
14
|
+
# The default logger.
|
15
|
+
@@logger = Logger.new STDOUT
|
16
|
+
|
17
|
+
# The default log level.
|
18
|
+
@@log_level = :debug
|
19
|
+
|
20
|
+
# Sets whether to log HTTP requests.
|
21
|
+
def self.log=(log)
|
22
|
+
@@log = log
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns whether to log HTTP requests.
|
26
|
+
def self.log?
|
27
|
+
@@log
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the logger.
|
31
|
+
def self.logger=(logger)
|
32
|
+
@@logger = logger
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the logger.
|
36
|
+
def self.logger
|
37
|
+
@@logger
|
38
|
+
end
|
39
|
+
|
40
|
+
# Sets the log level.
|
41
|
+
def self.log_level=(log_level)
|
42
|
+
@@log_level = log_level
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the log level.
|
46
|
+
def self.log_level
|
47
|
+
@@log_level
|
48
|
+
end
|
49
|
+
|
50
|
+
# Expects a SOAP +endpoint+ String. Also accepts an optional Hash
|
51
|
+
# of +options+ for specifying a proxy server.
|
52
|
+
def initialize(endpoint, options = {})
|
53
|
+
@endpoint = URI endpoint
|
54
|
+
@proxy = options[:proxy] ? URI(options[:proxy]) : URI("")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the endpoint URI.
|
58
|
+
attr_reader :endpoint
|
59
|
+
|
60
|
+
# Returns the proxy URI.
|
61
|
+
attr_reader :proxy
|
62
|
+
|
63
|
+
# Returns the HTTP headers for a SOAP request.
|
64
|
+
def headers
|
65
|
+
@headers ||= {}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the HTTP headers for a SOAP request.
|
69
|
+
def headers=(headers)
|
70
|
+
@headers = headers if headers.kind_of? Hash
|
71
|
+
end
|
72
|
+
|
73
|
+
# Sets the +username+ and +password+ for HTTP basic authentication.
|
74
|
+
def basic_auth(username, password)
|
75
|
+
@basic_auth = [username, password]
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sets the +username+ and +password+ for NTLM authentication.
|
79
|
+
def ntlm_auth(username, password)
|
80
|
+
@ntlm_auth = [username, password]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Retrieves WSDL document and returns the Net::HTTP response.
|
84
|
+
def wsdl
|
85
|
+
log "Retrieving WSDL from: #{@endpoint}"
|
86
|
+
http.endpoint @endpoint.host, @endpoint.port
|
87
|
+
http.use_ssl = @endpoint.ssl?
|
88
|
+
http.start { |h| h.request request(:wsdl) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Executes a SOAP request using a given Savon::SOAP instance and
|
92
|
+
# returns the Net::HTTP response.
|
93
|
+
def soap(soap)
|
94
|
+
@soap = soap
|
95
|
+
http.endpoint @soap.endpoint.host, @soap.endpoint.port
|
96
|
+
http.use_ssl = @soap.endpoint.ssl?
|
97
|
+
|
98
|
+
log_request
|
99
|
+
@response = http.start do |h|
|
100
|
+
h.request request(:soap) { |request| request.body = @soap.to_xml }
|
101
|
+
end
|
102
|
+
log_response
|
103
|
+
@response
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the Net::HTTP object.
|
107
|
+
def http
|
108
|
+
@http ||= Net::HTTP::Proxy(@proxy.host, @proxy.port).new @endpoint.host, @endpoint.port
|
109
|
+
end
|
110
|
+
|
111
|
+
private
|
112
|
+
|
113
|
+
# Logs the SOAP request.
|
114
|
+
def log_request
|
115
|
+
log "SOAP request: #{@soap.endpoint}"
|
116
|
+
log headers.merge(soap_headers).map { |key, value| "#{key}: #{value}" }.join(", ")
|
117
|
+
log @soap.to_xml
|
118
|
+
end
|
119
|
+
|
120
|
+
# Logs the SOAP response.
|
121
|
+
def log_response
|
122
|
+
log "SOAP response (status #{@response.code}):"
|
123
|
+
log @response.body
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns a Net::HTTP request for a given +type+. Yields the request
|
127
|
+
# to an optional block.
|
128
|
+
def request(type)
|
129
|
+
request = case type
|
130
|
+
when :wsdl then Net::HTTP::Get.new @endpoint.to_s, headers
|
131
|
+
when :soap then Net::HTTP::Post.new @soap.endpoint.to_s, headers.merge(soap_headers)
|
132
|
+
end
|
133
|
+
if @basic_auth
|
134
|
+
request.basic_auth *@basic_auth
|
135
|
+
elsif @ntlm_auth
|
136
|
+
log "Using NTLM authentication"
|
137
|
+
require 'net/ntlm_http'
|
138
|
+
request.ntlm_auth *@ntlm_auth
|
139
|
+
end
|
140
|
+
yield request if block_given?
|
141
|
+
request
|
142
|
+
end
|
143
|
+
|
144
|
+
# Returns a Hash containing the SOAP headers for an HTTP request.
|
145
|
+
def soap_headers
|
146
|
+
{ "Content-Type" => ContentType[@soap.version], "SOAPAction" => @soap.action }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Logs a given +message+.
|
150
|
+
def log(message)
|
151
|
+
self.class.logger.send self.class.log_level, message if log?
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns whether to log.
|
155
|
+
def log?
|
156
|
+
self.class.log? && self.class.logger.respond_to?(self.class.log_level)
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# == Savon::Response
|
4
|
+
#
|
5
|
+
# Represents the HTTP and SOAP response.
|
6
|
+
class Response
|
7
|
+
|
8
|
+
# The global setting of whether to raise errors.
|
9
|
+
@@raise_errors = true
|
10
|
+
|
11
|
+
# Sets the global setting of whether to raise errors.
|
12
|
+
def self.raise_errors=(raise_errors)
|
13
|
+
@@raise_errors = raise_errors
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the global setting of whether to raise errors.
|
17
|
+
def self.raise_errors?
|
18
|
+
@@raise_errors
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets a specific handler for errors to raise.
|
22
|
+
def self.error_handler(&blk)
|
23
|
+
@@error_handler = blk
|
24
|
+
end
|
25
|
+
|
26
|
+
# Expects a Net::HTTPResponse and handles errors.
|
27
|
+
def initialize(http)
|
28
|
+
@http = http
|
29
|
+
|
30
|
+
handle_soap_fault
|
31
|
+
handle_http_error
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns whether there was a SOAP fault.
|
35
|
+
def soap_fault?
|
36
|
+
@soap_fault ? true : false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the SOAP fault message.
|
40
|
+
attr_reader :soap_fault
|
41
|
+
|
42
|
+
# Returns whether there was an HTTP error.
|
43
|
+
def http_error?
|
44
|
+
@http_error ? true : false
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the HTTP error message.
|
48
|
+
attr_reader :http_error
|
49
|
+
|
50
|
+
# Returns the SOAP response body as a Hash.
|
51
|
+
def to_hash
|
52
|
+
@body ||= (Crack::XML.parse(@http.body) rescue {}).find_soap_body
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns the SOAP response XML.
|
56
|
+
def to_xml
|
57
|
+
@http.body
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns the HTTP response object.
|
61
|
+
attr_reader :http
|
62
|
+
|
63
|
+
alias :to_s :to_xml
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Handles SOAP faults. Raises a Savon::SOAPFault unless the default
|
68
|
+
# behavior of raising errors was turned off.
|
69
|
+
def handle_soap_fault
|
70
|
+
if soap_fault_message
|
71
|
+
@soap_fault = soap_fault_message
|
72
|
+
raise Savon::SOAPFault, @soap_fault if self.class.raise_errors?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns a SOAP fault message in case a SOAP fault was found.
|
77
|
+
def soap_fault_message
|
78
|
+
@soap_fault_message ||= soap_fault_message_by_version to_hash[:fault]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Expects a Hash that might contain information about a SOAP fault.
|
82
|
+
# Returns the SOAP fault message in case one was found.
|
83
|
+
def soap_fault_message_by_version(soap_fault)
|
84
|
+
return unless soap_fault
|
85
|
+
|
86
|
+
if @@error_handler
|
87
|
+
@@error_handler.call(soap_fault)
|
88
|
+
else
|
89
|
+
if soap_fault.keys.include? :faultcode
|
90
|
+
"(#{soap_fault[:faultcode]}) #{soap_fault[:faultstring]}"
|
91
|
+
elsif soap_fault.keys.include? :code
|
92
|
+
"(#{soap_fault[:code][:value]}) #{soap_fault[:reason][:text]}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Handles HTTP errors. Raises a Savon::HTTPError unless the default
|
98
|
+
# behavior of raising errors was turned off.
|
99
|
+
def handle_http_error
|
100
|
+
if @http.code.to_i >= 300
|
101
|
+
@http_error = "#{@http.message} (#{@http.code})"
|
102
|
+
@http_error << ": #{@http.body}" unless @http.body.empty?
|
103
|
+
raise Savon::HTTPError, http_error if self.class.raise_errors?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
data/lib/savon/soap.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# == Savon::SOAP
|
4
|
+
#
|
5
|
+
# Represents the SOAP parameters and envelope.
|
6
|
+
class SOAP
|
7
|
+
|
8
|
+
# SOAP namespaces by SOAP version.
|
9
|
+
SOAPNamespace = {
|
10
|
+
1 => "http://schemas.xmlsoap.org/soap/envelope/",
|
11
|
+
2 => "http://www.w3.org/2003/05/soap-envelope"
|
12
|
+
}
|
13
|
+
|
14
|
+
# Content-Types by SOAP version.
|
15
|
+
ContentType = { 1 => "text/xml", 2 => "application/soap+xml" }
|
16
|
+
|
17
|
+
# The global SOAP version.
|
18
|
+
@@version = 1
|
19
|
+
|
20
|
+
# Returns the global SOAP version.
|
21
|
+
def self.version
|
22
|
+
@@version
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets the global SOAP version.
|
26
|
+
def self.version=(version)
|
27
|
+
@@version = version if Savon::SOAPVersions.include? version
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the global SOAP header. Expected to be a Hash that can be translated
|
31
|
+
# to XML via Hash.to_soap_xml or any other Object responding to to_s.
|
32
|
+
def self.header=(header)
|
33
|
+
@@header = header
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns the global SOAP header. Defaults to an empty Hash.
|
37
|
+
def self.header
|
38
|
+
@@header ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets the global namespaces. Expected to be a Hash containing the
|
42
|
+
# namespaces (keys) and the corresponding URI's (values).
|
43
|
+
def self.namespaces=(namespaces)
|
44
|
+
@@namespaces = namespaces if namespaces.kind_of? Hash
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the global namespaces. A Hash containing the namespaces (keys)
|
48
|
+
# and the corresponding URI's (values).
|
49
|
+
def self.namespaces
|
50
|
+
@@namespaces ||= {}
|
51
|
+
end
|
52
|
+
|
53
|
+
# Initialzes the SOAP object.
|
54
|
+
def initialize
|
55
|
+
@builder = Builder::XmlMarkup.new
|
56
|
+
end
|
57
|
+
|
58
|
+
# Sets the WSSE options.
|
59
|
+
attr_writer :wsse
|
60
|
+
|
61
|
+
# Sets the SOAP action.
|
62
|
+
attr_writer :action
|
63
|
+
|
64
|
+
# Returns the SOAP action.
|
65
|
+
def action
|
66
|
+
@action ||= ""
|
67
|
+
end
|
68
|
+
|
69
|
+
# Sets the SOAP input.
|
70
|
+
attr_writer :input
|
71
|
+
|
72
|
+
# Returns the SOAP input.
|
73
|
+
def input
|
74
|
+
@input ||= ""
|
75
|
+
end
|
76
|
+
|
77
|
+
# Accessor for the SOAP endpoint.
|
78
|
+
attr_accessor :endpoint
|
79
|
+
|
80
|
+
# Sets the SOAP header. Expected to be a Hash that can be translated
|
81
|
+
# to XML via Hash.to_soap_xml or any other Object responding to to_s.
|
82
|
+
attr_writer :header
|
83
|
+
|
84
|
+
# Returns the SOAP header. Defaults to an empty Hash.
|
85
|
+
def header
|
86
|
+
@header ||= {}
|
87
|
+
end
|
88
|
+
|
89
|
+
# Sets the SOAP body. Expected to be a Hash that can be translated to
|
90
|
+
# XML via Hash.to_soap_xml or any other Object responding to to_s.
|
91
|
+
attr_writer :body
|
92
|
+
|
93
|
+
# Sets the namespaces. Expected to be a Hash containing the namespaces
|
94
|
+
# (keys) and the corresponding URI's (values).
|
95
|
+
attr_writer :namespaces
|
96
|
+
|
97
|
+
# Returns the namespaces. A Hash containing the namespaces (keys)
|
98
|
+
# and the corresponding URI's (values).
|
99
|
+
def namespaces
|
100
|
+
@namespaces ||= { "xmlns:env" => SOAPNamespace[version] }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Convenience method for setting the "xmlns:wsdl" namespace.
|
104
|
+
def namespace=(namespace)
|
105
|
+
namespaces["xmlns:wsdl"] = namespace
|
106
|
+
end
|
107
|
+
|
108
|
+
# Sets the SOAP version.
|
109
|
+
def version=(version)
|
110
|
+
@version = version if Savon::SOAPVersions.include? version
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns the SOAP version. Defaults to the global default.
|
114
|
+
def version
|
115
|
+
@version ||= self.class.version
|
116
|
+
end
|
117
|
+
|
118
|
+
# Returns the SOAP envelope XML.
|
119
|
+
def to_xml
|
120
|
+
unless @xml_body
|
121
|
+
@xml_body = @builder.env :Envelope, all_namespaces do |xml|
|
122
|
+
xml_header xml
|
123
|
+
xml_body xml
|
124
|
+
end
|
125
|
+
end
|
126
|
+
@xml_body
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# Adds a SOAP XML header to a given +xml+ Object.
|
132
|
+
def xml_header(xml)
|
133
|
+
xml.env(:Header) do
|
134
|
+
xml << all_header + wsse_header
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns a String containing the global and per request header.
|
139
|
+
def all_header
|
140
|
+
if self.class.header.kind_of?(Hash) && header.kind_of?(Hash)
|
141
|
+
self.class.header.merge(header).to_soap_xml
|
142
|
+
else
|
143
|
+
self.class.header.to_s + header.to_s
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Adds a SOAP XML body to a given +xml+ Object.
|
148
|
+
def xml_body(xml)
|
149
|
+
xml.env(:Body) do
|
150
|
+
xml.tag!(:wsdl, *input_array) do
|
151
|
+
xml << (@body.to_soap_xml rescue @body.to_s)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns a Hash containing the global and per request namespaces.
|
157
|
+
def all_namespaces
|
158
|
+
self.class.namespaces.merge namespaces
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns an Array of SOAP input names to append to the :wsdl namespace.
|
162
|
+
# Defaults to use the name of the SOAP action and may be an empty Array
|
163
|
+
# in case the specified SOAP input seems invalid.
|
164
|
+
def input_array
|
165
|
+
return [input.to_sym] unless input.blank?
|
166
|
+
return [action.to_sym] unless action.blank?
|
167
|
+
[]
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns the WSSE header or an empty String in case WSSE was not set.
|
171
|
+
def wsse_header
|
172
|
+
@wsse.respond_to?(:header) ? @wsse.header : ""
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
data/lib/savon/wsdl.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# Savon::WSDL
|
4
|
+
#
|
5
|
+
# Represents the WSDL document.
|
6
|
+
class WSDL
|
7
|
+
|
8
|
+
# Initializer, expects a Savon::Request.
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
end
|
12
|
+
|
13
|
+
# Sets whether to use the WSDL.
|
14
|
+
attr_writer :enabled
|
15
|
+
|
16
|
+
# Returns whether to use the WSDL. Defaults to +true+.
|
17
|
+
def enabled?
|
18
|
+
@enabled.nil? ? true : @enabled
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns the namespace URI of the WSDL.
|
22
|
+
def namespace_uri
|
23
|
+
@namespace_uri ||= stream.namespace_uri
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns an Array of available SOAP actions.
|
27
|
+
def soap_actions
|
28
|
+
@soap_actions ||= stream.operations.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a Hash of SOAP operations including their corresponding
|
32
|
+
# SOAP actions and inputs.
|
33
|
+
def operations
|
34
|
+
@operations ||= stream.operations
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns the SOAP endpoint.
|
38
|
+
def soap_endpoint
|
39
|
+
@soap_endpoint ||= stream.soap_endpoint
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns +true+ for available methods and SOAP actions.
|
43
|
+
def respond_to?(method)
|
44
|
+
return true if soap_actions.include? method
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the raw WSDL document.
|
49
|
+
def to_s
|
50
|
+
@document ||= @request.wsdl.body
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Returns the Savon::WSDLStream.
|
56
|
+
def stream
|
57
|
+
unless @stream
|
58
|
+
@stream = WSDLStream.new
|
59
|
+
REXML::Document.parse_stream to_s, @stream
|
60
|
+
end
|
61
|
+
@stream
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# Savon::WSDLStream
|
67
|
+
#
|
68
|
+
# Stream listener for parsing the WSDL document.
|
69
|
+
class WSDLStream
|
70
|
+
|
71
|
+
# The main sections of a WSDL document.
|
72
|
+
Sections = %w(definitions types message portType binding service)
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@depth, @operations = 0, {}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the namespace URI.
|
79
|
+
attr_reader :namespace_uri
|
80
|
+
|
81
|
+
# Returns the SOAP operations.
|
82
|
+
attr_reader :operations
|
83
|
+
|
84
|
+
# Returns the SOAP endpoint.
|
85
|
+
attr_reader :soap_endpoint
|
86
|
+
|
87
|
+
# Hook method called when the stream parser encounters a starting tag.
|
88
|
+
def tag_start(tag, attrs)
|
89
|
+
@depth += 1
|
90
|
+
tag = tag.strip_namespace
|
91
|
+
|
92
|
+
@section = tag.to_sym if @depth <= 2 && Sections.include?(tag)
|
93
|
+
@namespace_uri ||= attrs["targetNamespace"] if @section == :definitions
|
94
|
+
@soap_endpoint ||= URI(attrs["location"]) if @section == :service && tag == "address"
|
95
|
+
|
96
|
+
operation_from tag, attrs if @section == :binding && tag == "operation"
|
97
|
+
end
|
98
|
+
|
99
|
+
# Hook method called when the stream parser encounters a closing tag.
|
100
|
+
def tag_end(tag)
|
101
|
+
@depth -= 1
|
102
|
+
end
|
103
|
+
|
104
|
+
# Stores available operations from a given tag +name+ and +attrs+.
|
105
|
+
def operation_from(tag, attrs)
|
106
|
+
@input = attrs["name"] if attrs["name"]
|
107
|
+
|
108
|
+
if attrs["soapAction"]
|
109
|
+
@action = !attrs["soapAction"].blank? ? attrs["soapAction"] : @input
|
110
|
+
@input = @action.split("/").last if !@input || @input.empty?
|
111
|
+
|
112
|
+
@operations[@input.snakecase.to_sym] = { :action => @action, :input => @input }
|
113
|
+
@input, @action = nil, nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Catches calls to unimplemented hook methods.
|
118
|
+
def method_missing(method, *args)
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
data/lib/savon/wsse.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
module Savon
|
2
|
+
|
3
|
+
# Savon::WSSE
|
4
|
+
#
|
5
|
+
# Represents parameters for WSSE authentication.
|
6
|
+
class WSSE
|
7
|
+
|
8
|
+
# Base address for WSSE docs.
|
9
|
+
BaseAddress = "http://docs.oasis-open.org/wss/2004/01"
|
10
|
+
|
11
|
+
# Namespace for WS Security Secext.
|
12
|
+
WSENamespace = BaseAddress + "/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
13
|
+
|
14
|
+
# Namespace for WS Security Utility.
|
15
|
+
WSUNamespace = BaseAddress + "/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
16
|
+
|
17
|
+
# URI for "wsse:Password/@Type" #PasswordText.
|
18
|
+
PasswordTextURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
19
|
+
|
20
|
+
# URI for "wsse:Password/@Type" #PasswordDigest.
|
21
|
+
PasswordDigestURI = BaseAddress + "/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
22
|
+
|
23
|
+
# Global WSSE username.
|
24
|
+
@@username = nil
|
25
|
+
|
26
|
+
# Returns the global WSSE username.
|
27
|
+
def self.username
|
28
|
+
@@username
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets the global WSSE username.
|
32
|
+
def self.username=(username)
|
33
|
+
@@username = username.to_s if username.respond_to? :to_s
|
34
|
+
@@username = nil if username.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Global WSSE password.
|
38
|
+
@@password = nil
|
39
|
+
|
40
|
+
# Returns the global WSSE password.
|
41
|
+
def self.password
|
42
|
+
@@password
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sets the global WSSE password.
|
46
|
+
def self.password=(password)
|
47
|
+
@@password = password.to_s if password.respond_to? :to_s
|
48
|
+
@@password = nil if password.nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Global setting of whether to use WSSE digest.
|
52
|
+
@@digest = false
|
53
|
+
|
54
|
+
# Returns the global setting of whether to use WSSE digest.
|
55
|
+
def self.digest?
|
56
|
+
@@digest
|
57
|
+
end
|
58
|
+
|
59
|
+
# Global setting of whether to use WSSE digest.
|
60
|
+
def self.digest=(digest)
|
61
|
+
@@digest = digest
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sets the WSSE username per request.
|
65
|
+
def username=(username)
|
66
|
+
@username = username.to_s if username.respond_to? :to_s
|
67
|
+
@username = nil if username.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns the WSSE username. Defaults to the global setting.
|
71
|
+
def username
|
72
|
+
@username || self.class.username
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sets the WSSE password per request.
|
76
|
+
def password=(password)
|
77
|
+
@password = password.to_s if password.respond_to? :to_s
|
78
|
+
@password = nil if password.nil?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the WSSE password. Defaults to the global setting.
|
82
|
+
def password
|
83
|
+
@password || self.class.password
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sets whether to use WSSE digest per request.
|
87
|
+
attr_writer :digest
|
88
|
+
|
89
|
+
# Returns whether to use WSSE digest. Defaults to the global setting.
|
90
|
+
def digest?
|
91
|
+
@digest || self.class.digest?
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns the XML for a WSSE header or an empty String unless both
|
95
|
+
# username and password were specified.
|
96
|
+
def header
|
97
|
+
return "" unless username && password
|
98
|
+
|
99
|
+
builder = Builder::XmlMarkup.new
|
100
|
+
builder.wsse :Security, "xmlns:wsse" => WSENamespace do |xml|
|
101
|
+
xml.wsse :UsernameToken, "xmlns:wsu" => WSUNamespace do
|
102
|
+
xml.wsse :Username, username
|
103
|
+
xml.wsse :Nonce, nonce
|
104
|
+
xml.wsu :Created, timestamp
|
105
|
+
xml.wsse :Password, password_node, :Type => password_type
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
|
112
|
+
# Returns the WSSE password. Encrypts the password for digest authentication.
|
113
|
+
def password_node
|
114
|
+
return password unless digest?
|
115
|
+
|
116
|
+
token = nonce + timestamp + password
|
117
|
+
Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the URI for the "wsse:Password/@Type" attribute.
|
121
|
+
def password_type
|
122
|
+
digest? ? PasswordDigestURI : PasswordTextURI
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns a WSSE nonce.
|
126
|
+
def nonce
|
127
|
+
@nonce ||= Digest::SHA1.hexdigest String.random + timestamp
|
128
|
+
end
|
129
|
+
|
130
|
+
# Returns a WSSE timestamp.
|
131
|
+
def timestamp
|
132
|
+
@timestamp ||= Time.now.strftime Savon::SOAPDateTimeFormat
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|