blobsterix 0.0.34 → 0.0.35

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: de4c7f26e746a8a411576f67e073d959d8ae48dd
4
- data.tar.gz: 1adb250fcf9cc7546d6e5acd3ad493c6b14e6629
3
+ metadata.gz: 549a1162916756237d9df4372dbce37432d2cbb2
4
+ data.tar.gz: 85b118a8cb57a4140e922dbae3caf6c768b17cd3
5
5
  SHA512:
6
- metadata.gz: 0e9ba6fe49bf8f777f19f2409c1bec4fa434a108ec1886a62975145e639851cf07b6d1af236418aa06700432165061818f1ec517d4d83f508af569b7b30e55c3
7
- data.tar.gz: 48909960b47c5fce517eee45b253fede5eb5583c008017c6293b718da5d68dba0112b15a3e361608a63e476f449679f7de89d57cbd0bc5331c6148b35106ed2f
6
+ metadata.gz: 00f4d1abc5b5b64469090a20b5f6c6d303ba13d361a723961f606e79723fb23eb56755d8f5ee91d548d51f69466beabeab8fd6f01f4042b6ec718f621714a773
7
+ data.tar.gz: c79acbb55211463bb151cc0ed731aa85a7673b8c4936842d839a39a28b8cce7656c3efd8e9b6f829daee35897ecf3d9008260cf964b01eb4fab7aaf988dec342
@@ -3,30 +3,102 @@ module Blobsterix
3
3
  class V4
4
4
  include ::Blobsterix::S3UrlHelper
5
5
  include ::Blobsterix::UrlHelper
6
-
7
6
 
8
7
  V4_REGEX = /AWS4-HMAC-SHA256 Credential=(\w+\/\d+\/.+\/\w+\/aws4_request),.*SignedHeaders=(.+),.*Signature=(\w+)/
9
8
 
10
9
  def self.create(env)
11
- auth_string = env["HTTP_AUTHORIZATION"]
12
- matcher = V4_REGEX.match(auth_string)
10
+ matcher = V4_REGEX.match(env["HTTP_AUTHORIZATION"])
13
11
  matcher ? V4.new(env, matcher[1], matcher[2], matcher[3]) : nil
14
12
  end
15
13
 
16
- attr_reader :env, :credential, :signed_headers, :signature
14
+ attr_reader :env, :credential, :signed_headers, :signature, :access_key, :scope_date, :scope_region, :scope_service
15
+
17
16
  def initialize(env, credential, signed_headers, signature)
18
17
  @env = env
19
18
  @credential = credential
20
19
  @signed_headers = signed_headers
21
20
  @signature = signature
21
+
22
+ @access_key, @scope_date, @scope_region, @scope_service = @credential.split("/")
23
+ end
24
+
25
+ def check(secret_key_store)
26
+ return false if is_expired?
27
+
28
+ secret_key = secret_key_store.get_key(@access_key)
29
+ gen_signature(secret_key) == @signature
30
+ end
31
+
32
+ def gen_signature(secret_key)
33
+ signing_key = hmac256("AWS4"+secret_key, @scope_date)
34
+ signing_key = hmac256(signing_key, @scope_region)
35
+ signing_key = hmac256(signing_key, @scope_service)
36
+ signing_key = hmac256(signing_key, "aws4_request")
37
+
38
+ hmac256(signing_key, string_to_sign).unpack('H*').first
39
+ end
40
+
41
+ def string_to_sign
42
+ [
43
+ "AWS4-HMAC-SHA256",
44
+ header_datetime_iso8601,
45
+ [@scope_date, @scope_region, @scope_service, "aws4_request"].join("/"),
46
+ Digest::SHA256.hexdigest(canonical_request)
47
+ ].join("\n")
48
+ end
49
+
50
+ def canonical_request
51
+ [
52
+ @env["REQUEST_METHOD"].to_s.upcase,
53
+ @env["REQUEST_PATH"],
54
+ canonical_query,
55
+ canonical_headers,
56
+ @signed_headers,
57
+ @env["HTTP_X_AMZ_CONTENT_SHA256"] || Digest::SHA256.hexdigest("")
58
+ ].join("\n")
59
+ end
60
+
61
+ def canonical_query
62
+ @env["QUERY_STRING"].to_s.split("&").sort.map do |param|
63
+ escaped = param.split("=").map { |k_or_v| escape(k_or_v) }.join("=")
64
+ escaped["="] ? escaped : "#{escaped}="
65
+ end.join("&")
66
+ end
67
+
68
+ def canonical_headers
69
+ canonicalized = @env.select { |key,value| key.is_a?(String) }.inject({}) do |memo, (key,value)|
70
+ memo[key.gsub("HTTP_","").downcase.gsub("_","-")] = value.to_s.strip
71
+ memo
72
+ end
73
+
74
+ headers = ""
75
+
76
+ @signed_headers.split(";").each do |key|
77
+ raise StandardError.new("Missing signed header") unless canonicalized.key?(key)
78
+ headers << key + ":" + canonicalized[key] + "\n"
79
+ end
80
+
81
+ headers
82
+ end
83
+
84
+ def hmac256(key, data)
85
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha256"), key, data)
86
+ end
87
+
88
+ def header_datetime
89
+ Time.parse(@env["HTTP_X_AMZ_DATE"] || @env["HTTP_DATE"] || Time.now.to_s)
90
+ end
91
+
92
+ def header_datetime_iso8601
93
+ header_datetime.utc.strftime('%Y%m%dT%H%M%SZ')
94
+ end
95
+
96
+ def is_expired?
97
+ ::Blobsterix::S3Auth.current_time > header_datetime+15*60
22
98
  end
