savon 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ ## 0.9.5 (2011-07-03)
2
+
3
+ * Refactoring: Extracted WSSE authentication out into the [akami](http://rubygems.org/gems/akami) gem.
4
+
1
5
  ## 0.9.4 (2011-07-03)
2
6
 
3
7
  * Refactoring: Extracted the WSDL parser out into the [wasabi](http://rubygems.org/gems/wasabi) gem.
@@ -1,9 +1,10 @@
1
1
  require "httpi/request"
2
+ require "akami"
3
+
2
4
  require "savon/wasabi/document"
3
5
  require "savon/soap/xml"
4
6
  require "savon/soap/request"
5
7
  require "savon/soap/response"
6
- require "savon/wsse"
7
8
 
8
9
  module Savon
9
10
 
@@ -44,9 +45,9 @@ module Savon
44
45
  @http ||= HTTPI::Request.new
45
46
  end
46
47
 
47
- # Returns the <tt>Savon::WSSE</tt> object.
48
+ # Returns the <tt>Akami::WSSE</tt> object.
48
49
  def wsse
49
- @wsse ||= WSSE.new
50
+ @wsse ||= Akami.wsse
50
51
  end
51
52
 
52
53
  # Returns the <tt>Savon::SOAP::XML</tt> object. Please notice, that this object is only available
@@ -16,21 +16,6 @@ module Savon
16
16
  str
17
17
  end unless method_defined?(:snakecase)
18
18
 
19
- # Returns the String in lowerCamelCase.
20
- def lower_camelcase
21
- str = dup
22
- str.gsub!(/\/(.?)/) { "::#{$1.upcase}" }
23
- str.gsub!(/(?:_+|-+)([a-z])/) { $1.upcase }
24
- str.gsub!(/(\A|\s)([A-Z])/) { $1 + $2.downcase }
25
- str
26
- end
27
-
28
- # Returns whether the String starts with a given +prefix+.
29
- def starts_with?(prefix)
30
- prefix = prefix.to_s
31
- self[0, prefix.length] == prefix
32
- end unless method_defined?(:starts_with?)
33
-
34
19
  end
35
20
  end
36
21
  end
@@ -1,5 +1,5 @@
1
1
  module Savon
2
2
 
3
- Version = "0.9.4"
3
+ Version = "0.9.5"
4
4
 
5
5
  end
@@ -6,7 +6,7 @@ module Savon
6
6
 
7
7
  # = Savon::Wasabi::Document
8
8
  #
9
- # Extends the <tt>Wasabi::Document</tt> to extend its document handling by
9
+ # Extends the document handling of the <tt>Wasabi::Document</tt> by
10
10
  # adding support for remote and local WSDL documents.
11
11
  class Document < ::Wasabi::Document
12
12
 
@@ -15,6 +15,11 @@ module Savon
15
15
  @xml ||= document.kind_of?(String) ? resolve_document : document
16
16
  end
17
17
 
18
+ # Sets the <tt>HTTPI::Request</tt> for remote WSDL documents.
19
+ attr_writer :request
20
+
21
+ private
22
+
18
23
  # Sets up and returns the <tt>HTTPI::Request</tt>.
19
24
  def request
20
25
  @request ||= HTTPI::Request.new
@@ -22,11 +27,6 @@ module Savon
22
27
  @request
23
28
  end
24
29
 
25
- # Sets the <tt>HTTPI::Request</tt> for remote WSDL documents.
26
- attr_writer :request
27
-
28
- private
29
-
30
30
  # Resolves and returns the raw WSDL document.
31
31
  def resolve_document
32
32
  case document
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency "nori", "~> 1.0"
19
19
  s.add_dependency "httpi", "~> 0.9"
20
20
  s.add_dependency "wasabi", "~> 1.0"
21
+ s.add_dependency "akami", "~> 1.0"
21
22
  s.add_dependency "gyoku", ">= 0.4.0"
22
23
  s.add_dependency "nokogiri", ">= 1.4.0"
23
24
 
@@ -32,7 +32,7 @@ describe Savon::Client do
32
32
  Savon::Client.new do |wsdl, http, wsse|
33
33
  wsdl.should be_an(Savon::Wasabi::Document)
34
34
  http.should be_an(HTTPI::Request)
35
- wsse.should be_an(Savon::WSSE)
35
+ wsse.should be_an(Akami::WSSE)
36
36
  end
37
37
  end
38
38
  end
@@ -47,7 +47,7 @@ describe Savon::Client do
47
47
  end
48
48
 
49
49
  it "should let you access the WSSE object" do
50
- Savon::Client.new { wsse.should be_a(Savon::WSSE) }
50
+ Savon::Client.new { wsse.should be_a(Akami::WSSE) }
51
51
  end
52
52
  end
53
53
  end
@@ -65,8 +65,8 @@ describe Savon::Client do
65
65
  end
66
66
 
67
67
  describe "#wsse" do
68
- it "should return the Savon::WSSE object" do
69
- client.wsse.should be_a(Savon::WSSE)
68
+ it "should return the Akami::WSSE object" do
69
+ client.wsse.should be_a(Akami::WSSE)
70
70
  end
71
71
  end
72
72
 
@@ -171,7 +171,7 @@ describe Savon::Client do
171
171
  soap.should be_a(Savon::SOAP::XML)
172
172
  wsdl.should be_a(Savon::Wasabi::Document)
173
173
  http.should be_an(HTTPI::Request)
174
- wsse.should be_a(Savon::WSSE)
174
+ wsse.should be_a(Akami::WSSE)
175
175
  end
176
176
  end
177
177
  end
@@ -186,7 +186,7 @@ describe Savon::Client do
186
186
  end
187
187
 
188
188
  it "should let you access the WSSE object" do
189
- client.request(:authenticate) { wsse.should be_a(Savon::WSSE) }
189
+ client.request(:authenticate) { wsse.should be_a(Akami::WSSE) }
190
190
  end
191
191
 
192
192
  it "should let you access the WSDL object" do
@@ -222,10 +222,6 @@ describe Savon::Client do
222
222
  let(:client) { Savon::Client.new { wsdl.document = Endpoint.wsdl } }
223
223
  before { HTTPI.expects(:get).returns(new_response(:body => Fixture.wsdl(:authentication))) }
224
224
 
225
- it "should return a list of available SOAP actions" do
226
- client.wsdl.soap_actions.should == [:authenticate]
227
- end
228
-
229
225
  it "adds a SOAPAction header containing the SOAP action name" do
230
226
  HTTPI.stubs(:post).returns(new_response)
231
227
 
@@ -248,10 +244,6 @@ describe Savon::Client do
248
244
 
249
245
  before { HTTPI.expects(:get).never }
250
246
 
251
- it "should return a list of available SOAP actions" do
252
- client.wsdl.soap_actions.should == [:authenticate]
253
- end
254
-
255
247
  it "adds a SOAPAction header containing the SOAP action name" do
256
248
  HTTPI.stubs(:post).returns(new_response)
257
249
 
@@ -260,13 +252,6 @@ describe Savon::Client do
260
252
  end
261
253
  end
262
254
 
263
- it "should get #element_form_default from the WSDL" do
264
- HTTPI.stubs(:post).returns(new_response)
265
- Savon::Wasabi::Document.any_instance.expects(:element_form_default).returns(:qualified)
266
-
267
- client.request :authenticate
268
- end
269
-
270
255
  it "should execute SOAP requests and return the response" do
271
256
  HTTPI.expects(:post).returns(new_response)
272
257
  response = client.request(:authenticate)
@@ -298,13 +283,6 @@ describe Savon::Client do
298
283
  end
299
284
  end
300
285
 
301
- it "should not get #element_form_default from the WSDL" do
302
- HTTPI.stubs(:post).returns(new_response)
303
- Savon::Wasabi::Document.any_instance.expects(:element_form_default).never
304
-
305
- client.request :authenticate
306
- end
307
-
308
286
  it "should execute SOAP requests and return the response" do
309
287
  HTTPI.expects(:post).returns(new_response)
310
288
  response = client.request(:authenticate)
@@ -34,17 +34,4 @@ describe String do
34
34
  end
35
35
  end
36
36
 
37
- describe "lower_camelcase" do
38
- it "converts a snakecase String to lowerCamelCase" do
39
- "lower_camel_case".lower_camelcase.should == "lowerCamelCase"
40
- end
41
- end
42
-
43
- describe "starts_with?" do
44
- it "should return whether it starts with a given suffix" do
45
- "authenticate".starts_with?("auth").should be_true
46
- "authenticate".starts_with?("cate").should be_false
47
- end
48
- end
49
-
50
37
  end
@@ -98,9 +98,9 @@ describe Savon::SOAP::XML do
98
98
  end
99
99
 
100
100
  describe "#wsse" do
101
- it "should set the Savon::WSSE object" do
102
- xml.wsse = Savon::WSSE.new
103
- xml.wsse.should be_a(Savon::WSSE)
101
+ it "should set the Akami::WSSE object" do
102
+ xml.wsse = Akami.wsse
103
+ xml.wsse.should be_a(Akami::WSSE)
104
104
  end
105
105
  end
106
106
 
@@ -235,7 +235,7 @@ describe Savon::SOAP::XML do
235
235
 
236
236
  context "with WSSE authentication" do
237
237
  it "should containg a SOAP header with WSSE authentication details" do
238
- xml.wsse = Savon::WSSE.new
238
+ xml.wsse = Akami.wsse
239
239
  xml.wsse.credentials "username", "password"
240
240
 
241
241
  xml.to_xml.should include("<env:Header><wsse:Security")
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- hash: 51
4
+ hash: 49
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 4
10
- version: 0.9.4
9
+ - 5
10
+ version: 0.9.5
11
11
  platform: ruby
12
12
  authors:
13
13
  - Daniel Harrington
@@ -79,9 +79,24 @@ dependencies:
79
79
  type: :runtime
80
80
  version_requirements: *id004
81
81
  - !ruby/object:Gem::Dependency
82
- name: gyoku
82
+ name: akami
83
83
  prerelease: false
84
84
  requirement: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 15
90
+ segments:
91
+ - 1
92
+ - 0
93
+ version: "1.0"
94
+ type: :runtime
95
+ version_requirements: *id005
96
+ - !ruby/object:Gem::Dependency
97
+ name: gyoku
98
+ prerelease: false
99
+ requirement: &id006 !ruby/object:Gem::Requirement
85
100
  none: false
86
101
  requirements:
87
102
  - - ">="
@@ -93,11 +108,11 @@ dependencies:
93
108
  - 0
94
109
  version: 0.4.0
95
110
  type: :runtime
96
- version_requirements: *id005
111
+ version_requirements: *id006
97
112
  - !ruby/object:Gem::Dependency
98
113
  name: nokogiri
99
114
  prerelease: false
100
- requirement: &id006 !ruby/object:Gem::Requirement
115
+ requirement: &id007 !ruby/object:Gem::Requirement
101
116
  none: false
102
117
  requirements:
103
118
  - - ">="
@@ -109,11 +124,11 @@ dependencies:
109
124
  - 0
110
125
  version: 1.4.0
111
126
  type: :runtime
112
- version_requirements: *id006
127
+ version_requirements: *id007
113
128
  - !ruby/object:Gem::Dependency
114
129
  name: rake
115
130
  prerelease: false
116
- requirement: &id007 !ruby/object:Gem::Requirement
131
+ requirement: &id008 !ruby/object:Gem::Requirement
117
132
  none: false
118
133
  requirements:
119
134
  - - ~>
@@ -125,11 +140,11 @@ dependencies:
125
140
  - 7
126
141
  version: 0.8.7
127
142
  type: :development
128
- version_requirements: *id007
143
+ version_requirements: *id008
129
144
  - !ruby/object:Gem::Dependency
130
145
  name: rspec
131
146
  prerelease: false
132
- requirement: &id008 !ruby/object:Gem::Requirement
147
+ requirement: &id009 !ruby/object:Gem::Requirement
133
148
  none: false
134
149
  requirements:
135
150
  - - ~>
@@ -141,11 +156,11 @@ dependencies:
141
156
  - 0
142
157
  version: 2.5.0
143
158
  type: :development
144
- version_requirements: *id008
159
+ version_requirements: *id009
145
160
  - !ruby/object:Gem::Dependency
146
161
  name: mocha
147
162
  prerelease: false
148
- requirement: &id009 !ruby/object:Gem::Requirement
163
+ requirement: &id010 !ruby/object:Gem::Requirement
149
164
  none: false
150
165
  requirements:
151
166
  - - ~>
@@ -157,11 +172,11 @@ dependencies:
157
172
  - 8
158
173
  version: 0.9.8
159
174
  type: :development
160
- version_requirements: *id009
175
+ version_requirements: *id010
161
176
  - !ruby/object:Gem::Dependency
162
177
  name: timecop
163
178
  prerelease: false
164
- requirement: &id010 !ruby/object:Gem::Requirement
179
+ requirement: &id011 !ruby/object:Gem::Requirement
165
180
  none: false
166
181
  requirements:
167
182
  - - ~>
@@ -173,11 +188,11 @@ dependencies:
173
188
  - 5
174
189
  version: 0.3.5
175
190
  type: :development
176
- version_requirements: *id010
191
+ version_requirements: *id011
177
192
  - !ruby/object:Gem::Dependency
178
193
  name: autotest
179
194
  prerelease: false
180
- requirement: &id011 !ruby/object:Gem::Requirement
195
+ requirement: &id012 !ruby/object:Gem::Requirement
181
196
  none: false
182
197
  requirements:
183
198
  - - ">="
@@ -187,7 +202,7 @@ dependencies:
187
202
  - 0
188
203
  version: "0"
189
204
  type: :development
190
- version_requirements: *id011
205
+ version_requirements: *id012
191
206
  description: Ruby's heavy metal SOAP client
192
207
  email: me@rubiii.com
193
208
  executables: []
@@ -210,7 +225,6 @@ files:
210
225
  - lib/savon/client.rb
211
226
  - lib/savon/core_ext/object.rb
212
227
  - lib/savon/core_ext/string.rb
213
- - lib/savon/core_ext/time.rb
214
228
  - lib/savon/error.rb
215
229
  - lib/savon/global.rb
216
230
  - lib/savon/http/error.rb
@@ -221,7 +235,6 @@ files:
221
235
  - lib/savon/soap/xml.rb
222
236
  - lib/savon/version.rb
223
237
  - lib/savon/wasabi/document.rb
224
- - lib/savon/wsse.rb
225
238
  - savon.gemspec
226
239
  - spec/fixtures/gzip/message.gz
227
240
  - spec/fixtures/response/another_soap_fault.xml
@@ -232,15 +245,9 @@ files:
232
245
  - spec/fixtures/response/soap_fault.xml
233
246
  - spec/fixtures/response/soap_fault12.xml
234
247
  - spec/fixtures/wsdl/authentication.xml
235
- - spec/fixtures/wsdl/geotrust.xml
236
- - spec/fixtures/wsdl/namespaced_actions.xml
237
- - spec/fixtures/wsdl/no_namespace.xml
238
- - spec/fixtures/wsdl/soap12.xml
239
- - spec/fixtures/wsdl/two_bindings.xml
240
248
  - spec/savon/client_spec.rb
241
249
  - spec/savon/core_ext/object_spec.rb
242
250
  - spec/savon/core_ext/string_spec.rb
243
- - spec/savon/core_ext/time_spec.rb
244
251
  - spec/savon/http/error_spec.rb
245
252
  - spec/savon/savon_spec.rb
246
253
  - spec/savon/soap/fault_spec.rb
@@ -249,7 +256,6 @@ files:
249
256
  - spec/savon/soap/xml_spec.rb
250
257
  - spec/savon/soap_spec.rb
251
258
  - spec/savon/wasabi/document_spec.rb
252
- - spec/savon/wsse_spec.rb
253
259
  - spec/spec_helper.rb
254
260
  - spec/support/endpoint.rb
255
261
  - spec/support/fixture.rb
@@ -1,22 +0,0 @@
1
- module Savon
2
- module CoreExt
3
- module Time
4
-
5
- # Returns an xs:dateTime formatted String.
6
- def xs_datetime
7
- zone = if utc_offset < 0
8
- "-#{"%02d" % (- utc_offset / 3600)}:#{"%02d" % ((- utc_offset % 3600) / 60)}"
9
- elsif utc_offset > 0
10
- "+#{"%02d" % (utc_offset / 3600)}:#{"%02d" % ((utc_offset % 3600) / 60)}"
11
- else
12
- "Z"
13
- end
14
-
15
- strftime "%Y-%m-%dT%H:%M:%S#{zone}"
16
- end
17
-
18
- end
19
- end
20
- end
21
-
22
- Time.send :include, Savon::CoreExt::Time
@@ -1,156 +0,0 @@
1
- require "base64"
2
- require "digest/sha1"
3
-
4
- require "savon/core_ext/string"
5
- require "savon/core_ext/time"
6
-
7
- module Savon
8
-
9
- # = Savon::WSSE
10
- #
11
- # Provides WSSE authentication.
12
- class WSSE
13
-
14
- # Namespace for WS Security Secext.
15
- WSENamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
16
-
17
- # Namespace for WS Security Utility.
18
- WSUNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
19
-
20
- # URI for "wsse:Password/@Type" #PasswordText.
21
- PasswordTextURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
22
-
23
- # URI for "wsse:Password/@Type" #PasswordDigest.
24
- PasswordDigestURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
25
-
26
- # Returns a value from the WSSE Hash.
27
- def [](key)
28
- hash[key]
29
- end
30
-
31
- # Sets a value on the WSSE Hash.
32
- def []=(key, value)
33
- hash[key] = value
34
- end
35
-
36
- # Sets authentication credentials for a wsse:UsernameToken header.
37
- # Also accepts whether to use WSSE digest authentication.
38
- def credentials(username, password, digest = false)
39
- self.username = username
40
- self.password = password
41
- self.digest = digest
42
- end
43
-
44
- attr_accessor :username, :password, :created_at, :expires_at
45
-
46
- # Returns whether to use WSSE digest. Defaults to +false+.
47
- def digest?
48
- !!@digest
49
- end
50
-
51
- attr_writer :digest
52
-
53
- # Returns whether to generate a wsse:UsernameToken header.
54
- def username_token?
55
- username && password
56
- end
57
-
58
- # Returns whether to generate a wsu:Timestamp header.
59
- def timestamp?
60
- created_at || expires_at || @wsu_timestamp
61
- end
62
-
63
- # Sets whether to generate a wsu:Timestamp header.
64
- def timestamp=(timestamp)
65
- @wsu_timestamp = timestamp
66
- end
67
-
68
- # Returns the XML for a WSSE header.
69
- def to_xml
70
- if username_token? && timestamp?
71
- Gyoku.xml wsse_username_token.merge!(wsu_timestamp) {
72
- |key, v1, v2| v1.merge!(v2) {
73
- |key, v1, v2| v1.merge!(v2)
74
- }
75
- }
76
- elsif username_token?
77
- Gyoku.xml wsse_username_token.merge!(hash)
78
- elsif timestamp?
79
- Gyoku.xml wsu_timestamp.merge!(hash)
80
- else
81
- ""
82
- end
83
- end
84
-
85
- private
86
-
87
- # Returns a Hash containing wsse:UsernameToken details.
88
- def wsse_username_token
89
- if digest?
90
- security_hash :wsse, "UsernameToken",
91
- "wsse:Username" => username,
92
- "wsse:Nonce" => nonce,
93
- "wsu:Created" => timestamp,
94
- "wsse:Password" => digest_password,
95
- :attributes! => { "wsse:Password" => { "Type" => PasswordDigestURI } }
96
- else
97
- security_hash :wsse, "UsernameToken",
98
- "wsse:Username" => username,
99
- "wsse:Password" => password,
100
- :attributes! => { "wsse:Password" => { "Type" => PasswordTextURI } }
101
- end
102
- end
103
-
104
- # Returns a Hash containing wsu:Timestamp details.
105
- def wsu_timestamp
106
- security_hash :wsu, "Timestamp",
107
- "wsu:Created" => (created_at || Time.now).xs_datetime,
108
- "wsu:Expires" => (expires_at || (created_at || Time.now) + 60).xs_datetime
109
- end
110
-
111
- # Returns a Hash containing wsse/wsu Security details for a given
112
- # +namespace+, +tag+ and +hash+.
113
- def security_hash(namespace, tag, hash)
114
- {
115
- "wsse:Security" => {
116
- "#{namespace}:#{tag}" => hash,
117
- :attributes! => { "#{namespace}:#{tag}" => { "wsu:Id" => "#{tag}-#{count}", "xmlns:wsu" => WSUNamespace } }
118
- },
119
- :attributes! => { "wsse:Security" => { "xmlns:wsse" => WSENamespace } }
120
- }
121
- end
122
-
123
- # Returns the WSSE password, encrypted for digest authentication.
124
- def digest_password
125
- token = nonce + timestamp + password
126
- Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
127
- end
128
-
129
- # Returns a WSSE nonce.
130
- def nonce
131
- @nonce ||= Digest::SHA1.hexdigest random_string + timestamp
132
- end
133
-
134
- # Returns a random String of 100 characters.
135
- def random_string
136
- (0...100).map { ("a".."z").to_a[rand(26)] }.join
137
- end
138
-
139
- # Returns a WSSE timestamp.
140
- def timestamp
141
- @timestamp ||= Time.now.xs_datetime
142
- end
143
-
144
- # Returns a new number with every call.
145
- def count
146
- @count ||= 0
147
- @count += 1
148
- end
149
-
150
- # Returns a memoized and autovivificating Hash.
151
- def hash
152
- @hash ||= Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
153
- end
154
-
155
- end
156
- end