savon 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +28 -2
- data/lib/savon/core_ext/hash.rb +10 -0
- data/lib/savon/core_ext/time.rb +14 -0
- data/lib/savon/soap.rb +0 -3
- data/lib/savon/soap/fault.rb +1 -1
- data/lib/savon/version.rb +1 -1
- data/lib/savon/wsse.rb +73 -24
- data/savon.gemspec +1 -0
- data/spec/savon/core_ext/hash_spec.rb +9 -0
- data/spec/savon/core_ext/time_spec.rb +13 -0
- data/spec/savon/soap_spec.rb +1 -9
- data/spec/savon/wsse_spec.rb +50 -0
- metadata +24 -6
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,34 @@
|
|
1
|
-
## 0.8.
|
1
|
+
## 0.8.2 (2010-01-04)
|
2
|
+
|
3
|
+
* Fix for [issue #127](https://github.com/rubiii/savon/issues/127) ([0eb3da](https://github.com/rubiii/savon/commit/0eb3da4))
|
4
|
+
|
5
|
+
* Changed `Savon::WSSE` to be based on a Hash instead of relying on builder ([4cebc3](https://github.com/rubiii/savon/commit/4cebc3)).
|
6
|
+
|
7
|
+
`Savon::WSSE` now supports wsse:Timestamp headers ([issue #122](https://github.com/rubiii/savon/issues/122)) by setting
|
8
|
+
`Savon::WSSE#timestamp` to `true`:
|
9
|
+
|
10
|
+
client.request :some_method do
|
11
|
+
wsse.timestamp = true
|
12
|
+
end
|
13
|
+
|
14
|
+
or by setting `Savon::WSSE#created_at` or `Savon::WSSE#expires_at`:
|
15
|
+
|
16
|
+
client.request :some_method do
|
17
|
+
wsse.created_at = Time.now
|
18
|
+
wsse.expires_at = Time.now + 60
|
19
|
+
end
|
20
|
+
|
21
|
+
You can also add custom tags to the WSSE header ([issue #69](https://github.com/rubiii/savon/issues/69)):
|
22
|
+
|
23
|
+
client.request :some_method do
|
24
|
+
wsse["wsse:Security"]["wsse:UsernameToken"] = { "Organization" => "ACME", "Domain" => "acme.com" }
|
25
|
+
end
|
26
|
+
|
27
|
+
## 0.8.1 (2010-12-22)
|
2
28
|
|
3
29
|
* Update to depend on HTTPI v0.7.5 which comes with a fallback to use Net::HTTP when no other adapter could be required.
|
4
30
|
|
5
|
-
* Fix for [issue #72](https://github.com/rubiii/savon/issues/72) ([
|
31
|
+
* Fix for [issue #72](https://github.com/rubiii/savon/issues/72) ([22074a](https://github.com/rubiii/savon/commit/22074a8)).
|
6
32
|
|
7
33
|
* Loosen dependency on builder. Should be quite stable.
|
8
34
|
|
data/lib/savon/core_ext/hash.rb
CHANGED
@@ -8,6 +8,16 @@ module Savon
|
|
8
8
|
module CoreExt
|
9
9
|
module Hash
|
10
10
|
|
11
|
+
# Returns a new Hash with +self+ and +other_hash+ merged recursively.
|
12
|
+
# Modifies the receiver in place.
|
13
|
+
def deep_merge!(other_hash)
|
14
|
+
other_hash.each_pair do |k,v|
|
15
|
+
tv = self[k]
|
16
|
+
self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end unless defined? deep_merge!
|
20
|
+
|
11
21
|
# Returns the values from the soap:Body element or an empty Hash in case the soap:Body tag could
|
12
22
|
# not be found.
|
13
23
|
def find_soap_body
|
data/lib/savon/soap.rb
CHANGED
data/lib/savon/soap/fault.rb
CHANGED
data/lib/savon/version.rb
CHANGED
data/lib/savon/wsse.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require "base64"
|
2
2
|
require "digest/sha1"
|
3
|
-
require "builder"
|
4
3
|
|
5
4
|
require "savon/core_ext/string"
|
6
|
-
require "savon/
|
5
|
+
require "savon/core_ext/hash"
|
6
|
+
require "savon/core_ext/time"
|
7
7
|
|
8
8
|
module Savon
|
9
9
|
|
@@ -24,14 +24,25 @@ module Savon
|
|
24
24
|
# URI for "wsse:Password/@Type" #PasswordDigest.
|
25
25
|
PasswordDigestURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
26
26
|
|
27
|
-
#
|
27
|
+
# Returns a value from the WSSE Hash.
|
28
|
+
def [](key)
|
29
|
+
hash[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sets a value on the WSSE Hash.
|
33
|
+
def []=(key, value)
|
34
|
+
hash[key] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets authentication credentials for a wsse:UsernameToken header.
|
38
|
+
# Also accepts whether to use WSSE digest authentication.
|
28
39
|
def credentials(username, password, digest = false)
|
29
40
|
self.username = username
|
30
41
|
self.password = password
|
31
42
|
self.digest = digest
|
32
43
|
end
|
33
44
|
|
34
|
-
attr_accessor :username, :password
|
45
|
+
attr_accessor :username, :password, :created_at, :expires_at
|
35
46
|
|
36
47
|
# Returns whether to use WSSE digest. Defaults to +false+.
|
37
48
|
def digest?
|
@@ -40,26 +51,64 @@ module Savon
|
|
40
51
|
|
41
52
|
attr_writer :digest
|
42
53
|
|
43
|
-
# Returns
|
44
|
-
|
54
|
+
# Returns whether to generate a wsse:UsernameToken header.
|
55
|
+
def username_token?
|
56
|
+
username && password
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns whether to generate a wsse:Timestamp header.
|
60
|
+
def timestamp?
|
61
|
+
created_at || expires_at || @wsse_timestamp
|
62
|
+
end
|
63
|
+
|
64
|
+
# Sets whether to generate a wsse:Timestamp header.
|
65
|
+
def timestamp=(timestamp)
|
66
|
+
@wsse_timestamp = timestamp
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns the XML for a WSSE header.
|
45
70
|
def to_xml
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
xml.wsse :Nonce, nonce
|
53
|
-
xml.wsu :Created, timestamp
|
54
|
-
xml.wsse :Password, password_node, :Type => password_type
|
55
|
-
end
|
71
|
+
if username_token?
|
72
|
+
Gyoku.xml wsse_username_token.merge!(hash)
|
73
|
+
elsif timestamp?
|
74
|
+
Gyoku.xml wsse_timestamp.merge!(hash)
|
75
|
+
else
|
76
|
+
""
|
56
77
|
end
|
57
78
|
end
|
58
79
|
|
59
80
|
private
|
60
81
|
|
82
|
+
# Returns a Hash containing wsse:UsernameToken details.
|
83
|
+
def wsse_username_token
|
84
|
+
wsse_security "UsernameToken",
|
85
|
+
"wsse:Username" => username,
|
86
|
+
"wsse:Nonce" => nonce,
|
87
|
+
"wsu:Created" => timestamp,
|
88
|
+
"wsse:Password" => password_value,
|
89
|
+
:attributes! => { "wsse:Password" => { "Type" => password_type } }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns a Hash containing wsse:Timestamp details.
|
93
|
+
def wsse_timestamp
|
94
|
+
wsse_security "Timestamp",
|
95
|
+
"wsu:Created" => (created_at || Time.now).xs_datetime,
|
96
|
+
"wsu:Expires" => (expires_at || (created_at || Time.now) + 60).xs_datetime
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a Hash containing wsse:Security details for a given +tag+ and +hash+.
|
100
|
+
def wsse_security(tag, hash)
|
101
|
+
{
|
102
|
+
"wsse:Security" => {
|
103
|
+
"wsse:#{tag}" => hash,
|
104
|
+
:attributes! => { "wsse:#{tag}" => { "wsu:Id" => "#{tag}-#{count}", "xmlns:wsu" => WSUNamespace } }
|
105
|
+
},
|
106
|
+
:attributes! => { "wsse:Security" => { "xmlns:wsse" => WSENamespace } }
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
61
110
|
# Returns the WSSE password. Encrypts the password for digest authentication.
|
62
|
-
def
|
111
|
+
def password_value
|
63
112
|
return password unless digest?
|
64
113
|
|
65
114
|
token = nonce + timestamp + password
|
@@ -83,19 +132,19 @@ module Savon
|
|
83
132
|
|
84
133
|
# Returns a WSSE timestamp.
|
85
134
|
def timestamp
|
86
|
-
@timestamp ||= Time.now.
|
87
|
-
end
|
88
|
-
|
89
|
-
# Returns the "wsu:Id" attribute.
|
90
|
-
def wsu_id
|
91
|
-
"UsernameToken-#{count}"
|
135
|
+
@timestamp ||= Time.now.xs_datetime
|
92
136
|
end
|
93
137
|
|
94
|
-
#
|
138
|
+
# Returns a new number with every call.
|
95
139
|
def count
|
96
140
|
@count ||= 0
|
97
141
|
@count += 1
|
98
142
|
end
|
99
143
|
|
144
|
+
# Returns a memoized and autovivificating Hash.
|
145
|
+
def hash
|
146
|
+
@hash ||= Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
|
147
|
+
end
|
148
|
+
|
100
149
|
end
|
101
150
|
end
|
data/savon.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.add_development_dependency "rspec", "~> 2.0.0"
|
23
23
|
s.add_development_dependency "autotest"
|
24
24
|
s.add_development_dependency "mocha", "~> 0.9.7"
|
25
|
+
s.add_development_dependency "timecop", "~> 0.3.5"
|
25
26
|
|
26
27
|
s.files = `git ls-files`.split("\n")
|
27
28
|
s.require_path = "lib"
|
@@ -2,6 +2,15 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Hash do
|
4
4
|
|
5
|
+
describe "#deep_merge!" do
|
6
|
+
it "should recursively merge two Hashes" do
|
7
|
+
hash = { :one => 1, "two" => { "three" => 3 } }
|
8
|
+
other_hash = { :four => 4, "two" => { "three" => "merge", :five => 5 } }
|
9
|
+
|
10
|
+
hash.merge!(other_hash).should == { :one => 1, :four => 4, "two" => { "three" => "merge", :five => 5 } }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
describe "find_soap_body" do
|
6
15
|
it "should return the content from the 'soap:Body' element" do
|
7
16
|
soap_body = { "soap:Envelope" => { "soap:Body" => "content" } }
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Time do
|
4
|
+
|
5
|
+
describe "#xs_datetime" do
|
6
|
+
let(:time) { Time.utc(2011, 01, 04, 13, 45, 55) }
|
7
|
+
|
8
|
+
it "should return an xs:dateTime formatted String" do
|
9
|
+
time.xs_datetime.should == "2011-01-04T13:45:55UTC"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/savon/soap_spec.rb
CHANGED
@@ -13,17 +13,9 @@ describe Savon::SOAP do
|
|
13
13
|
Savon::SOAP::Versions.should == (1..2)
|
14
14
|
end
|
15
15
|
|
16
|
-
it "should contain the xs:dateTime format" do
|
17
|
-
Savon::SOAP::DateTimeFormat.should be_a(String)
|
18
|
-
Savon::SOAP::DateTimeFormat.should_not be_empty
|
19
|
-
|
20
|
-
DateTime.new(2012, 03, 22, 16, 22, 33).strftime(Savon::SOAP::DateTimeFormat).
|
21
|
-
should == "2012-03-22T16:22:33+00:00"
|
22
|
-
end
|
23
|
-
|
24
16
|
it "should contain a Regexp matching the xs:dateTime format" do
|
25
17
|
Savon::SOAP::DateTimeRegexp.should be_a(Regexp)
|
26
18
|
(Savon::SOAP::DateTimeRegexp === "2012-03-22T16:22:33").should be_true
|
27
19
|
end
|
28
|
-
|
20
|
+
|
29
21
|
end
|
data/spec/savon/wsse_spec.rb
CHANGED
@@ -158,6 +158,56 @@ describe Savon::WSSE do
|
|
158
158
|
wsse.to_xml.should include(Savon::WSSE::PasswordDigestURI)
|
159
159
|
end
|
160
160
|
end
|
161
|
+
|
162
|
+
context "with #timestamp set to true" do
|
163
|
+
before { wsse.timestamp = true }
|
164
|
+
|
165
|
+
it "should contain a wsse:Timestamp node" do
|
166
|
+
wsse.to_xml.should include('<wsse:Timestamp wsu:Id="Timestamp-1" ' +
|
167
|
+
'xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">')
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should contain a wsu:Created node defaulting to Time.now" do
|
171
|
+
created_at = Time.now
|
172
|
+
Timecop.freeze created_at do
|
173
|
+
wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should contain a wsu:Expires node defaulting to Time.now + 60 seconds" do
|
178
|
+
created_at = Time.now
|
179
|
+
Timecop.freeze created_at do
|
180
|
+
wsse.to_xml.should include("<wsu:Expires>#{(created_at + 60).xs_datetime}</wsu:Expires>")
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
context "with #created_at" do
|
186
|
+
before { wsse.created_at = Time.now + 86400 }
|
187
|
+
|
188
|
+
it "should contain a wsu:Created node with the given time" do
|
189
|
+
wsse.to_xml.should include("<wsu:Created>#{wsse.created_at.xs_datetime}</wsu:Created>")
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should contain a wsu:Expires node set to #created_at + 60 seconds" do
|
193
|
+
wsse.to_xml.should include("<wsu:Expires>#{(wsse.created_at + 60).xs_datetime}</wsu:Expires>")
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "with #expires_at" do
|
198
|
+
before { wsse.expires_at = Time.now + 86400 }
|
199
|
+
|
200
|
+
it "should contain a wsu:Created node defaulting to Time.now" do
|
201
|
+
created_at = Time.now
|
202
|
+
Timecop.freeze created_at do
|
203
|
+
wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should contain a wsu:Expires node set to the given time" do
|
208
|
+
wsse.to_xml.should include("<wsu:Expires>#{wsse.expires_at.xs_datetime}</wsu:Expires>")
|
209
|
+
end
|
210
|
+
end
|
161
211
|
end
|
162
212
|
|
163
213
|
end
|
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:
|
5
|
-
prerelease:
|
4
|
+
hash: 59
|
5
|
+
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 0.8.
|
9
|
+
- 2
|
10
|
+
version: 0.8.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Daniel Harrington
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2011-01-04 00:00:00 +01:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -128,6 +128,22 @@ dependencies:
|
|
128
128
|
version: 0.9.7
|
129
129
|
type: :development
|
130
130
|
version_requirements: *id007
|
131
|
+
- !ruby/object:Gem::Dependency
|
132
|
+
name: timecop
|
133
|
+
prerelease: false
|
134
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
135
|
+
none: false
|
136
|
+
requirements:
|
137
|
+
- - ~>
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
hash: 25
|
140
|
+
segments:
|
141
|
+
- 0
|
142
|
+
- 3
|
143
|
+
- 5
|
144
|
+
version: 0.3.5
|
145
|
+
type: :development
|
146
|
+
version_requirements: *id008
|
131
147
|
description: Savon is the heavy metal Ruby SOAP client.
|
132
148
|
email: me@rubiii.com
|
133
149
|
executables: []
|
@@ -151,6 +167,7 @@ files:
|
|
151
167
|
- lib/savon/core_ext/hash.rb
|
152
168
|
- lib/savon/core_ext/object.rb
|
153
169
|
- lib/savon/core_ext/string.rb
|
170
|
+
- lib/savon/core_ext/time.rb
|
154
171
|
- lib/savon/error.rb
|
155
172
|
- lib/savon/global.rb
|
156
173
|
- lib/savon/http/error.rb
|
@@ -180,6 +197,7 @@ files:
|
|
180
197
|
- spec/savon/core_ext/hash_spec.rb
|
181
198
|
- spec/savon/core_ext/object_spec.rb
|
182
199
|
- spec/savon/core_ext/string_spec.rb
|
200
|
+
- spec/savon/core_ext/time_spec.rb
|
183
201
|
- spec/savon/http/error_spec.rb
|
184
202
|
- spec/savon/savon_spec.rb
|
185
203
|
- spec/savon/soap/fault_spec.rb
|
@@ -224,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
242
|
requirements: []
|
225
243
|
|
226
244
|
rubyforge_project: savon
|
227
|
-
rubygems_version: 1.
|
245
|
+
rubygems_version: 1.4.1
|
228
246
|
signing_key:
|
229
247
|
specification_version: 3
|
230
248
|
summary: Heavy metal Ruby SOAP client
|