23
- def getSignatureKey(key, dateStamp, regionName, serviceName)
24
- kDate = OpenSSL::HMAC.digest('sha256', "AWS4" + key, dateStamp)
25
- kRegion = OpenSSL::HMAC.digest('sha256', kDate, regionName)
26
- kService = OpenSSL::HMAC.digest('sha256', kRegion, serviceName)
27
- kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
28
99
 
29
- kSigning
100
+ def escape(string)
101
+ string.gsub(/([^a-zA-Z0-9_.\-~]+)/) { "%" + $1.unpack("H2" * $1.bytesize).join("%").upcase }
30
102
  end
31
103
  end
32
104
  end
@@ -1,3 +1,3 @@
1
1
  module Blobsterix
2
- VERSION = "0.0.34"
2
+ VERSION = "0.0.35"
3
3
  end
@@ -63,7 +63,7 @@ describe Blobsterix::S3Auth do
63
63
  :path=>"/photos/puppy.jpg",
64
64
  :query=>"AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&Expires=1175139620",
65
65
  :head => {
66
- "host"=>"johnsmith.s3.amazonaws.com"
66
+ "host"=>"johnsmith.s3.amazonaws.com"
67
67
  }
68
68
  }
69
69
  }
@@ -97,12 +97,12 @@ describe Blobsterix::S3Auth do
97
97
  "HTTP_PROXY_CONNECTION"=>"Keep-Alive",
98
98
  "HTTP_DATE"=>"Fri, 24 May 2013 00:00:00 GMT",
99
99
  "HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,SignedHeaders=host;range;x-amz-content-sha256;x-amz-date,Signature=f0e8bdb87c964420e857bd35b5d6ed310bd44f0170aba48dd91039c6036bdb41",
100
- "HTTP_HOST"=>"examplebucket.s3.amazonaws.com:80",
100
+ "HTTP_HOST"=>"examplebucket.s3.amazonaws.com",
101
101
  "HTTP_TE"=>"trailers, deflate, gzip",
102
102
  "HTTP_CONNECTION"=>"TE, close",
103
103
  "CONTENT_LENGTH"=>"0",
104
104
  "REQUEST_METHOD"=>"GET",
105
- "REQUEST_URI"=>"http://examplebucket.s3.amazonaws.com:80/test.txt",
105
+ "REQUEST_URI"=>"http://examplebucket.s3.amazonaws.com/test.txt",
106
106
  "QUERY_STRING"=>nil,
107
107
  "HTTP_VERSION"=>"1.1",
108
108
  "SCRIPT_NAME"=>"",
@@ -117,6 +117,42 @@ describe Blobsterix::S3Auth do
117
117
  }
118
118
  }
119
119
 
