akami 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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