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.
@@ -0,0 +1,8 @@
1
+ .DS_Store
2
+ doc
3
+ coverage
4
+ pkg
5
+ *~
6
+ *.gem
7
+ .bundle
8
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,8 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - ruby-head
5
+ - ree
6
+ - rbx
7
+ - rbx-2.0
8
+ - jruby
@@ -0,0 +1,3 @@
1
+ ## 1.0.0 (2011-07-03)
2
+
3
+ * Initial version extracted from the [Savon](http://rubygems.org/gems/savon) library.
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
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.
@@ -0,0 +1,58 @@
1
+ Akami [![Build Status](http://travis-ci.org/rubiii/akami.png)](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.
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new do |t|
7
+ t.rspec_opts = %w(-c)
8
+ end
9
+
10
+ task :default => :spec
11
+ task :test => :spec
@@ -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
@@ -0,0 +1,11 @@
1
+ require "akami/version"
2
+ require "akami/wsse"
3
+
4
+ module Akami
5
+
6
+ # Returns a new <tt>Akami::WSSE</tt>.
7
+ def self.wsse
8
+ WSSE.new
9
+ end
10
+
11
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ module Akami
2
+
3
+ VERSION = "1.0.0"
4
+
5
+ end
@@ -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,12 @@
1
+ require "spec_helper"
2
+
3
+ describe Time do
4
+
5
+ describe "#xs_datetime" do
6
+ it "returns an xs:dateTime formatted String" do
7
+ time = Time.utc(2011, 01, 04, 13, 45, 55)
8
+ time.xs_datetime.should == "2011-01-04T13:45:55Z"
9
+ end
10
+ end
11
+
12
+ 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
@@ -0,0 +1,2 @@
1
+ require "bundler"
2
+ Bundler.require :default, :development
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