120
+ let(:v4_req_env_with_query_param) {
121
+ {
122
+ "HTTP_HOST"=>"firefox.s3.amazonaws.com",
123
+ "HTTP_ACCEPT_ENCODING"=>"identity",
124
+ "HTTP_X_AMZ_CONTENT_SHA256"=>"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
125
+ "HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20160217/US/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=10681a4fd1254dce4d80d7c254ae521dd8031b5ef871d77b7ffb28b260ee8372",
126
+ "HTTP_X_AMZ_DATE"=>"20160217T134245Z",
127
+ "CONTENT_LENGTH"=>"0",
128
+ "REQUEST_METHOD"=>"GET",
129
+ "REQUEST_URI"=>"http://firefox.s3.amazonaws.com/?delimiter=/",
130
+ "QUERY_STRING"=>"delimiter=/",
131
+ "HTTP_VERSION"=>"1.1",
132
+ "SCRIPT_NAME"=>"",
133
+ "REQUEST_PATH"=>"/",
134
+ "PATH_INFO"=>"/"
135
+ }
136
+ }
137
+
138
+ let(:v4_req_env_with_query_param_2) {
139
+ {
140
+ "HTTP_HOST"=>"firefox.s3.amazonaws.com",
141
+ "HTTP_ACCEPT_ENCODING"=>"identity",
142
+ "HTTP_X_AMZ_CONTENT_SHA256"=>"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
143
+ "HTTP_AUTHORIZATION"=>"AWS4-HMAC-SHA256 Credential=AKIAIOSFODNN7EXAMPLE/20160217/US/s3/aws4_request,SignedHeaders=host;x-amz-content-sha256;x-amz-date,Signature=d8fe8bcca8ada96996626125bec12258ed4ba0462f7c277d7412a8b9c230430d",
144
+ "HTTP_X_AMZ_DATE"=>"20160217T134245Z",
145
+ "CONTENT_LENGTH"=>"0",
146
+ "REQUEST_METHOD"=>"GET",
147
+ "REQUEST_URI"=>"http://firefox.s3.amazonaws.com/?location",
148
+ "QUERY_STRING"=>"location",
149
+ "HTTP_VERSION"=>"1.1",
150
+ "SCRIPT_NAME"=>"",
151
+ "REQUEST_PATH"=>"/",
152
+ "PATH_INFO"=>"/"
153
+ }
154
+ }
155
+
120
156
  before :all do
121
157
  Blobsterix.secret_key_store = Blobsterix::S3Auth::KeyStore.new(
122
158
  "AKIAIOSFODNN7EXAMPLE" => "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
@@ -174,8 +210,28 @@ describe Blobsterix::S3Auth do
174
210
  auth.class.should eql Blobsterix::S3Auth::V4
175
211
  end
176
212
 
213
+ it "should authenticate v4 request if the signature match and request time is within 15minutes window" do
214
+ Blobsterix::S3Auth.current_time=lambda{Time.parse("Fri, 24 May 2013 00:14:59 GMT")}
215
+ Blobsterix::S3Auth.authenticate(v4_req_env).check(Blobsterix.secret_key_store).should be_true
216
+ end
217
+
218
+ it "should not authenticate v4 request if the signature match but request time is older than 15 minutes" do
219
+ Blobsterix::S3Auth.current_time=lambda{Time.parse("Fri, 24 May 2013 00:15:01 GMT")}
220
+ Blobsterix::S3Auth.authenticate(v4_req_env).check(Blobsterix.secret_key_store).should be_false
221
+ end
222
+
223
+ it "should authenticate v4 request with query params" do
224
+ Blobsterix::S3Auth.current_time=lambda{Time.parse("20160217T135245Z")}
225
+ Blobsterix::S3Auth.authenticate(v4_req_env_with_query_param).check(Blobsterix.secret_key_store).should be_true
226
+ end
227
+
228
+ it "should authenticate v4 request with query param without equal character" do
229
+ Blobsterix::S3Auth.current_time=lambda{Time.parse("20160217T135245Z")}
230
+ Blobsterix::S3Auth.authenticate(v4_req_env_with_query_param_2).check(Blobsterix.secret_key_store).should be_true
231
+ end
232
+
177
233
  it "should at least recognize aws v2" do
178
234
  auth = Blobsterix::S3Auth.authenticate(v2_req_env)
179
235
  auth.class.should eql Blobsterix::S3Auth::V2
180
236
  end
181
- end
237
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blobsterix
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.34
4
+ version: 0.0.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Sudmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-18 00:00:00.000000000 Z
11
+ date: 2016-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -317,7 +317,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
317
317
  version: '0'
318
318
  requirements: []
319
319
  rubyforge_project:
320
- rubygems_version: 2.4.5.1
320
+ rubygems_version: 2.5.1
321
321
  signing_key:
322
322
  specification_version: 4
323
323
  summary: Blobsterix is a transcoding, caching, storage server.