akami 1.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.
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +58 -0
- data/Rakefile +11 -0
- data/akami.gemspec +28 -0
- data/lib/akami.rb +11 -0
- data/lib/akami/core_ext/time.rb +22 -0
- data/lib/akami/version.rb +5 -0
- data/lib/akami/wsse.rb +155 -0
- data/spec/akami/core_ext/time_spec.rb +12 -0
- data/spec/akami/wsse_spec.rb +233 -0
- data/spec/spec_helper.rb +2 -0
- metadata +176 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Daniel Harrington
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
Akami [](http://travis-ci.org/rubiii/akami)
|
2
|
+
=====
|
3
|
+
|
4
|
+
Building Web Service Security.
|
5
|
+
|
6
|
+
|
7
|
+
Installation
|
8
|
+
------------
|
9
|
+
|
10
|
+
Akami is available through [Rubygems](http://rubygems.org/gems/akami) and can be installed via:
|
11
|
+
|
12
|
+
```
|
13
|
+
$ gem install akami
|
14
|
+
```
|
15
|
+
|
16
|
+
|
17
|
+
Getting started
|
18
|
+
---------------
|
19
|
+
|
20
|
+
``` ruby
|
21
|
+
wsse = Akami.wsse
|
22
|
+
```
|
23
|
+
|
24
|
+
Set the credentials for wsse:UsernameToken basic auth:
|
25
|
+
|
26
|
+
``` ruby
|
27
|
+
wsse.credentials "username", "password"
|
28
|
+
```
|
29
|
+
|
30
|
+
Set the credentials for wsse:UsernameToken digest auth:
|
31
|
+
|
32
|
+
``` ruby
|
33
|
+
wsse.credentials "username", "password", :digest
|
34
|
+
```
|
35
|
+
|
36
|
+
Enable wsu:Timestamp headers. `wsu:Created` is automatically set to `Time.now`
|
37
|
+
and `wsu:Expires` is set to `Time.now + 60`:
|
38
|
+
|
39
|
+
``` ruby
|
40
|
+
wsse.timestamp = true
|
41
|
+
```
|
42
|
+
|
43
|
+
Manually specify the values for `wsu:Created` and `wsu:Expires`:
|
44
|
+
|
45
|
+
``` ruby
|
46
|
+
wsse.created_at = Time.now
|
47
|
+
wsse.expires_at = Time.now + 60
|
48
|
+
``
|
49
|
+
|
50
|
+
Akami is based on an autovivificating Hash. So if you need to add custom tags, you can add them.
|
51
|
+
|
52
|
+
``` ruby
|
53
|
+
wsse["wsse:Security"]["wsse:UsernameToken"] = { "Organization" => "ACME" }
|
54
|
+
```
|
55
|
+
|
56
|
+
When generating the XML for the request, this Hash will be merged with another Hash containing
|
57
|
+
all the default tags and values. This way you might digg into some code, but then you can even
|
58
|
+
overwrite the default values.
|
data/Rakefile
ADDED
data/akami.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "akami/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "akami"
|
7
|
+
s.version = Akami::VERSION
|
8
|
+
s.authors = ["Daniel Harrington"]
|
9
|
+
s.email = ["me@rubiii.com"]
|
10
|
+
s.homepage = "https://github.com/rubiii/#{s.name}"
|
11
|
+
s.summary = "Web Service Security"
|
12
|
+
s.description = "Building Web Service Security"
|
13
|
+
|
14
|
+
s.rubyforge_project = s.name
|
15
|
+
|
16
|
+
s.add_dependency "gyoku", ">= 0.4.0"
|
17
|
+
|
18
|
+
s.add_development_dependency "rake", "~> 0.8.7"
|
19
|
+
s.add_development_dependency "rspec", "~> 2.5.0"
|
20
|
+
s.add_development_dependency "mocha", "~> 0.9.8"
|
21
|
+
s.add_development_dependency "timecop", "~> 0.3.5"
|
22
|
+
s.add_development_dependency "autotest"
|
23
|
+
|
24
|
+
s.files = `git ls-files`.split("\n")
|
25
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
26
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
end
|
data/lib/akami.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Akami
|
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, Akami::CoreExt::Time
|
data/lib/akami/wsse.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
require "base64"
|
2
|
+
require "digest/sha1"
|
3
|
+
require "akami/core_ext/time"
|
4
|
+
require "gyoku"
|
5
|
+
|
6
|
+
module Akami
|
7
|
+
|
8
|
+
# = Akami::WSSE
|
9
|
+
#
|
10
|
+
# Building Web Service Security.
|
11
|
+
class WSSE
|
12
|
+
|
13
|
+
# Namespace for WS Security Secext.
|
14
|
+
WSE_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
15
|
+
|
16
|
+
# Namespace for WS Security Utility.
|
17
|
+
WSU_NAMESPACE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
18
|
+
|
19
|
+
# PasswordText URI.
|
20
|
+
PASSWORD_TEXT_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
21
|
+
|
22
|
+
# PasswordDigest URI.
|
23
|
+
PASSWORD_DIGEST_URI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
24
|
+
|
25
|
+
# Returns a value from the WSSE Hash.
|
26
|
+
def [](key)
|
27
|
+
hash[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets a value on the WSSE Hash.
|
31
|
+
def []=(key, value)
|
32
|
+
hash[key] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets authentication credentials for a wsse:UsernameToken header.
|
36
|
+
# Also accepts whether to use WSSE digest authentication.
|
37
|
+
def credentials(username, password, digest = false)
|
38
|
+
self.username = username
|
39
|
+
self.password = password
|
40
|
+
self.digest = digest
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_accessor :username, :password, :created_at, :expires_at
|
44
|
+
|
45
|
+
# Returns whether to use WSSE digest. Defaults to +false+.
|
46
|
+
def digest?
|
47
|
+
!!@digest
|
48
|
+
end
|
49
|
+
|
50
|
+
attr_writer :digest
|
51
|
+
|
52
|
+
# Returns whether to generate a wsse:UsernameToken header.
|
53
|
+
def username_token?
|
54
|
+
username && password
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns whether to generate a wsu:Timestamp header.
|
58
|
+
def timestamp?
|
59
|
+
created_at || expires_at || @wsu_timestamp
|
60
|
+
end
|
61
|
+
|
62
|
+
# Sets whether to generate a wsu:Timestamp header.
|
63
|
+
def timestamp=(timestamp)
|
64
|
+
@wsu_timestamp = timestamp
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the XML for a WSSE header.
|
68
|
+
def to_xml
|
69
|
+
if username_token? && timestamp?
|
70
|
+
Gyoku.xml wsse_username_token.merge!(wsu_timestamp) {
|
71
|
+
|key, v1, v2| v1.merge!(v2) {
|
72
|
+
|key, v1, v2| v1.merge!(v2)
|
73
|
+
}
|
74
|
+
}
|
75
|
+
elsif username_token?
|
76
|
+
Gyoku.xml wsse_username_token.merge!(hash)
|
77
|
+
elsif timestamp?
|
78
|
+
Gyoku.xml wsu_timestamp.merge!(hash)
|
79
|
+
else
|
80
|
+
""
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Returns a Hash containing wsse:UsernameToken details.
|
87
|
+
def wsse_username_token
|
88
|
+
if digest?
|
89
|
+
security_hash :wsse, "UsernameToken",
|
90
|
+
"wsse:Username" => username,
|
91
|
+
"wsse:Nonce" => nonce,
|
92
|
+
"wsu:Created" => timestamp,
|
93
|
+
"wsse:Password" => digest_password,
|
94
|
+
:attributes! => { "wsse:Password" => { "Type" => PASSWORD_DIGEST_URI } }
|
95
|
+
else
|
96
|
+
security_hash :wsse, "UsernameToken",
|
97
|
+
"wsse:Username" => username,
|
98
|
+
"wsse:Password" => password,
|
99
|
+
:attributes! => { "wsse:Password" => { "Type" => PASSWORD_TEXT_URI } }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns a Hash containing wsu:Timestamp details.
|
104
|
+
def wsu_timestamp
|
105
|
+
security_hash :wsu, "Timestamp",
|
106
|
+
"wsu:Created" => (created_at || Time.now).xs_datetime,
|
107
|
+
"wsu:Expires" => (expires_at || (created_at || Time.now) + 60).xs_datetime
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns a Hash containing wsse/wsu Security details for a given
|
111
|
+
# +namespace+, +tag+ and +hash+.
|
112
|
+
def security_hash(namespace, tag, hash)
|
113
|
+
{
|
114
|
+
"wsse:Security" => {
|
115
|
+
"#{namespace}:#{tag}" => hash,
|
116
|
+
:attributes! => { "#{namespace}:#{tag}" => { "wsu:Id" => "#{tag}-#{count}", "xmlns:wsu" => WSU_NAMESPACE } }
|
117
|
+
},
|
118
|
+
:attributes! => { "wsse:Security" => { "xmlns:wsse" => WSE_NAMESPACE } }
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns the WSSE password, encrypted for digest authentication.
|
123
|
+
def digest_password
|
124
|
+
token = nonce + timestamp + password
|
125
|
+
Base64.encode64(Digest::SHA1.hexdigest(token)).chomp!
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns a WSSE nonce.
|
129
|
+
def nonce
|
130
|
+
@nonce ||= Digest::SHA1.hexdigest random_string + timestamp
|
131
|
+
end
|
132
|
+
|
133
|
+
# Returns a random String of 100 characters.
|
134
|
+
def random_string
|
135
|
+
(0...100).map { ("a".."z").to_a[rand(26)] }.join
|
136
|
+
end
|
137
|
+
|
138
|
+
# Returns a WSSE timestamp.
|
139
|
+
def timestamp
|
140
|
+
@timestamp ||= Time.now.xs_datetime
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns a new number with every call.
|
144
|
+
def count
|
145
|
+
@count ||= 0
|
146
|
+
@count += 1
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns a memoized and autovivificating Hash.
|
150
|
+
def hash
|
151
|
+
@hash ||= Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Akami do
|
4
|
+
let(:wsse) { Akami.wsse }
|
5
|
+
|
6
|
+
it "contains the namespace for WS Security Secext" do
|
7
|
+
Akami::WSSE::WSE_NAMESPACE.should ==
|
8
|
+
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
9
|
+
end
|
10
|
+
|
11
|
+
it "contains the namespace for WS Security Utility" do
|
12
|
+
Akami::WSSE::WSU_NAMESPACE.should ==
|
13
|
+
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "contains the namespace for the PasswordText type" do
|
17
|
+
Akami::WSSE::PASSWORD_TEXT_URI.should ==
|
18
|
+
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"
|
19
|
+
end
|
20
|
+
|
21
|
+
it "contains the namespace for the PasswordDigest type" do
|
22
|
+
Akami::WSSE::PASSWORD_DIGEST_URI.should ==
|
23
|
+
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest"
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#credentials" do
|
27
|
+
it "sets the username" do
|
28
|
+
wsse.credentials "username", "password"
|
29
|
+
wsse.username.should == "username"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "sets the password" do
|
33
|
+
wsse.credentials "username", "password"
|
34
|
+
wsse.password.should == "password"
|
35
|
+
end
|
36
|
+
|
37
|
+
it "defaults to set digest to false" do
|
38
|
+
wsse.credentials "username", "password"
|
39
|
+
wsse.should_not be_digest
|
40
|
+
end
|
41
|
+
|
42
|
+
it "sets digest to true if specified" do
|
43
|
+
wsse.credentials "username", "password", :digest
|
44
|
+
wsse.should be_digest
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#username" do
|
49
|
+
it "sets the username" do
|
50
|
+
wsse.username = "username"
|
51
|
+
wsse.username.should == "username"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#password" do
|
56
|
+
it "sets the password" do
|
57
|
+
wsse.password = "password"
|
58
|
+
wsse.password.should == "password"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#digest" do
|
63
|
+
it "defaults to false" do
|
64
|
+
wsse.should_not be_digest
|
65
|
+
end
|
66
|
+
|
67
|
+
it "specifies whether to use digest auth" do
|
68
|
+
wsse.digest = true
|
69
|
+
wsse.should be_digest
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#to_xml" do
|
74
|
+
context "with no credentials" do
|
75
|
+
it "returns an empty String" do
|
76
|
+
wsse.to_xml.should == ""
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with only a username" do
|
81
|
+
before { wsse.username = "username" }
|
82
|
+
|
83
|
+
it "returns an empty String" do
|
84
|
+
wsse.to_xml.should == ""
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context "with only a password" do
|
89
|
+
before { wsse.password = "password" }
|
90
|
+
|
91
|
+
it "returns an empty String" do
|
92
|
+
wsse.to_xml.should == ""
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "with credentials" do
|
97
|
+
before { wsse.credentials "username", "password" }
|
98
|
+
|
99
|
+
it "contains a wsse:Security tag" do
|
100
|
+
namespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
|
101
|
+
wsse.to_xml.should include("<wsse:Security xmlns:wsse=\"#{namespace}\">")
|
102
|
+
end
|
103
|
+
|
104
|
+
it "contains a wsu:Id attribute" do
|
105
|
+
wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-1"')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "increments the wsu:Id attribute count" do
|
109
|
+
wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-1"')
|
110
|
+
wsse.to_xml.should include('<wsse:UsernameToken wsu:Id="UsernameToken-2"')
|
111
|
+
end
|
112
|
+
|
113
|
+
it "contains the WSE and WSU namespaces" do
|
114
|
+
wsse.to_xml.should include(Akami::WSSE::WSE_NAMESPACE, Akami::WSSE::WSU_NAMESPACE)
|
115
|
+
end
|
116
|
+
|
117
|
+
it "contains the username and password" do
|
118
|
+
wsse.to_xml.should include("username", "password")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "does not contain a wsse:Nonce tag" do
|
122
|
+
wsse.to_xml.should_not match(/<wsse:Nonce>.*<\/wsse:Nonce>/)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "does not contain a wsu:Created tag" do
|
126
|
+
wsse.to_xml.should_not match(/<wsu:Created>.*<\/wsu:Created>/)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "contains the PasswordText type attribute" do
|
130
|
+
wsse.to_xml.should include(Akami::WSSE::PASSWORD_TEXT_URI)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "with credentials and digest auth" do
|
135
|
+
before { wsse.credentials "username", "password", :digest }
|
136
|
+
|
137
|
+
it "contains the WSE and WSU namespaces" do
|
138
|
+
wsse.to_xml.should include(Akami::WSSE::WSE_NAMESPACE, Akami::WSSE::WSU_NAMESPACE)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "contains the username" do
|
142
|
+
wsse.to_xml.should include("username")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "does not contain the (original) password" do
|
146
|
+
wsse.to_xml.should_not include("password")
|
147
|
+
end
|
148
|
+
|
149
|
+
it "contains a wsse:Nonce tag" do
|
150
|
+
wsse.to_xml.should match(/<wsse:Nonce>\w+<\/wsse:Nonce>/)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "contains a wsu:Created tag" do
|
154
|
+
datetime_regexp = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
|
155
|
+
wsse.to_xml.should match(/<wsu:Created>#{datetime_regexp}.+<\/wsu:Created>/)
|
156
|
+
end
|
157
|
+
|
158
|
+
it "contains the PasswordDigest type attribute" do
|
159
|
+
wsse.to_xml.should include(Akami::WSSE::PASSWORD_DIGEST_URI)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "with #timestamp set to true" do
|
164
|
+
before { wsse.timestamp = true }
|
165
|
+
|
166
|
+
it "contains a wsse:Timestamp node" do
|
167
|
+
wsse.to_xml.should include('<wsu:Timestamp wsu:Id="Timestamp-1" ' +
|
168
|
+
'xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">')
|
169
|
+
end
|
170
|
+
|
171
|
+
it "contains a wsu:Created node defaulting to Time.now" do
|
172
|
+
created_at = Time.now
|
173
|
+
Timecop.freeze created_at do
|
174
|
+
wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it "contains a wsu:Expires node defaulting to Time.now + 60 seconds" do
|
179
|
+
created_at = Time.now
|
180
|
+
Timecop.freeze created_at do
|
181
|
+
wsse.to_xml.should include("<wsu:Expires>#{(created_at + 60).xs_datetime}</wsu:Expires>")
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context "with #created_at" do
|
187
|
+
before { wsse.created_at = Time.now + 86400 }
|
188
|
+
|
189
|
+
it "contains a wsu:Created node with the given time" do
|
190
|
+
wsse.to_xml.should include("<wsu:Created>#{wsse.created_at.xs_datetime}</wsu:Created>")
|
191
|
+
end
|
192
|
+
|
193
|
+
it "contains a wsu:Expires node set to #created_at + 60 seconds" do
|
194
|
+
wsse.to_xml.should include("<wsu:Expires>#{(wsse.created_at + 60).xs_datetime}</wsu:Expires>")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "with #expires_at" do
|
199
|
+
before { wsse.expires_at = Time.now + 86400 }
|
200
|
+
|
201
|
+
it "contains a wsu:Created node defaulting to Time.now" do
|
202
|
+
created_at = Time.now
|
203
|
+
Timecop.freeze created_at do
|
204
|
+
wsse.to_xml.should include("<wsu:Created>#{created_at.xs_datetime}</wsu:Created>")
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it "contains a wsu:Expires node set to the given time" do
|
209
|
+
wsse.to_xml.should include("<wsu:Expires>#{wsse.expires_at.xs_datetime}</wsu:Expires>")
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "whith credentials and timestamp" do
|
214
|
+
before do
|
215
|
+
wsse.credentials "username", "password"
|
216
|
+
wsse.timestamp = true
|
217
|
+
end
|
218
|
+
|
219
|
+
it "contains a wsu:Created node" do
|
220
|
+
wsse.to_xml.should include("<wsu:Created>")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "contains a wsu:Expires node" do
|
224
|
+
wsse.to_xml.should include("<wsu:Expires>")
|
225
|
+
end
|
226
|
+
|
227
|
+
it "contains the username and password" do
|
228
|
+
wsse.to_xml.should include("username", "password")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: akami
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Daniel Harrington
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-03 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: gyoku
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 15
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
- 4
|
32
|
+
- 0
|
33
|
+
version: 0.4.0
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 49
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
- 8
|
48
|
+
- 7
|
49
|
+
version: 0.8.7
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 27
|
61
|
+
segments:
|
62
|
+
- 2
|
63
|
+
- 5
|
64
|
+
- 0
|
65
|
+
version: 2.5.0
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: mocha
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 43
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
- 9
|
80
|
+
- 8
|
81
|
+
version: 0.9.8
|
82
|
+
type: :development
|
83
|
+
version_requirements: *id004
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: timecop
|
86
|
+
prerelease: false
|
87
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
88
|
+
none: false
|
89
|
+
requirements:
|
90
|
+
- - ~>
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
hash: 25
|
93
|
+
segments:
|
94
|
+
- 0
|
95
|
+
- 3
|
96
|
+
- 5
|
97
|
+
version: 0.3.5
|
98
|
+
type: :development
|
99
|
+
version_requirements: *id005
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: autotest
|
102
|
+
prerelease: false
|
103
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
type: :development
|
113
|
+
version_requirements: *id006
|
114
|
+
description: Building Web Service Security
|
115
|
+
email:
|
116
|
+
- me@rubiii.com
|
117
|
+
executables: []
|
118
|
+
|
119
|
+
extensions: []
|
120
|
+
|
121
|
+
extra_rdoc_files: []
|
122
|
+
|
123
|
+
files:
|
124
|
+
- .gitignore
|
125
|
+
- .rspec
|
126
|
+
- .travis.yml
|
127
|
+
- CHANGELOG.md
|
128
|
+
- Gemfile
|
129
|
+
- LICENSE
|
130
|
+
- README.md
|
131
|
+
- Rakefile
|
132
|
+
- akami.gemspec
|
133
|
+
- lib/akami.rb
|
134
|
+
- lib/akami/core_ext/time.rb
|
135
|
+
- lib/akami/version.rb
|
136
|
+
- lib/akami/wsse.rb
|
137
|
+
- spec/akami/core_ext/time_spec.rb
|
138
|
+
- spec/akami/wsse_spec.rb
|
139
|
+
- spec/spec_helper.rb
|
140
|
+
homepage: https://github.com/rubiii/akami
|
141
|
+
licenses: []
|
142
|
+
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
|
146
|
+
require_paths:
|
147
|
+
- lib
|
148
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
149
|
+
none: false
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
hash: 3
|
154
|
+
segments:
|
155
|
+
- 0
|
156
|
+
version: "0"
|
157
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
158
|
+
none: false
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
hash: 3
|
163
|
+
segments:
|
164
|
+
- 0
|
165
|
+
version: "0"
|
166
|
+
requirements: []
|
167
|
+
|
168
|
+
rubyforge_project: akami
|
169
|
+
rubygems_version: 1.8.5
|
170
|
+
signing_key:
|
171
|
+
specification_version: 3
|
172
|
+
summary: Web Service Security
|
173
|
+
test_files:
|
174
|
+
- spec/akami/core_ext/time_spec.rb
|
175
|
+
- spec/akami/wsse_spec.rb
|
176
|
+
- spec/spec_helper.rb
|